import os
import pandas as pd
import sys
import numpy as np

from Neo4j.baseline_model import find_best_doc_page_rank,find_best_doc_tf

def find_first_match(input_dataFrame,ref_uid,recall_at):
    ''' Return the position of the first match with the uid
    If first match is greater than recall_at the number is recall_at + 1
    If the is no match return recallat + 2
    '''
    try :
        number = input_dataFrame.uid[input_dataFrame.uid == ref_uid ].index.tolist()[0]
        if number > recall_at :
            number = recall_at + 1

    except IndexError :
        number = recall_at + 2
    return number

def compute_recall(uid_list ,number_of_uid ,recall_at) :
    ''' Given a list of uid return the recall
    '''
    count = 0
    for e in uid_list :
        if  e < recall_at + 1 :
            count +=1

    return count / number_of_uid

# Usefull functions to perform basic statistics
def compteNumberOfMiss(l):
    count = 0
    for e in l:
        if e > recall_at :
            count +=1
    return count

def mean(l):
    mean = 0
    for e in l:
        if e > recall_at :
            pass
        else :
            mean += e
    if len(l)-compteNumberOfMiss(l) != 0 :
        return mean/(len(l)-compteNumberOfMiss(l))
    else :
        return -1

def count_comon_element(list1,list2) :
    result = []
    for element in list1:
        if element in list2:
            result.append(element)
    return len(result)

if __name__ == '__main__':
    '''
    This script has been written to optimize the coefficient of our graph algorithms running on neo4j
    If you pass the argument :
        -find_best_doc_tf(query,coeff_1,coeff_2)[0] you'll get the best coefficient for tfidf algorithm
        -find_best_doc_page_rank(query,recall_at, coeff_1, coeff_2) you will get the best coefficient for
        pagerank algorithms
    '''

    # Constants
    recall_at = 10
    count = 0
    neo4j_tf = False
    neo4j_pr = True
    l_score =[]
    l_coefficient = []
    number_of_question = 100 # number of duplicat questions used to find the best coefficients

    # Path to the duplicate questions
    path_to_duplicate = '../Data/duplicate_questions.csv'

    # Load the duplicate questions
    df = pd.read_csv(path_to_duplicate)

    list_pd = []

    for coeff_1 in [0] :

        for coeff_2 in [1] :
            count = 0
            for index, row in df.iterrows():
                count +=1
                print('question number :', count)

                if count == number_of_question :
                    break

                question_uid = row['uid']
                question_title = eval(row['question'])['question_body'] + eval(row['question'])['question_title']

                theoritical_id_list = row['duplicate uid']

                # We query the title of the question
                query = question_title

                if neo4j_tf:
                    print('coefficent question_title_mutiplier', coeff_1)
                    print('coefficent question_body_mutiplier', coeff_2)
                    results_tf = find_best_doc_tf(query,question_title_mutiplier=coeff_1,question_body_mutiplier = coeff_2)[0]
                    results_tf= results_tf[0:recall_at-1]

                    common_element_term_frequency = count_comon_element(results_tf, theoritical_id_list)
                    print('number of matches TF', common_element_term_frequency)
                    common_element_pr = 0

                if neo4j_pr :
                    print('coefficent coefficientAuthor', coeff_1)
                    print('coefficent coefficientWord', coeff_2)
                    results_pr = find_best_doc_page_rank(query, recall_at, coefficientAuthor= coeff_1, coefficientWord=coeff_2)
                    results_pr = results_pr[0:recall_at-1]

                    common_element_pr = count_comon_element(results_pr, theoritical_id_list)
                    print('number of matches PR', common_element_pr)
                    common_element_term_frequency = 0

                list_pd.append([common_element_term_frequency,common_element_pr,coeff_1,coeff_2])

    df = pd.DataFrame(list_pd, columns=['nbr TF', 'nbr PR','Coeff 1','Coeff 2'])

    df.to_csv('optimization_coefficents_tf_local.csv')


"""
Stations/Channels download functions

:date: Dec 3, 2017

.. moduleauthor:: Riccardo Zaccarelli <rizac@gfz-potsdam.de>
"""
import re
from itertools import cycle
import logging
from urllib.request import Request

import pandas as pd
from sqlalchemy import or_, and_

from stream2segment.io.cli import get_progressbar
from stream2segment.io.db.pdsql import dbquery2df, shared_colnames, mergeupdate
from stream2segment.download.db.models import DataCenter, Station, Channel
from stream2segment.download.exc import FailedDownload
from stream2segment.download.url import read_async
from stream2segment.download.modules.utils import (get_dataframe_from_fdsn,
                                                   dbsyncdf, formatmsg,
                                                   logwarn_dataframe, strconvert)

# (https://docs.python.org/2/howto/logging.html#advanced-logging-tutorial):
logger = logging.getLogger(__name__)

def get_channels_df(session, datacenters_df, eidavalidator, net, sta, loc, cha,
                    starttime, endtime, min_sample_rate, update,
                    max_thread_workers, timeout, blocksize, db_bufsize,
                    show_progress=False):
    """Return a Dataframe representing a query to the station service of each
    URL in :func:`stream2segment.download.modules.datacenters_df` with the
    given arguments.

    :param datacenters_df: (DataFrame) the first item resulting from
        `get_datacenters_df`
    :param eidavalidator: None, or an instance of
        :class:`stream2segment.download.modules.datacenter.EidaValidator`
    :param min_sample_rate: minimum sampling rate, set to negative value
        for no-filtering (all channels)
    """
    postdata = get_post_data(net, sta, loc, cha, starttime, endtime)
    dc_url_key, dc_id_key = DataCenter.station_url.key, DataCenter.id.key
    iterable = ((id_, Request(url, data=('format=text\nlevel=channel\n'+
                                         post_data_str).encode('utf8')))
                for url, id_, post_data_str in zip(datacenters_df[dc_url_key],
                                                   datacenters_df[dc_id_key],
                                                   cycle([postdata])))

    ret = []
    url_failed_dc_ids = []
    with get_progressbar(show_progress, length=len(datacenters_df)) as pbar:
        for obj, url, result, exc, status_code in \
                read_async(iterable, urlkey=lambda obj: obj[-1], blocksize=blocksize,
                           max_workers=max_thread_workers, decode='utf8',
                           timeout=timeout):
            pbar.update(1)
            dcen_id = obj[0]
            if exc:
                url_failed_dc_ids.append(dcen_id)
                logger.warning(formatmsg("Unable to fetch stations", exc, url))
            else:
                try:
                    dframe = get_dataframe_from_fdsn(result, "channel", url)
                    if not dframe.empty:
                        dframe[Station.datacenter_id.key] = dcen_id
                        ret.append(dframe)
                except ValueError as verr:
                    logger.warning(formatmsg("Discarding response data", verr,
                                             url))

    db_cha_df = pd.DataFrame()
    if url_failed_dc_ids:
        # if some datacenter does not return station, warn with INFO
        dc_df_fromdb = datacenters_df.loc[datacenters_df[DataCenter.id.key].\
            isin(url_failed_dc_ids)]
        logger.info(formatmsg("Fetching stations from database for %d (of %d) "
                              "data center(s)", "download errors occurred"),
                    len(dc_df_fromdb), len(datacenters_df))
        logger.warning(dc_df_fromdb[DataCenter.dataselect_url.key].
                       to_string(index=False))
        db_cha_df = get_channels_df_from_db(session, dc_df_fromdb, net, sta,
                                            loc, cha, starttime, endtime,
                                            min_sample_rate)

    # build two dataframes which we will concatenate afterwards
    web_cha_df = pd.DataFrame()
    if ret:  # pd.concat complains for empty list
        try:
            web_cha_df = filter_channels_df(pd.concat(ret, axis=0,
                                                      ignore_index=True,
                                                      copy=False),
                                            net, sta, loc, cha,
                                            min_sample_rate)

            # this raises FailedDownload if we cannot save any element:
            web_cha_df = save_stations_and_channels(session, web_cha_df,
                                                    eidavalidator, update,
                                                    db_bufsize)
        except FailedDownload as qexc:
            if db_cha_df.empty:
                raise
            else:
                logger.warning(qexc)

    if db_cha_df.empty and web_cha_df.empty:
        # ok, now let's see if we have remaining datacenters to be
        # fetched from the db
        raise FailedDownload(formatmsg("No station found",
                                       "Unable to fetch stations from all "
                                       "data-centers, no data to fetch from "
                                       "the database. Check config and log "
                                       "for details"))
    ret = None
    if db_cha_df.empty:
        ret = web_cha_df
    elif web_cha_df.empty:
        ret = db_cha_df
    else:
        ret = pd.concat((web_cha_df, db_cha_df), axis=0, ignore_index=True,
                        sort=False)
    # the columns for the channels dataframe that will be returned
    return ret[[c.key for c in (Channel.id, Channel.station_id,
                                Station.latitude, Station.longitude,
                                Station.datacenter_id, Station.start_time,
                                Station.end_time, Station.network,
                                Station.station, Channel.location,
                                Channel.channel)]].copy()

def get_post_data(net, sta, loc, cha, starttime=None, endtime=None):
    """Return the string for a FDSN POST request according to the given
        net(works), sta(tions), loc(ations) and cha(nnels), all iterable of
        strings returned by :func:`stream2segment.download.utils.nslc_lists`

    Example:
    ```
    >>> get_post_data([], ['ABC'], [''], ['!A*', 'HH?', 'HN?'], None, None)
    '* ABC -- HH?,HN? * *'
    ```
    Note negations (!A*) not included: strings starting with "!" mean 'NOT' in
    this program's syntax: as this feature is not supported in an FDSN query it
    cannot be forwarded to any web service. The feature is used here in other
    module functions *after* downloading data

    Arguments are usually the output of
    :func:`stream2segment.download.utils.nslc_lists`:

    :param net: an iterable of strings denoting networks.
    :param sta: an iterable of strings denoting stations.
    :param loc: an iterable of strings denoting locations.
    :param cha: an iterable of strings denoting channels.
    """
    args = []
    for i, lst in enumerate([net, sta, loc, cha]):
        parsearg = '*'
        # parse arguments if given, and not all negations (in the latter case, just
        # provide * = accept all. We will filter out later, on the returned response)
        if lst and not all(v[0:1] == '!' for v in lst):
            parsearg = ",".join(v for v in lst if v[0:1] != '!')
            if i == 3 and not parsearg:  # loc: empty has to be input as '--'
                parsearg = '--'
        args.append(parsearg)

    args.append("*" if not starttime else starttime.isoformat())
    args.append("*" if not endtime else endtime.isoformat())

    return "{} {} {} {} {} {}".format(*args)

def filter_channels_df(channels_df, net, sta, loc, cha, min_sample_rate):
    """Filter out `channels_df` according to the given parameters. Raise
    `FailedDownload` if the returned filtered data frame woul be empty

    Note that `net, sta, loc, cha` filters will be considered only if negations
    (i.e., with leading exclamation mark: "!A*") because the 'positive' filters
    are FDSN stantard and are supposed to be already used in producing
    `channels_df`. Example:
    ```
        filter_channels_df(d, [], ['ABC'], [''], ['!A*', 'HH?', 'HN?'])
    ```
    basically takes the dataframe `d`, finds the column related to the
    `channels` key and removes all rows whose channel starts with 'A',
    returning the new filtered data frame.

    Arguments are usually the output of
    :func:`stream2segment.download.utils.nslc_lists`

    :param net: an iterable of strings denoting networks.
    :param sta: an iterable of strings denoting stations.
    :param loc: an iterable of strings denoting locations.
    :param cha: an iterable of strings denoting channels.
    :param min_sample_rate: numeric, minimum sample rate. If negative or zero,
        this parameter is ignored
    """
    # create a dict of regexps for pandas dataframe. FDSNWS do not support NOT
    # operators . Thus concatenate expression with OR
    dffilter = None
    sa_cols = (Station.network, Station.station, Channel.location,
               Channel.channel)

    for lst, sa_col in zip((net, sta, loc, cha), sa_cols):
        if not lst:
            continue
        lst = [_ for _ in lst if _[0:1] == '!']  # take only negation expr.
        if not lst:
            continue
        condition = ("^%s$" if len(lst) == 1 else "^(?:%s)$") % \
            "|".join(strconvert.wild2re(x[1:]) for x in lst)
        flt = channels_df[sa_col.key].str.match(re.compile(condition))
        if dffilter is None:
            dffilter = flt
        else:
            dffilter &= flt

    if min_sample_rate > 0:
        # account for Nones, thus negate the predicate below:
        flt = ~(channels_df[Channel.sample_rate.key] >= min_sample_rate)
        if dffilter is None:
            dffilter = flt
        else:
            dffilter &= flt

    ret = channels_df if dffilter is None else \
        channels_df[~dffilter].copy()  # noqa

    if ret.empty:
        raise FailedDownload("No channel matches user defined filters "
                             "(network, channel, sample rate, ...)")

    discarded_sr = len(channels_df) - len(ret)
    if discarded_sr:
        logger.warning("%d channel(s) discarded according to current "
                       "configuration filters (network, channel, sample rate, "
                       "...)", discarded_sr)

    return ret

def get_channels_df_from_db(session, datacenters_df, net, sta, loc, cha,
                            starttime, endtime, min_sample_rate):
    """Return a Dataframe of the database channels according to the
    arguments"""
    # Build SQL-Alchemy binary expressions (suffix '_be'), i.e. an object
    # reflecting a SQL clause)
    srate_be = True
    if min_sample_rate > 0:
        srate_be = Channel.sample_rate >= min_sample_rate
    # Select only relevant datacenters. Convert numnpy array `tolist()` because
    # database clauses work best with native Python objects:
    dc_be = Station.datacenter_id.in_(datacenters_df[DataCenter.id.key].
                                      tolist())
    # Select by starttime and endtime (below). Note that it must hold
    # station.endtime > starttime AND station.starttime< endtime
    stime_be = True
    if starttime:
        stime_be = ((Station.end_time == None) |
                    (Station.end_time > starttime))
    # endtime: Limit to metadata epochs ending on or before the specified end
    # time. Note that station's ent_time can be None
    etime_be = (Station.start_time < endtime) if endtime else True  # noqa
    sa_cols = [Channel.id, Channel.station_id, Station.latitude,
               Station.longitude, Station.start_time, Station.end_time,
               Station.datacenter_id, Station.network, Station.station,
               Channel.location, Channel.channel]
    # filter on net, sta, loc, cha, as specified in config and converted to
    # SQL-Alchemy binary expression:
    nslc_be = get_sqla_binexp(net, sta, loc, cha)
    # note below: binary expressions (all variables ending with "_be") might be
    # the boolean True. SQL-Alchemy seems to understand them as long as they
    # are preceded by a "normal" binary expression. Thus this works:
    # `q.filter(binary_expr & True)` and is equal to `q.filter(binary_expr)`,
    # whereas `q.filter(True & True)` (we hoped it could be a no-op filter)
    # is not working as a no-op filter, it simply does not work at all.
    # Here we should be safe cause `dc_be` is a non-True sql alchemy expression
    # (see above):
    qry = session.query(*sa_cols).join(Channel.station).filter(and_(dc_be,
                                                                    srate_be,
                                                                    nslc_be,
                                                                    stime_be,
                                                                    etime_be))
    return dbquery2df(qry)

def get_sqla_binexp(net, sta, loc, cha):
    """Return the sql-alchemy binary expression to be used as argument for
    database queries (e.g., `session.query(...)`) which translates to SQL the
    given net(works), sta(tions), loc(ations) and cha(nnels), all iterable of
    strings. Example:
    ```
    >>> get_sqla_binexp([], ['ABC'], [''], ['!A*', 'HH?', 'HN?'])
    'sta=ABC&loc=&cha=HH?,HN?'
    ```
    Note negations (!A*) mean 'NOT' in this program's syntax (this feature is
    not standard in an FDSN query).

    Arguments are usually the output of
    :func:`stream2segment.download.utils.nslc_lists`.

    :param net: an iterable of strings denoting networks.
    :param sta: an iterable of strings denoting stations.
    :param loc: an iterable of strings denoting locations.
    :param cha: an iterable of strings denoting channels.
    """
    # build a sql alchemy filter condition
    sa_cols = (Station.network, Station.station, Channel.location,
               Channel.channel)

    sa_bin_exprs = []

    wild2sql = strconvert.wild2sql  # conversion function

    for column, lst in zip(sa_cols, (net, sta, loc, cha)):
        matches = []
        for string in lst:
            negate = False
            if string[0:1] == '!':
                negate = True
                string = string[1:]

            if '?' in string or '*' in string:
                condition = column.like(wild2sql(string))
            else:
                condition = (column == string)

            if negate:
                condition = ~condition

            matches.append(condition)

        if matches:
            sa_bin_exprs.append(or_(*matches))

    return True if not sa_bin_exprs else and_(*sa_bin_exprs)

class ST:  # noqa
    """Simple enum-like container of strings defining the station's related
    database/dataframe columns needed in this module
    """
    ID = Station.id.key  # pylint: disable=invalid-name
    NET = Station.network.key  # pylint: disable=invalid-name
    STA = Station.station.key  # pylint: disable=invalid-name
    STIME = Station.start_time.key  # pylint: disable=invalid-name
    ETIME = Station.end_time.key  # pylint: disable=invalid-name
    DCID = Station.datacenter_id.key  # pylint: disable=invalid-name
    # set columns to show in the log on error ("no row written"):
    ERRCOLS = [NET, STA, STIME, DCID]  # pylint: disable=invalid-name

class CH:  # noqa
    """Simple enum-like container of strings defining the channel's related
    database/dataframe columns needed in this module
    """
    ID = Channel.id.key  # pylint: disable=invalid-name
    STAID = Channel.station_id.key  # pylint: disable=invalid-name
    LOC = Channel.location.key  # pylint: disable=invalid-name
    CHA = Channel.channel.key  # pylint: disable=invalid-name
    # set columns to show in the log on error ("no row written"):
    ERRCOLS = [ST.NET, ST.STA, LOC, CHA, ST.STIME, ST.DCID]  # noqa

def save_stations_and_channels(session, channels_df, eidavalidator, update,
                               db_bufsize):
    """Saves to db channels (and their stations) and returns a dataframe with
    only channels saved. The returned Dataframe will have the column 'id'
    (`Station.id`) renamed to 'station_id' (`Channel.station_id`) and a new
    'id' column referring to the Channel id (`Channel.id`)

    :param channels_df: pandas DataFrame
    """
    channels_df, conflict_between, conflict_within = \
        drop_duplicates(session, channels_df, eidavalidator)

    if channels_df.empty:
        raise FailedDownload('No channel left after cleanup '
                             '(e.g., drop duplicates)')

    # if update is True, don't update inventories HERE (handled later)
    _update_stations = update
    if _update_stations:
        _update_stations = [_ for _ in shared_colnames(Station, channels_df,
                                                       pkey=False)
                            if _ != Station.inventory_xml.key]

    # Add stations to db (Note: no need to check for `empty(channels_df)`,
    # `dbsyncdf` raises a `FailedDownload` in case). First set columns
    # defining channel identity (db unique constraint):
    cols = [Station.network, Station.station, Station.start_time]
    # Then add (sync actually, already existing stations are not inserted):
    sta_df = dbsyncdf(channels_df.drop_duplicates(subset=[ST.NET, ST.STA,
                                                          ST.STIME, ST.DCID]),
                      session, cols, Station.id, _update_stations,
                      buf_size=db_bufsize, keep_duplicates=False,
                      cols_to_print_on_err=ST.ERRCOLS)
    # `sta_df` will have the STA_ID columns, `channels_df` not: set it from the
    # former to the latter:
    channels_df = mergeupdate(channels_df, sta_df,
                              [ST.NET, ST.STA, ST.STIME, ST.DCID],
                              [ST.ID])
    # rename now 'id' to 'station_id' before writing the channels to db:
    channels_df.rename(columns={ST.ID: CH.STAID}, inplace=True)

    # check channels with empty station id (should never happen, let's be
    # picky):
    null_sta_id = channels_df[CH.STAID].isnull()
    conflict_null_sta_id = pd.DataFrame()
    if null_sta_id.any():
        conflict_null_sta_id = channels_df[null_sta_id]
        channels_df = channels_df[~null_sta_id]

    # Add channels to db. First seet columns defining channel identity (db
    # unique constraint):
    cols = [Channel.station_id, Channel.location, Channel.channel]
    # Then add (sync actually, already existing channels are not inserted):
    channels_df = dbsyncdf(channels_df, session, cols, Channel.id, update,
                           buf_size=db_bufsize, keep_duplicates=False,
                           cols_to_print_on_err=CH.ERRCOLS)

    log_unsaved_channels(conflict_between, conflict_within,
                         conflict_null_sta_id)

    return channels_df

def drop_duplicates(session, channels_df, eidavalidator):
    """Drop from channels_df duplicates (same station between or within data
    centers). For duplicates between data centers, uses `eidavalidator` or the
    database accessible via the session object, if `eidavalidator` is None.

    :return: the tuple of Dataframes:
        `(oks, conflict_between_dc, conflict_within_dc)`, where `oks` is a
        subset of `channels_df` with valid channels (one row per channel), and
        the other two contain channels discarded: `conflict between_dc`
        contains channels whose station is associated to several dc_id
        (datacenter id) and `conflict_within_dc` contains channels of the same
        dc_id violating a database unique constraint, e.g. two different
        channels with the same (network, station, location, channel,
        start_time, dc_id).
    """
    # Add to the list below the stations discarded because not uniquely mapped
    # to a single data center (dc_id). E.g.:
    #   net sta loc cha start_time dc_id
    #   N   S   L   C   2010-01-01 1
    #   N   S   L   C   2010-01-01 2
    # (Depending on the eidavalidator, only one or both rows will be added)
    conflict_between_dc = []
    # Add to the list below the stations discarded because of some non-unique
    # database constraint. E.g.:
    #   net sta loc cha start_time end_time   dc_id
    #   N   S   L   C   2010-01-01 None       1
    #   N   S   L   C   2010-01-01 2011-01-01 1
    # (both rows will be added)
    conflict_within_dc = []
    # list of ok channels (not falling in any category above):
    oks = []
    # station_datacenters_from_db = None  # dataframe lazy loaded (see below)

    # first drop duplicates (all columns the same):
    channels_df = channels_df.drop_duplicates()

    # From now on do not use anymore duplicated or drop_duplicates: the removal
    # of duplicates now is more tricky.
    # Let's  group by (net, sta, starttime), which is a unique constraints of
    # the station table, and analyse what we get:
    for (net, sta, stime), df_ in channels_df.\
            groupby([ST.NET, ST.STA, ST.STIME], sort=False):
        # if we have only ONE dc_id, skip "if" below.. Otherwise:
        if len(pd.unique(df_[ST.DCID])) > 1:
            # We have more than one data center mapped to the tuple
            # (net, sta, stime): get all ids from the eidavalidator (=object
            # representing the eida routing service) and put them in the set
            # below:
            real_dc_ids = set()
            # group stations by tuples (net, sta, stime, etime), because etime
            # is needed by the eidavalidator.get_dc_id. (pd.to_datetime fixes
            # https://github.com/pandas-dev/pandas/issues/35449)
            for etime in pd.to_datetime(pd.unique(df_[ST.ETIME])):
                # eidavalidator and session.query below expect Nones, not pd.NaT:
                stime = None if pd.isnull(stime) else stime  # should never be the case
                etime = None if pd.isnull(etime) else etime

                dcids = []
                if eidavalidator is not None:
                    # get the datacenter id(s) at a station level
                    # (loc, cha = None):
                    dcids = eidavalidator.get_dc_ids(net, sta, loc=None, cha=None,
                                                     stime=stime, etime=etime)

                if not dcids:
                    # eidavalidator null, or no dc_id found: query dc_id(s) from db:
                    dcids = [_[0] for _ in
                             session.query(Station.datacenter_id).filter(
                                 (Station.network == net) &
                                 (Station.station == sta) &
                                 (Station.start_time == stime) &
                                 (Station.end_time == etime)).all()
                             ]

                real_dc_ids.update(dcids)

            # Reminder: we are here if we have more than one datacenter mapped
            # to the same tuple (net, sta, stime). Now, real_dc_ids (the
            # real/reliable datacenter ids) might be one or more than one:
            if len(real_dc_ids) != 1:
                # The real datacenter ids are more than one => we can not save
                # the station: empty (=> discard) the dataframe
                conflict_between_dc.append(df_)
                df_ = df_[0:0]  # simply empty dataframe, with same columns
            else:
                # The real datacenter id is only one
                # => discard all (net, sta, stime) with different datacenter id
                dcid = list(real_dc_ids)[0]
                conflicting = df_[ST.DCID] != dcid
                conflict_between_dc.append(df_[conflicting])
                df_ = df_[~conflicting]

        # df_ now HAS SURELY ONE AND ONLY ONE dc_id, and same (net, sta, stime)
        if not df_.empty:
            # Last check: df_ will be written as ONE station (one row of the
            # "stations" table) and then, with the station_id, each df_ row
            # will be written as a different channel. The "channels" table has
            # as unique constraint the tuple (station_id, location, channel),
            # thus we need to drop NOW duplicated values of (location,
            # channel). PS: in case you wonder, yes, this has already happened
            # (e.g. two channels with all fields equal except `endtime`)
            dupes = df_.duplicated(subset=[CH.LOC, CH.CHA], keep=False)
            if dupes.any():
                conflict_within_dc.append(df_[dupes])
                df_ = df_[~dupes]

        if not df_.empty:
            oks.append(df_)

    oks = pd.DataFrame() if not oks else \
        pd.concat(oks, axis=0, sort=False, ignore_index=True, copy=True)
    conflict_between_dc = pd.DataFrame() if not conflict_between_dc else \
        pd.concat(conflict_between_dc, axis=0, sort=False)
    conflict_within_dc = pd.DataFrame() if not conflict_within_dc else \
        pd.concat(conflict_within_dc, axis=0, sort=False)

    return oks, conflict_between_dc, conflict_within_dc

def log_unsaved_channels(conflict_between, conflict_within,
                         conflict_null_sta_id):
    """log the results of channels and station saving.

    :param conflict_between: Dataframe of channels conflicts between
        datacenters (duplicated stations returned by more than one datacenter)
    :param conflict_within: Dataframe of channels conflicts within the same
        datacenter (violating channels unique constraints)
    :param conflict_null_sta_id: Dataframe of channels that did not have a
        matching station after station saving and before channel saving (this
        should never happen, but we check it for safety otherwise some db error
        occurs)
    """
    max_row_count = 50
    cols2show = [ST.NET, ST.STA, ST.STIME, ST.ETIME, ST.DCID]
    if not conflict_between.empty:
        # conflict_between happen at a station level (avoid unnecessary channel
        # details):
        _ = conflict_between.drop_duplicates(subset=[ST.NET, ST.STA, ST.STIME],
                                             keep='first')
        msg = formatmsg('%d station(s) and %d channel(s) not saved to db' %
                        (len(_), len(conflict_between)),
                        'wrong datacenter detected using either Routing '
                        'services or already saved stations')
        logwarn_dataframe(_, msg, cols2show, max_row_count)

    cols2show = [ST.NET, ST.STA, CH.LOC, CH.CHA, ST.STIME, ST.ETIME, ST.DCID]
    if not conflict_within.empty:
        # Do not count stations here, as some of those stations might have been
        # saved as part of other correct channels
        msg = formatmsg('%d channel(s) not saved to db' % len(conflict_within),
                        'conflicting data, e.g. unique constraint failed')
        logwarn_dataframe(conflict_within, msg, cols2show, max_row_count)

    if not conflict_null_sta_id.empty:
        # Do not count stations here, as some of those stations might have been saved as
        # part of other correct channels
        msg = formatmsg('%d channel(s) not saved to db' %
                        len(conflict_null_sta_id),
                        'station id not found, unknown cause')
        logwarn_dataframe(conflict_null_sta_id, msg, cols2show, max_row_count)

def chaid2mseedid_dict(channels_df, drop_mseedid_columns=True):
    """Return a dict of the form {channel_id: mseed_id} from `channels_df`,
    where mseed_id is a string of the form
    "[network].[station].[location].[channel]"

    :param channels_df: pandas Dataframe (one channel per row)
    :param drop_mseedid_columns: boolean (default: True), remove all columns
        related to the mseed id from `channels_df`. This might save up a lor
        of memory: pandas strings are stored as Python objects
        (https://www.dataquest.io/blog/pandas-big-data/)
    """
    net = channels_df[ST.NET].str.cat
    sta = channels_df[ST.STA].str.cat
    loc = channels_df[CH.LOC].str.cat
    cha = channels_df[CH.CHA]
    _mseedids = net(sta(loc(cha, sep='.', na_rep=''), sep='.', na_rep=''),
                    sep='.', na_rep='')

    if drop_mseedid_columns:
        # remove string columns, we do not need it anymore and
        # will save a lot of memory for subsequent operations
        channels_df.drop([ST.NET, ST.STA, CH.LOC, CH.CHA], axis=1,
                         inplace=True)
    # we could return
    # pd.DataFrame(index=channels_df[CHA_ID], {'mseed_id': _mseedids})
    # but the latter does NOT consume less memory (strings are python string in
    # pandas) and the search for an mseed_id given a loc[channel_id] is slower
    # than python dicts. As the returned element is intended for searching,
    # then return a dict:
    return {chaid: mseedid
            for chaid, mseedid in zip(channels_df[CH.ID], _mseedids)}


# -*- coding: utf-8 -*-
"""Open install dialog."""

import sys
from qtpy import QtWidgets

from .install_dialog import InstallDialog

RESULT = 0

def get_result(res: int):
    """Sets result returned from dialog."""
    global RESULT
    RESULT = res

app = QtWidgets.QApplication(sys.argv)

d = InstallDialog()
d.finished.connect(get_result)
d.open()
app.exec()
sys.exit(RESULT)


from pyautogui import *
import pyautogui
import time
import keyboard
import random
import win32api, win32con

time.sleep(2)
print("Starting")
def click(x,y):
    win32api.SetCursorPos((x,y))
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0)
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0)
def keystroke(i):
    VK_CODE = {'y':0x59} #add more keys here if needed
    win32api.keybd_event(VK_CODE[i], 0,0,0)
    time.sleep(.05)
    win32api.keybd_event(VK_CODE[i],0 ,win32con.KEYEVENTF_KEYUP ,0)
def clicklocation():
    location = pyautogui.locateOnScreen('filter.png', confidence=0.8, grayscale = True)
    center = pyautogui.center(location)
    centerx, centery = center
    click(centerx,centery)
    time.sleep(.8)
    print("Refreshing...")

while keyboard.is_pressed('q') == False:

    if pyautogui.locateOnScreen('purchase.png', region=(1670,150,200,50), confidence=0.8) != None:
        click(1765,174)
        time.sleep(.2)
        keystroke('y')
        print("Keycard detected")
    else:
        if pyautogui.locateOnScreen('filter.png', confidence=0.8) != None:
            clicklocation()


#! /usr/bin/python

from random import randint

f = open("input.txt", "w")

num = 5
inner = 10

f.write(str(num) + "\n");

for i in range(num):
    for j in range(num):
        f.write(str(randint(-inner, inner)) + " "),
    f.write("\n")

f.close()


from pyspark.sql import SparkSession
from pyspark.sql.functions import expr

spark = SparkSession.builder.master('local[*]').appName('alias').getOrCreate()
data = [("James", "Bond", "100", None),
        ("Ann", "Varsa", "200", 'F'),
        ("Tom Cruise", "XXX", "400", ''),
        ("Tom Brand", None, "400", 'M')]
columns = ['fname', 'lname', 'id', 'gender']
df = spark.createDataFrame(data).toDF(*columns)
# df2 = spark.createDataFrame(data=data, schema=columns)
# df3 = spark.createDataFrame(data, columns)
# df.show()
# df2.show()
# df3.show()
df.select(df.fname.alias('first_name'),
          df.lname.alias('last_name'),
          df.id.alias('identity_no'),
          df.gender).show()


"""
The main idea of this code is to extract all the social media URL's
that brand or company is registered on and have re-location to
"""

import re
import requests
import io

webs = ['https://deepfriedpeanut.com','https://acajunlife.com', 'https://padronflooring.com/', 'https://www.adappersandlapper.com/', 'https://adozencousins.com/', 'https://agood.com/', 'https://ahouselikethis.com']

class GetSocialMedia:

    def __init__(self, webs):
        self.fOut = dict()
        self.webs = webs
        self.pattern = re.compile(r'http(s)?://(www)?[.]?(facebook|instagram|pinterest|twitter|youtube)\.(com|ca)\/(user|chanel|channel|in)?([^plugins][^tr][^0-9]{4})[/]?\w*', re.IGNORECASE)
        self.setter = set()

    def main(self):
        self.user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36'
        self.headers = { 'User-Agent' : self.user_agent }

        for web in self.webs:
            self.temporary = []
            self.response = requests.get(web, headers = self.headers)
            self.content = str(self.response.text)
            self.matches = self.pattern.finditer(self.content)

            for match in self.matches:
                if match[0] not in self.temporary:
                    self.temporary.append(match[0])
            self.fOut[web] = self.temporary

        for k, v in self.fOut.items():
            print ('\n')
            if v != []:
                print (k, ' >> ', v)
                print ('-' * 50)

GetSocialMedia(webs = webs).main()


# Suppose we have two functions f and g.
# We want to calculate max of their output.
# You should calculate the output of those function in concurrent running.

import time
import random
from threading import Thread

def f():
    value = random.choice([1, 3, 5, 7, 9])
    print(f"value from f: {value}")
    time.sleep(value)
    return value

def g():
    value = random.choice([2, 4, 6, 8, 10])
    print(f"value from g: {value}")
    time.sleep(value)
    return value

print(max(f(), g()))

# Now lets use multithreading
threads = [Thread(target=f), Thread(target=g)]

# f and g run in separate threads
for thread in threads:
    thread.start()

# Wait until both f and g have finished
for thread in threads:
    thread.join()
# or print(max(threads[0].join(), threads[1].join()))

# At above implementation we have a limit.
# The limit is that we can't get return values from threads
# So lets implement in another way in which we can get return values from threads

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
        Thread.__init__(self, group, target, name, args, kwargs)
        self._return = None

    def run(self):
        if self._target is not None:
            self._return = self._target(*self._args, **self._kwargs)

    def join(self, *args):
        Thread.join(self, *args)
        return self._return

threads = [ThreadWithReturnValue(target=f), ThreadWithReturnValue(target=g)]
# f and g run in separate threads
for thread in threads:
    thread.start()

print(max(threads[0].join(), threads[1].join()))


#!/usr/bin/env python

"""Startpunt voor het programme van de drone"""

from classes.pyMultiwii import MultiWii
from sys import exit
from classes.server import Server
from classes.gamepad import JoyStick
from classes.sendCommands import SendCommands
from multiprocessing import Queue
from threading import Lock
import subprocess
from time import sleep
import datetime

class Main:
    """Startklasse van de applicatie"""

    def __init__(self):
        self.queue = Queue()
        self.board = MultiWii("/dev/ttyACM0")
        self.lock = Lock()
        self.sendCommands = SendCommands(self.board)
        print("Eerste data ontvangen")
        # Starten met starten van server en joystick
        self.server = Server(self.board, self.queue, self.lock, 8888)
        print("server aangeroepen")
        self.server.start()
        print("Server gestart")
        self.joystick = JoyStick(self.queue)
        self.joystick.start()
        print("JoyStick is begonnen")

    def loop(self):
        """Starts the main loop"""
        previousMsg = ""
        while 1:
            if not self.queue.empty():
                msg = self.queue.get()
                self.excecute(msg)
                previousMsg = msg

    def excecute(self, msg):
        """Executes the message"""
        if msg == "STOPSTOP::":
            raise Exception
        self.sendCommands.excecute(msg)

if __name__ == "__main__":
    print("Starting at " + datetime.datetime.now().strftime('%d-%m-%y %H:%M:%S'))
    try:
        print("Sleeping till system is fully opperationel...")
        sleep(8)
        main = Main()
        print("Main aangeroepen")
        main.loop()
    except KeyboardInterrupt:
        main.server.tornado.join(0.0)
        main.joystick.thread.join(0.0)
        main.sendCommands.thread.join(0.0)
        exit(0)
    except Exception:
        subprocess.Popen(["sudo", "shutdown", "-h", "now"])
        main.server.tornado.join(0.0)
        main.joystick.thread.join(0.0)
        main.sendCommands.thread.join(0.0)
        exit(1)


import numpy as np
from abc import ABC, abstractmethod
from base.center.crust.baseoperation import BaseOperation, BuildScalerOperation

from ..data.baseprepnn import PrepNN
from ..operation.baseoperation import LabelSamplerOperation, LabelPrepOperation, DataPrepOperation

class BasePrepNNOperation(BaseOperation):
    """ Base class for Process of preparing NN data. """
    @abstractmethod
    def perform_on_PrepNN(self, NNP: PrepNN):
        pass

class LabelSamplerPrepNNOperation(LabelSamplerOperation, BasePrepNNOperation):
    def perform_on_PrepNN(self, NNP: PrepNN):
        NNP.sampler = {}
        for method in ["halton", "uniform"]:
            NNP.sampler[method] = self.perform(NNP.dim, method)

class LabelPrepNNOperation(LabelPrepOperation, BasePrepNNOperation):
    def perform_on_PrepNN(self, NNP: PrepNN):
        NNP.ntrain = self.ntrain
        NNP.ntest  = self.ntest
        NNP.seed   = self.seed

        NNP.train["label"], NNP.test["label"] = self.perform(NNP.sampler["uniform"], NNP.sampler["halton"])

class DataPrepNNOperation(DataPrepOperation, BasePrepNNOperation):
    def perform_on_PrepNN(self, NNP: PrepNN):
        label       = np.vstack((NNP.train["label"], NNP.test["label"]))
        data, sigma = self.perform(label, NNP.interpolator, NNP.noiser)

        NNP.train["data"] , NNP.test["data"]  = data [:NNP.ntrain], data [NNP.ntrain:]
        NNP.train["sigma"], NNP.test["sigma"] = sigma[:NNP.ntrain], sigma[NNP.ntrain:]

#------------------------------------------------------------------------------
class NoiserPrepNNOperation(BasePrepNNOperation):
    def perform(self, Obs):
        noiser = lambda x: Obs.get_log_sigma(x, log=1)
        return noiser

    def perform_on_PrepNN(self, NNP: PrepNN):
        NNP.noiser = self.perform(NNP.Obs)

class LaberScalerPrepNNOperation(BuildScalerOperation, BasePrepNNOperation):
    def __init__(self, coordx_rng) -> None:
        super().__init__(0, coordx_rng)

    def perform_on_PrepNN(self, NNP: PrepNN):
        self.get_scalers()
        NNP.coordx = self.tick
        NNP.coordx_dim = len(self.tick)
        NNP.label_scaler = self.scaler
        NNP.label_rescaler = self.rescaler


from math import sqrt

from src.approximation.tools.matrix import solve_matrix
from src.approximation.tools.standart_deviation import variance, standard_deviation

def line_approximation(points):
    n = len(points)

    x = [point[0] for point in points]
    y = [point[1] for point in points]

    sum_x = sum(x)
    sum_x2 = sum([xi ** 2 for xi in x])

    sum_y = sum(y)
    sum_xy = sum([x[i] * y[i] for i in range(n)])

    matrix = [[sum_x2, sum_x, sum_xy],
              [sum_x, n, sum_y]]
    r = solve_matrix(matrix)
    a = r[1]
    b = r[0]

    result = {'a': a, 'b': b}

    f = lambda x: a * x + b

    result['function'] = f
    result['string_function'] = f"{round(a, 3)}x + {round(b, 3)}"
    result['variance'] = variance(points, f)
    result['standard_deviation'] = standard_deviation(points, f)

    mean_x = sum_x / n
    mean_y = sum_y / n

    sum_xy = sum([(point[0] - mean_x) * (point[1] - mean_y) for point in points])

    sum_x = sum([(xi - mean_y) ** 2 for xi in x])
    sum_y = sum([(yi - mean_y) ** 2 for yi in y])

    result['pirson'] = sum_xy / sqrt(sum_x * sum_y)

    return result


#! /usr/bin/env python

import rospy
from nav_msgs.msg import Odometry

def callback(msg):
   print(msg)

rospy.init_node('topic_subscriber')
sub=rospy.Subscriber('/odom',Odometry,callback)
rospy.spin()


from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework.permissions import IsAdminUser, IsAuthenticated
from rest_framework import status

from django.contrib.auth.models import  User
from main.serializers import GroupSerializer

from main.models import Group

@api_view(['GET'])
def getGroups(request):
  group = Group.objects.order_by('id')
  serializer = GroupSerializer(group, many = True)

  return Response(serializer.data)

@api_view(['GET'])
def getGroup(request, pk):
    group = Group.objects.get(id = pk)
    serializer = GroupSerializer(group, many=False)

    return Response(serializer.data)

@api_view(['POST'])
@permission_classes([IsAdminUser])
def createGroup(request):
    data = request.data

    group = Group.objects.create(
      name = data['name'],
    )

    serializer = GroupSerializer(group, many=False)
    return Response(serializer.data)

@api_view(['PUT'])
@permission_classes([IsAdminUser])
def updateGroup(request, pk):
    data = request.data
    group = Group.objects.get(id=pk)

    group.name = data['name']

    group.save()

    serializer = GroupSerializer(group, many=False)
    return Response(serializer.data)

@api_view(['DELETE'])
@permission_classes([IsAdminUser])
def deleteGroup(request,pk):
  group = Group.objects.get(id = pk)
  group.delete()
  return Response('Grupa usunięta')


import sys
sys.path.append('/home/cromero/projects/redol_model/')

import time
import util.properties as properties

from sklearn.model_selection import train_test_split
from sklearn import metrics

import pandas as pd

from redol import RedolRegressor
from sklearn.tree import DecisionTreeRegressor

def get_data():
    if len(sys.argv) > 1:
        try:
            dataset = pd.read_csv(f'../data/regression/{sys.argv[1]}.csv')

            X = dataset.drop(columns="class")
            y = dataset["class"]

            return X.to_numpy(), y.to_numpy()
        except IOError:
            print("File \"{}\" does not exist.".format(sys.argv[1]))
            return
    else:
        raise ValueError("File not found")

if __name__=="__main__":
    X, y = get_data()

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)

    n_trees = 100

    redol = RedolRegressor(n_estimators=n_trees, perc=0.75, repetitions=3, nearest_neighbours=5, iterations=10, n_jobs=4)
    redol.fit(X_train, y_train)
    y_pred = redol.predict(X_test)
    print(metrics.mean_squared_error(y_test, y_pred))

    tree = DecisionTreeRegressor()
    tree.fit(X_train, y_train)
    y_pred = tree.predict(X_test)
    print(metrics.mean_squared_error(y_test, y_pred))


from tictactoe import Player,HumanPlayer,RandomComputerPlayer
import time

class TicTacToe:
    def __init__(self):
        self.board = [" " for _ in range(9)]# we will use single list to represent 3*3 board
        self.current_winner = None # keep track of winner

    def print_board(self):
        # this is getting for rows
        for row in [self.board[i*3:(i+1)*3] for i in range(3)]:
            print(" | "+" | ".join(row)+" | ")

    @staticmethod
    def print_board_nums():
        #0|1|2 tells us what numbers corresponds to what box
        number_board = [[str(i) for i in range(j*3, (j+1)*3)] for j in range(3)]#nested loop
        for row in number_board:
             print(" | "+" | ".join(row)+" | ")

    def avaliable_moves(self):
        return[i for i, spot in enumerate(self.board) if spot ==" "]
        # This enumerated object can then be used directly for loops or converted into a list of tuples using the list() method.

    def empty_squares(self):
        return " " in self.board

    def num_empty_squares(self):
        return len(self.avaliable_moves())

    def make_move(self,square,letter):
        #if valid move, then make to move (assign square to letter)
        # then return true, if invalid return false
        if self.board[square]== " ":
            self.board[square]= letter
            if self.winner(square,letter):
                self.current_winner=letter
            return True
        return False

    def winner(self,square,letter):
        #winner if 3 in a row anywhere. we have to cheack all of them
        #first check the row
        row_ind= square//3
        row= self.board[row_ind*3: (row_ind+1)*3]#list of that row
        if all([spot ==letter for spot in row]):#everyhing in this list is true otherwise false
            return True

        #check column
        col_ind = square%3
        column= [self.board[col_ind + i*3] for i in range(3)]
        if all([spot ==letter for spot in column]):
            return True

        #check diagonals
        #but onyl if the square is an even number (0,2,4,6)
        #these are the only moves possible to win diagonal
        if square%2==0:
            diagonal1 = [self.board[i] for i in [0,4,8]]#left to rigth diagonal
            if all([spot ==letter for spot in diagonal1]):
                return True
            diagonal2 = [self.board[i] for i in [2, 4,8]]#right to left diagonal
            if all([spot ==letter for spot in diagonal2]):
                return True

        #if all of these fails
        return False

def play(game, x_player, o_player,print_game= True):
    #returs the winner of the game(letter) or None means a tie
    if print_game:
        game.print_board_nums()

    letter = "X"#starting letter
    #iterate while game still has empty squares
    #we dont have to worry about the winner because we' ll just return that which breaks the loop

    while game.empty_squares():
        #get the move from the apporiate player
        if letter == "O":
            square = o_player.get_move(game)
        else:
            square = x_player.get_move(game)#

        # lets define a function to define the move
        if game.make_move(square,letter):
            if print_game:
                print( letter+ f"make move to square {square}")
                game.print_board()
                print(" ")# empty line
            if game.current_winner:
                if print_game:
                    print(letter + "wins!")
                return letter

            #after first letter, our letter will change
            letter = "O" if letter== "X" else "X"
            #if letter =="X":
            #   letter= "O"
            # else:
            #   letter= "X"
        time.sleep(0.8)
    if print_game:
        print("It is a tie!")

if __name__ == "__main__":
    x_player = HumanPlayer("X")
    o_player = RandomComputerPlayer("O")
    t = TicTacToe()
    play(t,x_player,o_player,print_game = True)


import struct
from struct import unpack
import numpy as np
import skimage.draw  as draw
import matplotlib.pyplot as plt
import os

def unpack_drawing(file_handle):
    key_id, = unpack('Q', file_handle.read(8))
    country_code, = unpack('2s', file_handle.read(2))
    recognized, = unpack('b', file_handle.read(1))
    timestamp, = unpack('I', file_handle.read(4))
    n_strokes, = unpack('H', file_handle.read(2))
    image = []

    for i in range(n_strokes):
        n_points, = unpack('H', file_handle.read(2))
        fmt = str(n_points) + 'B'
        x = unpack(fmt, file_handle.read(n_points))
        y = unpack(fmt, file_handle.read(n_points))
        image.append((y,x))

    return {
        'key_id': key_id,
        'country_code': country_code,
        'recognized': recognized,
        'timestamp': timestamp,
        'image': image

    }

def unpack_drawings(filename):
    with open(filename, 'rb') as f:
        while True:
            try:
                yield unpack_drawing(f)
            except struct.error:
                break

path = '/mnt/hd-data/Datasets/quickdraw/binary'
for drawing in unpack_drawings(os.path.join(path, 'bee.bin')):
    # do something with the drawing
    print(drawing['key_id'])
    sketch = np.zeros((256,256), dtype = np.uint8)
    strokes = drawing['image']
    for stroke in strokes :
        ys = stroke[0]
        xs = stroke[1]
        for i  in range(1,len(ys)) :
            rr,cc = draw.line(ys[i-1], xs[i-1], ys[i], xs[i])
            sketch[rr,cc] = 255

    plt.imshow(sketch, cmap = 'gray')
    plt.show()


from __future__ import annotations

import json
import uuid
from datetime import datetime, timezone
from typing import Optional

import aioredis

from livestuff.live import LiveConnectionPool
from models.payload import GiveawayConfig
from models.response import AsyncResponse

class CreateGiveawayResponse(AsyncResponse):

    def __init__(self, authorization: str, data: GiveawayConfig, live: LiveConnectionPool, redis: aioredis.Redis):
        super().__init__()
        self.data: GiveawayConfig = data
        self.redis: aioredis.Redis = redis
        self.authorization: str = authorization
        self.live: LiveConnectionPool = live

    async def complete(self) -> CreateGiveawayResponse:
        username: Optional[bytes] = await self.redis.get(f"auth:{self.authorization}")

        # Get from Redis
        if username is None:
            self._status, self._payload, self._message = 400, None, "Auth failed"
            return self

        username: str = username.decode("utf-8")
        giveaway: Optional[dict] = self.live.giveaways.get_giveaway(username)

        # Check if tracking rn
        tracking = bool(self.live.clients.get(username))
        if not tracking:
            self._status, self._payload, self._message = 404, None, "Not currently tracking that user"
            return self

        # Check if already running, if so, cancel
        if giveaway is not None:
            self._status, self._payload, self._message = 401, None, "Giveaway is already running"
            return self

        name: str = self.data.prize_name[:20]
        join_word: str = self.data.keyword[:20].replace(" ", "")
        winner_count: int = max(1, min(self.data.winners, 5))
        duration: int = max(1, min(self.data.duration, 60))

        current_time: int = round(datetime.now(timezone.utc).timestamp())
        end_time: int = current_time + (duration * 60)
        giveaway_config: dict = {
            "name": name,
            "join_word": join_word,
            "winner_count": winner_count,
            "start_time": current_time,
            "end_time": end_time
        }

        self.live.giveaways.set_giveaway(username, giveaway_config)
        self._status, self._payload, self._message = 200, giveaway_config, "Started giveaway"
        return self


# --- Day 3: Spiral Memory ---
# You come across an experimental new kind of memory stored on an infinite two-dimensional grid.
#
# Each square on the grid is allocated in a spiral pattern starting at a location marked 1 and then counting up while
# spiraling outward. For example, the first few squares are allocated like this:
#
# 17  16  15  14  13 18   5   4   3  12 19   6   1   2  11 20   7   8   9  10 21  22  23---> ... While this is very
# space-efficient (no squares are skipped), requested data must be carried back to square 1 (the location of the only
# access port for this memory system) by programs that can only move up, down, left, or right. They always take the
# shortest path: the Manhattan Distance between the location of the data and square 1.
#
# For example:
#
# - Data from square 1 is carried 0 steps, since it's at the access port.
# - Data from square 12 is carried 3 steps, such as: down, left, left.
# - Data from square 23 is carried only 2 steps: up twice.
# - Data from square 1024 must be carried 31 steps.
#
# How many steps are required to carry the data from the square identified in your puzzle input all the way to the
# access port?

from math import ceil, sqrt

def calculate_distance(number_to_check: int) -> int:
    # initial variables
    full_power = ceil(sqrt(number_to_check))
    if full_power % 2 == 0:
        full_power += 1
    full_power_distance = full_power ** 2 - number_to_check

    # main logic - algorithmic approach for best time complexity
    if full_power_distance < full_power:  # bottom wall
        coordinates = [(full_power - 1) // 2, ((full_power - 1) // 2) - (full_power_distance % (full_power - 1))]

    elif full_power_distance < full_power * 2 - 1:  # left wall
        coordinates = [((full_power - 1) // 2) - (full_power_distance % (full_power - 1)), -((full_power - 1) // 2)]

    elif full_power_distance < full_power * 3 - 2:  # up wall
        coordinates = [(full_power - 1) // 2, -((full_power - 1) // 2) + (full_power_distance % (full_power - 1))]

    else:  # right wall
        coordinates = [-((full_power - 1) // 2) + (full_power_distance % (full_power - 1)), (full_power - 1) // 2]

    return sum(abs(x) for x in coordinates)

if __name__ == '__main__':
    puzzle_input = 361527
    result = calculate_distance(puzzle_input)
    print(f"Distance is: {result}")


# dataset settings
dataset_type = 'CocoDataset'
data_root = '../../dataset/' ### 실행하는 위치 기준
classes = ("General trash", "Paper", "Paper pack", "Metal", "Glass",
           "Plastic", "Styrofoam", "Plastic bag", "Battery", "Clothing")

# img_norm_cfg = dict(
#     mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
img_norm_cfg = dict(
    mean=[123.6506697 , 117.39730243, 110.07542563],
    std=[54.03457934, 53.36968771, 54.78390763], to_rgb=True)
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', with_bbox=True),
    dict(type='RandomFlip', flip_ratio=0.5),
    # dict(type='Resize', img_scale=(1024, 1024), keep_ratio=True),
    # dict(
    #     type='RandomCrop',
    #     crop_type='absolute_range',
    #     crop_size=(784, 784),
    #     allow_negative_crop=False),
    dict(type='Resize',
        img_scale=[(1024, 1024)],
        ratio_range=(0.5, 1.5),
        # multiscale_mode='range',
        keep_ratio=True), #이미지에 맞게 변경
    dict(
    type='CutOut',
    n_holes=(5, 10),
    cutout_shape=[(4, 4), (4, 8), (8, 4), (8, 8),
                    (16, 8), (8, 16), (16, 16), (16, 32), (32, 16), (32, 32),
                    (32, 48), (48, 32), (48, 48)],
    ),
    ###########################Albu############################################
    dict(type='Albu',
        transforms=[
            # dict(type='ShiftScaleRotate',
            #     shift_limit=0.0625,
            #     scale_limit=0.1,
            #     rotate_limit=0,
            #     interpolation=1,
            #     value=(1,1,1),
            #     p=0.5),
            dict(type='RandomRotate90', p=0.5),
            # dict(type='RandomFlip', flip_ratio=0.5),
            dict(type='OneOf', transforms=[
                                dict(type='RandomFog', alpha_coef=0.25,  fog_coef_upper=0.5),
                                dict(type='Blur', blur_limit=(3, 7), p=1),
                                ], p=0.5
                ),
            dict(type='OneOf', transforms=[
                                dict(type='HueSaturationValue',
                                    hue_shift_limit=(-20, 20),
                                    sat_shift_limit=(-30,30),
                                    val_shift_limit=(-20,20), p=1
                                    ),
                                dict(type='RandomBrightnessContrast', p=1),
                                ], p=0.5
                ),
            ],bbox_params=dict(
                type='BboxParams',
                format='pascal_voc',
                label_fields=['gt_labels'],
                min_visibility=0.0,
                filter_lost_elements=True),
            keymap=dict(img='image', gt_bboxes='bboxes'),
            update_pad_shape=False,
            skip_img_without_anno=True),
    #######################################################################
    dict(type='Normalize', **img_norm_cfg),
    dict(type='Pad', size_divisor=32),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
]
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(1024, 1024),#이미지에 맞게 변경
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True),
            dict(type='RandomFlip'),
            dict(type='Normalize', **img_norm_cfg),
            dict(type='Pad', size_divisor=32),
            dict(type='ImageToTensor', keys=['img']),
            dict(type='Collect', keys=['img']),
        ])
]
data = dict(
    samples_per_gpu=1, #batch size
    workers_per_gpu=2, # num of worker
    train=dict(
        type=dataset_type,
        classes = classes,
        # ann_file=data_root + 'fold_0_train.json',
        img_prefix=data_root,
        pipeline=train_pipeline
        ),
    val=dict(
        type=dataset_type,
        classes = classes,
        # ann_file=data_root + 'fold_0_val.json',
        img_prefix=data_root,
        pipeline=test_pipeline
        ),
    test=dict(
        type=dataset_type,
        classes = classes,
        ann_file=data_root + 'test.json',
        img_prefix=data_root,
        pipeline=test_pipeline
        )
    )
evaluation = dict(interval=1, metric='bbox', save_best='bbox_mAP_50', classwise=True)

##
# evaluation save_best mAP 추가


import dyno_colors as dc
from dyno_bit import DynoBit
from dyno_gif import make_temp_file_names, remove_temp_files, save_dyno_gif

# Numpy library to work with arrays: https://numpy.org/
import numpy as np

import copy

def make_eye_roll_dyno(gif_name, dyno=DynoBit()):
    frame_count = 4 # Because the eye is made of 4 bits (a nibble!)
    old_index_list = [(7,17), (6,17), (6,16), (7,16)]
    new_index_list = old_index_list[1:] + [old_index_list[0]]

    temp_file_names = make_temp_file_names(frame_count, "temp_eye_roll")

    if not frame_count == len(old_index_list) == len(new_index_list) == len(temp_file_names):
        raise ValueError(f"frame_count inconsistent: {frame_count} {len(old_index_list)} {len(new_index_list)} {len(temp_file_names)}")

    new_pixels = dyno.pixels()
    old_pixels = dyno.pixels()
    for file in temp_file_names:
        dyno.save_dyno_image(pixel_array=new_pixels, imgname=file)
        for i in range(frame_count): # Move pupil one position
            new_pixels[new_index_list[i]] = old_pixels[old_index_list[i]]
        old_pixels = np.copy(new_pixels)

    save_dyno_gif(gif_name, temp_file_names+[temp_file_names[0]], bounce=True, fps=8)

    remove_temp_files(temp_file_names)


import heapq
import sys

INF = int(1e9)
input = sys.stdin.readline

# 노드의 개수, 간선
n, m = map(int, input().split())
# 시작노드 번호
start = int(input())
# 각 노드에 연결되어있는 정보
graph = [[] for i in range(n+1)]
# 최단거리 테이블
distance = [INF] * (n+1)
# 모든 간선 정보 받기
for _ in range(m):
    a, b, c = map(int, input().split())
    graph[a].append((b, c))

def dijkstra(start):
    q = []
    # 시작 노드로 가기 위한 최단 경로는 0으로 설정하여 큐에 삽입
    heapq.heappush(q, (0, start))
    distance[start] = 0

    # q가 비어있지 않다면
    while q:
        dist, now = heapq.heappop(q)
        if distance[now] < dist:
            continue

        # 현재 노드와 연결된 다른 인접한 노드 확인
        for i in graph[now]:
            cost = dist + i[1]
            # 현재 노드를 거쳐서, 다른 노드로 이동하는 거리가
            # distance에 기록된 거리보다 짧은 경우
            if cost < distance[i[0]]:
                distance[i[0]] = cost
                heapq.heappush(q, (cost, i[0]))

# 다익스트라 알고리즘 수행
dijkstra(start)

# 모든 노드로 가기 위한 최단 거리 출력
for i in range(1, n+1):
    if distance[i] == INF:
        print("INF")
    else:
        print(distance[i])


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import collections
import random
import tokenization
import tensorflow as tf
import sys

flags = tf.flags

FLAGS = flags.FLAGS

flags.DEFINE_string("input_file", None,
                    "Input raw text file (or comma-separated list of files).")

flags.DEFINE_string(
    "output_file", None,
    "Output TF example file (or comma-separated list of files).")

flags.DEFINE_string("vocab_file", None,
                    "The vocabulary file that the electra model was trained on.")

flags.DEFINE_bool(
    "do_lower_case", True,
    "Whether to lower case the input text. Should be True for uncased "
    "models and False for cased models.")

flags.DEFINE_bool(
    "do_whole_word_mask", False,
    "Whether to use whole word masking rather than per-WordPiece masking.")

flags.DEFINE_integer("max_seq_length", 128, "Maximum sequence length.")

flags.DEFINE_integer("max_predictions_per_seq", 20,
                     "Maximum number of masked LM predictions per sequence.")

flags.DEFINE_integer("random_seed", 12345, "Random seed for data generation.")

flags.DEFINE_integer(
    "dupe_factor", 1,
    "Number of times to duplicate the input data (with different masks).")

flags.DEFINE_float("masked_lm_prob", 0.15, "Masked LM probability.")

flags.DEFINE_float(
    "short_seq_prob", 0.1,
    "Probability of creating sequences which are shorter than the "
    "maximum length.")

class TrainingInstance(object):
    """A single training instance (sentence pair)."""

    def __init__(self, tokens, segment_ids):
        self.tokens = tokens
        self.segment_ids = segment_ids

    def __str__(self):
        s = ""
        s += "tokens: %s\n" % (" ".join(
            [tokenization.printable_text(x) for x in self.tokens]))
        s += "\n"
        return s

    def __repr__(self):
        return self.__str__()

def write_instance_to_example_files(instances, tokenizer, max_seq_length,
                                    max_predictions_per_seq, output_files):
    """Create TF example files from `TrainingInstance`s."""
    writers = []
    for output_file in output_files:
        writers.append(tf.python_io.TFRecordWriter(output_file))

    writer_index = 0

    total_written = 0
    for (inst_index, instance) in enumerate(instances):
        input_ids = tokenizer.convert_tokens_to_ids(instance.tokens)
        input_mask = [1] * len(input_ids)
        segment_ids = list(instance.segment_ids)
        assert len(input_ids) <= max_seq_length

        while len(input_ids) < max_seq_length:
            input_ids.append(0)
            input_mask.append(0)
            segment_ids.append(0)

        assert len(input_ids) == max_seq_length
        assert len(input_mask) == max_seq_length
        assert len(segment_ids) == max_seq_length

        features = collections.OrderedDict()
        features["input_ids"] = create_int_feature(input_ids)
        features["input_mask"] = create_int_feature(input_mask)
        features["segment_ids"] = create_int_feature(segment_ids)

        tf_example = tf.train.Example(features=tf.train.Features(feature=features))

        writers[writer_index].write(tf_example.SerializeToString())
        writer_index = (writer_index + 1) % len(writers)

        total_written += 1

        if inst_index < 20:
            tf.logging.info("*** Example ***")
            tf.logging.info("tokens: %s" % " ".join(
                [tokenization.printable_text(x) for x in instance.tokens]))

            for feature_name in features.keys():
                feature = features[feature_name]
                values = []
                if feature.int64_list.value:
                    values = feature.int64_list.value
                elif feature.float_list.value:
                    values = feature.float_list.value
                tf.logging.info(
                    "%s: %s" % (feature_name, " ".join([str(x) for x in values])))

    for writer in writers:
        writer.close()

    tf.logging.info("Wrote %d total instances", total_written)

def create_int_feature(values):
    feature = tf.train.Feature(int64_list=tf.train.Int64List(value=list(values)))
    return feature

def create_float_feature(values):
    feature = tf.train.Feature(float_list=tf.train.FloatList(value=list(values)))
    return feature

def create_training_instances(input_files, tokenizer, max_seq_length,
                              dupe_factor, short_seq_prob, masked_lm_prob,
                              max_predictions_per_seq, rng):
    """Create `TrainingInstance`s from raw text."""
    all_documents = [[]]

    # Input file format:
    # (1) One sentence per line. These should ideally be actual sentences, not
    # entire paragraphs or arbitrary spans of text. (Because we use the
    # sentence boundaries for the "next sentence prediction" task).
    # (2) Blank lines between documents. Document boundaries are needed so
    # that the "next sentence prediction" task doesn't span between documents.
    for input_file in input_files:
        with tf.gfile.GFile(input_file, "r") as reader:
            while True:
                line = tokenization.convert_to_unicode(reader.readline())
                if not line:
                    break
                line = line.strip()

                # Empty lines are used as document delimiters
                if not line:
                    #all_documents.append([])
                    pass
                tokens = tokenizer.tokenize(line)
                if tokens:
                    all_documents[-1].append(tokens)

    # Remove empty documents
    all_documents = [x for x in all_documents if x]
    rng.shuffle(all_documents)

    vocab_words = list(tokenizer.vocab.keys())
    instances = []
    for _ in range(dupe_factor):
        for document_index in range(len(all_documents)):
            instances.extend(
                create_instances_from_document(
                    all_documents, document_index, max_seq_length, short_seq_prob,
                    masked_lm_prob, max_predictions_per_seq, vocab_words, rng))

    rng.shuffle(instances)
    return instances

def create_instances_from_document(
        all_documents, document_index, max_seq_length, short_seq_prob,
        masked_lm_prob, max_predictions_per_seq, vocab_words, rng):
    """Creates `TrainingInstance`s for a single document."""
    document = all_documents[document_index]

    # Account for [CLS], [SEP], [SEP]
    max_num_tokens = max_seq_length - 3

    target_seq_length = max_num_tokens
    if rng.random() < short_seq_prob:
        target_seq_length = rng.randint(2, max_num_tokens)

    # We DON'T just concatenate all of the tokens from a document into a long
    # sequence and choose an arbitrary split point because this would make the
    # next sentence prediction task too easy. Instead, we split the input into
    # segments "A" and "B" based on the actual "sentences" provided by the user
    # input.
    instances = []
    current_chunk = []
    current_length = 0
    i = 0
    while i < len(document):
        segment = document[i]
        current_chunk.append(segment)
        current_length += len(segment)
        if i == len(document) - 1 or current_length >= target_seq_length:
            if current_chunk:
                # `a_end` is how many segments from `current_chunk` go into the `A`
                # (first) sentence.
                a_end = 1
                if len(current_chunk) >= 2:
                    a_end = rng.randint(1, len(current_chunk) - 1)

                tokens_a = []
                for j in range(a_end):
                    tokens_a.extend(current_chunk[j])

                tokens_b = []

                for j in range(a_end, len(current_chunk)):
                    tokens_b.extend(current_chunk[j])
                truncate_seq_pair(tokens_a, tokens_b, max_num_tokens, rng)

                assert len(tokens_a) >= 1
                # assert len(tokens_b) >= 1

                tokens = []
                segment_ids = []
                tokens.append("[CLS]")
                segment_ids.append(0)
                for token in tokens_a:
                    tokens.append(token)
                    segment_ids.append(0)

                tokens.append("[SEP]")
                segment_ids.append(0)

                for token in tokens_b:
                    tokens.append(token)
                    segment_ids.append(1)
                tokens.append("[SEP]")
                segment_ids.append(1)

                instance = TrainingInstance(
                    tokens=tokens,
                    segment_ids=segment_ids)
                instances.append(instance)
            current_chunk = []
            current_length = 0
        i += 1

    return instances

def truncate_seq_pair(tokens_a, tokens_b, max_num_tokens, rng):
    """Truncates a pair of sequences to a maximum sequence length."""
    while True:
        total_length = len(tokens_a) + len(tokens_b)
        if total_length <= max_num_tokens:
            break

        trunc_tokens = tokens_a if len(tokens_a) > len(tokens_b) else tokens_b
        assert len(trunc_tokens) >= 1

        # We want to sometimes truncate from the front and sometimes from the
        # back to add more randomness and avoid biases.
        if rng.random() < 0.5:
            del trunc_tokens[0]
        else:
            trunc_tokens.pop()

def main(_):
    tf.logging.set_verbosity(tf.logging.INFO)

    tokenizer = tokenization.FullTokenizer(
        vocab_file=FLAGS.vocab_file, do_lower_case=FLAGS.do_lower_case)

    input_files = []
    for input_pattern in FLAGS.input_file.split(","):
        input_files.extend(tf.gfile.Glob(input_pattern))

    tf.logging.info("*** Reading from input files ***")
    for input_file in input_files:
        tf.logging.info("  %s", input_file)

    rng = random.Random(FLAGS.random_seed)
    instances = create_training_instances(
        input_files, tokenizer, FLAGS.max_seq_length, FLAGS.dupe_factor,
        FLAGS.short_seq_prob, FLAGS.masked_lm_prob, FLAGS.max_predictions_per_seq,
        rng)

    output_files = FLAGS.output_file.split(",")
    tf.logging.info("*** Writing to output files ***")
    for output_file in output_files:
        tf.logging.info("  %s", output_file)

    write_instance_to_example_files(instances, tokenizer, FLAGS.max_seq_length,
                                    FLAGS.max_predictions_per_seq, output_files)

if __name__ == "__main__":
    flags.mark_flag_as_required("input_file")
    flags.mark_flag_as_required("output_file")
    flags.mark_flag_as_required("vocab_file")
    tf.app.run()


import requests

from src.messenger.helper import get_user_info, send_message
from src.core.config import settings
from src.models.models import LanguageEnum
from src.messenger import bank_list
from src.core.config import API, THREAD_CONTROL_API
from src.messenger.templates import template_messages

async def template_message(sender_id):
    response = {
        "attachment": {
            "type": "template",
            "payload": {
                "template_type": "generic",
                "elements": [
                    {
                        "title": "Welcome!",
                        "image_url": "https://f1soft.com/img/logo-200.png",
                        "subtitle": "We have the right advice for everyone.",
                        "default_action": {
                            "type": "web_url",
                            "url": "https://f1soft.com/",
                            "webview_height_ratio": "tall",
                        },
                        "buttons": [
                            {
                                "type": "web_url",
                                "url": "https://f1soft.com/",
                                "title": "View Website"
                            }, {
                                "type": "postback",
                                "title": "Start Chatting",
                                "payload": "start_chatting_payload"
                            }
                        ]
                    },
                    {
                        "title": "Welcome!",
                        "image_url": "https://f1soft.com/img/f1soft_contact.jpg",
                        "subtitle": "We have the right tools for everyone.",
                        "default_action": {
                            "type": "web_url",
                            "url": "https://f1soft.com/",
                            "webview_height_ratio": "tall",
                        },
                        "buttons": [
                            {
                                "type": "web_url",
                                "url": "https://f1soft.com/",
                                "title": "View Website"
                            }, {
                                "type": "postback",
                                "title": "Start Chatting",
                                "payload": "DEVELOPER_DEFINED_PAYLOAD"
                            }
                        ]
                    }
                ]
            }
        }
    }
    await send_message(sender_id, response)

async def button_response(sender_id):
    response = {
        "attachment": {
            "type": "template",
            "payload": {
                "template_type": "button",
                "text": "What do you want to do next?",
                "buttons": [
                    {
                        "type": "web_url",
                        "url": "https://www.messenger.com",
                        "title": "Visit Messenger"
                    },
                    {
                        "type": "web_url",
                        "url": "https://www.youtube.com",
                        "title": "Visit Youtube"
                    },
                ]
            }
        }
    }
    await send_message(sender_id, response)

async def quick_message(sender_id):
    request_body = {
        "recipient": {
            "id": sender_id
        },
        "messaging_type": "RESPONSE",
        "message": {
            "text": "Pick a color:",
            "quick_replies": [
                {
                    "content_type": "text",
                    "title": "Red",
                    "payload": "RED_COLOR_POSTBACK",
                    "image_url": "https://w7.pngwing.com/pngs/614/895/png-transparent-circle-red-logo-red-circle-logo"
                                 "-sphere-alpha-compositing.png "
                }, {
                    "content_type": "text",
                    "title": "Green",
                    "payload": "GREEN_COLOR_POSTBACK",
                    "image_url": "https://w7.pngwing.com/pngs/51/802/png-transparent-circle-green-circle-color-grass"
                                 "-sphere.png "
                }
            ]
        }
    }
    response = requests.post(API, json=request_body).json()
    return response

async def red_color_postback(sender_id):
    await send_message(sender_id, {"text": "You picked Red Color, Great"})

async def green_color_postback(sender_id):
    await send_message(sender_id, {"text": "You picked Green Color, Great"})

async def image_message(sender_id):
    import random
    resolution = random.randint(720, 1920)
    response = {
        "attachment": {
            "type": "image",
            "payload": {
                "url": f"https://picsum.photos/{resolution}"
            }
        }
    }
    await send_message(sender_id, response)

async def hi_message(sender_id):
    user_info = await get_user_info(sender_id)
    await send_message(sender_id, {
        "text": f"Hello, {user_info['first_name']}"
    })

async def send_echo_message(sender_id, payload):
    # if payload['message'].get('text'):
    await send_message(sender_id, {"text": payload})
    #     messaging = event['messaging']
    #     for x in messaging:
    #         if x.get('message'):
    #             recipient_id = x['sender']['id']
    #             if x['message'].get('text'):
    #                 message = x['message']['text']
    #                 bot.send_text_message(recipient_id, message)
    #             if x['message'].get('attachments'):
    #                 for att in x['message'].get('attachments'):
    #                     bot.send_attachment_url(recipient_id, att['type'], att['payload']['url'])
    #         else:
    #             pass

async def send_message_to_new_user(sender_id: str):
    user_info = await get_user_info(sender_id)
    response1 = {
        "text": f"Hi {user_info['first_name']} ! Welcome to {settings.MESSENGER_BOT_NAME}, where, you will get all queries "
                f"answered"
    }

    response2 = {
        "attachment": {
            "type": "image",
            "payload": {
                "url": "https://f1soft.com/img/logo-200.png"
            }
        }
    }

    response3 = {
        "text": "At any time, use the menu below to navigate through the features."
    }

    response4 = {
        "text": "What can I do to help you today?",
        "quick_replies": [
            {
                "content_type": "text",
                "title": "Languages",
                "payload": "LANGUAGES",
            },
            {
                "content_type": "text",
                "title": "Banks",
                "payload": "LOOKUP_BANKS",
            },
            {
                "content_type": "text",
                "title": "Talk to an agent",
                "payload": "TALK_AGENT",
            },
            {
                "content_type": "text",
                "title": "Chat with me",
                "payload": "BOT_CHAT",
            },
        ]
    }

    await send_message(sender_id, response1)
    await send_message(sender_id, response2)
    await send_message(sender_id, response3)
    await send_message(sender_id, response4)

async def pass_thread_control(sender_id, param):
    target_app_id = ""
    metadata = ""

    if param == "page_inbox":
        target_app_id = settings.SECONDARY_RECEIVER_ID
        metadata = "Pass thread control to inbox chat"

    if param == "primary":
        target_app_id = settings.PRIMARY_RECEIVER_ID
        metadata = "Pass thread control to the bot, primary app"

    request_body = {
        "recipient": {
            "id": sender_id
        },
        "target_app_id": target_app_id,
        "metadata": metadata
    }

    requests.post(THREAD_CONTROL_API, json=request_body).json()

async def request_agent_talk(sender_id, language):
    if language == LanguageEnum.english:
        response = {
            "text": "Ok, Someone real will be with you in a few minutes"
        }
    else:
        response = {
            "text": "ठीक छ, कोही वास्तविक केही मिनेटमा तपाईंसँग हुनेछ"
        }
    await send_message(sender_id, response)

    """
    Switch this conversation to page inbox
    """
    await pass_thread_control(sender_id, "page_inbox")

async def back_to_main_menu(sender_id):
    response = template_messages.back_to_main_menu_template()
    await send_message(sender_id, response)

async def back_to_process_menu(sender_id, language):
    response = template_messages.back_to_process_menu_template(language)
    await send_message(sender_id, response)

async def take_thread_control(sender_id):
    request_body = {
        "recipient": {
            "id": sender_id
        },
        "metadata": "Pass this conversation from page inbox to the bot - primary app"
    }

    try:
        requests.post(THREAD_CONTROL_API, request_body)
    except Exception as e:
        try:
            await send_message(sender_id, {"text": "The chatbot default is back !!"})
            await back_to_main_menu(sender_id)
            print("Message sent")
        except Exception as e:
            print("Unable to send message while thread control")

async def lookup_banks(sender_id):
    response = template_messages.lookup_banks_categories_template()
    await send_message(sender_id, response)

async def list_government_banks(sender_id, repeat):
    if not repeat:
        await send_message(sender_id, {"text": "There are {} governmental banks in Nepal"
                           .format(len(bank_list.government_banks))})
    await send_message(sender_id, template_messages.government_banks_select())

async def list_private_banks(sender_id, repeat):
    if not repeat:
        await send_message(sender_id, {"text": "There are {} private banks in Nepal"
                           .format(23)})
    await send_message(sender_id, template_messages.private_banks_select())

async def selected_government_bank(sender_id, payload):
    bank_name = bank_list.government_banks[int(payload[-1]) - 1]['title']
    await send_message(sender_id, {"text": "You have selected {}".format(bank_name)})
    await send_message(sender_id, template_messages.banks_select_choices(payload[-1]))

async def selected_private_bank(sender_id, payload):
    bank_name = bank_list.private_banks[int(payload[-1]) - 1]['title']
    await send_message(sender_id, {"text": "You have selected {}".format(bank_name)})
    await send_message(sender_id, template_messages.private_banks_select_choices(payload[-1]))

async def government_post_operation(sender_id, payload):
    bank_name = bank_list.government_banks[int(payload[-1]) - 1]['title']
    await send_message(sender_id, template_messages.government_banks_post_choices(bank_name, payload[-1]))

async def private_post_operation(sender_id, payload):
    bank_name = bank_list.private_banks[int(payload[-1]) - 1]['title']
    await send_message(sender_id, template_messages.private_banks_post_choices(bank_name, payload[-1]))

async def describe_bank(sender_id, payload, choices):
    choices_operation = {
        "GOVERNMENT": government_post_operation(sender_id, payload),
        "PRIVATE": private_post_operation(sender_id, payload)
    }
    await choices_operation.get(choices, None)

async def chat_with_bot(sender_id):
    await send_message(sender_id, {"text": "What do you want to talk about?"})

async def whats_up_message(sender_id):
    user_info = await get_user_info(sender_id)
    await send_message(sender_id, {"text": f"I am fine {user_info['first_name']}, What about you?"})

async def government_bank_info(sender_id, payload):
    bank_desc = bank_list.government_banks[int(payload[-1]) - 1]['description']
    await send_message(sender_id, {"text": bank_desc})
    await government_post_operation(sender_id, payload)

async def private_bank_info(sender_id, payload):
    bank_desc = bank_list.private_banks[int(payload[-1]) - 1]['description']
    await send_message(sender_id, {"text": bank_desc})
    await private_post_operation(sender_id, payload)

async def government_mobile_banking(sender_id, payload):
    bank = bank_list.government_banks[int(payload[-1]) - 1]
    if bank['mobile_banking']["android"]:
        await send_message(sender_id, {"text": f"Mobile Banking is available for {bank['title']}"})
        await government_post_operation(sender_id, payload)
    else:
        await send_message(sender_id, {"text": f"Mobile Banking is not available for {bank['title']}"})
        await government_post_operation(sender_id, payload)

async def private_mobile_banking(sender_id, payload):
    bank = bank_list.private_banks[int(payload[-1]) - 1]
    if bank['mobile_banking']["android"]:
        await send_message(sender_id, {"text": f"Mobile Banking is available for {bank['title']}"})
        await private_post_operation(sender_id, payload)
    else:
        await send_message(sender_id, {"text": f"Mobile Banking is not available for {bank['title']}"})
        await private_post_operation(sender_id, payload)

async def government_branches(sender_id, payload):
    await send_message(sender_id, {"text": "Working on it!!!!!!!!!!!"})


from prophet import Prophet
from prophet.serialize import model_to_json, model_from_json

from sklearn.metrics import mean_squared_error

import pandas as pd
import numpy as np
import itertools
import datetime

class ProphetModel:
  models = {}

  def __init__(self, df, target:str, forecast_size:int=18):
    self.df = df
    self.target = target
    self.forecast_size = forecast_size

  def _model_tuning_and_train(self, interval_width=0.95, weekly_seasonality=True, save_path='prophet_model.json'):
    self.prophet = Prophet(interval_width=interval_width, weekly_seasonality=weekly_seasonality)
    prophet_target = pd.DataFrame(zip(list(self.df.index),list(self.df[self.target])),columns=['ds','y'])
    self.prophet.fit(prophet_target)
    forecast = self.prophet.make_future_dataframe(periods=self.forecast_size)
    forecast_target = forecast.copy()
    self.predictions = self.prophet.predict(forecast_target)
    score = np.sqrt(mean_squared_error(self.df[self.target], self.predictions['yhat'].head(self.df.shape[0])))

    ProphetModel.models['prophet'] = {
        'score':score,
        'model':self.prophet
    }
    # with open('saved_models/'+save_path, 'w') as f:
    #   f.write(model_to_json(self.prophet))

    return ProphetModel.models['prophet']

  def train(self):
    self._model_tuning_and_train()

  def data_predictions(self, start):
    preds = self.predictions[self.predictions['ds']>str(start)]
    # print(preds)
    return list(preds['yhat']), list(preds['ds'])

  def plot(self):
    print(self.prophet.plot(self.predictions))

"""
AN EVEN BETTER MODEL WITH HIGHER ACCURACY
"""

model_columns = ['date','new_cases','positive_rate',
                   'tests_per_case','male_smokers','female_smokers',
                   'new_vaccinations','handwashing_facilities',
                   'life_expectancy','new_tests','reproduction_rate'
                   ]
param_grid = {
    'changepoint_prior_scale': [0.001, 0.01, 0.1, 0.5],
    'seasonality_prior_scale': [0.01, 0.1, 1.0, 10.0]
}
all_params = [dict(zip(param_grid.keys(), v)) for v in itertools.product(*param_grid.values())]

def prophet_model(dat, target='new_cases', period=18):
  dat = dat[model_columns]
  dat.rename(columns={target:'y','date':'ds'}, inplace=True)

  for params in all_params:
    p = Prophet(interval_width=0.95, weekly_seasonality=True, **params)

  model_columns.remove('date')
  model_columns.remove('new_cases')

  for i in model_columns:
    p.add_regressor(i)

  p.fit(dat)
  # print(p)

  # prepping future values
  today = datetime.date.today()
  # print(today)
  period = period+1
  date_list = [str(today + datetime.timedelta(days=x)) for x in range(period)]
  date_list.remove(str(today))
  period = period-1
  temp = {}
  for i in model_columns:
    temp_ = [dat[i].mean()]*period
    temp[i] = temp_
  dummy_cases = [0]*period
  temp['ds'] = date_list
  temp['y'] = dummy_cases
  future_data = pd.DataFrame(temp)

  some = pd.concat([dat, future_data])
  # print(some)

  forecast_data = p.predict(some)
  return list(forecast_data['yhat']), [str(i.date()) for i in forecast_data['ds']]