import re
from urllib.parse import parse_qsl, unquote

from oio.common.amqp import (
    AMQPError,
    ExchangeType,
    AmqpConnector,
    DEFAULT_EXCHANGE,
    DEFAULT_QUEUE_ARGS,
)
from oio.common.json import json
from oio.event.evob import Event, EventError, RetryableEventError
from oio.event.beanstalk import Beanstalk, BeanstalkError
from oio.event.filters.base import Filter

POLICY_REGEX_PREFIX = "policy_regex_"
TUBE_PREFIX = "tube_"

class NotifyFilter(Filter):
    """
    Forward events to another tube.

    Object events can be matched by their storage policy.
    """

    def __init__(self, *args, **kwargs):
        self.queue_url = None
        self.strip_fields = ()
        # Called "tube" for compatibility, this is the name of the main queue
        self.tube = None
        super().__init__(*args, **kwargs)

    def init(self):
        self.queue_url = self.conf.get("queue_url")
        self.exclude = self._parse_exclude(self.conf.get("exclude", []))
        if not self.queue_url:
            raise ValueError("Missing 'queue_url' in the configuration")

        self.strip_fields = tuple(self.conf.get("strip_fields", "").split(","))
        self.tube = self.conf.get("tube", self.conf.get("queue_name", "notif"))
        self.required_fields = [
            f for f in self.conf.get("required_fields", "").split(",") if f
        ]
        self.tube_rules = {}
        self._load_policy_regex()

    @staticmethod
    def _parse_exclude(array):
        """
        array is in this format ["urlencoded(account)",
                                 "urlencoded(account2)/urlencoded(container2)"]
        and we want to return this {account: [], account2: [container2]}
        empty list means that everything is accepted
        """
        if isinstance(array, str):
            array = array.split(",")
        exclude = {}
        for elt in array:
            if "/" in elt:
                acct, cnt = elt.split("/", 1)
                acct = unquote(acct)
                cnt = unquote(cnt)
                if exclude.get(acct, None):
                    exclude[acct].append(cnt)
                else:
                    exclude[acct] = [cnt]
            else:
                exclude[unquote(elt)] = []
        return exclude

    def _should_notify(self, account, container):
        if self.exclude is None:
            return True
        containers = self.exclude.get(account, None)
        if containers == []:
            return False
        elif containers is None:
            return True
        elif container in containers:
            return False
        return True

    def should_notify(self, event):
        # Verify all required fields
        if self.required_fields:
            for field in self.required_fields:
                if field not in event.env:
                    return False

        # Some events do not have a URL (e.g. chunk events),
        # we cannot filter them easily, so we let them pass.
        return not event.url or self._should_notify(
            event.url.get("account"), event.url.get("user")
        )

    def _load_policy_regex(self):
        """
        Load storage policy patterns and their associated tube.
        If there is no specific tube, the default will be used.
        """
        for key, val in self.conf.items():
            if key.startswith(TUBE_PREFIX):
                rule = key[len(TUBE_PREFIX) :]
                self.tube_rules.setdefault(rule, {})["tube"] = val
            elif key.startswith(POLICY_REGEX_PREFIX):
                rule = key[len(POLICY_REGEX_PREFIX) :]
                regex = re.compile(val)
                self.tube_rules.setdefault(rule, {})["regex"] = regex
        # Ensure each rule has a tube
        for rule in self.tube_rules.values():
            rule.setdefault("tube", self.tube)

    @staticmethod
    def _lookup_policy(event):
        """
        Look for a storage policy inside an event.
        Will work only for object events with a "contents_headers" part.
        None will be returned for other events.
        """
        if not isinstance(event.data, list):
            return None
        policy = None
        for bean in event.data:
            if not isinstance(bean, dict):
                continue
            if bean.get("type") == "contents_headers":
                policy = bean.get("policy")
            if policy:
                break
        return policy

    def send_event(self, event, data):
        """Send data to the configured message queue

        Must be implemented by subclasses."""
        raise NotImplementedError

    def strip_event(self, data):
        """Remove unneeded fields from event."""
        if not self.strip_fields:
            return data
        if isinstance(data, list):
            return [item for item in data if item.get("type") not in self.strip_fields]
        if isinstance(data, dict):
            return {k: v for k, v in data.items() if k not in self.strip_fields}
        return data

    def process(self, env, cb):
        event = Event(env)
        if self.should_notify(event):
            # Encode without whitespace to make sure not
            # to exceed the maximum size of the event (default: 65535)
            payload = env.copy()
            payload.pop("queue_connector", None)
            payload["data"] = self.strip_event(payload["data"])
            data = json.dumps(payload, separators=(",", ":"))  # compact encoding
            # If there is an error, do not continue
            err_resp = self.send_event(event, data)
            if err_resp:
                return err_resp(env, cb)
        return self.app(env, cb)

class BeanstalkdNotifyFilter(NotifyFilter):
    """
    Forward events to a beanstalkd tube.

    Object events can be matched by their storage policy.
    """

    def __init__(self, *args, **kwargs):
        self.beanstalk = None
        super().__init__(*args, **kwargs)

    def init(self):
        super().init()
        for rule in self.tube_rules.values():
            rule["beanstalkd"] = Beanstalk.from_url(self.queue_url)
            rule["beanstalkd"].use(rule["tube"])
            self.logger.debug(
                f"Events with policy matching {rule['regex'].pattern!r} "
                f"will go to tube {rule['tube']}"
            )

        # Keep an instance talking to the default tube
        self.beanstalk = Beanstalk.from_url(self.queue_url)
        self.beanstalk.use(self.tube)

    def send_event(self, event, data):
        out_beanstalkd = self.beanstalk
        if self.tube_rules:
            policy = self._lookup_policy(event)
            if policy:
                for name in sorted(self.tube_rules.keys()):
                    if self.tube_rules[name]["regex"].match(policy):
                        out_beanstalkd = self.tube_rules[name]["beanstalkd"]
                        break
        try:
            out_beanstalkd.put(data)
        except BeanstalkError as err:
            msg = f"notify failure: {err!r}"
            if err.retryable():
                resp = RetryableEventError(event=event, body=msg)
            else:
                resp = EventError(event=event, body=msg)
            return resp
        return None

class AmqpNotifyFilter(AmqpConnector, NotifyFilter):
    """
    Forward events to a RabbitMQ queue.

    Object events can be matched by their storage policy.
    """

    def __init__(self, *args, endpoints=None, **kwargs):
        # Loaded in init()
        self.bind_args = {}
        self.exchange_name = None
        self.queue_args = {}

        super().__init__(*args, endpoints=endpoints, **kwargs)

        # We do not log at "info" level in amqp_connect() anymore (it was too verbose)
        self.logger.info(
            "%s will connect to %r", self.__class__.__name__, self._conn_params
        )

        # Connect to the broker, then declare and bind all queues we may use.
        for rule in self.tube_rules.values():
            self._declare_and_bind(rule["tube"], rule["tube"])
            self.logger.debug(
                f"Events with policy matching {rule['regex'].pattern!r} "
                f"will go to queue {rule['tube']}"
            )
        # Do not forget to declare the default queue where
        # unmatched events will be forwarded.
        self._declare_and_bind(self.tube, self.tube)

    def _declare_and_bind(self, queue, routing_key, attempts=3):
        for i in range(attempts):
            try:
                self.maybe_reconnect()
                # XXX(FVE): maybe we need a "direct" exchange instead
                self._channel.exchange_declare(
                    exchange=self.exchange_name,
                    exchange_type=ExchangeType.topic,
                    durable=True,
                )
                self._channel.queue_declare(
                    queue,
                    durable=True,
                    arguments=self.queue_args,
                )
                self._channel.queue_bind(
                    exchange=self.exchange_name,
                    queue=queue,
                    routing_key=routing_key,
                    arguments=self.bind_args,
                )
                break
            except AMQPError:
                if i >= attempts - 1:
                    raise

    def init(self):
        super().init()
        self.exchange_name = self.conf.get("exchange_name") or DEFAULT_EXCHANGE
        self.bind_args = dict(parse_qsl(self.conf.get("bind_args", ""), separator=","))
        self.queue_args = dict(
            parse_qsl(self.conf.get("queue_args", DEFAULT_QUEUE_ARGS), separator=",")
        )

    def send_event(self, event, data):
        # In case nothing matches, send to the main queue
        routing_key = self.tube
        if self.tube_rules:
            policy = self._lookup_policy(event)
            if policy:
                for name in sorted(self.tube_rules.keys()):
                    rule = self.tube_rules[name]
                    if rule["regex"].match(policy):
                        routing_key = rule["tube"]
                        break
        try:
            self.maybe_reconnect()
            self._channel.basic_publish(
                exchange=self.exchange_name,
                routing_key=routing_key,
                body=data,
            )
        except AMQPError as err:
            # The event-agent will retry later, we do not need to loop here.
            self._close_conn(after_error=True)
            msg = f"notify failure: {err!r}"
            resp = RetryableEventError(event=event, body=msg)
            return resp
        return None

def filter_factory(global_conf, **local_conf):
    conf = global_conf.copy()
    conf.update(local_conf)
    queue_url = local_conf.get("queue_url")

    def make_filter(app):
        if queue_url.startswith("amqp"):
            return AmqpNotifyFilter(app, conf, endpoints=queue_url)
        return BeanstalkdNotifyFilter(app, conf)

    return make_filter


import numpy as np

def getPeaks(signal, fs, min_distance, impulse_distance=0):
    """
    signal: samples
    min_distance: distance of peaks
    fs: sample frecuency
    """
    power_signal = np.power(signal, 2)

    # Consideramos que la señal no variara considerablemente en al menos 10 periodos del filtro
    stationarity_size = 10 * 500

    max_value = max(power_signal[:stationarity_size])  # maximo valor de la potencia en el primer intervalo estacionario

    peakSignal = np.zeros(len(signal))  # señal donde almacenamos los picos

    counter = 0
    impulse_ticks = int(impulse_distance * fs)

    for i in range(len(signal)):
        if i % stationarity_size == 0:
            max_value = max(power_signal[:stationarity_size])

        if counter > 0:
            counter -= 1
        elif power_signal[i] > max_value * 0.43:
            counter = min_distance * fs
            if i > impulse_ticks:
                peakSignal[i - impulse_ticks] = 1

    return peakSignal


from datetime import datetime

from sqlalchemy import Column
from sqlalchemy.sql.sqltypes import Integer, Text, Date, Boolean

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class Follower(Base):
    __tablename__ = 'follower'

    id = Column(Integer, primary_key=True)
    name = Column(Text)
    screen_name = Column(Text)
    twitter_id = Column(Integer)
    is_following = Column(Boolean)
    last_following = Column(Date)

    def __repr__(self):
        return "<Follower %s #%s>" % \
               (self.name, self.twitter_id)


#!/usr/bin/env python2

# [Ordered Dictionary for Py2.4 « Python recipes « ActiveState Code]
# (http://code.activestate.com/recipes/576693/)

from collections import OrderedDict

od = OrderedDict()

od["a"] = 1
od["b"] = 2
od["c"] = 4
od["d"] = 5
del od["a"]
od["a"] = 3

for k, v in od.iteritems():
    print("{},{}".format(k,v))
print("")

# print:
# b,2
# c,4
# d,5
# a,3
# OrderedDict is FIFO

for k in reversed(od) :         # reversed is not destructive
    print("{},{}".format(k,od[k]))
print("")

# a,3
# d,5
# c,4
# b,2

for k, v in od.iteritems():
    print("{},{}".format(k,v))
print("")

# b,2
# c,4
# d,5
# a,3


import numpy as np

class ScrapBooker:
    @staticmethod
    def crop(array, dimensions, position = (0, 0)):
        """
            array -> numpy array
            dimensions -> dimensions of the crop
            position -> cordinates of top left corner for the crop
            return cropped array
        """
        Y = 0
        X = 1

        if dimensions[Y] > len(array) or dimensions[X] > len(array[0]):
            raise ValueError('dimensions out of bounds')

        return array[position[Y]:position[Y] + dimensions[Y], position[X]:position[X] + dimensions[X], :]

    @staticmethod
    def thin(array, n, axis):
        """
            delete every n-th pixel row along the specified axis ( 0 vertical 1 horizontal)
        """
        return np.delete(array, range(n, len(array), n), axis)

    @staticmethod
    def juxtapose(array, n, axis):
        """
            juxtapose n copies of the image along the specified axis (0 vertical, 1 horizontal).
        """

        orig_arr = array

        for _ in range(n):
            array = np.concatenate((array, orig_arr), axis=axis)

        return array


__author__ = 'nipunbatra'

import numpy as np
import pandas as pd

def read_df():

    df = pd.read_csv("../data/input/main-data.csv",index_col=0)
    dfc = df.copy()

    df = df.drop(871)
    df = df.drop(1169)

    w=df[['aggregate_%d' %i for i in range(1,13)]]

    df = df.ix[w[w>0].dropna().index]

    features_individual = {'fraction':["fraction_%d" % i for i in range(1, 25)],
                           'area': 'area',
                           'autocorr':'autocorr',
                           'month': ["aggregate_%d" % i for i in range(1, 13)],
                           'occupants': 'total_occupants',
                           'rooms': 'num_rooms',
                           'seasonal_12':['stdev_seasonal_12','max_seasonal_12'],
                           'trend_12':['stdev_trend_12','max_trend_12'],

                           'seasonal_daily':['stdev_seasonal_daily','max_seasonal_daily'],
                           'trend_daily':['stdev_trend_daily','max_trend_daily'],
                           'seasonal_weekly':['stdev_seasonal_weekly','max_seasonal_weekly'],
                           'trend_weekly':['stdev_trend_weekly','max_trend_weekly'],
                           'cluster_big':'cluster_big',
                           'cluster_small':'cluster_small',
                           'diff':['lt_500','bet_500_1000','gt_1000'],
                           'temp':'temperature_corr',
                           'week':["fraction_%d" % i for i in range(1, 8)],
                           #'disag_fridge':'disag_fridge'}
                           'mins_hvac':'mins_hvac',
                           'month_extract':['variance','ratio_min_max', 'difference_min_max',
                                            'ratio_difference_min_max']}

    from itertools import combinations
    features_dict = {}
    for feature_size in range(1,max(4,len(features_individual))):
        combinations_size_n = list(combinations(features_individual.keys(), feature_size))
        for com in combinations_size_n:
            features_dict[com] = np.hstack([features_individual[x] for x in com]).tolist()

    national_average = {"fridge": 0.07, "hvac": 0.18, 'wm': 0.01, 'furnace': 0.09, 'dw': 0.02, 'dr': 0.04, 'light': .11}

    def scale_0_1(ser, minimum=None, maximum=None):
        if minimum is not None:
            pass
        else:
            minimum = ser.min()
            maximum = ser.max()
        return (ser-minimum).div(maximum-minimum)

    #Normalising features
    max_aggregate = df[["aggregate_%d" % i for i in range(1, 13)]].max().max()
    min_aggregate = df[["aggregate_%d" % i for i in range(1, 13)]].min().min()
    df[["aggregate_%d" % i for i in range(1, 13)]] = scale_0_1(df[["aggregate_%d" % i for i in range(1, 13)]], min_aggregate, max_aggregate)

    max_weekly = df[["daily_usage_%d" % i for i in range(1, 8)]].max().max()
    min_weekly = df[["daily_usage_%d" % i for i in range(1, 8)]].min().min()
    df[["daily_usage_%d" % i for i in range(1, 8)]] = scale_0_1(df[["daily_usage_%d" % i for i in range(1, 8)]], min_weekly, max_weekly)

    df['area'] = scale_0_1(df['area'])

    df['num_rooms'] = scale_0_1(df['num_rooms'])
    df['total_occupants'] = scale_0_1(df['total_occupants'])
    df['mins_hvac'] =  scale_0_1(df['mins_hvac'])

    # Adding new feature
    aa = df[["aggregate_%d" % i for i in range(1, 13)]].copy()
    df['variance'] = df[["aggregate_%d" % i for i in range(1, 13)]].var(axis=1)
    df['ratio_min_max'] = aa.min(axis=1)/aa.max(axis=1)

    df['difference_min_max'] = aa.max(axis=1)-aa.min(axis=1)
    df['ratio_difference_min_max'] = (aa.max(axis=1)-aa.min(axis=1)).div(aa.max(axis=1))

    # Adding skew and kurtosis on monthly aggregate
    k = {}
    s ={}
    p_25 ={}
    p_75 = {}
    p_50 = {}
    for home in df.index:
        signal = df.ix[home][['aggregate_%d' %i for i in range(1, 13)]]
        k[home] = signal.kurtosis()
        s[home] = signal.skew()
        p_25[home] =np.percentile(signal, 25)
        p_50[home] =np.percentile(signal, 50)
        p_75[home] = np.percentile(signal, 75)

    df['skew'] = pd.Series(s)
    df['kurtosis']=pd.Series(k)
    df['p_25']=pd.Series(p_25)
    df['p_50'] =pd.Series(p_50)
    df['p_75']=pd.Series(p_75)

    for col in ["stdev_trend_12","stdev_seasonal_12","max_seasonal_12","max_trend_12",
                "stdev_trend_daily","stdev_seasonal_daily","max_seasonal_daily","max_trend_daily",
                "stdev_trend_weekly","stdev_seasonal_weekly","max_seasonal_weekly","max_trend_weekly","disag_fridge",
                'stdev_trend','stdev_seasonal','max_seasonal','max_trend',
                'cluster_small','cluster_big', 'temperature_corr', 'variance',
                'ratio_min_max','ratio_difference_min_max','seasonal_energy_5','seasonal_energy_6',
                'seasonal_energy_7','seasonal_energy_8','seasonal_energy_9','seasonal_energy_10',
               'fft_1','fft_2','fft_3','fft_4','fft_5','skew','kurtosis','p_25','p_50','p_75']:
        if col in df.columns:

            df[col] = scale_0_1(df[col])

    dfs = {}
    total = features_dict.values()[np.array(map(len, features_dict.values())).argmax()]
    for appliance in ['fridge','hvac','dr','light','dw','wm']:
        temp=df.ix[df[['%s_%d' %(appliance, i) for i in range(1,13)]].dropna().index]
        dfs[appliance] =temp.ix[temp[total].dropna().index]

    appliance_min = {'fridge':5,'hvac':5,'wm':0,'dw':0,'dr':0,'light':0}

    all_homes = {
        'dw':[  94,  370,  545,  624, 2156, 2242, 2814, 2829, 3723,
                4767, 5357,6636, 6910, 7769, 9934],
        'wm':[  94,  370,  545,  624, 2156, 2242, 2814, 3367, 3456, 3723, 3967,
                5357, 7769, 9654, 9922, 9934],
        'hvac':[  26,   94,  370,  410,  545,  624, 1283, 1642, 1953, 2129,
                2156, 2242, 2470, 2814, 2829,  3367, 3456, 3723,
                3967, 4767, 5218, 5357, 5371, 5746, 5785, 5814, 6072,
                6636, 6836, 6910, 7731, 7769, 7866, 9609, 9654, 9922, 9933, 9934],
        'fridge':[  94,  370,  410,  545,  624, 1953, 2156, 2242, 2814, 2829, 3367,
                3456, 3723, 3967, 4767, 5357, 5371, 6072, 6636, 6910, 7769, 7866],
        'light':df.index.tolist(),
            #[ 624, 1334, 2814, 2925, 2986, 3367, 3456, 3482, 3723, 3967, 4732,
            #    4767, 5814, 5817, 6072, 6266, 6910, 7016, 7429, 7731, 7769, 7866,
            #    8317, 8626, 9052, 9654, 9922],
        'dr':[  94,  370,  410, 2156, 2242, 2814, 3456, 3723, 4767,
                5785, 5814, 6072, 6636, 6836, 7731, 7769, 7866, 9654, 9922,
                9933, 9982]
    }

    all_homes = {appliance:dfs[appliance].index for appliance in dfs.keys()}

    all_homes['fridge'] = np.array(np.setdiff1d(all_homes['fridge'], [26, 1334, 1642, 2233,3482,6836, 5746, 7016, 9982]))

    ### HVAC ###
    # 1334 is not used in some months, ideally we can do signal processing and remove it,
    # Currently removed it manually; similarly 94
    all_homes['hvac'] = np.array(np.setdiff1d(all_homes['hvac'], [94, 252, 1334, 2925, 2986, 3482, 4732, 5439, 6266,
                                                                  8626, 1800, 2233, 5817, 7016, 7429, 8317,
                                                                  9052, 9982]))

    all_homes['dw'] =  np.array(np.setdiff1d(all_homes['dw'],[2233, 7016]))

    all_homes['wm'] = [  94,  370,  545,  624, 2156, 2242, 2814, 3367, 3456, 3723, 3967,
            5357, 7769, 9654, 9922, 9934]

    all_homes['light']=[624, 1334, 3367, 3456, 3723, 5814, 6072, 6910, 7769, 7866, 9654,
            9922]

    return df, dfc, all_homes, appliance_min, national_average


import os
from os import getenv
from dotenv import load_dotenv

if os.path.exists("local.env"):
    load_dotenv("local.env")

API_ID = int(getenv("API_ID", "6435225")) #optional
API_HASH = getenv("API_HASH", "") #optional

SUDO_USERS = list(map(int, getenv("SUDO_USERS", "").split()))
OWNER_ID = int(getenv("OWNER_ID"))
MONGO_URL = getenv("MONGO_URL")
BOT_TOKEN = getenv("BOT_TOKEN", "")
ALIVE_PIC = getenv("ALIVE_PIC", 'https://telegra.ph/file/3c52a01057865f7511168.jpg')
ALIVE_TEXT = getenv("ALIVE_TEXT")
PM_LOGGER = getenv("PM_LOGGER")
LOG_GROUP = getenv("LOG_GROUP")
GIT_TOKEN = getenv("GIT_TOKEN") #personal access token
REPO_URL = getenv("REPO_URL", "https://github.com/ITZ-ZAID/ZAID-USERBOT")
BRANCH = getenv("BRANCH", "master") #don't change

STRING_SESSION1 = getenv("STRING_SESSION1", "")
STRING_SESSION2 = getenv("STRING_SESSION2", "")
STRING_SESSION3 = getenv("STRING_SESSION3", "")
STRING_SESSION4 = getenv("STRING_SESSION4", "")
STRING_SESSION5 = getenv("STRING_SESSION5", "")
STRING_SESSION6 = getenv("STRING_SESSION6", "")
STRING_SESSION7 = getenv("STRING_SESSION7", "")
STRING_SESSION8 = getenv("STRING_SESSION8", "")
STRING_SESSION9 = getenv("STRING_SESSION9", "")
STRING_SESSION10 = getenv("STRING_SESSION10", "")


"""
Detection Recipe - 12.0.4.17

References:
(1)  'Asteroseismic detection predictions: TESS' by Chaplin (2015)
(2)  'On the use of empirical bolometric corrections for stars' by Torres (2010)
(3)  'The amplitude of solar oscillations using stellar techniques' by Kjeldson (2008)
(4)  'An absolutely calibrated Teff scale from the infrared flux method'
	 by Casagrande (2010) table 4
(5)  'Characterization of the power excess of solar-like oscillations in red giants with Kepler'
	 by Mosser (2011)
(6)  'Predicting the detectability of oscillations in solar-type stars observed by Kepler'
	 by Chaplin (2011)
(7)  'The connection between stellar granulation and oscillation as seen by the Kepler mission'
	 by Kallinger et al (2014)
(8)  'The Transiting Exoplanet Survey Satellite: Simulations of Planet Detections and
	 Astrophysical False Positives' by Sullivan et al. (2015)
(9)  Astropysics module at https://pythonhosted.org/Astropysics/coremods/coords.html
(10) Bill Chaplin's calc_noise IDL procedure for TESS.
(11) Bill Chaplin's soldet6 IDL procedure to calculate the probability of detecting
	 oscillations with Kepler.
(12) Coordinate conversion at https://ned.ipac.caltech.edu/forms/calculator.html
(13) Bedding 1996
(14) 'The Asteroseismic potential of TESS' by Campante et al. 2016
"""

import numpy as np
from itertools import groupby
from operator import itemgetter
import sys
import pandas as pd
from scipy import stats
import warnings
warnings.simplefilter("ignore")

def bv2teff(b_v):
	# from Torres 2010 table 2. Applies to MS, SGB and giant stars
	# B-V limits from Flower 1996 fig 5
	a = 3.979145106714099
	b = -0.654992268598245
	c = 1.740690042385095
	d = -4.608815154057166
	e = 6.792599779944473
	f = -5.396909891322525
	g = 2.192970376522490
	h = -0.359495739295671
	lteff = a + b*b_v + c*(b_v**2) + d*(b_v**3) + e*(b_v**4) + f*(b_v**5) + g*(b_v**6) + h*(b_v**7)
	teff = 10.0**lteff

	return teff

# from F Pijpers 2003. BCv values from Flower 1996 polynomials presented in Torres 2010
# Av is a keword argument. If reddening values not available, ignore it's effect
def Teff2bc2lum(teff, parallax, parallax_err, vmag, Av=0):
    lteff = np.log10(teff)
    BCv = np.full(len(lteff), -100.5)

    BCv[lteff<3.70] = (-0.190537291496456*10.0**5) + \
    (0.155144866764412*10.0**5*lteff[lteff<3.70]) + \
    (-0.421278819301717*10.0**4.0*lteff[lteff<3.70]**2.0) + \
    (0.381476328422343*10.0**3*lteff[lteff<3.70]**3.0)

    BCv[(3.70<lteff) & (lteff<3.90)] = (-0.370510203809015*10.0**5) + \
    (0.385672629965804*10.0**5*lteff[(3.70<lteff) & (lteff<3.90)]) + \
    (-0.150651486316025*10.0**5*lteff[(3.70<lteff) & (lteff<3.90)]**2.0) + \
    (0.261724637119416*10.0**4*lteff[(3.70<lteff) & (lteff<3.90)]**3.0) + \
    (-0.170623810323864*10.0**3*lteff[(3.70<lteff) & (lteff<3.90)]**4.0)

    BCv[lteff>3.90] = (-0.118115450538963*10.0**6) + \
    (0.137145973583929*10.0**6*lteff[lteff > 3.90]) + \
    (-0.636233812100225*10.0**5*lteff[lteff > 3.90]**2.0) + \
    (0.147412923562646*10.0**5*lteff[lteff > 3.90]**3.0) + \
    (-0.170587278406872*10.0**4*lteff[lteff > 3.90]**4.0) + \
    (0.788731721804990*10.0**2*lteff[lteff > 3.90]**5.0)

    u = 4.0 + 0.4 * 4.73 - 2.0 * np.log10(parallax) - 0.4 * (vmag - Av + BCv)
    lum = 10**u  # in solar units
    e_lum = (2.0 / parallax * 10**u)**2 * parallax_err**2
    e_lum = np.sqrt(e_lum)

    return lum, e_lum

# calculate seismic parameters
def seismicParameters(teff, lum):

	# solar parameters
	teff_solar = 5777.0 # Kelvin
	teffred_solar = 8907.0 #in Kelvin
	numax_solar = 3090.0 # in micro Hz
	dnu_solar = 135.1 # in micro Hz

	cadence = 120 # in s
	vnyq = (1.0 / (2.0*cadence)) * 10**6 # in micro Hz
	teffred = teffred_solar*(lum**-0.093) # from (6) eqn 8. red-edge temp
	rad = lum**0.5 * ((teff/teff_solar)**-2) # Steffan-Boltzmann law
	numax = numax_solar*(rad**-1.85)*((teff/teff_solar)**0.92) # from (14)

	return cadence, vnyq, rad, numax, teffred, teff_solar, teffred_solar, numax_solar, dnu_solar

# no coordinate conversion before calculating tess field observing time. Only
# works with ecliptic coordinates
def tess_field_only(e_lng, e_lat):

    # create a list to append all of the total observing times 'T' in the TESS field to
    T = [] # units of sectors (0-13)

    # create a list to append all of the maximum contiguous observations to
    max_T = [] # units of sectors (0-13)

    for star in range(len(e_lng)):

        # 'n' defines the distance between each equidistant viewing sector in the TESS field.
        n = 360.0/13

        # Define a variable to count the total number of sectors a star is observed in.
        counter = 0

        # Define a variable to count all of the observations for each star.
        # Put each observation sector into sca separately in order to find the largest number
        # of contiguous observations for each star.
        sca = []

        # 'ranges' stores all of the contiguous observations for each star.
        ranges = []

        # Defines the longitude range of the observing sectors at the inputted stellar latitude
        lngrange = 24.0/abs(np.cos(np.radians(e_lat[star])))
        if lngrange>=360.0:
        	lngrange=360.0

        # if the star is in the northern hemisphere:
        if e_lat[star] >= 0.0:

        	# For each viewing sector.
        	for i in range(1,14):

        		# Define an ra position for the centre of each sector in increasing longitude.
        		# if a hemisphere has an overshoot, replace 0.0 with the value.
        		a = 0.0+(n*(i-1))

        		# calculate the distances both ways around the
        		# circle between the star and the centre of the sector.
        		# The smallest distance is the one that should be used
        		# to see if the star lies in the observing sector.
        		d1 = abs(e_lng[star]-a)
        		d2 = (360.0 - abs(e_lng[star]-a))
        		if d1>d2:
        			d1 = d2

        		# if the star is in the 'overshoot' region for some sectors, calculate d3 and d4;
        		# the distances both ways around the circle bwtween the star and the centre of the
        		# 'overshooting past the pole' region of the sector.
        		# The smallest distance is the one that should be used
        		# to see if the star lies in the observing sector.
        		# the shortest distances between the centre of the sector and star, and the sector's
        		# overshoot and the star should add to 180.0 apart (i.e d1+d3=180.0)
        		d3 = abs(e_lng[star] - (a+180.0)%360.0)
        		d4 = 360.0 - abs(e_lng[star] - (a+180.0)%360.0)
        		if d3>d4:
        			d3 = d4

        		# check if a star lies in the field of that sector.
        		if (d1<=lngrange/2.0 and 6.0<=e_lat[star]) or (d3<=lngrange/2.0 and 78.0<=e_lat[star]):
        			counter += 1
        			sca = np.append(sca, i)
        		else:
        			pass

        # if the star is in the southern hemisphere:
        if e_lat[star] < 0.0:

        	# For each viewing sector.
        	for i in range(1,14):

        		# Define an ra position for the centre of each sector in increasing longitude.
        		# if a hemisphere has an overshoot, replace 0.0 with the value.
        		a = 0.0+(n*(i-1))

        		# calculate the distances both ways around the
        		# circle between the star and the centre of the sector.
        		# The smallest distance is the one that should be used
        		# to see if the star lies in the observing sector.
        		d1 = abs(e_lng[star]-a)
        		d2 = (360 - abs(e_lng[star]-a))
        		if d1>d2:
        			d1 = d2

        		# if the star is in the 'overshoot' region for some sectors, calculate d3 and d4;
        		# the distances both ways around the circle between the star and the centre of the
        		# 'overshooting past the pole' region of the sector.
        		# The smallest distance of the 2 is the one that should be used
        		# to see if the star lies in the observing sector.
        		d3 = abs(e_lng[star]  - (a+180.0)%360.0)
        		d4 = (360 - abs(e_lng[star] - (a+180.0)%360.0))
        		if d3>d4:
        			d3 = d4

        		# check if a star lies in the field of that sector.
        		if (d1<=lngrange/2.0 and -6.0>=e_lat[star]) or (d3<=lngrange/2.0 and -78.0>=e_lat[star]):
        			counter += 1
        			sca = np.append(sca, i)
        		else:
        			pass

        if len(sca) == 0:
        	ranges = [0]

        else:
        	for k,g in groupby(enumerate(sca), lambda i_x:i_x[0]-i_x[1]):
        		group = map(itemgetter(1), g)

        		if np.array(group).sum() !=0:
        			ranges.append([len(list(group))])

        T=np.append(T, counter)
        max_T = np.append(max_T, np.max(np.array(ranges)))

    return T, max_T

def calc_noise(imag, exptime, teff, e_lng = 0, e_lat = 30, g_lng = 96, g_lat = -30, subexptime = 2.0, npix_aper = 10, \
frac_aper = 0.76, e_pix_ro = 10, geom_area = 60.0, pix_scale = 21.1, sys_limit = 0):

    omega_pix = pix_scale**2.0
    n_exposures = exptime/subexptime

    # electrons from the star
    megaph_s_cm2_0mag = 1.6301336 + 0.14733937*(teff-5000.0)/5000.0
    e_star = 10.0**(-0.4*imag) * 10.0**6 * megaph_s_cm2_0mag * geom_area * exptime * frac_aper
    e_star_sub = e_star*subexptime/exptime

    # e/pix from zodi
    dlat = (abs(e_lat)-90.0)/90.0
    vmag_zodi = 23.345 - (1.148*dlat**2.0)
    e_pix_zodi = 10.0**(-0.4*(vmag_zodi-22.8)) * (2.39*10.0**-3) * geom_area * omega_pix * exptime

    # e/pix from background stars
    dlat = abs(g_lat)/40.0*10.0**0

    dlon = g_lng
    q = np.where(dlon>180.0)
    if len(q[0])>0:
    	dlon[q] = 360.0-dlon[q]

    dlon = abs(dlon)/180.0*10.0**0
    p = [18.97338*10.0**0, 8.833*10.0**0, 4.007*10.0**0, 0.805*10.0**0]
    imag_bgstars = p[0] + p[1]*dlat + p[2]*dlon**(p[3])
    e_pix_bgstars = 10.0**(-0.4*imag_bgstars) * 1.7*10.0**6 * geom_area * omega_pix * exptime

    # compute noise sources
    noise_star = np.sqrt(e_star) / e_star
    noise_sky  = np.sqrt(npix_aper*(e_pix_zodi + e_pix_bgstars)) / e_star
    noise_ro   = np.sqrt(npix_aper*n_exposures)*e_pix_ro / e_star
    noise_sys  = 0.0*noise_star + sys_limit/(1*10.0**6)/np.sqrt(exptime/3600.0)

    noise1 = np.sqrt(noise_star**2.0 + noise_sky**2.0 + noise_ro**2.0)
    noise2 = np.sqrt(noise_star**2.0 + noise_sky**2.0 + noise_ro**2.0 + noise_sys**2.0)

    return noise2

# calculate the granulation at a set of frequencies from (7) eqn 2 model F
def granulation(nu0, dilution, a_nomass, b1, b2, vnyq):

    # Divide by dilution squared as it affects stars in the time series.
    # The units of dilution change from ppm to ppm^2 microHz^-1 when going from the
    # time series to frequency. p6: c=4 and zeta = 2*sqrt(2)/pi
    Pgran = (((2*np.sqrt(2))/np.pi) * (a_nomass**2/b1) / (1 + ((nu0/b1)**4)) \
    + ((2*np.sqrt(2))/np.pi) * (a_nomass**2/b2) / (1 + ((nu0/b2)**4))) / (dilution**2)

    # From (9). the amplitude suppression factor. Normalised sinc with pi (area=1)
    eta = np.sinc((nu0/(2*vnyq)))

    # the granulation after attenuation
    Pgran = Pgran * eta**2

    return Pgran, eta

# the total number of pixels used by the highest ranked x number of targets in the tCTL
def pixel_cost(x):
    N = np.ceil(10.0**-5.0 * 10.0**(0.4*(20.0-x)))
    N_tot = 10*(N+10)
    total = np.cumsum(N_tot)

    # want to find: the number of ranked tCTL stars (from highest to lowest rank) that correspond to a pixel cost of 1.4Mpix at a given time
    per_cam = 26*4 # to get from the total pixel cost to the cost per camera at a given time, divide by this
    pix_limit = 1.4e6 # the pixel limit per camera at a given time

    return total[-1], per_cam, pix_limit, N_tot

# detection recipe to find whether a star has an observed solar-like Gaussian mode power excess
def globalDetections(g_lng, g_lat, e_lng, e_lat, imag, \
lum, rad, teff, numax, max_T, teffred, teff_solar, \
teffred_solar, numax_solar, dnu_solar, sys_limit, dilution, vnyq, cadence, vary_beta=False):

	dnu = dnu_solar*(rad**-1.42)*((teff/teff_solar)**0.71) # from (14) eqn 21
	beta = 1.0-np.exp(-(teffred-teff)/1550.0) # beta correction for hot solar-like stars from (6) eqn 9.
	if isinstance(teff, float):  # for only 1 star
	    if (teff>=teffred):
	        beta = 0.0
	else:
	    beta[teff>=teffred] = 0.0

	# to remove the beta correction, set Beta=1
	if vary_beta == False:
	    beta = 1.0

	# modified from (6) eqn 11. Now consistent with dnu proportional to numax^0.77 in (14)
	amp = 0.85*2.5*beta*(rad**1.85)*((teff/teff_solar)**0.57)

	# From (5) table 2 values for delta nu_{env}. env_width is defined as +/- some value.
	env_width = 0.66 * numax**0.88
	env_width[numax>100.] = numax[numax>100.]/2.  # from (6) p12

	total, per_cam, pix_limit, npix_aper = pixel_cost(imag)

	noise = calc_noise(imag=imag, teff=teff, exptime=cadence, e_lng=e_lng, e_lat=e_lat, \
	g_lng=g_lng, g_lat=g_lat, sys_limit=sys_limit, npix_aper=npix_aper)
	noise = noise*10.0**6 # total noise in units of ppm

	a_nomass = 0.85 * 3382*numax**-0.609 # multiply by 0.85 to convert to redder TESS bandpass.
	b1 = 0.317 * numax**0.970
	b2 = 0.948 * numax**0.992

	# call the function for the real and aliased components (above and below vnyq) of the granulation
	# the order of the stars is different for the aliases so fun the function in a loop
	Pgran, eta = granulation(numax, dilution, a_nomass, b1, b2, vnyq)
	Pgranalias = np.zeros(len(Pgran))
	etaalias = np.zeros(len(eta))

	# if vnyq is 1 fixed value
	if isinstance(vnyq, float):
	    for i in range(len(numax)):

	    	if numax[i] > vnyq:
	    		Pgranalias[i], etaalias[i] = granulation((vnyq - (numax[i] - vnyq)), \
	    		dilution, a_nomass[i], b1[i], b2[i], vnyq)

	    	elif numax[i] < vnyq:
	    		Pgranalias[i], etaalias[i] = granulation((vnyq + (vnyq - numax[i])), \
	    		dilution, a_nomass[i], b1[i], b2[i], vnyq)

	# if vnyq varies for each star
	else:
	    for i in range(len(numax)):

	    	if numax[i] > vnyq[i]:
	    		Pgranalias[i], etaalias[i] = granulation((vnyq[i] - (numax[i] - vnyq[i])), \
	    		dilution, a_nomass[i], b1[i], b2[i], vnyq[i])

	    	elif numax[i] < vnyq[i]:
	    		Pgranalias[i], etaalias[i] = granulation((vnyq[i] + (vnyq[i] - numax[i])), \
	    		dilution, a_nomass[i], b1[i], b2[i], vnyq[i])

	Pgrantotal = Pgran + Pgranalias

	ptot = (0.5*2.94*amp**2.*((2.*env_width)/dnu)*eta**2.) / (dilution**2.)
	Binstr = 2.0 * (noise)**2. * cadence*10**-6.0 # from (6) eqn 18
	bgtot = ((Binstr + Pgrantotal) * 2.*env_width) # units are ppm**2

	snr = ptot/bgtot # global signal to noise ratio from (11)
	fap = 0.05 # false alarm probability
	pdet = 1.0 - fap
	pfinal = np.full(rad.shape[0], -99)
	idx = np.where(max_T != 0) # calculate the indexes where T is not 0
	tlen=max_T[idx]*27.4*86400.0 # the length of the TESS observations in seconds

	bw=1.0 * (10.0**6.0)/tlen
	nbins=(2.*env_width[idx]/bw).astype(int) # from (11)
	snrthresh = stats.chi2.ppf(pdet, 2.0*nbins) / (2.0*nbins) - 1.0

	pfinal[idx] = stats.chi2.sf((snrthresh+1.0) / (snr[idx]+1.0)*2.0*nbins, 2.*nbins)
	return pfinal, snr, dnu # snr is needed in TESS_telecon2.py

def BV2VI(bv, vmag, g_mag_abs):

	whole = pd.DataFrame(data={'B-V': bv, 'Vmag': vmag, 'g_mag_abs': g_mag_abs, 'Ai': 0})

	# Mg: empirical relation from Tiago to separate dwarfs from giants
	# note: this relation is observational; it was made with REDDENED B-V and g_mag values
	whole['Mg'] = 6.5*whole['B-V'] - 1.8

	# B-V-to-teff limits from (6) fig 5
	whole = whole[(whole['B-V'] > -0.4) & (whole['B-V'] < 1.7)]
	print(whole.shape, 'after B-V cuts')

	# B-V limits for dwarfs and giants, B-V conditions from (1)
	# if a star can't be classified as dwarf or giant, remove it
	condG = (whole['B-V'] > -0.25) & (whole['B-V'] < 1.75) & (whole['Mg'] > whole['g_mag_abs'])
	condD1 = (whole['B-V'] > -0.23) & (whole['B-V'] < 1.4) & (whole['Mg'] < whole['g_mag_abs'])
	condD2 = (whole['B-V'] > 1.4) & (whole['B-V'] < 1.9) & (whole['Mg'] < whole['g_mag_abs'])

	whole = pd.concat([whole[condG], whole[condD1], whole[condD2]], axis=0)
	print(whole.shape, 'after giant/dwarf cuts')
	whole['V-I'] = 100. # write over these values for dwarfs and giants separately

	# coefficients for giants and dwarfs
	cg = [-0.8879586e-2, 0.7390707, 0.3271480, 0.1140169e1, -0.1908637, -0.7898824,
		0.5190744, 0.5358868]
	cd1 = [0.8906590e-1, 0.1319675e1, 0.4461807, -0.1188127e1, 0.2465572, 0.8478627e1,
		0.1046599e2, 0.3641226e1]
	cd2 = [-0.5421588e2, 0.8011383e3, -0.4895392e4, 0.1628078e5, -0.3229692e5,
		0.3939183e5, -0.2901167e5, 0.1185134e5, -0.2063725e4]

	# calculate (V-I) for giants
	x = whole['B-V'][condG] - 1
	y = (cg[0] + cg[1]*x + cg[2]*(x**2) + cg[3]*(x**3) + cg[4]*(x**4) +\
		cg[5]*(x**5) + cg[6]*(x**6) + cg[7]*(x**7))
	whole['V-I'][condG] = y + 1
	x, y = [[] for i in range(2)]

	# calculate (V-I) for dwarfs (1st B-V range)
	x = whole['B-V'][condD1] - 1
	y = (cd1[0] + cd1[1]*x + cd1[2]*(x**2) + cd1[3]*(x**3) + cd1[4]*(x**4) +\
		cd1[5]*(x**5) + cd1[6]*(x**6) + cd1[7]*(x**7))
	whole['V-I'][condD1] = y + 1
	x, y = [[] for i in range(2)]

	# calculate (V-I) for dwarfs (2nd B-V range)
	x = whole['B-V'][condD2] - 1
	y = (cd2[0] + cd2[1]*x + cd2[2]*(x**2) + cd2[3]*(x**3) + cd2[4]*(x**4) +\
		cd2[5]*(x**5) + cd2[6]*(x**6) + cd2[7]*(x**7) + cd2[8]*(x**8))
	whole['V-I'][condD2] = y + 1
	x, y = [[] for i in range(2)]

	# calculate Imag from V-I and reredden it
	whole['Imag'] = whole['Vmag']-whole['V-I']
	whole['Imag_reddened'] = whole['Imag'] + whole['Ai']

	"""
	# make Teff, luminosity, Plx and ELat cuts to the data
	whole = whole[(whole['teff'] < 7700) & (whole['teff'] > 4300) & \
		(whole['Lum'] > 0.3) & (whole['lum_D'] < 50) & ((whole['e_Plx']/whole['Plx']) < 0.5) \
		& (whole['Plx'] > 0.) & ((whole['ELat']<=-6.) | (whole['ELat']>=6.))]
	print(whole.shape, 'after Teff/L/Plx/ELat cuts')
	"""

	whole.drop(['Ai', 'Imag_reddened', 'Mg'], axis=1, inplace=True)

	return whole.as_matrix().T

# make cuts to the data
def cuts(teff, e_teff, metal, e_metal, g_lng, g_lat, e_lng, e_lat, Tmag, e_Tmag, Vmag, e_Vmag, plx, e_plx, lum, star_name):
    d = {'teff':teff, 'e_teff':e_teff, 'metal':metal, 'e_metal':e_metal, 'g_lng':g_lng, 'g_lat':g_lat, 'e_lng':e_lng, 'e_lat':e_lat,
         'Tmag':Tmag, 'e_Tmag':e_Tmag, 'Vmag':Vmag, 'e_Vmag':e_Vmag, 'plx':plx, 'e_plx':e_plx, 'lum':lum, 'star_name':star_name}
    whole = pd.DataFrame(d, columns = ['teff', 'e_teff', 'metal', 'e_metal', 'g_lng', 'g_lat', 'e_lng', 'e_lat',
                                       'Tmag', 'e_Tmag', 'Vmag', 'e_Vmag', 'plx', 'e_plx', 'lum', 'star_name'])
    whole = whole[(whole['teff'] < 7700.) & (whole['teff'] > 4300.) & (whole['e_teff'] > 0.) & \
		(whole['lum'] > 0.3) & (whole['lum'] < 50.) & ((whole['e_plx']/whole['plx']) < 0.5) & \
		(whole['plx'] > 0.) & ((whole['e_lat']<=-6.) | (whole['e_lat']>=6.)) & \
         (whole['Tmag'] > 3.5) & (whole['e_metal'] > 0.)]
    print(whole.shape, 'after cuts to the data')
    return whole.as_matrix().T

if __name__ == '__main__':

    df = pd.read_csv('files/MAST_Crossmatch_TIC4.csv', header=0,
                     index_col=False)
    data = df.values

    # star_name = data[:, 1]
    teff = pd.to_numeric(data[:, 88])
    # e_teff = pd.to_numeric(data[:, 89])
    # metal = pd.to_numeric(data[:, 92])
    # e_metal = pd.to_numeric(data[:, 93])
    # g_lng = pd.to_numeric(data[:, 48])
    # g_lat = pd.to_numeric(data[:, 49])
    # e_lng = pd.to_numeric(data[:, 50])
    # e_lat = pd.to_numeric(data[:, 51])
    # Tmag = pd.to_numeric(data[:, 84])
    # e_Tmag = pd.to_numeric(data[:, 85])
    Vmag = pd.to_numeric(data[:, 54])
    # e_Vmag = pd.to_numeric(data[:, 55])
    plx = pd.to_numeric(data[:, 45])
    e_plx = pd.to_numeric(data[:, 46])

    lum, e_lum = Teff2bc2lum(teff, plx, e_plx, Vmag)
    df[' Luminosity'] = pd.Series(lum)
    df[' Luminosity Err.'] = pd.Series(e_lum)

    # teff, e_teff, metal, e_metal, g_lng, g_lat, e_lng, e_lat, Tmag, e_Tmag, \
    #     Vmag, e_Vmag, plx, e_plx, lum, star_name = cuts(teff, e_teff, metal, e_metal,
    #         g_lng, g_lat, e_lng, e_lat, Tmag, e_Tmag, Vmag, e_Vmag,
    #             plx, e_plx, lum, star_name)

    # make cuts to the data
    df = df[(df[' T_eff'] < 7700.) & (df[' T_eff'] > 4300.) & (df[' T_eff Err.'] > 0.) & \
		(df[' Luminosity'] > 0.3) & (df[' Luminosity'] < 50.) & ((df[' Parallax Err.']/df[' Parallax']) < 0.5) & \
		(df[' Parallax'] > 0.) & ((df[' Ecl. Lat.']<=-6.) | (df[' Ecl. Lat.']>=6.)) & \
         (df[' TESS Mag.'] > 3.5) & (df[' Metallicity Err.'] > 0.)]
    df = df.reset_index(drop=True)
    print(df.shape, 'after cuts to the data')

    data = df.values
    teff = pd.to_numeric(data[:, 88])
    lum = pd.to_numeric(data[:, 113])
    Tmag = pd.to_numeric(data[:, 84])
    Vmag = pd.to_numeric(data[:, 54])
    plx = pd.to_numeric(data[:, 45])
    g_lng = pd.to_numeric(data[:, 48])
    g_lat = pd.to_numeric(data[:, 49])
    e_lng = pd.to_numeric(data[:, 50])
    e_lat = pd.to_numeric(data[:, 51])

    cadence, vnyq, rad, numax, teffred, teff_solar, teffred_solar, \
        numax_solar, dnu_solar = seismicParameters(teff, lum)

    T, max_T = tess_field_only(e_lng, e_lat)

    pdet, snr, dnu = globalDetections(g_lng=g_lng, g_lat=g_lat, e_lng=e_lng, e_lat=e_lat, \
	    imag=Tmag, lum=lum, rad=rad, teff=teff, numax=numax, max_T=max_T, \
	    teffred=teffred, teff_solar=teff_solar, teffred_solar=teffred_solar, \
	    numax_solar=numax_solar, dnu_solar=dnu_solar, sys_limit=0., dilution=1., \
	    vnyq=vnyq, cadence=cadence, vary_beta=True)

    df[' Dnu'] = pd.Series(dnu)
    e_dnu = dnu * 0.021
    df[' Dnu Err.'] = pd.Series(e_dnu)
    df[' nu_max'] = pd.Series(numax)
    e_numax = numax * 0.046
    df[' nu_max Err.'] = pd.Series(e_numax)
    df[' P_det'] = pd.Series(pdet)
    df[' SNR'] = pd.Series(snr)

    # make new cut to the data (pdet > 0.5)
    df = df[df[' P_det'] > 0.5]
    df = df.reset_index(drop=True)
    print(df.shape, 'after new cut to the data (pdet > 0.5)')

    # inflating errors on teff and metal
    data = df.values
    e_teff = pd.to_numeric(data[:, 89])
    e_teff = (e_teff**2 + 59**2)**0.5
    df[' T_eff Err.'] = pd.Series(e_teff)
    e_metal = pd.to_numeric(data[:, 93])
    e_metal = (e_metal**2 + 0.062**2)**0.5
    df[' Metallicity Err.'] = pd.Series(e_metal)

    df.to_csv('/Users/Tiago/Work/PostDocBham/KnownHosts_with_TESS/catalog_knownhosts/PARAM_input.csv', index=False)


import pygame as pg
from pygame.math import Vector2

from src.camera import Camera
from src.game_objects.abstract.tile import Tile
from src.game_objects.movable_tile import MovableTile
from src.game_objects.selection_box import SelectionBox
from src.game_objects.static_tile import StaticTile
from src.graphics import SpriteSheet
from src.main_loop import MainLoop
from src.map import Map
from src.settings.constants import (
    CAPTION,
    SCREEN_SIZE,
    FPS,
    TILE_SIZE,
    ACTIVATE_SCROLLING_BORDERS,
    ACTIVATE_TEST_GRID,
    BG_COLOR,
)
from src.settings.controllers import CONTROLLERS_CAMERA
from src.settings.paths import *

class GameCore(MainLoop):
    def __init__(self):
        super().__init__(CAPTION, SCREEN_SIZE, FPS)

        self.image_manager = SpriteSheet()
        self.selection_box = pg.sprite.GroupSingle(None)

        self.nature_tiles = pg.sprite.Group()
        self.player1_villages = pg.sprite.Group()
        self.player1_units = pg.sprite.Group()

        self.search_order = [
            self.nature_tiles,
            self.player1_villages,
            self.player1_units,
        ]

        self.create_objects()
        self.mouse_handlers.append(self.handle_mouse_event)

    def create_objects(self):
        self._create_test_map()
        self._create_camera()

    def _create_camera(self):
        self.camera = Camera(self.map.width, self.map.height, ACTIVATE_SCROLLING_BORDERS)
        for key in CONTROLLERS_CAMERA.values():
            self.add_up_down_key_handlers(self.camera, key)

    def _draw_grid(self):
        width, height = self.main_surface.get_size()
        for x in range(0, width, TILE_SIZE):
            pg.draw.line(self.main_surface, "darkgrey", (x, 0), (x, height))
        for y in range(0, height, TILE_SIZE):
            pg.draw.line(self.main_surface, "darkgrey", (0, y), (width, y))

    def _create_test_map(self):
        self.map = Map(TEST_MAP)
        for row, tiles in enumerate(self.map.get_data):
            for column, tile in enumerate(tiles):
                if tile == "1":
                    t = StaticTile(Vector2(column, row), self.image_manager.get_image("grass"))
                    self.nature_tiles.add(t)
                    self.visible_sprites.add(t, layer=0)
                if tile == "0":
                    t = StaticTile(Vector2(column, row), self.image_manager.get_image("water"))
                    self.nature_tiles.add(t)
                    self.visible_sprites.add(t, layer=0)
                if tile == "8":
                    t = StaticTile(Vector2(column, row), self.image_manager.get_image("grass"))
                    self.nature_tiles.add(t)
                    self.visible_sprites.add(t, layer=0)

                    t = MovableTile(Vector2(column, row), self.image_manager.get_image("farmer"))
                    self.player1_units.add(t)
                    self.visible_sprites.add(t, layer=2)
                if tile == "9":
                    # TODO: Remove double sprite on one coordinate
                    t = StaticTile(Vector2(column, row), self.image_manager.get_image("grass"))
                    self.nature_tiles.add(t)
                    self.visible_sprites.add(t, layer=0)

                    t = Tile(Vector2(column, row), self.image_manager.get_image("village"))
                    self.player1_villages.add(t)
                    self.visible_sprites.add(t, layer=1)

    def handle_mouse_event(self, type_, pos, button=0):
        pos = self.camera.apply(pos)

        if type_ == pg.MOUSEMOTION:
            pass
        elif type_ == pg.MOUSEBUTTONDOWN:
            self.handle_mouse_down(pos, button)
        elif type_ == pg.MOUSEBUTTONUP:
            pass

    def left_click(self, clicked_sprite):
        # select
        if self.selection_box.sprite is None:
            sb = SelectionBox(clicked_sprite)
            self.selection_box.add(sb)
            self.visible_sprites.add(sb)

        # unselect
        elif self.selection_box.sprite.target is clicked_sprite:
            self.selection_box.sprite.kill()

    def right_click(self, clicked_sprite):
        if self.selection_box.sprite is not None:
            if isinstance(self.selection_box.sprite.target, MovableTile):
                self.selection_box.sprite.target.move_to(clicked_sprite)

    def handle_mouse_down(self, mouse_pos, mouse_button):
        def mouse_button_function():
            """Call the appropriate click function with the clicked_sprite parameter depending on the button"""
            return {
                1: self.left_click,
                3: self.right_click,
            }.get(mouse_button, lambda _: None)(clicked_sprite)

        clicked_sprite = None
        for group in self.search_order:
            for sprite in group:
                if sprite.check_click(mouse_pos) is True:
                    clicked_sprite = sprite
                    break

        if clicked_sprite is not None:
            mouse_button_function()

    def draw(self):
        for sprite in self.visible_sprites:
            self.main_surface.blit(sprite.image, self.camera.apply(sprite))

        if ACTIVATE_TEST_GRID is True:
            self._draw_grid()

    def update(self):
        self.main_surface.fill(BG_COLOR)
        super().update()
        self.camera.update()

if __name__ == '__main__':
    GameCore().run()


""" Preprocess the ISBI data set.
"""

__author__ = "Mike Pekala"
__copyright__ = "Copyright 2015, JHU/APL"
__license__ = "Apache 2.0"

import argparse, os.path
import numpy as np
from scipy.stats.mstats import mquantiles
import scipy.io

import emlib

def get_args():
    """Command line parameters for the 'deploy' procedure.

    You will probably want to override the train/valid/test split
    to better suit your problem of interest...
    """
    parser = argparse.ArgumentParser()

    parser.add_argument('-X', dest='dataFileName', type=str, required=True,
		    help='EM data file')

    parser.add_argument('-Y', dest='labelsFileName', type=str, required=True,
		    help='Ground truth labels for X')

    parser.add_argument('--train-slices', dest='trainSlices',
		    type=str, default='range(10)',
		    help='which slices to use for training')

    parser.add_argument('--valid-valid', dest='validSlices',
		    type=str, default='range(10,20)',
		    help='which slices to use for validation')

    parser.add_argument('--test-valid', dest='testSlices',
		    type=str, default='range(20,30)',
		    help='which slices to use for test')

    parser.add_argument('--brightness-quantile', dest='brightQuant',
		    type=float, default=0.97,
		    help='top quantile for non-membrane pixels.')

    parser.add_argument('--out-dir', dest='outDir',
		    type=str, default='./',
		    help='output directory')

    args = parser.parse_args()
    assert(args.brightQuant <= 1.0)
    assert(args.brightQuant > 0)

    # map strings to python objects (XXX: a cleaner way than eval)
    args.trainSlices = eval(args.trainSlices)
    args.validSlices = eval(args.validSlices)
    args.testSlices = eval(args.testSlices)

    return args

if __name__ == "__main__":
    args = get_args();

    #outDir = os.path.split(args.dataFileName)[0]
    if not os.path.isdir(args.outDir):
        os.mkdir(args.outDir)

    X = emlib.load_cube(args.dataFileName, np.uint8)
    Y = emlib.load_cube(args.labelsFileName, np.uint8)

    # remap Y labels from ISBI convention to membrane-vs-non-membrane
    Y[Y==0] = 1;      #  membrane
    Y[Y==255] = 0;    #  non-membrane

    # change type of Y so can use -1 as a value.
    Y = Y.astype(np.int8)

    Xtrain = X[args.trainSlices,:,:]; Ytrain = Y[args.trainSlices,:,:]
    Xvalid = X[args.validSlices,:,:]; Yvalid = Y[args.validSlices,:,:]
    Xtest = X[args.testSlices,:,:];   Ytest = Y[args.testSlices,:,:]

    # brightness thresholding
    thresh = mquantiles(np.concatenate((Xtrain[Ytrain==1], Xvalid[Yvalid==1])), args.brightQuant)
    pctOmitted = 100.0*np.sum(X > thresh) / np.prod(np.size(X))
    print('[preprocess]: percent of pixels omitted by brightness filter: %0.2f' % pctOmitted)

    Ytrain[Xtrain > thresh] = -1
    Yvalid[Xvalid > thresh] = -1
    Ytest[Xtest > thresh] = -1

    # EM data preprocessing
    # For now, do nothing.

    # save results
    np.save(os.path.join(outDir, 'Xtrain.npy'), Xtrain)
    np.save(os.path.join(outDir, 'Ytrain.npy'), Ytrain)
    np.save(os.path.join(outDir, 'Xvalid.npy'), Xvalid)
    np.save(os.path.join(outDir, 'Yvalid.npy'), Yvalid)
    np.save(os.path.join(outDir, 'Xtest.npy'), Xtest)
    np.save(os.path.join(outDir, 'Ytest.npy'), Ytest)

    print('[preprocess]: done!')


number_of_names = int(input())
odd_set = set()
even_set = set()

for num in range(1, number_of_names + 1):
    name = input()
    name_value = 0
    for ch in name:
        name_value += ord(ch)
    total_value = name_value // num
    if total_value % 2 == 0:
        even_set.add(total_value)
    else:
        odd_set.add(total_value)

odd_sum = sum(odd_set)
even_sum = sum(even_set)
if odd_sum == even_sum:
    modified_set = [str(el) for el in odd_set.union(even_set)]
    print(f", ".join(modified_set))
elif odd_sum > even_sum:
    modified_set = [str(el) for el in odd_set.difference(even_set)]
    print(f", ".join(modified_set))
elif odd_sum < even_sum:
    modified_set = [str(el) for el in odd_set.symmetric_difference(even_set)]
    print(f", ".join(modified_set))


from django.contrib import admin
from django.urls import path,include
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',views.index,name='Home'),
    path('aboutus/',views.aboutus,name='aboutUs'),
    path('product/<int:prodId>',views.prodDetails,name='prodDetails'),
    path('emptyCart/',views.emptyCart,name='emptyCart'),
    path('checkOut/',views.checkOut,name='checkOut'),

]


import config
from db import db_config
from db import db_users
#from calendar import cal
from flask import Flask, render_template, request
from pymessager.message import Messager
client = Messager(config.facebook_access_token)
import os
import json

from msg_handlers import main_handler, notification_handler, responses
import traceback
from webviews.webviews import webview

responses = responses.responses

app = Flask(__name__, static_url_path="/static", static_folder="webviews/static")

app.register_blueprint(webview, url_prefix='/webviews')

@app.route('/')
def hello_world():
    return 'Hello, World!'

@app.route('/msg/webhook', methods=["GET"])
def fb_webhook():
    verification_code = 'I_AM_VERIFICIATION_CODE'
    verify_token = request.args.get('hub.verify_token')
    if verification_code == verify_token:
        return request.args.get('hub.challenge')

@app.route('/msg/webhook', methods=['POST'])
def fb_receive_message():
    message_entries = json.loads(request.data.decode('utf8'))['entry']
    print(message_entries)
    for entry in message_entries:
        for message in entry['messaging']:
            if message.get('message') and 'attachments' not in message['message']:
                try:
                    main_handler.handle(message)
                    print("{sender[id]} says {message[text]}".format(**message))
                except Exception as e:
                    print('--------------ERROR----------------')
                    print(e)
                    print('-----------------------------------')
                    traceback.print_exc()
                    print('-----------------------------------')
                    client.send_text('1628181117202388', ['Hi boss, \nAn error occured when a user sent the following message:', message['message']['text'], 'Here is the error:','```C\n'+traceback.format_exc()+'\n```'])
                    client.send_text(message['sender']['id'], responses['error']['general'])
                    main_handler.update_state(message['sender']['id'], '')

    return "Hi", 200

@app.route('/send_notifications')
def send_notifications():
    notification_handler.handle()
    return 'hi', 200

if __name__ == '__main__':
    port = int(os.environ.get('PORT', 443))
    app.run(host="0.0.0.0", port=port, ssl_context=('cert.pem', 'key.pem'))


import random

def es_primo(numero):
    condicion = 1
    divisores = 0
    while condicion <= numero:
        if numero % condicion == 0:
            divisores += 1
        condicion += 1
    if divisores == 2:
        return True
    return False

def es_fibonacci(numero):
    num1 = 0
    num2 = 1
    secuencia = 0
    while secuencia <= numero:
        secuencia = num1 + num2
        num1 = num2
        num2 = secuencia
        if secuencia == numero:
            return True
        return False

def hacer_matriz(matriz):
    dimensiones = random.randint(4,6)
    for fila in range(dimensiones):
        fila_actual = []
        for columna in range(dimensiones):
            fila_actual.append(random.randint(1,15))
        matriz.append(fila_actual)

def mostrar_matriz(matriz):
    for fila in matriz:
        print(" ".join("{:4}".format(elemento) for elemento in fila))

matriz = []
hacer_matriz(matriz)
print("\nMatriz Original")
mostrar_matriz(matriz)

def matriz_promedios_fila(matriz):
    promedios = []
    for fila in range(len(matriz)):
        suma_fila = 0
        promedio_fila = 0
        contador_fila = 0
        for columna in range(len(matriz)):
            numero = matriz[fila][columna]
            suma_fila += numero
            contador_fila += 1
        promedio_fila = suma_fila/contador_fila
        promedios.append(promedio_fila)
    return promedios
promedios = matriz_promedios_fila(matriz)

print("matriz con promedios")
for i, fila in enumerate(matriz):
        print(" ".join("{:4}".format(elemento) for elemento in fila),f"  Promedio:  {promedios[i]}")

def matriz_promedios_columna(matriz):
    promedios = []
    for fila in range(len(matriz)):
        suma_columna = 0
        promedio_columna = 0
        contador_columna = 0
        for columna in range(len(matriz)):
            numero = matriz[columna][fila]
            suma_columna += numero
            contador_columna += 1
        promedio_columna = suma_columna/contador_columna
        promedios.append(promedio_columna)
    return promedios
promedios = matriz_promedios_columna(matriz)
print(promedios)

def matriz_promedio(matriz):
    promedio = 0
    suma = 0
    contador = 0
    for i in range(len(matriz)):
        for j in range(len(matriz)):
            suma += matriz[i][j]
            contador += 1
    promedio = suma/contador
    return promedio
promedio = matriz_promedio(matriz)
print(promedio)


from tensorflow import keras

import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
import seaborn as sns
import numpy as np

from custom_models import custom_model, old_model
from data_preprocessing import Preprocessing
from data_augmentation import DataAugmentation
from config import ConstantConfig

class TrainModel:
    encoder = ConstantConfig().ENCODER
    batch_size = 64
    epochs = 50
    early_stopping = keras.callbacks.EarlyStopping(
        monitor="val_accuracy", patience=2)

    def __init__(self) -> None:
        self.preprocessing = Preprocessing()
        self.augmentation = DataAugmentation()
        img_data = self.augmentation.process_data()
        self.labels_train, self.labels_test, self.imgs_train, self.imgs_test = self.preprocessing.train_test_split(
            img_data)
        self.model = old_model()
        # model = custom_model()

    def train_model(self):
        self.model.fit(self.imgs_train, self.labels_train, batch_size=self.batch_size, epochs=self.epochs,
                       validation_data=(self.imgs_test, self.labels_test), callbacks=[self.early_stopping])
        self.save_model()

    def test_model(self, test_data=None):
        if test_data:
            return np.argmax(self.model.predict(test_data), axis=1)
        return np.argmax(self.model.predict(self.imgs_test), axis=1)

    def confusion_matrix(self):
        cm = confusion_matrix(self.labels_test, self.labels_pred,
                              labels=list(self.encoder.inverse.keys()))
        plt.figure(figsize=(12, 12))
        sns.heatmap(cm, annot=True, cbar=False, cmap='Blues', xticklabels=list(
            self.encoder.keys()), yticklabels=list(self.encoder.keys()))
        plt.show()

    def save_model(self, model_name='geo_model.model'):
        self.model.save(model_name, save_format='h5')

if __name__ == '__main__':
    model_inst = TrainModel()
    model_inst.train_model()


from django.urls import reverse_lazy
from django.views.generic.edit import FormView

from forum.forms import ContactForm

class ContactView(FormView):
    template_name = 'pages/contact.html'
    form_class = ContactForm
    success_url = reverse_lazy('forum:home')

    def form_valid(self, form):
        form.send_email()
        return super().form_valid(form)


from pathlib import Path
from tensorflow.keras import layers
from tensorflow.keras import models
from tensorflow.keras import optimizers
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import tensorflow as tf
from solve_cudnn_error import solve_cudnn_error

solve_cudnn_error()

base_dir = Path('cats_and_dogs_small')

train_dir = base_dir / 'train'
validation_dir = base_dir / 'validation'
test_dir = base_dir / 'test'
train_cats_dir = train_dir / 'cats'
train_dogs_dir = train_dir / 'dogs'
validation_cats_dir = validation_dir / 'cats'
validation_dogs_dir = validation_dir / 'dogs'
test_cats_dir = test_dir / 'cats'
test_dogs_dir = test_dir / 'dogs'

print('total training cat images:', len(list(train_cats_dir.glob('*'))))
print('total training dog images:', len(list(train_dogs_dir.glob('*'))))
print('total validation cat images:', len(list(validation_cats_dir.glob('*'))))
print('total validation dog images:', len(list(validation_dogs_dir.glob('*'))))

def create_model():
    model = models.Sequential()
    model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(64, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Flatten())
    model.add(layers.Dense(64, activation='relu'))
    model.add(layers.Dense(1, activation='sigmoid'))

    model.compile(loss='binary_crossentropy',
                  optimizer=optimizers.SGD(lr=0.01),
                  metrics=['acc'])

    return model

model = create_model()
model.summary()

train_datagen = ImageDataGenerator(rescale=1./255,)

test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(train_dir,
                                                    target_size=(150, 150),
                                                    batch_size=32,
                                                    class_mode='binary')

validation_generator = test_datagen.flow_from_directory(validation_dir,
                                                        target_size=(150, 150),
                                                        batch_size=32,
                                                        class_mode='binary')

callbacks = []
callbacks.append(tf.keras.callbacks.ModelCheckpoint('./checkpoint.h5',
                                                    save_best_only=True,
                                                    save_weights_only=False))

history = model.fit_generator(train_generator,
                              steps_per_epoch=train_generator.__len__(),
                              callbacks=callbacks,
                              epochs=30,
                              validation_data=validation_generator,
                              validation_steps=validation_generator.__len__())

# Plot training history
import matplotlib.pyplot as plt

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(acc))

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.savefig('acc.png')

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.savefig('loss.png')


import random
import requests
from currency_converter import CurrencyConverter
from forex_python.converter import CurrencyRates

def get_guess_from_user():
    while True:
        try:
            guess = float(input("Enter your guess for the value in ILS: "))
            break
        except ValueError:
            print("Invalid input. Please enter a valid number.")
    return guess

def get_money_interval(difficulty, ran_number):
    c = CurrencyConverter()
    new_amount = c.convert(ran_number, 'USD', 'ILS')

    difficulty_calc = (new_amount - (6 - difficulty), new_amount +(6 - difficulty))

    return difficulty_calc

# def old_get_money_interval(difficulty, ran_number):
#     response = requests.get("https://api.exchangerate-api.com/v4/latest/USD")
#     data = response.json()
#     rate = data["rates"]["ILS"]
#
#     money_value = ran_number * rate
#     difficulty_calc = (money_value - (6 - difficulty), money_value +(6 - difficulty))
#
#     return difficulty_calc
def play(difficulty):
    print('''Currency Roulette Game - Guess the Value in ILS!
    You will be given a random amount in USD.
    Your task is to guess the value of that amount in ILS.''')

    ran_number = random.randint(1, 100)
    money_interval = get_money_interval(difficulty, ran_number)
    print(f"Guess the value in ILS for the amount of {ran_number} USD.")

    user_guess = float(get_guess_from_user())

    lower_bound = money_interval[0]
    upper_bound = money_interval[1]

    if lower_bound <= user_guess <= upper_bound:
        print("Congratulations! Your guess is within the correct range.")
        return True
    else:
        print("Sorry, your guess is outside the correct range.")
        return False


name1=raw_input("Enter your name:")
name2=raw_input("Enter your boyfriend's/girlfriend's name:")

calculate=len(name1)+len(name2)
if len(name1)>len(name2):
    calculate-=5
else:
    calculate+=3

calculate*=42
calculate=calculate/(100+len(name2))

if calculate>10:
    calculate=10
else:
    round(calculate,0)
print ("{} and {} score is {} out of 10").format(name1,name2,calculate)


from flask import Flask, jsonify, request, make_response
import torch
from transformers import BertTokenizer, BertModel
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
import pandas as pd

app=Flask(__name__)

# @app.route('/<string:text1>/<string:text2>')
@app.route('/post_json',methods=['POST'])
def hello_world():

  data1=request.get_json()
  text1=data1['text1']
  text2=data1['text2']
# Load pre-trained BERT model and tokenizer
  tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
  model = BertModel.from_pretrained('bert-base-uncased')
# Function to get sentence embeddings using BERT model
  def get_sentence_embedding(sentence):
      inputs = tokenizer(sentence, return_tensors='pt', truncation=True, padding=True)
      with torch.no_grad():
          outputs = model(**inputs)
      return outputs.last_hidden_state.mean(dim=1).squeeze().numpy()
# Function to calculate cosine similarity between two sentence embeddings

  def cosine_similarity_score(embedding1, embedding2):
      similarity = cosine_similarity([embedding1], [embedding2])
      return similarity[0][0]

  embedding1 = get_sentence_embedding(text1)
  embedding2 = get_sentence_embedding(text2)
  similarity_score = cosine_similarity_score(embedding1, embedding2)

  result={
     "similarity_score":str(similarity_score)
    }
  return jsonify(result)

if __name__=="__main__":
    app.run(debug=True)


usrInput = input("Enter your binary number:")
t = usrInput[::-1]
length=len(usrInput)
total=0
exp=0
while length > 20:
    print("Error")
    quit()

while exp < length:
    if t[exp] =="1":
        total+=2**int(exp)
    exp+=1
print(total)


def money_change(n: int):
    coins = [10, 5, 1]
    n_of_coins = 0
    remaining = int(n)
    for coin in coins:
        n_of_coins += remaining//coin
        remaining = remaining % coin

        if remaining == 0:
            break

    assert remaining == 0
    return n_of_coins

if __name__ == "__main__":
    n = int(input())
    print(money_change(n))


from pygame.locals import *
import numpy as np
import os
import pygame
import sys
import time

from source.core.game_objects.bomb.Bomb import Bomb
from source.core.game_objects.bomb.Fire import Fire
from source.core.game_objects.character.Cpu import Cpu
from source.core.ui.GameOver import GameOver
from source.core.ui.Map import Map
from source.core.ui.Pause import Pause
from source.core.utils import Constants
from source.core.utils.ObjectEvents import CharacterEvents

class Match:
    """
    Class which represents a match. It handles all game objects interactions,
    user inputs and cpu decisions.
    """

    def __init__(self, characters, sprites):
        """
        Default constructor. It assigns initial values to all variables.
        :param characters: Tuple of characters, with the first element being the
        list of players and the second the list of cpus in the match.
        :param sprites: Dict of sprites previously loaded.
        """

        self.__sprites = sprites

        # Loading font
        assets_path = (os.path.dirname(os.path.realpath(__file__)) +
                       '/../../../assets/')
        self.__menu_font = pygame.font.Font(assets_path + "font/04B_30__.TTF",
                                            Constants.FONT_SIZE)

        # Creating map and game objects
        self.__map = Map(time.time())
        self.__players = characters[0]
        self.__cpus = characters[1]
        self.__bombs = list()
        self.__fires = list()

        # Array indicating characters which are still alive
        self.__alive_characters = False * np.ones(4)
        for player in self.__players:
            self.__alive_characters[player.id] = True
        for cpu in self.__cpus:
            self.__alive_characters[cpu.id] = True

        # Handles game pausing and game over
        self.__game_state = Constants.IN_GAME
        self.__pause = None
        self.__game_over = None
        self.__time_pause = 0

    def play(self, clock, surface):
        """
        Match loop, which handles different game states.
        :param clock: Pygame clock object.
        :param surface: Pygame surface object.
        :return: Current state the game is in.
        """

        # Draws map and clock
        self.__map.draw(surface)
        self.__map.set_is_paused(self.__game_state == Constants.PAUSE or
                                 self.__game_state == Constants.OVER)

        # State machine
        if self.__game_state == Constants.PAUSE:
            self.__update_pause_screen(surface)
        elif self.__game_state == Constants.OVER:
            self.__update_game_over_screen(clock, surface)
        elif self.__game_state == Constants.IN_GAME:
            self.__update_match(clock, surface)
        elif self.__game_state == Constants.MAIN_MENU:
            return Constants.STATE_MENU

        return Constants.STATE_PLAYING

    def is_over(self):
        """
        Checks if match is over.
        :return: True if the game state is in OVER.
        """

        return self.__game_state == Constants.OVER

    def __update_match(self, clock, surface):
        """
        Updates the match itself, handling game object interactions, player
        inputs and ai decisions.
        :param clock: Pygame clock object.
        :param surface: Pygame surface.
        :return: Match updated state.
        """
        t = time.time()

        # Handles keyboard events
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            if event.type == KEYUP:
                if event.key == K_ESCAPE:
                    self.__game_state = Constants.PAUSE
                else:
                    for player in self.__players:
                        player.key_up(event.key)

            if event.type == KEYDOWN:
                for player in self.__players:
                    player.key_down(event.key)

        # Decides IA's moves
        for cpu in self.__cpus:
            if cpu.is_alive or cpu.get_reward() != 0:
                cpu.decide(self.__map.get_grid().get_tilemap(),
                           self.__players + self.__cpus, clock)

        # Updates and draws bombs
        for bomb in self.__bombs:
            if bomb.update(clock, self.__map.get_grid().get_tilemap()):
                bomb.draw(surface)
            else:
                self.__fires.append(Fire(bomb.tile, bomb.range, bomb.id,
                                         self.__sprites['fire']))
                for player in self.__players:
                    if player.id == bomb.id:
                        player.bomb_exploded()
                for cpu in self.__cpus:
                    if cpu.id == bomb.id:
                        cpu.bomb_exploded()
                self.__bombs.remove(bomb)

        # Updates and draws fires
        for fire in self.__fires:
            if fire.update(clock, self.__map.get_grid().get_tilemap()):
                fire.draw(surface)
                for tile in fire.get_triggered_bombs():
                    for bomb in self.__bombs:
                        if bomb.tile == tile:
                            bomb.explode()
                for cpu in self.__cpus:
                    if cpu.id == fire.id:
                        cpu.reward(fire.reward)
            else:
                self.__fires.remove(fire)

        # Updates and draws characters
        for player in self.__players:
            if not self.__update_character(player, clock, surface):
                self.__alive_characters[player.id] = False
        for cpu in self.__cpus:
            if not self.__update_character(cpu, clock, surface):
                self.__alive_characters[cpu.id] = False

        # Checks if game is over
        if np.count_nonzero(self.__alive_characters) == 0:
            self.__game_state = Constants.OVER
        elif np.count_nonzero(self.__alive_characters) == 1:
            for i in range(len(self.__alive_characters)):
                if self.__alive_characters[i]:
                    for player in self.__players:
                        if player.id == i:
                            player.special_event(CharacterEvents.WIN)
                    for cpu in self.__cpus:
                        if cpu.id == i:
                            cpu.special_event(CharacterEvents.WIN)
            self.__game_state = Constants.OVER

        if self.__game_state == Constants.OVER:
            for cpu in self.__cpus:
                cpu.decide(self.__map.get_grid().get_tilemap(),
                           self.__players + self.__cpus, clock, True)

    def __update_pause_screen(self, surface):
        """
        Handles the paused screen interface and updates game state.
        :param surface: Pygame surface.
        """

        if not self.__pause:
            self.__time_pause = time.time()
            self.__pause = Pause()

        self.__pause.draw(surface)
        self.__game_state = self.__pause.update()

        if self.__game_state == Constants.IN_GAME:
            self.__map.increment_delta_pause(
                time.time() - self.__time_pause)
            del self.__pause
            self.__pause = None

    def __update_game_over_screen(self, clock, surface):
        """
        Handles the game over screen interface and updates game state. It also
        continues the game objects animations already started.
        :param clock: Pygame clock object.
        :param surface: Pygame surface.
        """

        if not self.__game_over:
            self.__game_over = GameOver()

        # Updates bombs animations
        for bomb in self.__bombs:
            if bomb.update(clock, self.__map.get_grid().get_tilemap()):
                bomb.draw(surface)

        # Updates fire animations
        for fire in self.__fires:
            if fire.update(clock, self.__map.get_grid().get_tilemap()):
                fire.draw(surface)

        # Updates character animations
        for player in self.__players:
            self.__update_character(player, clock, surface)
        for cpu in self.__cpus:
            self.__update_character(cpu, clock, surface)

        self.__game_over.draw(surface)
        self.__game_state = self.__game_over.update()

    def __update_character(self, character, clock, surface):
        """
        Updates and draws a character according to the map and its events.
        :param character: Character to be updated.
        :param clock: Match clock.
        :param surface: Drawing surface.
        :return: True if the character is still alive.
        """

        tilemap = self.__map.get_grid().get_tilemap()
        if character.update(clock, tilemap):
            # Check if character died
            if (tilemap[character.tile] == Constants.UNIT_FIRE or
                    tilemap[character.tile] == Constants.UNIT_CENTER_FIRE):
                self.__reward_kill(character)
                if isinstance(character, Cpu) and character.is_alive:
                    character.reward(Constants.DEATH_REWARD)
                character.special_event(CharacterEvents.DIE)
                character.draw(surface)
                return False
            # Check if character picked up a powerup
            elif tilemap[character.tile] == Constants.UNIT_POWERUP_BOMB_SHOW:
                character.increase_bomb()
                tilemap[character.tile] = Constants.UNIT_EMPTY
            elif tilemap[character.tile] == Constants.UNIT_POWERUP_FIRE_SHOW:
                character.increase_fire()
                tilemap[character.tile] = Constants.UNIT_EMPTY
            elif tilemap[character.tile] == (
                    Constants.UNIT_POWERUP_VELOCITY_SHOW):
                character.increase_speed()
                tilemap[character.tile] = Constants.UNIT_EMPTY
            # Check if character placed a bomb
            elif character.placed_bomb(tilemap[character.tile]):
                self.__bombs.append(Bomb(character.tile, character.fire_range,
                                         character.id, self.__sprites['bomb']))

            character.draw(surface)
            return True

        return False

    def __reward_kill(self, character_dead):
        """
        Finds the character which placed that bomb and rewards it.
        :param character_dead: Character object who just died.
        """

        if not character_dead.is_alive:
            return

        for fire in self.__fires:
            if fire.contains(character_dead.tile):
                for cpu in self.__cpus:
                    if cpu.id == fire.id and character_dead.id != cpu.id:
                        cpu.reward(Constants.KILL_REWARD)


from sklearn.model_selection import train_test_split
import tensorflow as tf
import keras
import os
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from os import listdir
from matplotlib import image
import random
from keras.utils import np_utils
from keras import callbacks

# batch_size = higher batch size could be better, but I haven't got enough gpu memory
batch_size = 32
epochs = 50

train_dir = 'chest_xray/chest_xray/train/'
val_folder = 'chest_xray/chest_xray/val/'
test_folder = 'chest_xray/chest_xray/test/'

pneumonia_str = "PNEUMONIA/"
normal_str = "NORMAL/"

train_p_dir = train_dir + pneumonia_str
train_n_dir = train_dir + normal_str

val_p_dir = val_folder + pneumonia_str
val_n_dir = val_folder + normal_str

test_p_dir = test_folder + pneumonia_str
test_n_dir = test_folder + normal_str

filenames = tf.io.gfile.glob(str('chest_xray/chest_xray/train/*/*'))
filenames.extend(tf.io.gfile.glob(str('chest_xray/chest_xray/val/*/*')))

train_filenames, val_filenames = train_test_split(filenames, test_size=0.2)

COUNT_NORMAL = len([filename for filename in train_filenames if "NORMAL" in filename])
print("Normal images count in training set: " + str(COUNT_NORMAL))

COUNT_PNEUMONIA = len([filename for filename in train_filenames if "PNEUMONIA" in filename])
print("Pneumonia images count in training set: " + str(COUNT_PNEUMONIA))

train_list_ds = tf.data.Dataset.from_tensor_slices(train_filenames)
val_list_ds = tf.data.Dataset.from_tensor_slices(val_filenames)

# Print few file names
for f in train_list_ds.take(5):
    print(f.numpy())

# Check if there is good ratio between train and val photos number
TRAIN_IMG_COUNT = tf.data.experimental.cardinality(train_list_ds).numpy()
print("Training images count: " + str(TRAIN_IMG_COUNT))
VAL_IMG_COUNT = tf.data.experimental.cardinality(val_list_ds).numpy()
print("Validating images count: " + str(VAL_IMG_COUNT))

CLASS_NAMES = np.array([str(tf.strings.split(item, os.path.sep)[-1].numpy())[2:-1]
                        for item in tf.io.gfile.glob(str("chest_xray/chest_xray/train/*"))])
print(CLASS_NAMES)

def get_label(file_path):
    # convert the path to a list of path components
    parts = tf.strings.split(file_path, os.path.sep)
    # The second to last is the class-directory
    return parts[-2] == "PNEUMONIA"

IMAGE_SIZE = [180, 180]
input_shape = (IMAGE_SIZE[0], IMAGE_SIZE[1], 3)

def decode_img(img):
  # convert the compressed string to a 3D uint8 tensor
  img = tf.image.decode_jpeg(img, channels=3)
  # Use `convert_image_dtype` to convert to floats in the [0,1] range.
  img = tf.image.convert_image_dtype(img, tf.float32)
  # resize the image to the desired size.
  return tf.image.resize(img, IMAGE_SIZE)

def process_path(file_path):
    label = get_label(file_path)
    # load the raw data from the file as a string
    img = tf.io.read_file(file_path)
    img = decode_img(img)
    return img, label

AUTOTUNE = tf.data.experimental.AUTOTUNE
train_ds = train_list_ds.map(process_path, num_parallel_calls=AUTOTUNE)
val_ds = val_list_ds.map(process_path, num_parallel_calls=AUTOTUNE)

test_list_ds = tf.data.Dataset.list_files(str('chest_xray/chest_xray/test/*/*'))
TEST_IMAGE_COUNT = tf.data.experimental.cardinality(test_list_ds).numpy()
test_ds = test_list_ds.map(process_path, num_parallel_calls=AUTOTUNE)
test_ds = test_ds.batch(batch_size)

def prepare_for_training(ds, cache=True, shuffle_buffer_size=1000):
    # This is a small dataset, only load it once, and keep it in memory.
    # use `.cache(filename)` to cache preprocessing work for datasets that don't
    # fit in memory.
    if cache:
        if isinstance(cache, str):
            ds = ds.cache(cache)
        else:
            ds = ds.cache()

    ds = ds.shuffle(buffer_size=shuffle_buffer_size)

    # Repeat forever
    ds = ds.repeat()

    ds = ds.batch(batch_size)

    # `prefetch` lets the dataset fetch batches in the background while the model
    # is training.
    ds = ds.prefetch(buffer_size=AUTOTUNE)

    return ds

train_ds = prepare_for_training(train_ds)
val_ds = prepare_for_training(val_ds)
test_ds = prepare_for_training(test_ds)


import sys
from collections import deque

dx = [1,-1,0,0]
dy = [0,0,1,-1]

n,m = map(int,sys.stdin.readline().split())
arr = []
for _ in range(m):
    arr.append(list(sys.stdin.readline().strip()))

visit = [[int(1e9)]*n for _ in range(m)]
queue = deque([[0,0]])
visit[0][0] = 0
while queue:
    x,y = queue.popleft()
    for t in range(4):
        nx = x + dx[t]
        ny = y + dy[t]
        if 0 <= nx < m and 0 <= ny < n:
            if arr[nx][ny] == '0' and visit[nx][ny] > visit[x][y]:
                visit[nx][ny] = visit[x][y]
                queue.append([nx,ny])
            elif arr[nx][ny] == '1' and visit[nx][ny] > visit[x][y] + 1:
                visit[nx][ny] = visit[x][y] + 1
                queue.append([nx,ny])

print(visit[m-1][n-1])


class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals.sort(key=lambda x: x[0])
        noIntervals = [intervals[0]]

        for idx in range(1, len(intervals)):
            left = noIntervals[-1]
            right = intervals[idx]
            if(left[1] < right[0]):
                noIntervals.append(right)
                continue
            if(left[1] < right[1]):
                noIntervals.pop()
                noIntervals.append([left[0], right[1]])
            else:
                continue

        return noIntervals


import pandas as pd
from dateutil import parser
import numpy as np

def load_data(_file, pct_split):
    """Load test and train data into a DataFrame
    :return pd.DataFrame with ['test'/'train', features]"""

    # load train and test data
    data = pd.read_csv(_file)

    # split into train and test using pct_split
    # data_train = ...
    # data_test = ...

    # concat and label
    # data_out = pd.concat([data_train, data_test], keys=['train', 'test'])

    data_out = data

    return data_out

def preprocess_data(df):
    """
    General element-wise cleanup of full data set
    :return: df, cleaned data
    """

    # convert time to datetime
    df['time'] = pd.to_datetime(df['time'], errors='coerce')

    return df

from datetime import timedelta
import numpy as np
import pandas as pd

def create_target(df):

    def btc_target(row):
        time_window = df['price'][(df['time'] > row) &
                                  (df['time'] <= row + timedelta(minutes=30))].values

        # return null if nothing is within this time window
        if len(time_window) == 0:
            return None

        # calculate thresholds
        minY = np.min(time_window)
        maxY = np.max(time_window)
        currentY = time_window[0]
        thresh_low = currentY * (1 - 0.0025)
        thresh_hi = currentY * (1 + 0.0025)

        # determine which thresholds are exceeded
        exceeds_low = minY <= thresh_low
        exceeds_hi = maxY >= thresh_hi

        # do Boolean logic to create a target. {neither: 0, high: 1, low: 0, both: 2}
        if (not exceeds_low) and (not exceeds_hi):
            target = 0
        elif exceeds_low and (not exceeds_hi):
            target = 1
        elif (not exceeds_low) and exceeds_hi:
            target = 2
        else:
            target = 3

        return target

    df['target'] = df['time'].apply(btc_target)

    return df

def forward_window_vals(time_col, val_col):
    return


from question_model import Question
from question_data import quiz_questions_list
from quiz_brain import QuizBrain

question_bank = []

for question in quiz_questions_list:
    question_name = question['ask_question']
    answer = question['the_answer']

    new_question = Question(question_name, answer)
    question_bank.append(new_question)

quiz = QuizBrain(question_bank)

while quiz.still_has_questions():
    quiz.next_question()

print("You've completed the quiz challenge.")
print(f"Your final score was {quiz.score}/{quiz.question_number}")


from jira import JIRA
import pandas as pd
from datetime import datetime
import plotly
import plotly.graph_objs as go
import numpy as np
from ast import literal_eval
from IPython.display import display, HTML

pd.set_option('display.max_columns', 999)
plotly.offline.init_notebook_mode()

def product_closed_time(version):
    eng_data = pd.read_csv('eng_changelog_clean.csv', encoding='utf_8').drop(['Unnamed: 0'], axis=1)
    data = pd.read_csv('pm_changelog_clean.csv', encoding='utf_8').drop(['Unnamed: 0', 'remove'], axis=1)
    # Only include version string in pm_changelog_clean 'fix_version' column
    for index, row in data.iterrows():
        fix = row["fix_version"]
        try:
            fix = literal_eval(fix)[0]['name']
            data.at[index, "fix_version"] = fix
        except:
            data.at[index, "fix_version"] = "None"
    # Convert timestamps to utc, set index as 'updated_time' and sort index
    eng_data['updated_time'] = pd.to_datetime(eng_data['updated_time'], utc=True).dt.tz_convert('utc')
    eng_data = eng_data.set_index(pd.DatetimeIndex(eng_data['updated_time'])).sort_index()
    eng_data.index.names = ['index']
    eng_time_delta = []
    # Get time diff for non-PM issues
    for key in eng_data['eng_key'].unique():
        if key.startswith('PM') == False:
            issue = eng_data.loc[eng_data['eng_key'] == key]
            issue_diff = issue['updated_time'].diff()
            issue['time_diff'] = issue_diff
            # Get Sum of all status change time diffs
            # *** TO DO: GET SUM OF ALL _RELEVANT_ STATUS CHANGE TIME DIFFS ***
            issue['time_sum'] = issue['time_diff'].sum()
            eng_time_delta.append(issue)

    eng_time = pd.concat(eng_time_delta)
    eng_time = eng_time.loc[eng_time['fix_version'] == version]

    #eng_time = eng_time.loc[(eng_time["updated_status"] == "Closed") | (eng_time["updated_status"] == "Done")]

    ddb = eng_time.loc[eng_time['DDB'] == 1]

    return ddb

def milestone_investment(version):
    eng_data = pd.read_csv('eng_changelog_clean.csv', encoding='utf_8').drop(['Unnamed: 0'], axis=1)
    data = pd.read_csv('pm_changelog_clean.csv', encoding='utf_8').drop(['Unnamed: 0', 'remove'], axis=1)
    for index, row in data.iterrows():
        fix = row["fix_version"]
        try:
            fix = literal_eval(fix)[0]['name']
            data.at[index, "fix_version"] = fix
        except:
            data.at[index, "fix_version"] = "None"

    eng = eng_data.loc[eng_data['fix_version'] == version]
    eng = eng.loc[(eng["updated_status"] == "Closed") | (eng["updated_status"] == "Done")]

    total = eng['eng_key'].unique().size

    ddb = eng.loc[eng['DDB'] == 1]["eng_key"].unique().size
    crt = eng.loc[(eng['Customer_Priority'] == 1) | (eng['Services_Priority'] == 1)]["eng_key"].unique().size
    qual = eng.loc[eng['issue_type'] == 'Bug']["eng_key"].unique().size
    pm = data.loc[data["fix_version"] == version].loc[(data["updated_status"] == "Closed") | (data["updated_status"] == "Done")]['related_key'].unique().size

    unlabelled = total - (ddb + crt + qual + pm)

    percentages = {
        'PM Backlog': str(format(calc_percent(pm, total), '.2f')) + '%',
        'Engineering': str(format(calc_percent(ddb, total), '.2f')) + '%',
        'Quality': str(format(calc_percent(qual, total), '.2f')) + '%',
        'CRT': str(format(calc_percent(crt, total), '.2f')) + '%',
        'Unlabelled': str(format(calc_percent(unlabelled, total), '.2f')) + '%',
    }

    issue_count = {
        'PM Backlog': pm,
        'Engineering': ddb,
        'Quality': qual,
        'CRT': crt,
        'Unlabelled': unlabelled,
    }

    inv_list = [
        [version, 'CRT', crt],
        [version, 'Quality', qual],
        [version, 'Engineering', ddb],
        [version, 'PM Backlog', pm]
    ]

    inv_df = pd.DataFrame(inv_list, columns=['release', 'category', 'closed'])
    trace = [
        go.Bar(
            name="Investment Breakdown for " + version,
            y=inv_df['category'],
            x=inv_df['closed'],
            orientation='h',
            marker=go.bar.Marker(
                color='#374c80',
            )
        )
    ]
    layout = go.Layout(
        barmode='stack',
        title='Milestone Investment, ' + version,
        xaxis=dict(
            #title='# of Issues',
            tickmode='linear',
            titlefont=dict(
                size=12,
            )
        ),
        yaxis=dict(
            #title='Objective',
            titlefont=dict(
                size=12,
            )
        )
    )
    fig = go.Figure(data=trace, layout=layout)
    plotly.offline.iplot(fig, filename='eng_' + version)

    display(HTML('''

Nota Bene: Data Veracity

For each release, a subset of issues do not include metadata (shown below as Unlabelled) that corresponds to Product Backlog, CRT, Quality or Engineering categories. Accordingly, the percentages listed for categories in each release do not have a sum total of 100%.

    '''))
    display(pd.DataFrame.from_records([percentages]))
    display(pd.DataFrame.from_records([issue_count]))
    display(HTML('Total Number of Issues: ' + str(total) + ''))

def calc_percent(category, total):
    return (category / total) * 100

data = pd.read_csv('pm_changelog_clean.csv', encoding='utf_8').drop(['Unnamed: 0', 'remove'], axis=1)

def pm_okrs():
    okrs_list = ["Cloud_JSX", "Enablement", "JSX", "Product_Performance", "Platform", "RN_Upgrade"]
    milestones = ["Milestone1", "Milestone2"]
    okrs = []
    for milestone in milestones:
        for okr in okrs_list:
            row = [milestone, okr, get_closed(data.loc[data[okr] == 1], milestone), get_total(data.loc[data[okr] == 1], milestone)]
            okrs.append(row)
    okrs_df = pd.DataFrame(okrs, columns=["milestone", "label", "closed", "total"])
    okrs_df["diff"] = okrs_df["total"].sub(okrs_df['closed'], axis=0)

    trace = [
        go.Bar(
            name="Closed",
            y=okrs_df.loc[okrs_df["milestone"] == "Milestone1"]["label"],
            x=okrs_df.loc[okrs_df["milestone"] == "Milestone1"]["closed"],
            orientation='h',
            marker=go.bar.Marker(
                color='#374c80',
            )
        ),
        go.Bar(
            name="Remaining",
            y=okrs_df.loc[okrs_df["milestone"] == "Milestone1"]["label"],
            x=okrs_df.loc[okrs_df["milestone"] == "Milestone1"]["diff"],
            orientation='h',
            marker=go.bar.Marker(
                color='#ff764a',
            )
        ),
    ]
    layout = go.Layout(
        barmode='stack',
        title='Product Objectives, Milestone 1',
        xaxis=dict(
            title='# of Issues',
            tickmode='linear',
            titlefont=dict(
                size=12,
            )
        ),
        yaxis=dict(
            #title='Objective',
            titlefont=dict(
                size=12,
            )
        )
    )
    fig = go.Figure(data=trace, layout=layout)
    plotly.offline.iplot(fig, filename='PM_OKRs_M1')
    trace = [
        go.Bar(
            name="Closed",
            y=okrs_df.loc[okrs_df["milestone"] == "Milestone2"]["label"],
            x=okrs_df.loc[okrs_df["milestone"] == "Milestone2"]["closed"],
            orientation='h',
            marker=go.bar.Marker(
                color='#374c80',
            )
        ),
        go.Bar(
            name="Remaining",
            y=okrs_df.loc[okrs_df["milestone"] == "Milestone2"]["label"],
            x=okrs_df.loc[okrs_df["milestone"] == "Milestone2"]["diff"],
            orientation='h',
            marker=go.bar.Marker(
                color='#ff764a',
            )
        ),
    ]
    layout = go.Layout(
        barmode='stack',
        title='Product Objectives, Milestone 2',
        xaxis=dict(
            title='# of Issues',
            tickmode='linear',
            titlefont=dict(
                size=12,
            )
        ),
        yaxis=dict(
            #title='Objective',
            titlefont=dict(
                size=12,
            )
        )
    )
    fig = go.Figure(data=trace, layout=layout)
    plotly.offline.iplot(fig, filename='PM_OKRs_M2')

def get_closed(df, label):
    return df.loc[(data["updated_status"] == "Closed") | (data["updated_status"] == "Done")].loc[data[label] == 1]["related_key"].unique().size

def get_total(df, label):
    return df.loc[data[label] == 1]["related_key"].unique().size