import copy
import math
from pypaq.lipytools.printout import stamp, progress_
from pypaq.lipytools.pylogger import get_pylogger, get_child
from pypaq.mpython.mptools import Que, QMessage
from torchness.tbwr import TBwr
import random
import statistics
import time
from tqdm import tqdm
from typing import Dict, List, Tuple, Optional, Union

from envy import MODELS_FD, DMK_MODELS_FD, N_TABLE_PLAYERS, PyPoksException
from pologic.potable import QPTable
from podecide.dmk import FolDMK, HuDMK
from gui.gui_hdmk import GUI_HDMK

def stdev_with_none(values) -> Optional[float]:
    if len(values) < 2:
        return None
    return statistics.stdev(values)

# separated factor for two results
def separated_factor(
        a_wonH: Optional[float],
        a_wonH_mean_stdev: Optional[float],
        b_wonH: Optional[float],
        b_wonH_mean_stdev: Optional[float],
        n_stdev: float) -> float:
    if a_wonH_mean_stdev is None or b_wonH_mean_stdev is None:
        return 0.0
    if a_wonH_mean_stdev + b_wonH_mean_stdev == 0:
        return 1000
    return abs(a_wonH - b_wonH) / (n_stdev * (a_wonH_mean_stdev + b_wonH_mean_stdev))

# prepares separation report
def separation_report(
        dmk_results: Dict,
        n_stdev: float,
        sep_pairs: Optional[List[Tuple[str,str]]]=  None,
        max_nf: float=                              1.1,
) -> Dict:

    sep_nc = 0.0
    sep_nf = 0.0
    sep_pairs_nc = 0.0
    sep_pairs_nf = 0.0
    sep_pairs_stat = []

    n_dmk = len(dmk_results)

    # prepare separation data
    dmk_sep = {}
    for dn in dmk_results:
        wonH_IV_stdev = stdev_with_none(dmk_results[dn]['wonH_IV'])
        dmk_sep[dn] = {
            'wonH_IV_stdev':        wonH_IV_stdev,
            'wonH_IV_mean_stdev':   wonH_IV_stdev / math.sqrt(len(dmk_results[dn]['wonH_IV'])) if wonH_IV_stdev is not None else None,
            'last_wonH_afterIV':    dmk_results[dn]['wonH_afterIV'][-1] if dmk_results[dn]['wonH_afterIV'] else None}

    # compute separated normalized count & normalized factor
    for dn_a in dmk_sep:
        dmk_sep[dn_a]['separated'] = n_dmk - 1
        for dn_b in dmk_sep:
            if dn_a != dn_b:
                sf = separated_factor(
                    a_wonH=             dmk_sep[dn_a]['last_wonH_afterIV'],
                    a_wonH_mean_stdev=  dmk_sep[dn_a]['wonH_IV_mean_stdev'],
                    b_wonH=             dmk_sep[dn_b]['last_wonH_afterIV'],
                    b_wonH_mean_stdev=  dmk_sep[dn_b]['wonH_IV_mean_stdev'],
                    n_stdev=            n_stdev)
                if sf < 1:
                    dmk_sep[dn_a]['separated'] -= 1
                sep_nf += min(sf, max_nf)
        sep_nc += dmk_sep[dn_a]['separated']
    n_max = (n_dmk - 1) * n_dmk
    sep_nc /= n_max
    sep_nf /= n_max

    # same for given pairs
    if sep_pairs:
        for sp in sep_pairs:
            sf = separated_factor(
                a_wonH=             dmk_sep[sp[0]]['last_wonH_afterIV'],
                a_wonH_mean_stdev=  dmk_sep[sp[0]]['wonH_IV_mean_stdev'],
                b_wonH=             dmk_sep[sp[1]]['last_wonH_afterIV'],
                b_wonH_mean_stdev=  dmk_sep[sp[1]]['wonH_IV_mean_stdev'],
                n_stdev=            n_stdev)
            sep_pairs_stat.append(0 if sf<1 else 1)
            if sf>=1: sep_pairs_nc += 1
            sep_pairs_nf += min(sf, max_nf)
        sep_pairs_nc /= len(sep_pairs)
        sep_pairs_nf /= len(sep_pairs)

    return {
        'sep_nc':           sep_nc,         # <0.0;1.0> normalized count of separated
        'sep_nf':           sep_nf,         # <0.0;1.1> normalized factor of separation
        'sep_pairs_nc':     sep_pairs_nc,   # <0.0;1.0> normalized count of separated pairs
        'sep_pairs_nf':     sep_pairs_nf,   # <0.0;1.1> normalized factor of pairs separation
        'sep_pairs_stat':   sep_pairs_stat} # [0,1, ..] each par marked as separated or not

# manages games of DMKs (at least QueDMKs)
class GamesManager:

    def __init__(
            self,
            dmk_pointL: List[Dict],         # points with eventually added 'dmk_type'
            name: Optional[str]=    None,
            logger=                 None,
            loglevel=               20,
            debug_dmks=             False,
            debug_tables=           False):

        self.name = name or f'GM_{stamp()}'

        if not logger:
            logger = get_pylogger(
                name=       self.name,
                folder=     MODELS_FD,
                level=      loglevel)
        self.logger = logger
        self.debug_tables = debug_tables

        self.logger.info(f'*** GamesManager : {self.name} *** starts..')

        self.que_to_gm = Que()  # here GM receives data from DMKs and Tables

        dmk_pointL = copy.deepcopy(dmk_pointL) # copy to not modify original list
        dmk_types = [point.pop('dmk_type',FolDMK) for point in dmk_pointL]
        dmk_logger = get_child(self.logger, name='dmks_logger', change_level=-10 if debug_dmks else 10)
        dmks = [dmk_type(logger=dmk_logger, **point) for dmk_type,point in zip(dmk_types, dmk_pointL)]
        self.dmkD = {dmk.name: dmk for dmk in dmks} # Dict[str, dmk_type] INFO:is not typed because DMK may have diff types

        for dmk in self.dmkD.values(): dmk.que_to_gm = self.que_to_gm  # DMKs are build from folders, they need que to be updated then
        self.families = set([dmk.family for dmk in self.dmkD.values()])

        self.tbwr = TBwr(logdir=f'{DMK_MODELS_FD}/{self.name}')

        self.tables = None

    # starts DMKs (starts loops)
    def _start_dmks(self):

        self.logger.debug('> starts DMKs..')

        idmk = tqdm(self.dmkD.values()) if self.logger.level<20 else self.dmkD.values()
        for dmk in idmk: dmk.start()
        self.logger.debug('> initializing..')
        idmk = tqdm(self.dmkD) if self.logger.level < 20 else self.dmkD
        for _ in idmk:
            message = self.que_to_gm.get()
            self.logger.debug(f'>> {message}')
        self.logger.debug(f'> initialized {len(self.dmkD)} DMKs!')

        message = QMessage(type='start_dmk_loop', data=None)
        for dmk in self.dmkD.values(): dmk.que_from_gm.put(message) # synchronizes DMKs a bit..
        for _ in self.dmkD:
            message = self.que_to_gm.get()
            self.logger.debug(f'>> {message}')
        self.logger.debug(f'> started {len(self.dmkD)} DMKs!')

    def _save_dmks(self):

        self.logger.debug('> saves DMKs')

        n_saved = 0
        message = QMessage(type='save_dmk', data=None)
        for dmk in self.dmkD.values():
            dmk.que_from_gm.put(message)
            n_saved += 1
        for _ in range(n_saved):
            self.que_to_gm.get()
        self.logger.debug('> all DMKs saved!')

    # stops DMKs loops
    def _stop_dmks_loops(self):
        self.logger.debug('Stopping DMKs loops..')
        message = QMessage(type='stop_dmk_loop', data=None)
        for dmk in self.dmkD.values(): dmk.que_from_gm.put(message)
        idmk = tqdm(self.dmkD) if self.logger.level < 20 else self.dmkD
        for _ in idmk:
            self.que_to_gm.get()
        self.logger.debug('> all DMKs loops stopped!')

    # stops DMKs processes
    def _stop_dmks_processes(self):
        self.logger.debug('Stopping DMKs processes..')
        message = QMessage(type='stop_dmk_process', data=None)
        for dmk in self.dmkD.values(): dmk.que_from_gm.put(message)
        idmk = tqdm(self.dmkD) if self.logger.level < 20 else self.dmkD
        for _ in idmk:
            self.que_to_gm.get()
        self.logger.debug('> all DMKs exited!')

    # creates new tables & puts players with random policy
    def _put_players_on_tables(self):

        self.logger.info('> puts players on tables..')

        # build dict of lists of players (per family): {family: [(pid, que_to_pl, que_from_pl)]}
        fam_ques: Dict[str, List[Tuple[str,Que,Que]]] = {fam: [] for fam in self.families}
        for dmk in self.dmkD.values():
            for k in dmk.queD_to_player: # {pid: que_to_pl}
                fam_ques[dmk.family].append((k, dmk.queD_to_player[k], dmk.que_from_player))

        # shuffle players in families
        for fam in fam_ques:
            random.shuffle(fam_ques[fam])
            random.shuffle(fam_ques[fam])

        quesLL = [fam_ques[fam] for fam in fam_ques] # convert to list of lists

        ### convert to flat list

        # cut in equal pieces
        min_len = min([len(l) for l in quesLL])
        cut_quesLL = []
        for l in quesLL:
            while len(l) > 1.66*min_len:
                cut_quesLL.append(l[:min_len])
                l = l[min_len:]
            cut_quesLL.append(l)
        quesLL = cut_quesLL
        random.shuffle(quesLL)
        random.shuffle(quesLL)

        quesL = []  # flat list
        qLL_IXL = []
        while quesLL:

            if not qLL_IXL:
                qLL_IXL = list(range(len(quesLL)))      # fill indexes
                random.shuffle(qLL_IXL)                 # shuffle them
            qLL_IX = qLL_IXL.pop()                      # now take last index

            quesL.append(quesLL[qLL_IX].pop())          # add last from list
            if not quesLL[qLL_IX]:
                quesLL.pop(qLL_IX)                      # remove empty list
                qLL_IXL = list(range(len(quesLL)))      # new indexes then
                random.shuffle(qLL_IXL)                 # shuffle them

        num_players = len(quesL)
        if num_players % N_TABLE_PLAYERS != 0:
            raise PyPoksException(f'num_players ({num_players}) has to be a multiple of N_TABLE_PLAYERS ({N_TABLE_PLAYERS})')

        # put on tables
        self.tables = []
        table_ques = []
        table_logger = get_child(self.logger, name='table_logger', change_level=-10) if self.debug_tables else None
        while quesL:
            table_ques.append(quesL.pop())
            if len(table_ques) == N_TABLE_PLAYERS:
                self.tables.append(QPTable(
                    name=       f'tbl{len(self.tables)}',
                    que_to_gm=  self.que_to_gm,
                    pl_ques=    {t[0]: (t[1], t[2]) for t in table_ques},
                    logger=     table_logger))
                table_ques = []

    # starts all tables
    def _start_tables(self):
        self.logger.debug('> starts tables..')
        itbl = tqdm(self.tables) if self.logger.level < 20 else self.tables
        for tbl in itbl: tbl.start()
        for _ in itbl:
            self.que_to_gm.get()
        self.logger.debug(f'> tables ({len(self.tables)}) processes started!')

    # stops tables
    def _stop_tables(self):
        self.logger.debug('> stops tables loops..')
        message = QMessage(type='stop_table', data=None)
        for table in self.tables: table.que_from_gm.put(message)
        itbl = tqdm(self.tables) if self.logger.level < 20 else self.tables
        for _ in itbl:
            self.que_to_gm.get()
        # INFO: tables now are just Process objects with target loop stopped
        self.logger.debug('> tables loops stopped!')

    # runs game, returns DMK results dictionary
    def run_game(
            self,
            game_size=                                  10000,  # number of hands for a game (per DMK)
            sleep=                                      10,     # loop sleep (seconds)
            progress_report=                            True,
            publish_GM=                                 False,
            sep_all_break: bool=                        False,  # breaks game when all DMKs are separated
            sep_pairs: Optional[List[Tuple[str,str]]]=  None,   # pairs of DMK names for separation condition
            sep_pairs_factor: float=                    0.9,    # factor of separated pairs needed to break the game
            sep_n_stdev: float=                         2.0,
    ) -> Dict[str, Dict]:
        """
        By now, by design run_game() may be called only once,
        cause DMK processes are started and then stopped and process cannot be started twice,
        there is no real need to change this design.
        """

        # save of DMK results + additional DMK info
        dmk_results = {
            dn: {
                'wonH_IV':      [],     # wonH (won $ / hand) of interval
                'wonH_afterIV': [],     # wonH (won $ / hand) after interval
                'family':       self.dmkD[dn].family,
                'trainable':    self.dmkD[dn].trainable,
                'global_stats': None,   # SM.global_stats, will be updated by DMK at the end of the game
            } for dn in self._get_dmk_focus_names()}

        # starts all subprocesses
        self._put_players_on_tables()
        self._start_tables()
        self._start_dmks()

        stime = time.time()
        time_last_report = stime
        n_hands_last_report = 0

        self.logger.info(f'{self.name} starts a game..')
        loop_ix = 0
        while True:

            time.sleep(sleep)

            reports = self._get_reports({dn: len(dmk_results[dn]['wonH_IV']) for dn in dmk_results}) # actual DMK reports
            for dn in reports:
                dmk_results[dn]['wonH_IV'] += reports[dn]['wonH_IV']
                dmk_results[dn]['wonH_afterIV'] += reports[dn]['wonH_afterIV']

            # calculate game factor
            n_hands = sum([reports[dn]['n_hands'] for dn in reports])
            game_factor = n_hands / len(reports) / game_size
            if game_factor >= 1: game_factor = 1

            sr = separation_report(
                dmk_results=    dmk_results,
                n_stdev=        sep_n_stdev,
                sep_pairs=      sep_pairs)
            sep_nc = sr['sep_nc']
            sep_nf = sr['sep_nf']
            sep_pairs_nc = sr['sep_pairs_nc']
            sep_pairs_nf = sr['sep_pairs_nf']

            if publish_GM:
                self.tbwr.add(value=sep_nc, tag=f'GM/sep_nc', step=loop_ix)
                self.tbwr.add(value=sep_nf, tag=f'GM/sep_nf', step=loop_ix)
                if sep_pairs:
                    self.tbwr.add(value=sep_pairs_nc, tag=f'GM/sep_pairs_nc', step=loop_ix)
                    self.tbwr.add(value=sep_pairs_nf, tag=f'GM/sep_pairs_nf', step=loop_ix)

            # INFO: progress relies on reports, and reports may be prepared in custom way (overridden) by diff GMs
            if progress_report:

                # progress
                passed = (time.time()-stime)/60
                left_nfo = ' - '
                if game_factor > 0:
                    full_time = passed / game_factor
                    left = (1-game_factor) * full_time
                    left_nfo = f'{left:.1f}'

                # speed
                hdiff = n_hands-n_hands_last_report
                hd_pp = int(hdiff / len(reports))
                spd_report = f'{int(hdiff / (time.time()-time_last_report))}H/s (+{hd_pp}Hpp)'
                n_hands_last_report = n_hands
                time_last_report = time.time()

                sep_report_pairs = f'::{sep_pairs_nc:.2f}[{sep_pairs_nf:.2f}]' if sep_pairs else ''

                progress_(
                    current=    game_factor,
                    total=      1.0,
                    prefix=     f'GM: {passed:.1f}min left:{left_nfo}min',
                    suffix=     f'{spd_report} -- SEP:{sep_nc:.2f}[{sep_nf:.2f}]{sep_report_pairs}',
                    length=     20)

            # games break - factor condition
            if game_factor == 1:
                self.logger.info('> finished game (game factor condition)')
                break

            # games break - all DMKs separation condition
            if sep_all_break and sep_nc == 1.0:
                self.logger.info(f'> finished game (all DMKs separation condition), game factor: {game_factor:.2f})')
                break

            # games break - pairs separation breaking value condition
            if sep_pairs and sep_pairs_nc >= sep_pairs_factor:
                self.logger.info(f'> finished game (pairs separation factor: {sep_pairs_factor:.2f}, game factor: {game_factor:.2f})')
                break

            loop_ix += 1

        self.tbwr.flush()

        self._stop_tables()
        self._stop_dmks_loops()

        message = QMessage(type='send_global_stats', data=None)
        for dn in dmk_results:
            self.dmkD[dn].que_from_gm.put(message)
        for _ in dmk_results:
            message = self.que_to_gm.get()
            data = message.data
            dmk_name = data.pop('dmk_name')
            dmk_results[dmk_name]['global_stats'] = data['global_stats']

        self._save_dmks()
        self._stop_dmks_processes()

        taken_sec = time.time() - stime
        taken_nfo = f'{taken_sec / 60:.1f}min' if taken_sec > 100 else f'{taken_sec:.1f}sec'
        speed = n_hands / taken_sec
        self.logger.info(f'{self.name} finished run_game, avg speed: {speed:.1f}H/s, time taken: {taken_nfo}')
        loop_stats = {'speed': speed}

        return {
            'dmk_results':  dmk_results,
            'loop_stats':   loop_stats}

    # prepares list of DMK names GM is focused on while preparing dmk_results
    def _get_dmk_focus_names(self) -> List[str]:
        return list(self.dmkD.keys())

    # asks DMKs to send reports, but only form given IV
    def _get_reports(
            self,
            dmk_report_IV:Dict[str,int] # {dn: from_IV}
    ) -> Dict[str, Dict]:
        reports: Dict[str, Dict] = {} # {dn: {n_hands, wonH_IV, wonH_afterIV}}
        for dn,from_IV in dmk_report_IV.items():
            message = QMessage(type='send_dmk_report', data=from_IV)
            self.dmkD[dn].que_from_gm.put(message)
        for _ in dmk_report_IV:
            message = self.que_to_gm.get()
            report = message.data
            dmk_name = report.pop('dmk_name')
            reports[dmk_name] = report
        return reports

# GamesManager for Play & TRain concept for FolDMKs (some DMKs may play, some DMKs may train)
class GamesManager_PTR(GamesManager):

    def __init__(
            self,
            dmk_point_PLL: Optional[List[Dict]]=    None, # playable DMK list
            dmk_point_TRL: Optional[List[Dict]]=    None, # trainable DMK list
            dmk_n_players: int=                     60,
            name: Optional[str]=                    None,
            **kwargs):

        """
        there are 3 possible scenarios:
        1.playable & trainable:
            dmk_point_PLLa & dmk_point_PLLb are merged together into dmk_point_PLL
            dmk_n_players - sets number of players of one trainable DMK (dmk_point_TRL)
            number of players of each playable DMK is equal: dmk_n_players * (N_TABLE_PLAYERS - 1)
            (each trainable has one table full of playable)
        2.only trainable:
            dmk_n_players - sets number of players of one trainable DMK
            number of tables = len(dmk)*dmk_n_players / N_TABLE_PLAYERS
        3.only playable
            if there are dmk_point_PLLa AND dmk_point_PLLb...
            otherwise dmk_point_PLLa & dmk_point_PLLb are merged together into dmk_point_PLL
            ...
            dmk_n_players - sets number of players of one playable DMK
            number of tables = len(dmk)*dmk_n_players / N_TABLE_PLAYERS
        TODO: edit this doc
        """

        if not dmk_point_PLL: dmk_point_PLL =  []
        if not dmk_point_TRL: dmk_point_TRL = []

        if not (dmk_point_PLL or dmk_point_TRL):
            raise PyPoksException('playing OR training DMKs must be given')

        n_tables = len(dmk_point_TRL) * dmk_n_players # default when there are both playable & trainable
        if not dmk_point_PLL or not dmk_point_TRL:
            dmk_dnaL = dmk_point_PLL or dmk_point_TRL
            if (len(dmk_dnaL) * dmk_n_players) % N_TABLE_PLAYERS != 0:
                raise PyPoksException('Please correct number of DMK players: n DMKs * n players must be multiplication of N_TABLE_PLAYERS')
            n_tables = int((len(dmk_dnaL) * dmk_n_players) / N_TABLE_PLAYERS)

        # override to train (each DMK by default is saved as a trainable - we set also trainable to have this info here for later usage, it needs n_players to be set)
        for dmk in dmk_point_TRL:
            dmk.update({
                'n_players':    dmk_n_players,
                'trainable':    True})

        if dmk_point_PLL:

            # both
            if dmk_point_TRL:
                n_rest_players = n_tables * (N_TABLE_PLAYERS-1)
                rest_names = [dna['name'] for dna in dmk_point_PLL]
                rest_names = random.choices(rest_names, k=n_rest_players)
                for point in dmk_point_PLL:
                    point.update({
                        'n_players': len([nm for nm in rest_names if nm == point['name']]),
                        'trainable': False})

            # only playable
            else:
                play_dna = {
                    'n_players': dmk_n_players,
                    'trainable': False}
                for dmk in dmk_point_PLL:
                    dmk.update(play_dna)

        self.dmk_name_PLL = [dna['name'] for dna in dmk_point_PLL]
        self.dmk_name_TRL = [dna['name'] for dna in dmk_point_TRL]

        nm = 'PL' if self.dmk_name_PLL else 'TR'
        if self.dmk_name_PLL and self.dmk_name_TRL:
            nm = 'TR+PL'
        GamesManager.__init__(
            self,
            dmk_pointL= dmk_point_PLL + dmk_point_TRL,
            name=       name or f'GM_{nm}_{stamp()}',
            **kwargs)

        self.logger.info(f'*** GamesManager_PTR started with (PL:{len(dmk_point_PLL)} TR:{len(dmk_point_TRL)}) DMKs on {n_tables} tables')
        for dna in dmk_point_PLL + dmk_point_TRL:
            self.logger.debug(f'> {dna["name"]} with {dna["n_players"]} players, trainable: {dna["trainable"]}')

    # creates new tables & puts players with PTR policy
    def _put_players_on_tables(self):

        # use previous policy
        if not (self.dmk_name_PLL and self.dmk_name_TRL):
            return GamesManager._put_players_on_tables(self)

        self.logger.info('> puts players on tables with PTR policy..')

        ques_PL = []
        ques_TR = []

        for dmk in self.dmkD.values():
            ques = ques_TR if dmk.trainable else ques_PL
            for k in dmk.queD_to_player: # {pid: que_to_pl}
                ques.append((k, dmk.queD_to_player[k], dmk.que_from_player))

        # shuffle players
        random.shuffle(ques_PL)
        random.shuffle(ques_TR)

        # put on tables
        self.tables = []
        table_ques =  []
        table_logger = get_child(self.logger, name='table_logger', change_level=-10) if self.debug_tables else None
        while ques_TR:
            table_ques.append(ques_TR.pop())
            while len(table_ques) < N_TABLE_PLAYERS: table_ques.append(ques_PL.pop())
            random.shuffle(table_ques)
            self.tables.append(QPTable(
                name=       f'tbl{len(self.tables)}',
                que_to_gm=  self.que_to_gm,
                pl_ques=    {t[0]: (t[1], t[2]) for t in table_ques},
                logger=     table_logger))
            table_ques = []
        assert not ques_PL and not ques_TR

    # adds age update to dmk_results
    def run_game(self, **kwargs) -> Dict:

        # update trainable age - needs to be done before game, cause after game DMKs are saved
        for dmk in self.dmkD.values():
            if dmk.trainable: dmk.age += 1

        rgd = GamesManager.run_game(self, **kwargs)

        for dn in rgd['dmk_results']:
            rgd['dmk_results'][dn]['age'] = self.dmkD[dn].age
        return rgd

    # at GamesManager_PTR we are focused on TRL (or PLL if not)
    def _get_dmk_focus_names(self) -> List[str]:
        return self.dmk_name_TRL or self.dmk_name_PLL

# manages DMKs for human games
class HuGamesManager(GamesManager):

    def __init__(
            self,
            dmk_names: Union[List[str],str],
            logger=     None,
            loglevel=   20):

        if not logger:
            logger = get_pylogger(level=loglevel)

        if N_TABLE_PLAYERS != 3:
            raise PyPoksException('HuGamesManage supports now only 3-handed tables')

        logger.info(f'HuGamesManager starts with given dmk_names: {dmk_names}')

        h_name = 'hm0'

        hdna = {
            'name':             h_name,
            'family':           'h',
            'trainable':        False,
            'n_players':        1,
            #'publish':          False,
            'fwd_stats_step':   10}

        if type(dmk_names) is str: dmk_names = [dmk_names]

        self.tk_gui = GUI_HDMK(players=[h_name]+dmk_names, imgs_FD='gui/imgs')

        hdmk = HuDMK(tk_gui=self.tk_gui, **hdna)

        if len(dmk_names) not in [1,2]:
            raise PyPoksException('Number of given DMK names must be equal 1 or 2')

        ddL = [{
            'name':             nm,
            'trainable':        False,
            'n_players':        N_TABLE_PLAYERS - len(dmk_names),
            #'publish':          False,
            'fwd_stats_step':   10} for nm in dmk_names]

        GamesManager.__init__(self, dmk_pointL=ddL, logger=logger)

        # update/override with HuDMK
        self.dmkD[hdna['name']] = hdmk
        self.families.add(hdna['family'])
        hdmk.que_to_gm = self.que_to_gm

    # starts all subprocesses
    def start_games(self):
        self._put_players_on_tables()
        self._start_tables()
        self._start_dmks()

    # an alternative way of stopping all subprocesses (dmks & tables)
    def kill_games(self):
        self.logger.info('HuGamesManager is killing games..')
        for dmk in self.dmkD.values(): dmk.kill()
        for table in self.tables: table.kill()

    def run_tk(self): self.tk_gui.run_tk()


import numpy as np
from scipy.integrate import solve_ivp

class VanDerPolOscillator:
    def __init__(self, epsilon):
        self.epsilon = epsilon

    def coupledEquation(self, t, x):
        x1 = x[0]
        x2 = x[1]
        fx1 = x2
        fx2 = -x1 - (self.epsilon * ((x1 ** 2) - 1) * x2)
        return np.array([fx1, fx2], float)

    def methodRK45(self, initialState, t0=0):
        tSpan = [t0, (8 * np.pi)]
        points = solve_ivp(self.coupledEquation, tSpan, initialState, method='RK45', first_step=0.01, max_step=0.01)
        tPoints = points['t']
        fx1Points = points['y'][0]
        fx2Points = points['y'][1]
        return tPoints, fx1Points, fx2Points


from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils.text import slugify
from django_resized import ResizedImageField

from .base import BaseModel
from .images import Images

def file_size(value):
    limit = 6 * 1024 * 1024
    if value.size > limit:
        raise ValidationError("Plik który chcesz wrzucić jest większy niż 6MB.")

class Articles(BaseModel):
    id = models.AutoField(primary_key=True)
    category = models.ForeignKey(
        "category", on_delete=models.CASCADE, verbose_name="Kategoria artykułu"
    )
    title = models.CharField(verbose_name="Tytyuł artykułu", max_length=256)
    slug = models.SlugField(verbose_name="Slug", blank=True, null=True, max_length=256)
    body = models.TextField(verbose_name="Treść artukułu")
    image = ResizedImageField(
        verbose_name="Zdjęcie główne",
        size=[1280, 960],
        upload_to="images/articles/",
        validators=[file_size],
        null=True,
        blank=True,
    )
    image_alt = models.CharField(
        verbose_name="Alternatywny text dla obrazka",
        max_length=125,
        blank=True,
        null=True,
    )
    image_title = models.CharField(
        verbose_name="Title dla obrazka", blank=True, null=True, max_length=70
    )
    meta_description = models.CharField(
        verbose_name="Meta description dla artykułu", blank=True, null=True, max_length=160
    )
    meta_title = models.CharField(
        verbose_name="Meta title dla artykułu", blank=True, null=True, max_length=60
    )
    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        super(Articles, self).save()

    def get_absolute_url(self):
        return reverse(
            "article_details",
            kwargs={
                "category": self.category.slug,
                "title": self.slug,
                "pk": self.id,
            },
        )

    class Meta:
        ordering = ("-created_time",)
        verbose_name_plural = "Artykuły"

    def images(self):
        return Images.objects.filter(article_id=self)

    def __str__(self):
        return self.category.name + ", " + self.title


"""The filtersets tests module."""
import pytest
from django.db.models.query import QuerySet
from django.http.request import HttpRequest

from communication.filtersets import (_get_interlocutors, _get_recipients,
                                      _get_reviews, _get_senders)

pytestmark = pytest.mark.django_db

def test_get_reviews(reviews: QuerySet):
    """Should return the filtered list of reviews."""
    assert not _get_reviews(None).count()
    request = HttpRequest()
    user = reviews[0].professional.user
    request.user = user
    result = _get_reviews(request)
    assert result.count() == 1
    assert result[0].professional.user == reviews[0].professional.user

def test_get_recipients(messages: QuerySet):
    """Should return recipients."""
    assert not _get_recipients(None).count()
    request = HttpRequest()
    user = messages[0].sender
    request.user = user
    result = _get_recipients(request)
    assert result.count() == 1
    assert result[0] == messages[0].recipient

def test_get_senders(messages: QuerySet):
    """Should return senders."""
    assert not _get_senders(None).count()
    request = HttpRequest()
    user = messages[0].recipient
    request.user = user
    result = _get_senders(request)
    assert result.count() == 1
    assert result[0] == messages[0].sender

def test_get_interlocutors(messages: QuerySet):
    """Should return interlocutors."""
    assert not _get_interlocutors(None).count()
    request = HttpRequest()
    user = messages[0].recipient
    request.user = user
    result = _get_interlocutors(request)
    assert result.count() == 1
    assert result[0] == messages[0].sender


import argparse
import socket
import sys
import json
import urllib.request
import redis
import base64
import re
import boto3
import os
import subprocess
from faker import Faker
import logging

logging.basicConfig(level=logging.DEBUG)
fake = Faker('en_US')
Faker.seed(1337)

kms_client = boto3.client('kms')
kms_key_id = os.environ.get('KMS_KEY_ID')

r = redis.Redis(unix_socket_path='/run/redis.sock')
for i in range(1,100):
	name = fake.name()
	r.set(name, 'bar{}'.format(i))

# Running server you have pass port the server  will listen to. For Example:
# $ python3 /app/server.py server 5005
class VsockListener:
	# Server
	def __init__(self, conn_backlog=128):
		self.conn_backlog = conn_backlog

	def bind(self, port):
		# Bind and listen for connections on the specified port
		self.sock = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
		self.sock.bind((socket.VMADDR_CID_ANY, port))
		self.sock.listen(self.conn_backlog)

	def recv_data(self):
		# Receive data from a remote endpoint
		while True:
			try:
				logging.info("Let's accept stuff")
				(from_client, (remote_cid, remote_port)) = self.sock.accept()
				logging.info("Connection from " + str(from_client) + str(remote_cid) + str(remote_port))

				query = json.loads(base64.b64decode(from_client.recv(4096).decode()).decode())
				logging.info("Message received: {}".format(query))
				query_type = list(query.keys())[0]
				query = query[query_type]

				logging.info("{} {}".format(query_type, query))
				if query_type == 'get':
					response = query_redis(query)
				elif query_type == 'set':
					response = put_in_redis(query)
				else:
					response = "Bad query type\n"

				# Send back the response
				from_client.send(str(response).encode())

				from_client.close()
				logging.info("Client call closed")
			except Exception as ex:
				logging.info(ex)

KMS_PROXY_PORT="8000"

def get_plaintext(credentials):
		"""
		prepare inputs and invoke decrypt function
		"""

		# take all data from client
		access = credentials['access_key_id']
		secret = credentials['secret_access_key']
		token = credentials['token']
		ciphertext= credentials['ciphertext']
		region = credentials['region']

		logging.info('ciphertext: {}'.format(ciphertext))
		creds = decrypt_cipher(access, secret, token, ciphertext, region)
		return creds

def decrypt_cipher(access, secret, token, ciphertext, region):
		"""
		use KMS Tool Enclave Cli to decrypt cipher text
		"""
		logging.info('in decrypt_cypher')
		proc_params = [
				"/app/kmstool_enclave_cli",
				"decrypt",
				"--region", region,
				"--proxy-port", KMS_PROXY_PORT,
				"--aws-access-key-id", access,
				"--aws-secret-access-key", secret,
				"--aws-session-token", token,
				"--ciphertext", ciphertext,
		]
		logging.debug('proc_params: {}'.format(proc_params))
		proc = subprocess.Popen(
			proc_params,
			stdout=subprocess.PIPE,
			stderr=subprocess.PIPE
		)

		ret = proc.communicate()

		logging.debug('proc: {}'.format(proc.communicate()))

		if ret[0]:
				logging.info('No KMS error')
				logging.debug('ret[0]: {}'.format(ret[0]))
				b64text = proc.communicate()[0].decode().split()[1]
				logging.debug('b64text: {}'.format(b64text))
				plaintext = base64.b64decode(b64text).decode()
				return (0, plaintext)
		else:
				logging.info('KMS error')
				return (1, "KMS Error. Decryption Failed.\n")

def server_handler(args):
	server = VsockListener()
	server.bind(args.port)
	logging.info("Started listening to port : {}".format(args.port))
	server.recv_data()

def put_in_redis(query):
	status, query = get_plaintext(query)
	if status:
		logging.info(query)
		return query
	try:
		query = json.loads(query)
	except ValueError:
		return 'Failed to put in data: Mot valid JSON\n'
	for key in query.keys():
		r.set(key, query[key])
	return "Put the data in\n"

# Get list of current ip ranges for the S3 service for a region.
# Learn more here: https://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html#aws-ip-download
def query_redis(query):
	status, value = get_plaintext(query)
	if status:
		logging.info(value)
		return value
	value = r.get(value)
	if value != None:
		logging.info("Key exists")
		return "The key exists\n"
	elif value == None:
		logging.info("Key doesn't exist")
		return "They key does not exist\n"
	else:
		logging.info("In Else")
		return "Somehow here with value: {}\n".format(value)

def main():
	parser = argparse.ArgumentParser(prog='vsock-sample')
	parser.add_argument("--version", action="version",
						help="Prints version information.",
						version='%(prog)s 0.1.0')
	subparsers = parser.add_subparsers(title="options")

	server_parser = subparsers.add_parser("server", description="Server",
											help="Listen on a given port.")
	server_parser.add_argument("port", type=int, help="The local port to listen on.")
	server_parser.set_defaults(func=server_handler)

	if len(sys.argv) < 2:
		parser.print_usage()
		sys.exit(1)

	args = parser.parse_args()
	args.func(args)

if __name__ == "__main__":
	main()


from geometry_msgs.msg import Twist
import pyzbar.pyzbar as pyzbar
from datetime import datetime
import pyrealsense2 as rs
import numpy as np
import schedule
import rospy
import time
import cv2

frame_crop_x1 = 0
frame_crop_y1 = 120
frame_crop_x2 = 639
frame_crop_y2 = 479

minLineLength = 30
maxLineGap = 15

speed = 0
angle = 0
avr_x = 0
turn = -0.5

code_start = "start"
barcode_data_line_QR = []
text_0 = ""
text_1 = ""

## 동일한 qr코드 인식시 스피드 0 or 움직임
view_same_QR = 0
view_start_QR_and_no_product = 0

obstacle_view = 0

cap_0 = cv2.VideoCapture(2)
cap_1 = cv2.VideoCapture(4)

cap_1.set(cv2.CAP_PROP_FRAME_HEIGHT,180)
cap_1.set(cv2.CAP_PROP_FRAME_WIDTH,320)

def cam_0_read():
    global retval_0, frame_0, original, gray_line_0, gray_line_1
    retval_0, frame_0 = cap_0.read()
    original = frame_0
    gray_line_0 = cv2.cvtColor(frame_0, cv2.COLOR_BGR2GRAY)
    gray_line_1 = cv2.cvtColor(frame_0, cv2.COLOR_BGR2GRAY)

def cam_1_read():
    global retval_1, frame_1, gray_product_0
    retval_1, frame_1 = cap_1.read()
    gray_product_0 = cv2.cvtColor(frame_1, cv2.COLOR_BGR2GRAY)

def cam_0_use_line():
    global retval_0, frame_0, original, theta
    blurred = gray_line_0[frame_crop_y1:frame_crop_y2,frame_crop_x1:frame_crop_x2]
    blurred = cv2.boxFilter(blurred, ddepth=-1, ksize=(31,31))
    retval2 ,blurred = cv2.threshold(blurred, 100, 255, cv2.THRESH_BINARY)
    edged = cv2.Canny(blurred, 85, 85)
    lines = cv2.HoughLinesP(edged,1,np.pi/180,10,minLineLength,maxLineGap)
    max_diff = 1000
    final_x = 0
    if ( lines is not None ):
        if ( lines is not None ):
            add_line = 0
            for line in lines:
                x1, y1, x2, y2 = line[0]
                cv2.line(original,(x1+frame_crop_x1,y1+frame_crop_y1),(x2+frame_crop_x1,y2+frame_crop_y1),(0,255,0),3)
                mid_point = ( x1 + x2 ) / 2
                diff = abs((640/2) - mid_point)
                if ( max_diff > diff ) :
                    max_diff = diff
                    final_x = mid_point
                add_line = add_line + final_x
            average_x = add_line / len(lines)
        if ( int(average_x) != 0 ) :
            original = cv2.circle(original,(int(average_x),int((frame_crop_y1+frame_crop_y2)/2)),5,(0,0,255),-1)
            original = cv2.rectangle(original,(int(frame_crop_x1),int(frame_crop_y1)),(int(frame_crop_x2),int(frame_crop_y2)),(0,0,255),1)
        frame_0 = original
        theta = int(( int(average_x) - 320.0 ) / 640.0 * 100)
    if ( lines is None ):
        theta = -50

def cam_0_use_qrcode():
    global barcode_data_line_QR, barcode_type_line_QR
    decoded_line_QR = pyzbar.decode(gray_line_1)
    for _ in decoded_line_QR:
        x, y, w, h = _.rect
        barcode_data_line_QR = _.data.decode("utf-8")
        barcode_type_line_QR = _.type
        cv2.rectangle(frame_0, (x, y), (x + w, y + h), (0, 0, 255), 2)
        text_0 = '%s (%s)' % (barcode_data_line_QR, barcode_type_line_QR)
        cv2.putText(frame_0, text_0, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2, cv2.LINE_AA)
    if decoded_line_QR == [] :
        barcode_data_line_QR = "QR_X"

def cam_1_use_qrcode():
    global barcode_data_product_QR, barcode_type_product_QR
    decoded_product_QR = pyzbar.decode(gray_product_0)
    for _ in decoded_product_QR:
        a, b, c, d = _.rect
        barcode_data_product_QR = _.data.decode("utf-8")
        barcode_type_product_QR = _.type
        cv2.rectangle(frame_1, (a, b), (a + c, b + d), (0, 0, 255), 2)
        text_1 = '%s (%s)' % (barcode_data_product_QR, barcode_type_product_QR)
        cv2.putText(frame_1, text_1, (a, b), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2, cv2.LINE_AA)
    if decoded_product_QR == [] :
        barcode_data_product_QR = "QR_X"

def cam_lidar_read():
    global pipeline
    pipeline = rs.pipeline()
    config = rs.config()
    config.enable_stream(rs.stream.depth, 320, 240, rs.format.z16, )
    pipeline.start(config)

def cam_lidar_use():
    global add_num, add_edge_num, add_edge_remain_num, image_all, left_view
    frames = pipeline.wait_for_frames()
    depth = frames.get_depth_frame()
    coverage = [0]*32
    image_all = []
    add_num = 0
    add_edge_num = 0
    add_edge_remain_num = 0
    for y in range(240):
        for x in range(320):
            dist = depth.get_distance(x, y)
            if 0 < dist and dist < 1:
                coverage[x//10] += 1
        if y%20 is 19:
            line = ""
            for c in coverage:
                line += " 12345678"[c//25]
            coverage = [0]*32
            image_all.append(line)
            for a in range(1,32):
                if line[a] == " " : add_num = add_num
                else : add_num = add_num + int(line[a])
            for a in range(1,2):
                if line[a] == " " : add_edge_num = add_edge_num
                else : add_edge_num = add_edge_num + int(line[a])
            for a in range(3,32):
                if line[a] == " " : add_edge_remain_num = add_edge_remain_num
                else : add_edge_remain_num = add_edge_remain_num + int(line[a])

def speed_and_angle_make():
    global angle, speed
    angle = round((-theta) * (0.012), 2)
    speed = 0.3 - abs(angle * 0.2)

def speed_and_angle_turn():
    global angle, speed
    speed = 0
    angle = turn

def speed_and_angle_main():
    global angle, speed, barcode_data_product_QR, barcode_data_line_QR, turn, obstacle_view, view_same_QR, view_start_QR_and_no_product

    if barcode_data_line_QR == "right_turn"  : turn = -0.5
    if barcode_data_line_QR == "left_turn" : turn = 0.5

    if view_same_QR == 0 and view_start_QR_and_no_product == 0 :
        if obstacle_view == 0 :
            if theta != -50:
                if add_num <= 10                                                                                : speed_and_angle_make()
                if add_num <= 10 and barcode_data_product_QR != "QR_X" and barcode_data_product_QR == barcode_data_line_QR : view_same_QR = 1
                if add_num <= 10 and barcode_data_product_QR == "QR_X" and barcode_data_line_QR    == "start"              : view_start_QR_and_no_product = 1
                if add_num >  10 :
                    speed = 0
                    obstacle_view = 1
            if theta == -50 : speed_and_angle_turn()

        if obstacle_view == 1 :
            if add_num != 0 :
                if add_edge_num        > 0 and add_edge_remain_num == 0 : angle = -0.4
                if add_edge_remain_num > 0 and add_edge_num == 0 : angle = -0.4
                if add_edge_remain_num > 0 and add_edge_num > 0 : angle = -0.4
                speed = 0.2
            if add_num == 0                  : angle = 0.4
            if theta != -50 and add_num == 0 : obstacle_view = 0

    if view_same_QR == 1 or view_start_QR_and_no_product == 1:
        speed = 0
        angle = 0
        if view_same_QR == 1                 and barcode_data_product_QR == "QR_X" : view_same_QR = 0
        if view_start_QR_and_no_product == 1 and barcode_data_product_QR != "QR_X" : view_start_QR_and_no_product = 0

def talker():
    global speed, angle
    rospy.init_node("line_qr_sensor")
    pub = rospy.Publisher("/cmd_vel", Twist, queue_size=10)
    msg = Twist()

    cam_lidar_read()

    while not rospy.is_shutdown():

        msg.linear.x = speed
        msg.angular.z = angle
        pub.publish(msg)

        cam_0_read()
        cam_0_use_line()
        cam_0_use_qrcode()

        cam_1_read()
        cam_1_use_qrcode()

        cam_lidar_use()

        speed_and_angle_main()

        for y in range(12):
            print(image_all[y])
        print(add_num)
        print("장애물 : ", obstacle_view)
        print("세타값 : ", theta)
        print("턴값 : ", turn)
        cv2.imshow('frame_0', frame_0)
        cv2.imshow('frame_1', frame_1)

        key = cv2.waitKey(25)
        if key == 27: #ESC
            break

if __name__ == "__main__":
    try:
        talker()
    except rospy.ROSInterruptException:
        pass


import commands

import sys
sys.path.append('../../')

import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

import django
django.setup()

path = os.getcwd()

pangu_info = "/".join([path,"mysite/config/ecs_pangu.txt"])

def parse_srv_status():
    oss_srv_stat = {}
    ret = []
    pangu_srv_status = commands.getoutput("cat %s |grep -Ei \"normal|disconnected\""%pangu_info)
    split_data = pangu_srv_status.split('\n')
    for srv in split_data:
        oss_srv_stat["status"] = srv.split()[1]
        oss_srv_stat['ip'] = srv.split()[5].split('//')[1]
        oss_srv_stat['hostname'] = srv.split()[6]
        ret.append(dict(oss_srv_stat))
    return ret


import numpy as np
import matplotlib.pyplot as plt
import sys

def h(X, theta):
	return 1 / (1 + np.e ** -(X.dot(theta.T)))

def J(X, y, theta):
	m = X.shape[0]
	y_hat = h(X, theta)
	erro = (-y * np.log(y_hat) - (1-y) * np.log(1-y_hat)).sum(0)
	return erro / m

def GD(X, y, theta, alpha, niters):
	m = X.shape[0]
	cost = np.zeros((niters,1))
	print('iteração:')
	for k in range(0, niters):
		print(' ',k,end='')
		y_hat = h(X, theta)
		erro = ((y_hat-y) *X).sum(0)/m
		theta -= (alpha * (erro))
		cost[k] = J(X, y, theta)
		print('\r\r\r\r\r\r',end='')
	return (cost, theta)

def featureScaling(X):
  X=X-np.min(X,0)
  den=np.max(X,0)-np.min(X,0)
  return X/den

if len(sys.argv) < 5:
	print('Usage %s <dataset> <# of iterations> <alpha> <delimiter>'%sys.argv[0])

f=sys.argv[1]
niters=int(sys.argv[2])
alpha=float(sys.argv[3])
delim=sys.argv[4]
## delete \n at the end of the string
data=np.genfromtxt(f,delimiter=delim)
## split the string into the values
# now data is a list of lists
X=data[:,:-1]
X=np.array(X,dtype=float)
#### Inicio da área que é permita alguma mudança
X=featureScaling(X)
X=np.insert(X,0,1,axis=1)
#### Fim da área que é permitida alguma mudança
y=data[:,-1]
y=np.reshape(np.array(y,dtype=int),(len(y),1))

Theta=np.zeros((1,X.shape[1]))

nsize=int(X.shape[0]*.7)

Xtr=X[:nsize,:]
Xte=X[nsize:,:]
ytr=y[:nsize]
yte=y[nsize:]

c,t=GD(Xtr,ytr,Theta,alpha,niters)

y_hat=np.round(h(Xtr,t))
print('Home made learner:')
print('   Taxa de acerto (treino):', np.mean(y_hat==ytr))
y_hat=np.round(h(Xte,t))
print('   Taxa de acerto (teste):', np.mean(y_hat==yte))
plt.plot(c)
plt.show()
#------------
from sklearn import linear_model
r=linear_model.LogisticRegression()
ytr=np.ravel(ytr)
r.fit(Xtr,ytr)
yte=np.ravel(yte)
y_hat=r.predict(Xtr)
print('Home made learner:')
print('   Taxa de acerto (treino):', np.mean(y_hat==ytr))
y_hat=r.predict(Xte)
print('   Taxa de acerto (teste):', np.mean(y_hat==yte))


# -*- coding: utf-8 -*-
"""
Created on Tue Dec  5 21:43:03 2017

@author: ly
"""
import numpy as np
import pandas as pd
import os
import seaborn as sns # data visualization library
import matplotlib.pyplot as plt
import xgboost as xgb
import math

from sklearn import metrics
from sklearn.model_selection import KFold
from xgboost.sklearn import XGBClassifier
from sklearn.linear_model import Lasso
from sklearn.metrics import confusion_matrix

def prepare_data(filepath):

    filepath=r"E:\workspace\Dementia\Q_DLB_nonDLB_after_removing_education_normalizion_0_to_1.csv"
    dataset=pd.read_csv(filepath, index_col=None)
    temp = dataset.copy()
    DLB_count = 0
    nonDLB_count = 0
    for i in range(len(temp)):
        if temp.loc[i,'Diagnosis'] == 'DLB':
            temp.loc[i,'Diagnosis'] = 1
            DLB_count+=1
        else:
            temp.loc[i,'Diagnosis'] = 0
            nonDLB_count+=1
    return temp,DLB_count,nonDLB_count

#filepath =r"E:\workspace\Dementia\Q_DLB_nonDLB_after_removing_education_normalizion_0_to_1.csv"
#temp,DLB_count,nonDLB_count = prepare_data(filepath)
#raw_target = list(temp.loc[:,'Diagnosis'] )
#Label_Array = temp.columns[:-1]
#import itertools
#combination = list(itertools.combinations(Label_Array,2))

def statistic(true,predict):
    TP=0 #TP：正确的正例
    TN=0 #TN：正确的负例
    FP=0 #FP：错误的正例
    FN=0 #FN：错误的负例
    for i in range(len(true)):
        if(true[i]==1):
            if(predict[i]==1):
                TP+=1 #真实为1，预测也为1
            else :
                FP+=1 #真实为1，预测为0

        elif(predict[i]==1):
            FN+=1 #真实为0，预测为1
        else :
            TN+=1 #真实为0，预测为0

    return [TP,FP,TN,FN]

#统计准确率衡量的5个指标：Sn,Sp,Avc,Acc,Mcc
def assess(TP,FP,TN,FN):
    Sn=Sp=Acc=Avc=Mcc=0 #评价分类器所用指标

    if(TP+FN!=0):
        Sn=TP*1.0/(TP+FN) #预测为1（正）的正确率

    if(TN+FP!=0):
        Sp=TN*1.0/(TN+FP) #预测为0（负）的正确率

    Avc=(Sn+Sp)*1.0/2 #正负平均准确率
    Acc=(TP+TN)*1.0/(TP+FP+TN+FN) #总体预测准确率

    if((TP+FN)*(TP+FP)*(TN+FP)*(TN+FN)!=0):
        Mcc=(TP*TN-FP*FN)*1.0/math.sqrt((TP+FN)*(TP+FP)*(TN+FP)*(TN+FN))

    return [Sn,Sp,Acc,Avc,Mcc]

def kFoldTest(clf, raw_data, raw_target):
    '''
    十折交叉检验，clf是分类器，返回预测集
    '''
    predict=[]
    kf = KFold(n_splits=10)
    for train_index, test_index in kf.split(raw_data):
        #print("TRAIN:", train_index, "TEST:", test_index)#查看如何分割数据
        X_train, X_test = raw_data[[train_index]], raw_data[[test_index]]
        #Y_test在这里没作用，为了数据变量对齐0.0
        Y_train, Y_test = raw_target[:test_index[0]]+raw_target[test_index[-1]+1:], raw_target[test_index[0]:test_index[-1]+1]
        clf.fit(X_train,Y_train)
        test_target_temp=clf.predict(X_test)
        predict.append(test_target_temp)
    test_target = [i for temp in predict for i in temp]#将10次测试集展平
    return test_target

def common_classier(raw_data, raw_target):
    '''
    使用常见的分类器进行分类
    '''
    from sklearn import neighbors
    from sklearn.svm import SVC
    from sklearn.tree import DecisionTreeClassifier
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.ensemble import AdaBoostClassifier

    df=pd.DataFrame(index=['Sn','Sp','ACC','AVC','MCC'])#该dataframe为了将正确率写入Excel

    clf = SVC(kernel='linear', C=1)
    value = statistic(raw_target,kFoldTest(clf, raw_data, raw_target))
    df['SVM']=assess(value[0],value[1],value[2],value[3])

    #用KNN
    clf=neighbors.KNeighborsClassifier(n_neighbors = 3 )
    value=statistic(raw_target,kFoldTest(clf, raw_data, raw_target))
    df['KNN']=assess(value[0],value[1],value[2],value[3])

#    #NB,pca的时候不能用
#    clf=MultinomialNB(alpha=0.01)
#    ACC,DLB,nonDLB=statistics(raw_target,kFoldTest(clf, ra

    #Dtree
    clf = DecisionTreeClassifier(random_state=0)
    value=statistic(raw_target,kFoldTest(clf, raw_data, raw_target))
    df['Dtree']=assess(value[0],value[1],value[2],value[3])

    #随机森林
    clf = RandomForestClassifier(n_estimators= 30, max_depth=13, min_samples_split=110,
                                 min_samples_leaf=20,max_features='sqrt' ,oob_score=True,random_state=10)
    value=statistic(raw_target,kFoldTest(clf, raw_data, raw_target))
    df['RF']=assess(value[0],value[1],value[2],value[3])

    #boosting
    clf = AdaBoostClassifier(n_estimators=100)
    value=statistic(raw_target,kFoldTest(clf, raw_data, raw_target))
    df['adaboost']=assess(value[0],value[1],value[2],value[3])

    return df

'''
filepath = r"E:\workspace\Dementia\Q_DLB_nonDLB_after_removing_education_normalizion_0_to_1.csv"
temp,DLB_count,nonDLB_count = prepare_data(filepath)
raw_data = temp.drop('Diagnosis',1).as_matrix(columns=None)
raw_label = list(temp.loc[:,'Diagnosis'])
df = common_classier(raw_data,raw_label)

temp_acc = max(list(df.loc['ACC',:] ))
Label_Array = temp.columns[:-1]
import itertools
combination = list(itertools.combinations(Label_Array,2))
writer = pd.ExcelWriter(r'E:\workspace\Dementia\acc.xlsx')
i = 0
max_acc = 0

for label_index in combination:
    sheet = "combination" + str(i)
    i += 1
    raw_data = temp.loc[:,label_index].as_matrix(columns=None)
    temp_df = common_classier(raw_data, raw_label)
    temp_acc = max(list(temp_df.loc['ACC',:] ))
    if temp_acc >= max_acc :
        temp_df.to_excel(writer,sheet_name=sheet,index=True)
        max_acc = temp_acc
writer.save()
'''
def Combination(temp, DLB_count, nonDLB_count, num):
    '''
    对数据集temp特征进行组合再进行分类
    '''
    raw_target = list(temp.loc[:,'Diagnosis'] )
    Label_Array = temp.columns[:-1]
    import itertools
    combination = list(itertools.combinations(Label_Array,num))
    writer = pd.ExcelWriter(r'E:\workspace\Dementia\acc.xlsx')
    i = 0
    max_acc = 0
    for label_index in combination:
        sheet = "combination" + str(i)
        i += 1
        raw_data = temp.loc[:,label_index].as_matrix(columns=None)
        temp_df = common_classier(raw_data, raw_target)
        temp_acc = max(list(temp_df.loc['ACC',:] ))
        if temp_acc >= max_acc :
            temp_df.to_excel(writer,sheet_name=sheet,index=True)
            max_acc = temp_acc
    writer.save()

if __name__ == '__main__':

    filepath = r"E:\workspace\Dementia\Q_DLB_nonDLB_after_removing_education_normalizion_0_to_1.csv"
    temp,DLB_count,nonDLB_count = prepare_data(filepath)
    raw_data =  temp.drop('Diagnosis',1).as_matrix(columns=None)
    raw_label = list(temp.loc[:,'Diagnosis'])
    Combination(raw_data, DLB_count, nonDLB_count, 2)


# -*- coding: utf-8 -*-
#
# Author: Ingelrest François (Francois.Ingelrest@gmail.com)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

import gtk, modules, os.path

from tools   import consts, loadGladeFile, prefs
from gettext import gettext as _

MOD_INFO = ('Status Icon', _('Status Icon'), _('Add an icon to the notification area'), [], False, False)

class StatusIcon(modules.Module):

    def __init__(self):
        """ Constructor """
        modules.Module.__init__(self, (consts.MSG_EVT_MOD_LOADED, consts.MSG_EVT_MOD_UNLOADED,  consts.MSG_EVT_APP_STARTED,
                                       consts.MSG_EVT_NEW_TRACK,  consts.MSG_EVT_PAUSED,        consts.MSG_EVT_UNPAUSED,
                                       consts.MSG_EVT_STOPPED,    consts.MSG_EVT_NEW_TRACKLIST, consts.MSG_EVT_TRACK_MOVED))

    def install(self):
        """ Install the Status icon """
        self.tooltip          = consts.appName
        self.isPaused         = False
        self.popupMenu        = None
        self.isPlaying        = False
        self.icoNormal        = None
        self.mainWindow       = prefs.getWidgetsTree().get_widget('win-main')
        self.trackHasNext     = False
        self.trackHasPrev     = False
        self.emptyTracklist   = True
        self.isMainWinVisible = True
        # The status icon does not support RGBA, so make sure to use the RGB color map when creating it
        colormap = self.mainWindow.get_screen().get_rgb_colormap()
        gtk.widget_push_colormap(self.mainWindow.get_screen().get_rgb_colormap())
        self.statusIcon = gtk.StatusIcon()
        gtk.widget_pop_colormap()
        # GTK+ handlers
        self.statusIcon.connect('activate',     self.toggleWinVisibility)
        self.statusIcon.connect('popup-menu',   self.onPopupMenu)
        self.statusIcon.connect('size-changed', self.renderIcons)
        # Install everything
        self.statusIcon.set_tooltip(consts.appName)
        self.onNewTrack(None)
        self.statusIcon.set_visible(True)

    def uninstall(self):
        """ Uninstall the Status icon """
        self.statusIcon.set_visible(False)
        self.statusIcon = None
        if not self.isMainWinVisible:
            self.mainWindow.show()
            self.isMainWinVisible = True

    def renderIcons(self, statusIcon, availableSize):
        """ (Re) Create icons based the available tray size """
        # Normal icon
        if   availableSize >= 48+2: self.icoNormal = gtk.gdk.pixbuf_new_from_file(consts.fileImgIcon48)
        elif availableSize >= 32+2: self.icoNormal = gtk.gdk.pixbuf_new_from_file(consts.fileImgIcon32)
        elif availableSize >= 24+2: self.icoNormal = gtk.gdk.pixbuf_new_from_file(consts.fileImgIcon24)
        else:                       self.icoNormal = gtk.gdk.pixbuf_new_from_file(consts.fileImgIcon16)

        # Paused icon
        self.icoPause = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, self.icoNormal.get_width(), self.icoNormal.get_height())
        self.icoPause.fill(0x00000000)
        self.icoNormal.composite(self.icoPause, 0, 0, self.icoNormal.get_width(), self.icoNormal.get_height(), 0, 0, 1, 1, gtk.gdk.INTERP_HYPER, 100)

        if self.icoNormal.get_width() == 16: pauseStock = self.mainWindow.render_icon(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_MENU)
        else:                                pauseStock = self.mainWindow.render_icon(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_BUTTON)

        diffX = self.icoPause.get_width() - pauseStock.get_width()
        diffY = self.icoPause.get_height() - pauseStock.get_height()
        pauseStock.composite(self.icoPause, 0, 0, pauseStock.get_width(), pauseStock.get_height(), diffX/2, diffY/2, 1, 1, gtk.gdk.INTERP_HYPER, 255)

        # Use the correct icon
        if self.isPaused: statusIcon.set_from_pixbuf(self.icoPause)
        else:             statusIcon.set_from_pixbuf(self.icoNormal)

    def onNewTrack(self, track):
        """ A new track is being played, None if none """
        if track is None: self.tooltip = consts.appName
        else:             self.tooltip  = '%s - %s' % (track.getArtist(), track.getTitle())

        self.isPaused  = False
        self.isPlaying = track is not None

        self.statusIcon.set_from_pixbuf(self.icoNormal)
        self.statusIcon.set_tooltip(self.tooltip)

    def onPause(self):
        """ The current track has been paused """
        self.isPaused = True
        self.statusIcon.set_from_pixbuf(self.icoPause)
        self.statusIcon.set_tooltip(_('%(tooltip)s [paused]') % {'tooltip': self.tooltip})

    def onUnpause(self):
        """ The current track has been unpaused """
        self.isPaused = False
        self.statusIcon.set_from_pixbuf(self.icoNormal)
        self.statusIcon.set_tooltip(self.tooltip)

    def toggleWinVisibility(self, statusIcon):
        """ Show/hide the main window """
        if not self.isMainWinVisible:
            self.mainWindow.show()
            self.isMainWinVisible = True
        elif self.mainWindow.has_toplevel_focus():
            self.mainWindow.hide()
            self.isMainWinVisible = False
        else:
            self.mainWindow.hide()
            self.mainWindow.show()

    # --== Message handler ==--

    def handleMsg(self, msg, params):
        """ Handle messages sent to this module """
        if   msg == consts.MSG_EVT_PAUSED:        self.onPause()
        elif msg == consts.MSG_EVT_STOPPED:       self.onNewTrack(None)
        elif msg == consts.MSG_EVT_UNPAUSED:      self.onUnpause()
        elif msg == consts.MSG_EVT_NEW_TRACK:     self.onNewTrack(params['track'])
        elif msg == consts.MSG_EVT_MOD_LOADED:    self.install()
        elif msg == consts.MSG_EVT_TRACK_MOVED:   self.trackHasNext, self.trackHasPrev = params['hasNext'], params['hasPrevious']
        elif msg == consts.MSG_EVT_APP_STARTED:   self.install()
        elif msg == consts.MSG_EVT_MOD_UNLOADED:  self.uninstall()
        elif msg == consts.MSG_EVT_NEW_TRACKLIST: self.emptyTracklist = (len(params['tracks']) == 0)

    # --== GTK handlers ==--

    def onPopupMenu(self, statusIcon, button, time):
        """ The user asks for the popup menu """
        if self.popupMenu is None:
            wTree              = loadGladeFile('StatusIconMenu.glade')
            self.menuPlay      = wTree.get_widget('item-play')
            self.menuStop      = wTree.get_widget('item-stop')
            self.menuNext      = wTree.get_widget('item-next')
            self.popupMenu     = wTree.get_widget('menu-popup')
            self.menuPause     = wTree.get_widget('item-pause')
            self.menuPrevious  = wTree.get_widget('item-previous')
            self.menuSeparator = wTree.get_widget('item-separator')
            # Connect handlers
            wTree.get_widget('item-quit').connect('activate', lambda btn: modules.postQuitMsg())
            wTree.get_widget('item-preferences').connect('activate', lambda btn: modules.showPreferences())
            self.menuPlay.connect('activate',     lambda btn: modules.postMsg(consts.MSG_CMD_TOGGLE_PAUSE))
            self.menuStop.connect('activate',     lambda btn: modules.postMsg(consts.MSG_CMD_STOP))
            self.menuNext.connect('activate',     lambda btn: modules.postMsg(consts.MSG_CMD_NEXT))
            self.menuPrevious.connect('activate', lambda btn: modules.postMsg(consts.MSG_CMD_PREVIOUS))
            self.menuPause.connect('activate',    lambda btn: modules.postMsg(consts.MSG_CMD_TOGGLE_PAUSE))
            self.popupMenu.show_all()

        # Enable only relevant menu entries
        self.menuStop.set_sensitive(self.isPlaying)
        self.menuNext.set_sensitive(self.isPlaying and self.trackHasNext)
        self.menuPause.set_sensitive(self.isPlaying and not self.isPaused)
        self.menuPrevious.set_sensitive(self.isPlaying and self.trackHasPrev)
        self.menuPlay.set_sensitive((not (self.isPlaying or self.emptyTracklist)) or self.isPaused)

        self.popupMenu.popup(None, None, gtk.status_icon_position_menu, button, time, statusIcon)


import pandas as pd
import streamlit as st
import fitz
from PIL import Image
from dataExtractor import DataExtractor
from image2 import Canvas
from firebase import FirebaseDB
import json
from st_keyup import st_keyup

json_data = {'Tear Down': ['cable', 'bomba', 'intake'],
             'Production': ['simulacion', 'equipo'],
             'Artificial Lift': ['cable', 'bomba', 'intake', 'motor', 'sensor', 'protector'],
             'Efficiency': ['general', 'bomba', 'motor']}

st.write(f"Define a new extraction {st.session_state.user['nombre']}")

def read_pdf(uploaded):
    file = fitz.open(stream=uploaded.read())
    return file

def create_image_list_from_pdf(file):
    images = []
    for page_number in range(len(file)):
        page = file.load_page(page_number)
        pix = page.get_pixmap(matrix=fitz.Matrix(2, 2))
        image = Image.frombytes("RGB", (pix.width, pix.height), pix.samples)
        w, h = 700, 500
        resized_image = image.resize((w, h))
        images.append(resized_image)
    return images

def replace_image_in_canvas(canvas, image, key):
    new_image = image  # Get the new image
    new_key = key  # Get the new key
    canvas.reset_canvas(new_image, new_key)  # Call the reset_canvas method of the canvas object

def load_canvas(image, page_number, draw_mode, update):
    canvas = Canvas(image, draw_mode, update_streamlit=update)
    canvas.create_canvas(page_number)
    canvas.process_drawing()
    return canvas

def store_scaled_coordinates(page_number, coordinates, delete_columns):
    if coordinates is not None:
        # Fill the 'page' column with the page_number value
        coordinates['page'] = page_number

        # Drop the specified columns
        coordinates = coordinates.drop(delete_columns, axis=1)

        return pd.DataFrame(coordinates)

def present_dataframe(dataframe, markdown):
    if isinstance(dataframe, pd.DataFrame):
        st.subheader(markdown)
        st.dataframe(dataframe)
    else:
        st.write("No DataFrame was provided.")

def first_page():
    st.subheader("PDF Document")
    st.write("Upload and define attributes.")
    file = st.file_uploader("Upload PDF", type=['pdf'])
    if file is not None:
        if "file" not in st.session_state:
            st.session_state["file"] = file

        if "regex" not in st.session_state:
            st.session_state["regex"] = pd.DataFrame()

        if "subject" not in st.session_state:
            st.session_state["subject"] = None

def get_report_main_topics(report):
    st.session_state.atributos_reporte = json_data[report]

def add_coordinates_to_firebase(dataframe, collection_name, subject):
    firebase_db = FirebaseDB()
    return firebase_db.add_coordinates(dataframe, collection_name, subject)

def selection():
    # Usage example
    subject_name = "testmail/test2"
    #To upload a report
    st.subheader("PDF Document Extraction")
    uploaded_file = st.file_uploader("Upload PDF sample", type=['pdf'])
    realtime_update = st.checkbox("Update in realtime", True)
    st.write("Select between defining the area of the table (rect),"
             "or modify a predefined area (transform)")
    drawing_mode = st.selectbox("Drawing tool:", ["rect", "transform"])

    if uploaded_file is not None:
        if "compiled_scales" not in st.session_state:
            st.session_state["compiled_scales"] = pd.DataFrame()

        if "page_number" not in st.session_state:
            st.session_state["page_number"] = 0

        pdf = read_pdf(uploaded_file)
        image_list = create_image_list_from_pdf(pdf)

        canvas_obj = load_canvas(image_list[st.session_state["page_number"]],
                                 st.session_state["page_number"],
                                 drawing_mode, realtime_update)

        st.caption("Note: This canvas version could define columns or cells with None values,"
                   " consider to select a table or area of it in order that the table extraction preview"
                   " contains the elements you want.")

        present_dataframe(st.session_state["compiled_scales"], "All Scaled Coordinates")

        objects_df = canvas_obj.get_objects_dataframe()
        all_scaled_coordinates = None
        if objects_df is not None and 'type' in objects_df.columns:
            table_objects = objects_df.loc[objects_df['type'] == 'rect']

            if len(table_objects) > 0:
                difference = (len(st.session_state["atributos_reporte"])-len(st.session_state["compiled_scales"]))
                #st.write(difference)
                data = st.session_state["atributos_reporte"][-difference:] if difference > 0 else []
                #st.write(data)
                all_scaled_coordinates = canvas_obj.process_tables(table_objects,
                                                                   pdf.load_page(st.session_state["page_number"]),
                                                                   data)
                if all_scaled_coordinates is not None:
                    st.markdown("### Scaled Page Coordinates")
                    st.table(all_scaled_coordinates)

                    st.markdown("### Extracted Page Tables")

                    table_count = 0

                    for _, row in all_scaled_coordinates.iterrows():
                        top = row['Top']
                        left = row['Left']
                        height = row['Final height']
                        width = row['Final width']
                        titles = row['Title']

                        data_extractor = DataExtractor(uploaded_file, st.session_state["page_number"] + 1, top, left,
                                                       width, height)
                        tables, title = data_extractor.extract_tables(titles)

                        if tables:
                            st.subheader(f"Table {titles}")
                            table_count += 1
                            for i in range(len(tables)):
                                st.dataframe(tables[i])
                        else:
                            st.write("No tables were extracted.")

                else:
                    st.write("No rectangle selections found on the canvas.")

        else:
            st.write("No rectangle selections found on the canvas.")

        canvas_element = st.empty()  # Create an empty element to display the canvas

        if "disabled" not in st.session_state:
            st.session_state["disabled"] = False

        next_button = st.button("Next", disabled=st.session_state["disabled"])
        save_button = st.button("Save", disabled=not st.session_state["disabled"])

        if next_button:
            canvas_element.empty()  # Clear the canvas element
            st.session_state["page_number"] += 1
            new_scaled_coordinates = store_scaled_coordinates(st.session_state["page_number"], all_scaled_coordinates,
                                                              ["scaleX", "scaleY", "Width", "Height"])
            if new_scaled_coordinates is not None:
                st.session_state["compiled_scales"] = pd.concat([st.session_state["compiled_scales"],
                                                                 new_scaled_coordinates], ignore_index=True)
            if st.session_state["page_number"] >= len(image_list) - 1:
                # st.session_state["page_number"] = 0
                st.session_state["disabled"] = True

            canvas_obj.reset_canvas(image_list[st.session_state["page_number"]], st.session_state["page_number"])

        if st.session_state["disabled"]:
            st.write("Before you save, define the mail subject for extraction (this implies how will be the subject"
                     " text when an email arrives to your inbox):")
            subject_value = st_keyup("", value="Report_sample", key="subject")
            st.write(f"Subject: {subject_value}")
            subject = st.session_state.user['email'] + "/" + subject_value
            subject_name = subject.replace(" ", "_")
            st.write(f"Final parameters: {subject}")

        if save_button:
            st.session_state["page_number"] += 1
            new_scaled_coordinates = store_scaled_coordinates(st.session_state["page_number"], all_scaled_coordinates,
                                                              ["scaleX", "scaleY", "Width", "Height"])
            if new_scaled_coordinates is not None:
                st.session_state["compiled_scales"] = pd.concat([st.session_state["compiled_scales"],
                                                                 new_scaled_coordinates], ignore_index=True)
            present_dataframe(st.session_state["compiled_scales"], "Final Scaled Coordinates")

            id_num = add_coordinates_to_firebase(st.session_state["compiled_scales"], "db_coord", subject_name)
            st.markdown("Data saved with id " + str(id_num))
            st.button("Finish", on_click= lambda : reset_all(uploaded_file))

        canvas_obj.display_canvas()
    else:
        st.session_state["page_number"] = 0
        st.session_state["disabled"] = False
        st.session_state["compiled_scales"] = pd.DataFrame()

# Función para mostrar la pantalla dependiendo del botón seleccionado
def mostrar_pantalla():
    # Inicializar el session state
    if 'boton_seleccionado' not in st.session_state:
        st.session_state.boton_seleccionado = None

    if 'input_text' not in st.session_state:
        st.session_state.input_text = False

    if not st.session_state.user['imap']:
        st.header("Additional process")
        st.subheader("As this app works with email (IMAP), it is important to get access to your email account.")
        input_text = st.text_input("Input you mail password", key='input_text_value')
        if st.button("Save"):
            firebasedb = FirebaseDB()
            firebasedb.set_user_data(st.session_state.user['uid'], 'ek', input_text)
            # Cambia el valor a True para mostrar los botones
            st.session_state.user['imap'] = True
        st.caption(":red[Gmail:] _For Gmail accounts, it is important to enable IMAP and input an app password, "
                   "for this you can look at the next link:_ https://support.google.com/mail/answer/185833?hl=es-419")
    else:
        # Mostrar el header dependiendo del botón seleccionado
        if st.session_state.boton_seleccionado is not None:
            if 'atributos_reporte' not in st.session_state:
                st.session_state.atributos_reporte = []

            st.header(f"Report type: {st.session_state.boton_seleccionado}")
            print(st.session_state.boton_seleccionado)
            get_report_main_topics(st.session_state.boton_seleccionado)
            print(st.session_state.atributos_reporte)
            st.write(st.session_state.atributos_reporte)
            selection()

        # Botones para seleccionar
        if st.session_state.boton_seleccionado is None:
            if st.button('Tear Down', key='button1',
                         on_click=lambda: st.session_state.update(boton_seleccionado="Tear Down")):
                pass
            if st.button('Production', key='button2',
                         on_click=lambda: st.session_state.update(boton_seleccionado="Production")):
                pass
            if st.button('Artificial Lift', key='button3',
                         on_click=lambda: st.session_state.update(boton_seleccionado="Artificial Lift")):
                pass
            if st.button('Efficiency', key='button4',
                         on_click=lambda: st.session_state.update(boton_seleccionado="Efficiency")):
                pass

        if st.session_state.boton_seleccionado is None:
            st.write("Please, select a report type")

# Mostrar la pantalla
mostrar_pantalla()

def reset_all(file):
    st.session_state.boton_seleccionado = None
    st.session_state["page_number"] = 0
    st.session_state["disabled"] = False
    st.session_state["compiled_scales"] = pd.DataFrame()
    file = None


import pygame
from SupportFuncs import load_image

class URadioButtons(pygame.sprite.Sprite):
    def __init__(self, screen, coords, group):
        super(URadioButtons, self).__init__(group)
        self.coords = coords
        self.buttons = []
        self.checked_button = 0
        self.font = pygame.font.Font('font/arial.ttf', 15)
        self.screen = screen
        self.draw()

    def draw(self):
        self.image = pygame.Surface((65 * len(self.buttons), 50), pygame.SRCALPHA)
        self.rect = self.image.get_rect()
        self.rect.x = self.coords[0]
        self.rect.y = self.coords[1]
        for i in range(len(self.buttons)):
            color = (0, 0, 0)
            image_name = 'ui_images/RadioButtonDefault.png'
            if i == self.checked_button:
                color = (255, 0, 0)
                image_name = 'ui_images/RadioButtonChecked.png'
            text_pg = self.font.render(self.buttons[i][0], True, color)
            btn_img = pygame.transform.scale(load_image(image_name, colorkey=-1),
                                             (50, 50))
            self.image.blit(btn_img, (50 * i + 5 * (i + 1), 0))
            self.image.blit(text_pg, (50 * i + 10 + 5 * (i + 1), 40 - text_pg.get_height()))
            self.screen.blit(self.image, (self.coords[0], self.coords[1]))

    def click_check(self, pos):
        if pygame.sprite.collide_rect(pos, self):
            cell_x = (pos.rect.x - 10) // 50 - self.coords[0] // 50
            cell_y = (pos.rect.y - 10) // 50
            if cell_x < 0 or cell_x >= len(self.buttons) or cell_y != 0:
                return
            self.checked_button = cell_x
            self.buttons[cell_x][1]()
            self.draw()

    def hover_check(self, pos):
        pass

    def add_button(self, text, func):
        self.buttons.append([text, func])

class ULineEdit(pygame.sprite.Sprite):
    def __init__(self, screen, coords, group):
        super(ULineEdit, self).__init__(group)
        self.font = pygame.font.Font('font/arial.ttf', 15)
        self.screen = screen
        self.coords = coords
        self.text = ''
        self.en_to_ru = {'A': 'ф', 'B': 'и', 'C': 'с',
                         'D': 'в', 'E': 'у', 'F': 'а',
                         'G': 'п', 'H': 'р', 'I': 'ш',
                         'J': 'о', 'K': 'л', 'L': 'д',
                         'M': 'ь', 'N': 'т', 'O': 'щ',
                         'P': 'з', 'Q': 'й', 'R': 'к',
                         'S': 'ы', 'T': 'е', 'U': 'г',
                         'V': 'м', 'W': 'ц', 'X': 'ч',
                         'Y': 'н', 'Z': 'я', ',': 'б',
                         '.': 'ю', ';': 'ж', '\'': 'э',
                         '[': 'х', ']': 'ъ', '/': ','}
        self.draw()

    def draw(self):
        self.image = pygame.Surface((200, 50), pygame.SRCALPHA)
        self.rect = self.image.get_rect()
        self.rect.x = self.coords[0]
        self.rect.y = self.coords[1]
        self.image.blit(pygame.transform.scale(load_image('ui_images/LineEdit.png', colorkey=-1), (200, 50)), (0, 0))
        text_pg = self.font.render(self.text, True, (0, 0, 0))
        self.image.blit(text_pg, (10, 40 - text_pg.get_height()))
        self.screen.blit(self.image, (self.coords[0], self.coords[1]))

    def click_check(self, pos):
        pass

    def hover_check(self, pos, event):
        if pygame.sprite.collide_rect(pos, self):
            if event.type == pygame.KEYDOWN:
                key = pygame.key.name(event.key)
                if key == 'backspace':
                    if len(self.text) >= 1:
                        self.text = self.text[:-1]
                elif key in ['б', 'ю', 'ж', 'э', 'х', 'ъ']:
                    self.text += key
                elif key.upper() in self.en_to_ru:
                    self.text += self.en_to_ru[key.upper()]
                elif key.isdigit():
                    self.text += key
                elif key == 'space':
                    self.text += ' '

    def get_text(self):
        return self.text

    def set_text(self, text):
        self.text = text

class UButton(pygame.sprite.Sprite):
    def __init__(self, screen, coords, group, text, func, image_name='ui_images/ButtonBlue.png'):
        super(UButton, self).__init__(group)
        self.font = pygame.font.Font('font/arial.ttf', 15)
        self.screen = screen
        self.coords = coords
        self.text = text
        self.func = func
        self.image_name = image_name
        self.draw()

    def draw(self):
        self.image = pygame.Surface((70, 50), pygame.SRCALPHA)
        self.rect = self.image.get_rect()
        self.rect.x = self.coords[0]
        self.rect.y = self.coords[1]
        self.image.blit(pygame.transform.scale(load_image(self.image_name, colorkey=-1), (70, 50)), (0, 0))
        text_pg = self.font.render(self.text, True, (0, 0, 0))
        self.image.blit(text_pg, (10, 40 - text_pg.get_height()))
        self.screen.blit(self.image, (self.coords[0], self.coords[1]))

    def hover_check(self, pos):
        pass

    def click_check(self, pos):
        if pygame.sprite.collide_rect(pos, self):
            self.func()

class ULabel(pygame.sprite.Sprite):
    def __init__(self, screen, coords, group, text, height=40, font_size=10):
        super(ULabel, self).__init__(group)
        self.font_size = font_size
        self.font = pygame.font.Font('font/arial.ttf', self.font_size)
        self.screen = screen
        self.coords = coords
        self.text = text
        self.height = height
        self.on_flag = True
        self.draw()

    def draw(self):
        if self.on_flag:
            self.image = pygame.Surface((len(self.text) * self.font_size * 0.55, self.height), pygame.SRCALPHA)
            self.rect = self.image.get_rect()
            self.rect.x = self.coords[0]
            self.rect.y = self.coords[1]
            self.image.blit(pygame.transform.scale(load_image('ui_images/Label.png', colorkey=-1),
                                                   (len(self.text) * self.font_size * 0.55, self.height)), (0, 0))
            text_pg = self.font.render(self.text, True, (0, 0, 0))
            self.image.blit(text_pg, (10, self.height - text_pg.get_height()))
            self.screen.blit(self.image, (self.coords[0], self.coords[1]))

    def set_text(self, text):
        self.text = text

    def off_on(self):
        self.on_flag = not self.on_flag


"""
Given a list of numbers, calculate another list in which
i_th element is the product of all numbers in the list except
the original i_th element.
"""

from functools import reduce
from typing import List

def solution_1(input_nums: List[int]) -> List[int]:
    """Calculate the result list via the first solution."""

    result: List[int] = []
    prod: int = reduce(lambda x, y: x * y, nums)
    for num in input_nums:
        try:
            replacement = int(prod / num)

        except ZeroDivisionError:
            replacement = prod
        result.append(replacement)

    return result

def solution_2(input_nums: List[int]) -> List[int]:
    """Calculate the result list via the second solution."""

    result: List[int] = [1] * len(input_nums)
    prod = 1
    for i, _ in enumerate(result):
        result[i] *= prod
        prod *= input_nums[i]

    prod = 1
    for i in range(len(result) - 1, -1, -1):
        result[i] *= prod
        prod *= input_nums[i]

    return result

nums: List[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9]

print(solution_1(nums))
print(solution_2(nums))

nums: List[int] = [2, 3, 4]

print(solution_1(nums))
print(solution_2(nums))


import os, gtts, PIL, praw, PIL.Image, PIL.ImageDraw, PIL.ImageFont, moviepy.editor, shutil

class program: #the main class
    class output: #the class for controlled stdout within the program
        outputEnabled = True #controls whether or not to print controlled output lines
        def print(string) -> None: #will only print if <program.output.outputEnabled == True>.
            if (program.output.outputEnabled):
                print (string)
    class tts: #the class for text to speech stuff
        def makeTTSFile(ttsText, language = 'en') -> str: #this outputs a .mp3 file and returns the path
            try:
                currentNumberCount = int(str(open('./tts-file-count-number.txt').read()))
            except:
                currentNumberCount = 1
            file = open('./tts-file-count-number.txt', 'w')
            file.write(str(currentNumberCount + 1))
            file.close()
            if ('tmp' not in os.listdir('.')):
                os.mkdir('tmp')
            filePath = './tmp/{}.mp3'.format(str(currentNumberCount))
            textToSpeech = gtts.gTTS(text = str(ttsText), lang = language)
            textToSpeech.save(filePath)
            return filePath
    class reddit: #the class that has the functions and data that has to do with reddit
        reddit = praw.Reddit('bot1', user_agent = 'bot1 user agent')
        def getRepliesFromTopPost() -> dict: #returns a list of the post's replies sorted by their score
            comments = {}
            sbmsn = None
            for submission in program.reddit.reddit.subreddit('askreddit').hot(limit = 1):
                sbmsn = submission
                for comment in submission.comments:
                    try:
                        isAscii = True
                        normalChars = [ #I dont know any better way to do this so I had to hardcode it
                            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
                            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '`', '~', '!',
                            '@', '#', '$', '%', '^', '&', '*', '(', ')', '[', ']', '{', '}',
                            '|', '\\', '"', "'", ';', ':', ',', '<', '>', '.', '/', '?', ' ',
                                    '-', '_', '+', '=', '\n'
                        ]
                        for each in str(comment.body): #nested for loop paradise...
                            if (each.lower() in normalChars):
                                pass
                            else:
                                isAscii = False
                        if (isAscii):
                            comments[int(comment.score)] = str(comment.body)
                    except:
                        pass
            return [comments, sbmsn]
    class presets: #the class for the configuration variables
        numberOfAskredditCommentsToShow = 10 #will show the top x amount of comments from the post.
    class utils:
        def asciitize(string):
            normalChars = [ #I dont know any better way to do this so I had to hardcode it
                'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
                'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '`', '~', '!',
                '@', '#', '$', '%', '^', '&', '*', '(', ')', '[', ']', '{', '}',
                '|', '\\', '"', "'", ';', ':', ',', '<', '>', '.', '/', '?', ' ',
                '-', '_', '+', '=', '\n'
            ]
            newString = ''
            for each in string:
                if (each.lower() in normalChars):
                    pass
                else:
                    each = '?'
                newString += each
            return newString
    class images: #the class that has the functions for generating images
        def generateImageWithTextOnIt(text, dimensions = [1920, 1080], bgcolor = 'white', fgcolor = 'black'): #generates an image that can later be stitched into the video
            image = PIL.Image.new('RGB', (dimensions[0], dimensions[1]), bgcolor)
            text = text.replace('\n', '')
            newText = []
            tmpText = ''
            last = 1
            lineWidthInChars = 50 #make the lines 50 characters long each
            for each in text: #split the string into 50 characer long segments
                last += 1
                tmpText = str(tmpText) + str(each)
                if (last >= lineWidthInChars):
                    last = 1
                    tmpText += '-'
                    newText.append(tmpText)
                    tmpText = ''
            if (tmpText != ''):
                newText.append(tmpText)
            if ('tmp' not in os.listdir('.')):
                os.mkdir('tmp')
            try:
                currentNumberCount = int(str(open('./image-file-count-number.txt').read()))
            except:
                currentNumberCount = 1
            file = open('./image-file-count-number.txt', 'w')
            file.write(str(currentNumberCount + 1))
            file.close()
            filePath = './tmp/{}.png'.format(str(currentNumberCount))

            textTopYCoordinate = 0 #int(image.size[1] / 4) #there will be no text above this y coordinate
            textHeight = int(image.size[1] - textTopYCoordinate)
            textHeight /= len(newText)
            if (textHeight > (image.size[0] / 30)):
                textHeight = int(image.size[0] / 30)
            font = PIL.ImageFont.truetype('./utils/default-font.ttf', int(textHeight))
            draw = PIL.ImageDraw.Draw(image)
            lastYCoord = textTopYCoordinate
            for textLine in newText:
                textSize = draw.textsize(textLine, font = font)
                textCoords = [0, 0]
                textCoords[0] = int((image.size[0] - textSize[0]) / 2)
                textCoords[1] = int(lastYCoord)
                lastYCoord += textSize[1]
            if (lastYCoord % 2 == 0):
                pass
            else:
                lastYCoord += 1
            image = image.resize((dimensions[0], lastYCoord), PIL.Image.ANTIALIAS)
            lastYCoord = textTopYCoordinate
            font = PIL.ImageFont.truetype('./utils/default-font.ttf', int(textHeight))
            draw = PIL.ImageDraw.Draw(image)
            for textLine in newText:
                textSize = draw.textsize(textLine, font = font)
                textCoords = [0, 0]
                textCoords[0] = int((image.size[0] - textSize[0]) / 2)
                textCoords[1] = int(lastYCoord)
                draw.text(textCoords, textLine, fgcolor, font = font)
                lastYCoord += textSize[1]
            newImage = PIL.Image.new('RGB', (dimensions[0], dimensions[1]), bgcolor)
            '''if (image.size[1] > newImage.size[1]):
                aspectRatio = image.size[0] / image.size[1]
                newImageSize = [0, 0]
                newImageSize[0] = int(newImage.size[0] * aspectRatio)
                newImageSize[1] = int(newImage.size[1] / aspectRatio)
                image = image.resize((*newImageSize), PIL.Image.ANTIALIAS)'''#fix the resizing method so that the image doesnt overflow on the y axis
            newImageCoords = [int((newImage.size[0] - image.size[0]) / 2), int((newImage.size[1] - image.size[1]) / 2)]
            newImage.paste(image, newImageCoords)
            newImage.save(filePath)
            return filePath

program.output.print('Program started.')
program.output.print('Getting the comments from the top askreddit post.')
askRedditCommentList = program.reddit.getRepliesFromTopPost()

commentUpvotesSorted = sorted(askRedditCommentList[0])
commentUpvotesSorted.reverse()

program.output.print(program.utils.asciitize('Found a post titled "{}" with {} upvotes that is in hot.'.format(askRedditCommentList[1].title, askRedditCommentList[1].score)))

if (program.presets.numberOfAskredditCommentsToShow > len(commentUpvotesSorted)):
    program.output.print('The number of comments you chose to display was larger than the amount of available comments - <program.presets.numberOfAskredditCommentsToShow> was changed to the max amount of comments and nothing more.')
    program.presets.numberOfAskredditCommentsToShow = len(commentUpvotesSorted)

topComments = {}
topCommentsUpvotesSorted = []

iterationStage = 0
while (iterationStage < program.presets.numberOfAskredditCommentsToShow):
    topComments[commentUpvotesSorted[iterationStage]] = askRedditCommentList[0][commentUpvotesSorted[iterationStage]]
    topCommentsUpvotesSorted.append(commentUpvotesSorted[iterationStage])
    iterationStage += 1

ttsFilePathsInOrderOfTopCommentsSortedByUpvotes = [program.tts.makeTTSFile(askRedditCommentList[1].title)] #10/10 file naming :)
iterationStage = 0
for comment in topComments:
    iterationStage += 1
    program.output.print('Making TTS file {}/{}.'.format(str(iterationStage), str(len(topComments))))
    commentText = topComments[comment]
    ttsPath = program.tts.makeTTSFile(commentText)
    ttsFilePathsInOrderOfTopCommentsSortedByUpvotes.append(ttsPath)

imageFilePathsInOrderOfTopCommentsSortedByUpvotes = [program.images.generateImageWithTextOnIt(askRedditCommentList[1].title, fgcolor = '#9494FF')] #sorry :)
iterationStage = 0
for comment in topComments:
    iterationStage += 1
    program.output.print('Making image file {}/{}.'.format(str(iterationStage), str(len(topComments))))
    imagePath = program.images.generateImageWithTextOnIt(topComments[comment], fgcolor = '#ff4301')
    imageFilePathsInOrderOfTopCommentsSortedByUpvotes.append(imagePath)

outputMp4List = []
for each in range(len(imageFilePathsInOrderOfTopCommentsSortedByUpvotes)):
    program.output.print('Stitching together audio and video files ({}/{}).'.format(str(each + 1), str(len(imageFilePathsInOrderOfTopCommentsSortedByUpvotes))))
    imagePath = imageFilePathsInOrderOfTopCommentsSortedByUpvotes[each]
    audioPath = ttsFilePathsInOrderOfTopCommentsSortedByUpvotes[each]
    os.system('ffmpeg.exe -loop 1 -i {} -i {} -c:v libx264 -tune stillimage -c:a aac -b:a 192k -pix_fmt yuv420p -shortest ./tmp/out{}.mp4'.format(imagePath, audioPath, str(each)))
    outputMp4List.append('./tmp/out{}.mp4'.format(str(each)))

program.output.print('Stitching together the videos.')

videoFileList = []
for each in outputMp4List:
    videoFileList.append(moviepy.editor.VideoFileClip(each))
finalVideo = moviepy.editor.concatenate_videoclips(videoFileList)
finalVideo.write_videofile('output.mp4')

program.output.print('Done!')

shutil.rmtree('tmp')


from pyspark import SparkConf, SparkContext
from pyspark.streaming import StreamingContext
from pyspark.streaming.kafka import KafkaUtils
from cassandra.cluster import Cluster
import signal

# if (contents.length > 0 && !contents[0].equalsIgnoreCase("year") && !contents[18].equalsIgnoreCase("1")) {
#     String origin = contents[15];
#     int delay = (int) (Float.parseFloat(contents[14]));
#     String destinationDelay = contents[8] + "_" + delay;
#     context.write(new Text(origin), new Text(destinationDelay));
# }

top = []
top_airports_table = "TopAirlinesByAirport"

def get_airport_carrier_delay(content):
    data = content[1].split(',')

    try:
        if len(data) > 0 and not data[0] == 'year' and not data[18] == '1\n':
            origin_carrier = data[15] + "_" + data[8]
            destination_delay = float(data[14])
            return [(origin_carrier, (destination_delay, 1))]
    except:
        return []

def init_cassandra():
    cluster = Cluster(['127.0.0.1'])
    return cluster.connect('tp')

def top_complex_average(rdd):
    global top

    chandle = init_cassandra()

    # iterate locally on driver (master) host
    curr = rdd.toLocalIterator()
    # concat top and curr values
    top_dict = dict(top)
    total = 0
    for el in curr:
        total += 1
        key = el[0].split('-')[0]
        subkey = el[0].split('-')[1]
        if key in top_dict:
            if subkey in top_dict[key]:
                top_dict[key][subkey] = (top_dict[key][subkey][0] + el[1][0], top_dict[key][subkey][1] + el[1][1])
            else:
                top_dict[key][subkey] = el[1]
        else:
            top_dict[key] = {subkey: el[1]}

    top = top_dict

    prepared_stmt = chandle.prepare(
        'INSERT INTO {} (airport_name,airline_name) values (?, ?, ?)'.format(top_airports_table))
    for origin in top:
        carriers = ' '.join(["%s=%0.2f" % (el[0], el[1][0] / el[1][1]) for el in
                             sorted(top[origin].items(), key=lambda el: el[1][0] / el[1][1])][10])
        chandle.execute(prepared_stmt, (origin, carriers))

    chandle.shutdown()

def stop_streaming():
    global ssc
    ssc.stop(stopSparkContext=True, stopGraceFully=True)

def stream_kafka():
    global ssc

    kstream = KafkaUtils.createDirectStream(ssc, topics=['2008'], kafkaParams={
        "metadata.broker.list": 'ip-172-31-12-78.us-west-1.compute.internal:6667'})

    contents = kstream.flatMap(get_airport_carrier_delay).reduceByKey(
        lambda a, b: (a[0] + b[0], a[1] + b[1])).foreachRDD(top_complex_average)

    ssc.start()
    ssc.awaitTerminationOrTimeout(15000)
    ssc.stop(stopSparkContext=True, stopGraceFully=True)

def main():
    global ssc

    conf = SparkConf()
    conf.setAppName("TopAirports")
    conf.set("spark.streaming.kafka.maxRatePerPartition", "0")
    conf.set('spark.streaming.stopGracefullyOnShutdown', True)

    sc = SparkContext(conf=conf)

    ssc = StreamingContext(sc, 1)  # Stream every 1 second
    ssc.checkpoint("/tmp/checkpoint")

    signal.signal(signal.SIGINT, stop_streaming)

    stream_kafka()

if __name__ == "__main__":
    main()


from .models import TodoModel
from django import forms

class TodoForm(forms.ModelForm):
    class Meta:
        model = TodoModel
        fields = '__all__'

        labels ={
            'subject':'',
            'details':'',
        }

        widgets = {
         'subject': forms.TextInput(attrs={'class': 'form-control bg-info rounded-5  p-3','rows': 2,'cols': 1,'placeholder': 'Enter Subjecct','id': 'id_content' }),
         'details': forms.Textarea(attrs={'class': 'form-control bg-info border-0 p-3','rows': 3,'cols': 1,'placeholder': 'Write Details','id': 'id_content' })
        }


from django.db import connection

def ingredient_name_and_amount_query(receipt_id):
    with connection.cursor() as cursor:
        cursor.execute(f"SELECT ingredient_calories.ingredient_name, receipt_ingredient.amount, receipt_ingredient.amount_type \
                        FROM  receipt_ingredient \
                        Inner Join ingredient_calories on \
                        receipt_ingredient.ingredient_name_frk_id = ingredient_calories.id \
                        where receipt_ingredient.recipe_frk_id = %s", (receipt_id,))
        rows = cursor.fetchall()
        ingredients_of_receipt = []
        for row in rows:
            ingredient = {"ingredient_name": row[0], "amount": row[1], "amount_type": row[2]}
            ingredients_of_receipt.append(ingredient)
        return ingredients_of_receipt

# total_receipt_cal_per_100gr:
def total_receipt_cal_per_100gr(receipt_id):
    with connection.cursor() as cursor:
        cursor.execute(f"select (COALESCE(SUM (ingredient_calories.ingredient_calories_per_100_gr_or_ml), 0) * 100)/COALESCE(SUM(receipt_ingredient.amount),0) \
                       from receipt_ingredient, ingredient_calories \
                       WHERE ingredient_calories.id = receipt_ingredient.ingredient_name_frk_id and \
                       receipt_ingredient.recipe_frk_id = %s \
                       group by receipt_ingredient.recipe_frk_id", (receipt_id,))
        total_cal = cursor.fetchone()[0]
        return total_cal

# search_recipe_by_category:
def search_recipe_by_category(category):
    with connection.cursor() as cursor:
        cursor.execute(f"SELECT recipe.id, recipe.recipe_name, recipe.pic_url \
                       FROM  recipe \
                       where recipe.recipe_category = %s", (category,))
        rows = cursor.fetchall()
        all_recipes_by_category = []
        for row in rows:
            recipe_details = {"recipe_id": row[0], "recipe_name": row[1], "recipe_url": row[2]}
            # all_recipes_by_category[row[0]] = recipe_details
            all_recipes_by_category.append(recipe_details)
        print(all_recipes_by_category)
        return all_recipes_by_category


import findspark
findspark.init()
from operator import add
from pyspark import SparkContext
from pyspark.sql import SparkSession
from pyspark.sql.types import IntegerType
from pyspark.sql import *

if __name__ == "__main__":
    spark = SparkSession \
        .builder \
        .appName("q4") \
        .getOrCreate()
    sc = SparkContext.getOrCreate()

    business = spark.read.format("csv").option("delimiter", ":").load("C:/Users/psait/Desktop/bda/business.csv").toDF("business_id", "tempCol1", "full_address", "tempCol3", "categories").drop("tempCol1", "tempCol3")
    review = spark.read.format("csv").option("delimiter", ":").load("C:/Users/psait/Desktop/bda/review.csv").toDF("review_id", "tempCol1", "user_id", "tempCol3", "business_id", "tempCol5", "stars").drop("tempCol1", "tempCol3", "tempCol5", "review_id")

    review = review.withColumn("stars", review["stars"].cast(IntegerType()))
    jf = business.join(review, "business_id").select("business_id", "full_address", "categories", "stars").groupBy("business_id",
                                                                                "full_address",
                                                                                "categories").avg(
        "stars");
    opframe = jf.toDF("business_id", "full_address", "categories", "avg_rating").sort("avg_rating",ascending = False).take(10)
    op = sc.parallelize(list(opframe)).toDF()
    final = op.rdd.map(lambda x :str(x[0]) + "\t" + str(x[1]) + "\t" + str(x[2]) + "\t" + str(x[3]))
    final.repartition(1).saveAsTextFile("C:/Users/psait/Desktop/bda/q4.txt")