from abc import ABC, abstractmethod
from typing import List, Tuple, Dict
from collections import Counter

import numpy as np
import pandas as pd
from presidio_evaluator import InputSample, EvaluationResult, ModelError
from tqdm import tqdm

class ModelEvaluator(ABC):

    def __init__(self, entities_to_keep: List[str] = None,
                 verbose: bool = False,
                 use_spans: bool = False, labeling_scheme="BIO",
                 compare_by_io=True):

        """
        Abstract class for evaluating NER models and others
        :param entities_to_keep: Which entities should be evaluated? All other
        entities are ignored. If None, none are filtered
        :param verbose: Whether to print more debug info
        :param labeling_scheme: Type of scheme used for labeling (BILOU,
        BIO/LOB or IO)
        :param compare_by_io: True if comparison should be done on the entity
        level and not the sub-entity level

        """
        self.entities = entities_to_keep
        self.verbose = verbose
        self.use_spans = use_spans
        self.compare_by_io = compare_by_io
        self.labeling_scheme = labeling_scheme

    @abstractmethod
    def predict(self, sample: InputSample) -> List[str]:
        """
        Abstract. Returns the predicted tokens/spans from the evaluated model
        :param sample: Sample to be evaluated
        :return: if self.use spans: list of spans
                 if not self.use_spans: tags in self.labeling_scheme format
        """
        pass

    def compare(self, input_sample: InputSample, prediction: List[str]):

        """
        Compares gound truth tags (annotation) and predicted (prediction)
        :param input_sample: input sample containing list of tags with scheme
        :param prediction: predicted value for each token
        self.labeling_scheme

        """
        annotation = input_sample.tags
        tokens = input_sample.tokens

        if len(annotation) != len(prediction):
            print("Annotation and prediction do not have the"
                  "same length. Sample={}".format(input_sample))
            return Counter(), []

        results = Counter()
        mistakes = []

        new_annotation = annotation.copy()

        if self.compare_by_io:
            new_annotation = self._to_io(new_annotation)
            prediction = self._to_io(prediction)

        # Ignore annotations that aren't in the list of
        # requested entities.
        if self.entities:
            prediction = self._adjust_per_entities(prediction)
            new_annotation = self._adjust_per_entities(new_annotation)
        for i in range(0, len(new_annotation)):
            results[(new_annotation[i], prediction[i])] += 1

            if self.verbose:
                print('Annotation:', new_annotation[i])
                print('Prediction:', prediction[i])
                print(results)

            # check if there was an error
            is_error = (new_annotation[i] != prediction[i])
            if is_error:
                if prediction[i] == 'O':
                    mistakes.append(ModelError("FN",
                                               new_annotation[i],
                                               prediction[i],
                                               tokens[i],
                                               input_sample.full_text,
                                               input_sample.metadata))
                elif new_annotation[i] == 'O':
                    mistakes.append(ModelError("FP",
                                               new_annotation[i],
                                               prediction[i],
                                               tokens[i],
                                               input_sample.full_text,
                                               input_sample.metadata))
                else:
                    mistakes.append(ModelError("Wrong entity",
                                               new_annotation[i],
                                               prediction[i],
                                               tokens[i],
                                               input_sample.full_text,
                                               input_sample.metadata))

        return results, mistakes

    def _adjust_per_entities(self, tags):
        if self.entities:
            return [tag if tag in self.entities else 'O' for tag in tags]

    @staticmethod
    def _to_io(tags):
        """
        Translates BILOU/BIO/IOB to IO - only In or Out of entity.
        ['B-PERSON','I-PERSON','L-PERSON'] is translated into
        ['PERSON','PERSON','PERSON']
        :param tags: the input tags in BILOU/IOB/BIO format
        :return: a new list of IO tags
        """
        return [tag[2:] if '-' in tag else tag for tag in tags]

    def evaluate_sample(self, sample: InputSample) -> EvaluationResult:
        if self.verbose:
            print("Input sentence: {}".format(sample.full_text))

        prediction = self.predict(sample)
        results, mistakes = self.compare(
            input_sample=sample,
            prediction=prediction)
        return EvaluationResult(results, mistakes, sample.full_text)

    def evaluate_all(self, dataset: List[InputSample]) -> List[EvaluationResult]:
        evaluation_results = []
        for sample in tqdm(dataset, desc='Evaluating {}'.format(self.__class__)):
            evaluation_result = self.evaluate_sample(sample)
            evaluation_results.append(evaluation_result)

        return evaluation_results

    def calculate_score(self, evaluation_results: List[
        EvaluationResult], beta: float = 1) \
            -> EvaluationResult:
        """
        Returns the pii_precision, pii_recall and f_measure either for each entity
        or for all entities (ignore_entity_type = True)
        :param evaluation_results: List of EvaluationResult
        :param beta: F measure beta value
        between different entity types, or to treat these as misclassifications
        :return: EvaluationResult with precision, recall and f measures
        """

        # aggregate results
        all_results = sum([er.results for er in evaluation_results], Counter())

        # compute pii_recall per entity
        entity_recall = {}
        entity_precision = {}
        if self.entities:
            entities = self.entities
        else:
            entities = list(
                set([x[0] for x in all_results.keys() if x[0] != 'O']))

        for entity in entities:
            # all annotation of given type
            annotated = sum(
                [all_results[x] for x in all_results if x[0] == entity])
            predicted = sum(
                [all_results[x] for x in all_results if x[1] == entity])
            tp = all_results[(entity, entity)]

            if annotated > 0:
                entity_recall[entity] = tp / annotated
            else:
                entity_recall[entity] = np.NaN

            if predicted > 0:
                per_entity_tp = all_results[(entity, entity)]
                entity_precision[entity] = per_entity_tp / predicted
            else:
                entity_precision[entity] = np.NaN

        # compute pii_precision and pii_recall
        annotated_all = sum(
            [all_results[x] for x in all_results if x[0] != 'O'])
        predicted_all = sum(
            [all_results[x] for x in all_results if x[1] != 'O'])
        if annotated_all > 0:
            pii_recall = sum([all_results[x] for x in all_results if
                              (x[0] != 'O' and x[1] != 'O')]) / annotated_all
        else:
            pii_recall = np.NaN
        if predicted_all > 0:
            pii_precision = sum([all_results[x] for x in all_results if
                                 (x[0] != 'O' and x[1] != 'O')]) / predicted_all
        else:
            pii_precision = np.NaN
        # compute pii_f_beta-score
        pii_f_beta = self.f_beta(pii_precision, pii_recall, beta)

        # aggregate errors
        errors = []
        for res in evaluation_results:
            if res.model_errors:
                errors.extend(res.model_errors)

        evaluation_result = EvaluationResult(results=all_results, model_errors=errors)
        evaluation_result.pii_precision = pii_precision
        evaluation_result.pii_recall = pii_recall
        evaluation_result.entity_recall_dict = entity_recall
        evaluation_result.entity_precision_dict = entity_precision
        evaluation_result.pii_f = pii_f_beta

        return evaluation_result

    @staticmethod
    def precision(tp: int, fp: int) -> float:
        return tp / (tp + fp + 1e-100)

    @staticmethod
    def recall(tp: int, fn: int) -> float:
        return tp / (tp + fn + 1e-100)

    @staticmethod
    def f_beta(precision: float, recall: float, beta: float) -> float:
        """
        Returns the F score for precision, recall and a beta parameter
        :param precision: a float with the precision value
        :param recall: a float with the recall value
        :param beta: a float with the beta parameter of the F measure,
        which gives more or less weight to precision
        vs. recall
        :return: a float value of the f(beta) measure.
        """
        if np.isnan(precision) or np.isnan(recall) or (
                precision == 0 and recall == 0):
            return np.nan

        return ((1 + beta ** 2) * precision * recall) / (
                ((beta ** 2) * precision) + recall)

    @staticmethod
    def align_input_samples_to_presidio_analyzer(input_samples: List[InputSample],
                                                 entities_mapping: Dict[str, str],
                                                 presidio_fields: List[str]=None) \
            -> List[InputSample]:
        """
        Change input samples to conform with Presidio's entities
        :return: new list of InputSample
        """

        new_input_samples = input_samples.copy()

        # Match entity names to Presidio's
        if not presidio_fields:
            presidio_fields = ['CREDIT_CARD', 'CRYPTO', 'DATE_TIME', 'DOMAIN_NAME', 'EMAIL_ADDRESS', 'IBAN_CODE',
                           'IP_ADDRESS', 'NRP', 'LOCATION', 'PERSON', 'PHONE_NUMBER', 'US_SSN']

        # A list that will contain updated input samples,
        new_list = []

        # Iterate on all samples
        for input_sample in new_input_samples:
            contains_presidio_field = False
            new_spans = []
            # Update spans to match Presidio's entity name
            for span in input_sample.spans:
                in_presidio_field = False
                if span.entity_type in entities_mapping.keys():
                    new_name = entities_mapping.get(span.entity_type)
                    span.entity_type = new_name
                    contains_presidio_field = True

                    # Add to new span list, if the span contains an entity relevant to Presidio
                    new_spans.append(span)
            input_sample.spans = new_spans

            # Update tags in case this sample has relevant entities for evaluation
            if contains_presidio_field:
                for i, tag in enumerate(input_sample.tags):
                    has_prefix = '-' in tag
                    if has_prefix:
                        prefix = tag[:2]
                        clean = tag[2:]
                    else:
                        prefix = ""
                        clean = tag

                    if clean in entities_mapping.keys():
                        new_name = entities_mapping.get(clean)
                        input_sample.tags[i] = "{}{}".format(prefix, new_name)
                    else:
                        input_sample.tags[i] = 'O'

            new_list.append(input_sample)
        return new_list

    @staticmethod
    def get_false_positives(errors=List[ModelError], entity=None):
        """
        Get a list of all false positive errors in the results
        """
        if isinstance(entity, str):
            entity = [entity]

        if entity:
            return [model_error for model_error in errors if
                    model_error.error_type == 'FP' and model_error.prediction in entity]
        else:
            return [model_error for model_error in errors if model_error.error_type == 'FP']

    @staticmethod
    def get_false_negatives(errors=List[ModelError], entity=None):
        """
        Get a list of all false positive negative errors in the results (False negatives and wrong entity detection)
        """
        if isinstance(entity, str):
            entity = [entity]
        if entity:
            return [model_error for model_error in errors if
                    model_error.error_type != 'FP' and model_error.annotation in entity]
        else:
            return [model_error for model_error in errors if model_error.error_type != 'FP']

    @staticmethod
    def most_common_fp_tokens(errors=List[ModelError], n: int = 10, entity=None):
        """
        Print the n most common false positive tokens (tokens thought to be an entity)
        """
        fps = ModelEvaluator.get_false_positives(errors, entity)

        tokens = [err.token.text for err in fps]
        from collections import Counter
        by_frequency = Counter(tokens)
        most_common = by_frequency.most_common(n)
        print("Most common false positive tokens:")
        print(most_common)
        print("Example sentence with each FP token:")
        for tok, val in most_common:
            with_tok = [err for err in fps if err.token.text == tok]
            print(with_tok[0].full_text)

    @staticmethod
    def most_common_fn_tokens(errors=List[ModelError], n: int = 10, entity=None):
        """
        Print all tokens that were missed by the model, including an example of the full text in which they appear
        """
        fns = ModelEvaluator.get_false_negatives(errors, entity)

        fns_tokens = [err.token.text for err in fns]
        from collections import Counter
        by_frequency_fns = Counter(fns_tokens)
        most_common_fns = by_frequency_fns.most_common(50)
        print(most_common_fns)
        for tok, val in most_common_fns:
            with_tok = [err for err in fns if err.token.text == tok]
            print("Token: {}, Annotation: {}, Full text: {}".format(with_tok[0].token, with_tok[0].annotation,
                                                                    with_tok[0].full_text))

    @staticmethod
    def get_errors_df(errors=List[ModelError], entity: List[str] = None, error_type: str = 'FN'):
        """
        Get ModelErrors as pd.DataFrame
        """
        if error_type == 'FN':
            filtered_errors = ModelEvaluator.get_false_negatives(errors, entity)
        elif error_type == 'FP':
            filtered_errors = ModelEvaluator.get_false_positives(errors, entity)
        else:
            raise ValueError("error_type should be either FP or FN")

        if len(filtered_errors) == 0:
            print("No errors of type {} and entity {} were found".format(error_type,entity))
            return None

        errors_df = pd.DataFrame.from_records([error.__dict__ for error in filtered_errors])
        metadata_df = pd.DataFrame(errors_df['metadata'].tolist())
        errors_df.drop(['metadata'], axis=1, inplace=True)
        new_errors_df = pd.concat([errors_df, metadata_df], axis=1)
        return new_errors_df

    @staticmethod
    def get_fps_dataframe(errors=List[ModelError], entity: List[str] = None):
        """
        Get false positive ModelErrors as pd.DataFrame
        """
        return ModelEvaluator.get_errors_df(errors, entity, error_type='FP')

    @staticmethod
    def get_fns_dataframe(errors=List[ModelError], entity: List[str] = None):
        """
        Get false negative ModelErrors as pd.DataFrame
        """
        return ModelEvaluator.get_errors_df(errors, entity, error_type='FN')


import requests
import unittest

class TestCourse(unittest.TestCase):
    def testPost(self):
        api = 'http://127.0.0.1:8000/course_arrange/api/scheduling/course/'
        response = requests.post(url=api,
                      data={'teacher_id': "000123", 'course_id': "00010001", 'time_id': "[3, 4, 5]", 'room_id': "1", 'duplicate': 2})
        print(response.text)

    def testGet(self):
        api = 'http://127.0.0.1:8000/course_arrange/api/scheduling/course/'
        response = requests.get(url=api, params={'page': 1})
        print(response.text)


import numpy as np
import pandas as pd
import pathlib
import os

import matplotlib.pyplot as plt

from bokeh.layouts import row
from bokeh.plotting import figure, output_file, show, gridplot
from bokeh.models import ColumnDataSource
from bokeh.palettes import Spectral6
###############################################################################
current_week = "30"
output_week = "/Users/christianhilscher/desktop/dynsim/output/week" + str(current_week) + "/"
pathlib.Path(output_week).mkdir(parents=True, exist_ok=True)
###############################################################################
input_path = "/Users/christianhilscher/Desktop/dynsim/input/"
output_path = "/Users/christianhilscher/Desktop/dynsim/output/"
plot_path = "/Users/christianhilscher/Desktop/dynsim/src/plotting/"
os.chdir(plot_path)

df = pd.read_pickle(output_week + "df_analysis")

def remove_outliers(arr, low, high):
    arr = arr.copy()
    lower = np.quantile(arr, low)
    upper = np.quantile(arr, high)

    out = arr[np.logical_and(arr>lower, arr<upper)]
    return out

def _hist_df(arr):
    arr = arr.copy()
    arr = remove_outliers(arr, 0.05, 0.95)
    bins = 40
    hist, edges = np.histogram(arr, bins)
    hist_df = pd.DataFrame({"obs": hist,
                            "left": edges[:-1],
                            "right": edges[1:]})
    hist_df["bottom"] = 0
    return hist_df

def make_plot_earnings(dataf, ahead):
    dataf = dataf.copy()
    dataf = dataf[dataf["hours"]>0]
    df_ana = dataf[dataf["period_ahead"]==ahead]

    earnings_diff_ml = df_ana["hours_x"] - df_ana["hours_y"]
    earnings_diff_standard = df_ana["hours_x"] - df_ana["hours"]

    abc = _hist_df(earnings_diff_ml)
    ghi = _hist_df(earnings_diff_standard)

    abc = abc.add_suffix("_ml")
    ghi = ghi.add_suffix("_standard")

    hist_df = pd.concat([abc, ghi], axis=1)

    src = ColumnDataSource(hist_df)

    sze = len(df_ana)
    title = "hours: Errors with " + str(ahead) + " years ahead prediction. Sample Size: " + str(sze)
    p = figure(plot_height = 600, plot_width = 600,
                  y_axis_label = "Count", title=title)

    p.quad(bottom = "bottom_ml", top = "obs_ml",left = "left_ml", right = "right_ml", source = src, fill_alpha=0.5, fill_color=Spectral6[0], legend_label="ml")

    p.quad(bottom = "bottom_standard", top = "obs_standard",left = "left_standard", right = "right_standard", source = src, fill_alpha=0.5, fill_color=Spectral6[3], legend_label="standard")

    return p

def make_plts(dataf):
    dataf = dataf.copy()
    ahead_ls = np.arange(1, 25, 5)
    plts = []
    for a in ahead_ls:
        plts.append(make_plot_earnings(dataf, a))

    p = row(plts)
    return p

pic = make_plts(df)
output_file(output_week + "hours_difference" + ".html")
show(pic)


'''
Support classes for providing shared memory numpy arrays

+++
We are moving away from using python multiprocessing/shared memory,
so most of this will be deprecated and/or refactored into non-shm classes (eg to
    provide support for local buffer groups/queues)

'''

import multiprocessing as mp
import numpy as np
import ctypes
from awrams.utils.metatypes import ObjectDict as o

_ctypes_to_numpy = {
    ctypes.c_float  : np.dtype('float32'),
    ctypes.c_double : np.dtype('float64'),
                'd' : np.dtype('float64')}

_numpy_to_ctypes = dict(list(zip(list(_ctypes_to_numpy.values()),
                            list(_ctypes_to_numpy.keys()))))

def shm_as_ndarray(mp_array, shape=None,order='C'):
    '''Given a multiprocessing.Array, returns an ndarray pointing to
    the same data.'''

    #+++
    #Need to take into account Fortran/C ordering

    # support SynchronizedArray:
    if not hasattr(mp_array, '_type_'):
        mp_array = mp_array.get_obj()
    dtype = _ctypes_to_numpy[mp_array._type_]
    result = np.frombuffer(mp_array, dtype)

    if shape is not None:
        result = result.reshape(shape,order=order)

    return np.asarray(result)

def init_shm_ndarray(shape,dtype=np.float64):
    '''Initialize a shared memory array of the supplied shape and dtype'''
    shm = mp.Array(_numpy_to_ctypes[np.dtype(dtype)],int(np.prod(shape)),lock=False)
    return shm

def init_shm_value(dtype=np.float64):
    '''Initialize a shared memory value of the supplied dtype'''
    shm = mp.Value(_numpy_to_ctypes[np.dtype(dtype)],lock=False)
    return shm

class BufferManager:
    '''
    Manages a queue of shared memory numpy arrays
    '''
    def __init__(self,buffers,queue,shape=None,build=True):
        self.queue = queue
        self.buffers_shm = buffers
        self.buf_shape = shape
        if build:
            self.rebuild_buffers()

    def rebuild_buffers(self):
        self.buffers_np = []
        for b in self.buffers_shm:
            self.buffers_np.append(shm_as_ndarray(b,self.buf_shape))

    def get_buffer(self,timeout=None):
        '''
        Retrieve a buffer from the queue
        '''
        buffer_id = self.queue.get(timeout=timeout)
        return buffer_id, self.map_buffer(buffer_id)

    def reclaim(self,buffer_id):
        '''
        Return a used buffer to the queue
        '''
        self.queue.put(buffer_id)

    def map_buffer(self,buffer_id,indices=None):
        '''
        Return mapped nd_arrays of the appropriate shape
        '''
        if not indices:
            return self.buffers_np[buffer_id]
        else:
            return self.buffers_np[buffer_id][indices]

def create_managed_buffers(count,shape,dtype=np.float64,build=True):
    buffers,q = create_buffers(count,shape,dtype)
    return BufferManager(buffers,q,shape,build)

def create_buffers(buf_count,buf_shape,dtype=np.float64):
    '''
    Initialise buf_count buffers of supplied shape and datatype
    '''
    buffers = []
    q = mp.Queue()
    for i in range(buf_count):
        buffers.append(init_shm_ndarray(buf_shape,dtype))
        q.put(i)

    return buffers,q

def create_dict_buffers(recordables,buf_count,buf_len,dtype=np.float64):
    '''
    Initialise buf_count dictionaries of supplied length and datatype
    '''
    buffers = []
    q = mp.Queue()
    for i in range(buf_count):
        cur_buf_shm = {}
        for k in recordables:
            cur_buf_shm[k] = init_shm_ndarray(buf_len,dtype)
        buffers.append(cur_buf_shm)
        q.put(i)

    return buffers,q

def create_shm_dict(keys,shape,dtype=np.float64):
    '''
    Create dict of shm buffers
    '''
    buffers = o()
    shapes = o()
    for k in keys:
        buffers[k] = init_shm_ndarray(shape,dtype)
        shapes[k] = shape
    return o(buffers=buffers,shape=shapes)

def create_shm_dict_inputs(shape_map,dtype=np.float64):
    '''
    Create dict of shm buffers
    '''
    buffers = o()
    shapes = o()
    for k in shape_map:
        if shape_map[k] is None or len(shape_map[k])==0:
            buffers[k] = init_shm_ndarray((1,),dtype)
        else:
            buffers[k] = init_shm_ndarray(shape_map[k],dtype)
        shapes[k] = shape_map[k]
    return o(buffers=buffers,shapes=shapes)

def shm_to_nd_dict_inputs(buffers,shapes,order='C'):
    '''
    Convert a dictionary of shared memory arrays into process-local numpy arrays
    Shape can be a single common shape, or a dictionary of shapes
    '''
    nd_dict = o()

    for k,v in list(buffers.items()):
        if shapes[k] is None or len(shapes[k])==0:
            nd_dict[k] = shm_as_ndarray(v,(1,),order) #np.asscalar(a)
        else:
            nd_dict[k] = shm_as_ndarray(v,shapes[k],order)
    return nd_dict

def shm_to_nd_dict(buffers,shape=None,order='C'):
    '''
    Convert a dictionary of shared memory arrays into process-local numpy arrays
    Shape can be a single common shape, or a dictionary of shapes
    '''
    nd_dict = o()
    to_shape = shape
    for k,v in list(buffers.items()):
        if hasattr(shape,'items'):
            to_shape = shape[k]
        nd_dict[k] = shm_as_ndarray(v,to_shape,order)
    return nd_dict

class BufferedDictHandler:
    '''
    Mixin providing base functionality for Manager classes
    '''
    def __init__(self,buffers,build=True):
        self.buffers_shm = buffers
        self.buffers_nd = {}
        if build:
            self.rebuild_buffers()

    def rebuild_buffers(self):
        '''
        Reconvert shared-mem buffers to numpy
        (e.g after passing process boundaries)
        '''
        for k, v in self.buffers_shm.items():
            self.buffers_nd[k] = (shm_to_nd_dict(**v))

    def map_buffer(self,buffer_id,index=None):
        '''
        Return mapped nd_arrays of the appropriate length
        '''
        out_bufs = self.buffers_nd[buffer_id]

        if index is None:
            return out_bufs
        else:
            sub_out = {}
            for k,v in out_bufs.items():
                sub_out[k] = v[index]
            return sub_out

class BufferedDictManager(BufferedDictHandler):
    '''
    Manages shared memory dictionaries of numpy arrays
    '''
    def __init__(self,buffers,queue,build=True):
        super().__init__(buffers,build)
        self.queue = queue

    def get_buffer(self,index=None,timeout=None):
        '''
        Retrieve a buffer from the queue
        '''
        buffer_id = self.queue.get(timeout=timeout)
        buffers = self.map_buffer(buffer_id,index=index)
        return buffer_id, buffers

    def reclaim(self,buffer_id):
        '''
        Return a used buffer to the queue
        '''
        self.queue.put(buffer_id)

class MultiClientBufferedDictManager(BufferedDictHandler):
    '''
    Manages buffered dicts that will be used by multiple clients simultaneously
    Use BufferedDictManager for client access
    '''
    def __init__(self,buffers,client_queues,build=True):
        super().__init__(buffers,build)
        self.client_queues = client_queues

    def reclaim(self,buffer_id):
        '''
        Return a used buffer to the queue
        '''
        for q in self.client_queues:
            q.put(buffer_id)

    def get_client_managers(self):
        return [BufferedDictManager(self.buffers_shm,q,False) for q in self.client_queues]

    def get_handler(self):
        return BufferedDictHandler(self.buffers_shm,False)

def create_multiclient_manager(keys,shape,nbuffers,nclients,build=True,extra_bufs=None):
    client_queues = [mp.Queue() for q in range(nclients)]

    bufs = {}
    for i in range(nbuffers):
        buf = create_shm_dict(keys,shape)
        [q.put(i) for q in client_queues]
        bufs[i] = buf
    if extra_bufs is not None:
        bufs.update(extra_bufs)

    return MultiClientBufferedDictManager(bufs,client_queues,build)


from base_dao import BaseDao
from article  import Article

class ArticleDao(BaseDao):
    def get_articles(self):
        sql = 'select * from article'

        cursor = self.open()
        cursor.execute(sql)
        datas = cursor.fetchall()
        self.close(cursor)

        result = []
        for data in datas:
            result.append(Article(data))
        return result

    def add_article(self, article):
        sql = """
            insert into
                article(article_id, create_time, view_count, preview, content)
                value(%s, now(), %s, %s, %s)
        """

        cursor = self.open()
        cursor.execute(sql, (
            article.article_id,
            article.view_count,
            article.preview,
            article.content))
        self.commit()
        result = cursor.rowcount
        self.close(cursor)

        return result

    def delete_article(self, article):
        sql = """
            delete from article where article_id = %s
        """

        cursor = self.open()
        cursor.execute(sql, (
            article.article_id,
        ))
        self.commit()
        result = cursor.rowcount
        self.close(cursor)

        return result


import dict_tools
import pop.hub
import mock
import pytest
import random
import string
from typing import Any, Dict, List

@pytest.fixture(scope="session", autouse=True)
def acct_subs() -> List[str]:
    return ["lxd"]

@pytest.fixture(scope="session", autouse=True)
def acct_profile() -> str:
    return "test_development_idem_lxd"

@pytest.fixture(scope="session")
def hub(hub):
    hub.pop.sub.add("idem.idem")
    with mock.patch("sys.argv", ["idem", "state"]):
        hub.pop.config.load(["idem", "acct"], "idem", parse_cli=True)
    yield hub

@pytest.mark.asyncio
@pytest.fixture(scope="module")
async def ctx(hub, acct_subs: List[str], acct_profile: str) -> Dict[str, Any]:
    ctx = dict_tools.data.NamespaceDict(run_name="test", test=False)
    # Add the profile to the account
    hub.acct.init.unlock(hub.OPT.acct.acct_file, hub.OPT.acct.acct_key)
    ctx.acct = await hub.acct.init.gather(acct_subs, acct_profile)
    yield ctx

@pytest.fixture(scope="session", autouse=True)
def event_loop():
    hub = pop.hub.Hub()
    hub.pop.loop.create()
    yield hub.pop.Loop
    hub.pop.Loop.close()


import sys
sys.stdin = open("input.txt")

n = int(input())
tree = [[] for _ in range(n)]

for _ in range(n-1):
    p, c = list(map(int, input().split(" ")))
    tree[p-1].append(c-1)
    tree[c-1].append(p-1)

# 1번 인덱스를 루트노드라고 할 때 부모 노드를 찾는 법. -> BFS를 돌면서 여기서 갈 수 있는 애는 해당 노드를 넣어준다.
queue = [0]
queue_index = 0
visited = [0 for _ in range(n)]
answer = ""
while queue_index < len(queue):
    node = queue[queue_index]
    now_nodes = tree[node]
    for next_node in now_nodes:
        if visited[next_node] == 0:
            visited[next_node] = node + 1
            queue.append(next_node)
    queue_index += 1

for i in range(1, n):
    print(visited[i])


import json
import os
import re
from itertools import filterfalse

def xstr(s):
    if s is None:
        return ""
    if isinstance(s, bool):
        return "1" if s else "0"
    return str(s)

def preprocess(inputFile: str, outputFile: str):
    with open(inputFile) as fIn, open(outputFile, "w") as fOut:
        tab = "\t"
        headers = (
            "area",
            "district",
            "address",
            "floors",
            "bedrooms",
            "toilets",
            "front",
            "entrance",
            "house_aspect",
            "balcony_aspect",
            "interior",
            "near_center",
            "owner",
            "alley",
            "villa",
            "new",
            "price",
        )
        fOut.write(f"{tab.join(headers)}\n")
        for i, line in enumerate(fIn):
            row = json.loads(line)
            if row["area"] == "Không xác định":
                area = None
            else:
                area = float(row["area"][:-3])

            if row["price"].endswith(" tỷ"):
                price = float(row["price"][:-3]) * 1000
            elif row["price"].endswith(" triệu"):
                price = float(row["price"][:-6])
            elif row["price"].endswith(" triệu/m²"):
                if area is not None:
                    price = float(row["price"][:-9]) * area
                else:
                    price = None
            else:
                price = None

            if price is None:
                continue

            district = row["district"]
            detail = row["detail"]
            features = detail["features"]

            address = features.get("Địa chỉ")
            if address is not None:
                address = address.replace(district, "").strip(" \t\r\n,.")
                if len(address) == 0:
                    address = None
            district = district.replace("Hồ Chí Minh", "").strip(" \t\r\n,.")

            floors = features.get("Số tầng")
            if floors is not None:
                floors = int(floors.replace(" (tầng)", ""))

            bedrooms = features.get("Số phòng ngủ")
            if bedrooms is not None:
                bedrooms = int(bedrooms.replace(" (phòng)", ""))

            toilets = features.get("Số toilet")
            if toilets is not None:
                toilets = int(toilets)

            front = features.get("Mặt tiền")
            if front is not None:
                front = float(front.replace(" (m)", "").replace(",", "."))

            entrance = features.get("Đường vào")
            if entrance is not None:
                entrance = float(entrance.replace(" (m)", "").replace(",", "."))

            houseAspect = features.get("Hướng nhà")
            balconyAspect = features.get("Hướng ban công")
            if balconyAspect is not None:
                if balconyAspect == "KXĐ":
                    balconyAspect = None

            description = detail["description"].lower()

            interiorStr = features.get("Nội thất")
            if interiorStr is None:
                interior = None
            else:
                interiorStr = interiorStr.lower()
                noStartStrs = ["ko", "khong", "không", "k ", "kxđ"]
                # fmt: off
                if interiorStr == "k" or any(interiorStr.startswith(s) for s in noStartStrs):
                    interior = False
                else:
                    interior = True
                # fmt: on

            # match = re.match(
            #     r"([0-9]*[.,]?[0-9]+)\s*m?\s*[*x]\s*([0-9]*[.,]?[0-9]+)", description,
            # )
            # if match:
            #     width, length = match.group(1, 2)
            # else:
            #     pass

            nearCenter = district in ["Quận 1", "Quận 3", "Quận 4", "Bình Thạnh"]
            owner = any(
                searchStr in description
                for searchStr in ["sổ đỏ", "sổ hồng", "chính chủ", "pháp lý"]
            )

            alley = False
            if address is not None:
                alley = "/" in address

            if not alley:
                alley = "hẻm" in description

            villa = "biệt thự" in description

            new = any(
                searchStr in description
                for searchStr in ["nhà mới", "mới xây", "mới 100"]
            )

            row = (
                xstr(area),
                xstr(district),
                xstr(address),
                xstr(floors),
                xstr(bedrooms),
                xstr(toilets),
                xstr(front),
                xstr(entrance),
                xstr(houseAspect),
                xstr(balconyAspect),
                xstr(interior),
                xstr(nearCenter),
                xstr(owner),
                xstr(alley),
                xstr(villa),
                xstr(new),
                xstr(price),
            )
            fOut.write(f"{tab.join(row)}\n")

# from: https://docs.python.org/3/library/itertools.html#itertools-recipes
def unique_everseen(iterable, key=None):
    "List unique elements, preserving order. Remember all elements ever seen."
    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
    # unique_everseen('ABBCcAD', str.lower) --> A B C D
    seen = set()
    seen_add = seen.add
    if key is None:
        for element in filterfalse(seen.__contains__, iterable):
            seen_add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen_add(k)
                yield element

def removeDuplicates(inputFile: str, outputFile: str):
    with open(inputFile) as fIn, open(outputFile, "w") as fOut:
        for line in unique_everseen(fIn):
            fOut.write(line)

dataFile = "data/2019-11-29_23_18_43.json"
noDuplicatedFile = "data/2019-11-29_23_18_43-no-dup.json"
outputFile = "data/2019-11-29_23_18_43.csv"

if not os.path.exists(noDuplicatedFile):
    removeDuplicates(inputFile=dataFile, outputFile=noDuplicatedFile)
preprocess(inputFile=noDuplicatedFile, outputFile=outputFile)


# coding=utf-8
"""
This module represents OctoPrint's plugin subsystem. This includes management and helper methods as well as the
registered plugin types.

.. autofunction:: plugin_manager

.. autofunction:: plugin_settings

.. autofunction:: call_plugin

.. autoclass:: PluginSettings
   :members:
"""

from __future__ import absolute_import, division, print_function

__author__ = "Gina Häußge <osd@foosel.net>"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
__copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License"

import os
import logging

from octoprint.settings import settings as s
from octoprint.plugin.core import (PluginInfo, PluginManager, Plugin)
from octoprint.plugin.types import *

from octoprint.util import deprecated

# singleton
_instance = None

def _validate_plugin(phase, plugin_info):
	if phase == "after_load":
		if plugin_info.implementation is not None and isinstance(plugin_info.implementation, AppPlugin):
			# transform app plugin into hook
			import warnings
			warnings.warn("{name} uses deprecated plugin mixin AppPlugin, use octoprint.accesscontrol.appkey hook instead".format(name=plugin_info.key), DeprecationWarning)

			hooks = plugin_info.hooks
			if not "octoprint.accesscontrol.appkey" in hooks:
				hooks["octoprint.accesscontrol.appkey"] = plugin_info.implementation.get_additional_apps
			setattr(plugin_info.instance, PluginInfo.attr_hooks, hooks)
	return True

def plugin_manager(init=False, plugin_folders=None, plugin_types=None, plugin_entry_points=None, plugin_disabled_list=None,
                   plugin_restart_needing_hooks=None, plugin_obsolete_hooks=None, plugin_validators=None):
	"""
	Factory method for initially constructing and consecutively retrieving the :class:`~octoprint.plugin.core.PluginManager`
	singleton.

	Arguments:
	    init (boolean): A flag indicating whether this is the initial call to construct the singleton (True) or not
	        (False, default). If this is set to True and the plugin manager has already been initialized, a :class:`ValueError`
	        will be raised. The same will happen if the plugin manager has not yet been initialized and this is set to
	        False.
	    plugin_folders (list): A list of folders (as strings containing the absolute path to them) in which to look for
	        potential plugin modules. If not provided this defaults to the configured ``plugins`` base folder and
	        ``src/plugins`` within OctoPrint's code base.
	    plugin_types (list): A list of recognized plugin types for which to look for provided implementations. If not
	        provided this defaults to the plugin types found in :mod:`octoprint.plugin.types` without
	        :class:`~octoprint.plugin.OctoPrintPlugin`.
	    plugin_entry_points (list): A list of entry points pointing to modules which to load as plugins. If not provided
	        this defaults to the entry point ``octoprint.plugin``.
	    plugin_disabled_list (list): A list of plugin identifiers that are currently disabled. If not provided this
	        defaults to all plugins for which ``enabled`` is set to ``False`` in the settings.
	    plugin_restart_needing_hooks (list): A list of hook namespaces which cause a plugin to need a restart in order
	        be enabled/disabled. Does not have to contain full hook identifiers, will be matched with startswith similar
	        to logging handlers
	    plugin_obsolete_hooks (list): A list of hooks that have been declared obsolete. Plugins implementing them will
	        not be enabled since they might depend on functionality that is no longer available.
	    plugin_validators (list): A list of additional plugin validators through which to process each plugin.

	Returns:
	    PluginManager: A fully initialized :class:`~octoprint.plugin.core.PluginManager` instance to be used for plugin
	        management tasks.

	Raises:
	    ValueError: ``init`` was True although the plugin manager was already initialized, or it was False although
	        the plugin manager was not yet initialized.
	"""

	global _instance
	if _instance is not None:
		if init:
			raise ValueError("Plugin Manager already initialized")

	else:
		if init:
			if plugin_types is None:
				plugin_types = [StartupPlugin,
				                ShutdownPlugin,
				                TemplatePlugin,
				                SettingsPlugin,
				                SimpleApiPlugin,
				                AssetPlugin,
				                BlueprintPlugin,
				                EventHandlerPlugin,
				                SlicerPlugin,
				                AppPlugin,
				                ProgressPlugin,
				                WizardPlugin,
				                UiPlugin]

			if plugin_restart_needing_hooks is None:
				plugin_restart_needing_hooks = ["octoprint.server.http"]

			if plugin_obsolete_hooks is None:
				plugin_obsolete_hooks = ["octoprint.comm.protocol.gcode"]

			if plugin_validators is None:
				plugin_validators = [_validate_plugin]
			else:
				plugin_validators.append(_validate_plugin)

			_instance = PluginManager(plugin_folders,
			                          plugin_types,
			                          plugin_entry_points,
			                          logging_prefix="octoprint.plugins.",
			                          plugin_disabled_list=plugin_disabled_list,
			                          plugin_restart_needing_hooks=plugin_restart_needing_hooks,
			                          plugin_obsolete_hooks=plugin_obsolete_hooks,
			                          plugin_validators=plugin_validators)
		else:
			raise ValueError("Plugin Manager not initialized yet")
	return _instance

def plugin_settings(plugin_key, defaults=None, get_preprocessors=None, set_preprocessors=None, settings=None):
	"""
	Factory method for creating a :class:`PluginSettings` instance.

	Arguments:
	    plugin_key (string): The plugin identifier for which to create the settings instance.
	    defaults (dict): The default settings for the plugin, if different from get_settings_defaults.
	    get_preprocessors (dict): The getter preprocessors for the plugin.
	    set_preprocessors (dict): The setter preprocessors for the plugin.
	    settings (octoprint.settings.Settings): The settings instance to use.

	Returns:
	    PluginSettings: A fully initialized :class:`PluginSettings` instance to be used to access the plugin's
	        settings
	"""
	if settings is None:
		settings = s()
	return PluginSettings(settings, plugin_key, defaults=defaults,
	                      get_preprocessors=get_preprocessors,
	                      set_preprocessors=set_preprocessors)

def plugin_settings_for_settings_plugin(plugin_key, instance, settings=None):
	"""
	Factory method for creating a :class:`PluginSettings` instance for a given :class:`SettingsPlugin` instance.

	Will return `None` if the provided `instance` is not a :class:`SettingsPlugin` instance.

	Arguments:
	    plugin_key (string): The plugin identifier for which to create the settings instance.
	    implementation (octoprint.plugin.SettingsPlugin): The :class:`SettingsPlugin` instance.
	    settings (octoprint.settings.Settings): The settings instance to use. Defaults to the global OctoPrint settings.

	Returns:
	    PluginSettings or None: A fully initialized :class:`PluginSettings` instance to be used to access the plugin's
	        settings, or `None` if the provided `instance` was not a class:`SettingsPlugin`
	"""
	if not isinstance(instance, SettingsPlugin):
		return None

	try:
		get_preprocessors, set_preprocessors = instance.get_settings_preprocessors()
	except:
		logging.getLogger(__name__).exception("Error while retrieving preprocessors for plugin {}".format(plugin_key))
		return None

	return plugin_settings(plugin_key, get_preprocessors=get_preprocessors, set_preprocessors=set_preprocessors, settings=settings)

def call_plugin(types, method, args=None, kwargs=None, callback=None, error_callback=None, sorting_context=None):
	"""
	Helper method to invoke the indicated ``method`` on all registered plugin implementations implementing the
	indicated ``types``. Allows providing method arguments and registering callbacks to call in case of success
	and/or failure of each call which can be used to return individual results to the calling code.

	Example:

	.. sourcecode:: python

	   def my_success_callback(name, plugin, result):
	       print("{name} was called successfully and returned {result!r}".format(**locals()))

	   def my_error_callback(name, plugin, exc):
	       print("{name} raised an exception: {exc!s}".format(**locals()))

	   octoprint.plugin.call_plugin(
	       [octoprint.plugin.StartupPlugin],
	       "on_startup",
	       args=(my_host, my_port),
	       callback=my_success_callback,
	       error_callback=my_error_callback
	   )

	Arguments:
	    types (list): A list of plugin implementation types to match against.
	    method (string): Name of the method to call on all matching implementations.
	    args (tuple): A tuple containing the arguments to supply to the called ``method``. Optional.
	    kwargs (dict): A dictionary containing the keyword arguments to supply to the called ``method``. Optional.
	    callback (function): A callback to invoke after an implementation has been called successfully. Will be called
	        with the three arguments ``name``, ``plugin`` and ``result``. ``name`` will be the plugin identifier,
	        ``plugin`` the plugin implementation instance itself and ``result`` the result returned from the
	        ``method`` invocation.
	    error_callback (function): A callback to invoke after the call of an implementation resulted in an exception.
	        Will be called with the three arguments ``name``, ``plugin`` and ``exc``. ``name`` will be the plugin
	        identifier, ``plugin`` the plugin implementation instance itself and ``exc`` the caught exception.

	"""

	if not isinstance(types, (list, tuple)):
		types = [types]
	if args is None:
		args = []
	if kwargs is None:
		kwargs = dict()

	plugins = plugin_manager().get_implementations(*types, sorting_context=sorting_context)
	for plugin in plugins:
		if hasattr(plugin, method):
			try:
				result = getattr(plugin, method)(*args, **kwargs)
				if callback:
					callback(plugin._identifier, plugin, result)
			except Exception as exc:
				logging.getLogger(__name__).exception("Error while calling plugin %s" % plugin._identifier)
				if error_callback:
					error_callback(plugin._identifier, plugin, exc)

class PluginSettings(object):
	"""
	The :class:`PluginSettings` class is the interface for plugins to their own or globally defined settings.

	It provides a couple of convenience methods for directly accessing plugin settings via the regular
	:class:`octoprint.settings.Settings` interfaces as well as means to access plugin specific folder locations.

	All getter and setter methods will ensure that plugin settings are stored in their correct location within the
	settings structure by modifying the supplied paths accordingly.

	Arguments:
	    settings (Settings): The :class:`~octoprint.settings.Settings` instance on which to operate.
	    plugin_key (str): The plugin identifier of the plugin for which to create this instance.
	    defaults (dict): The plugin's defaults settings, will be used to determine valid paths within the plugin's
	        settings structure

	.. method:: get(path, merged=False, asdict=False)

	   Retrieves a raw value from the settings for ``path``, optionally merging the raw value with the default settings
	   if ``merged`` is set to True.

	   :param path: The path for which to retrieve the value.
	   :type path: list, tuple
	   :param boolean merged: Whether to merge the returned result with the default settings (True) or not (False,
	       default).
	   :returns: The retrieved settings value.
	   :rtype: object

	.. method:: get_int(path)

	   Like :func:`get` but tries to convert the retrieved value to ``int``.

	.. method:: get_float(path)

	   Like :func:`get` but tries to convert the retrieved value to ``float``.

	.. method:: get_boolean(path)

	   Like :func:`get` but tries to convert the retrieved value to ``boolean``.

	.. method:: set(path, value, force=False)

	   Sets the raw value on the settings for ``path``.

	   :param path: The path for which to retrieve the value.
	   :type path: list, tuple
	   :param object value: The value to set.
	   :param boolean force: If set to True, the modified configuration will even be written back to disk if
	       the value didn't change.

	.. method:: set_int(path, value, force=False)

	   Like :func:`set` but ensures the value is an ``int`` through attempted conversion before setting it.

	.. method:: set_float(path, value, force=False)

	   Like :func:`set` but ensures the value is an ``float`` through attempted conversion before setting it.

	.. method:: set_boolean(path, value, force=False)

	   Like :func:`set` but ensures the value is an ``boolean`` through attempted conversion before setting it.
	"""

	def __init__(self, settings, plugin_key, defaults=None, get_preprocessors=None, set_preprocessors=None):
		self.settings = settings
		self.plugin_key = plugin_key

		if defaults is not None:
			self.defaults = dict(plugins=dict())
			self.defaults["plugins"][plugin_key] = defaults
			self.defaults["plugins"][plugin_key]["_config_version"] = None
		else:
			self.defaults = None

		if get_preprocessors is None:
			get_preprocessors = dict()
		self.get_preprocessors = dict(plugins=dict())
		self.get_preprocessors["plugins"][plugin_key] = get_preprocessors

		if set_preprocessors is None:
			set_preprocessors = dict()
		self.set_preprocessors = dict(plugins=dict())
		self.set_preprocessors["plugins"][plugin_key] = set_preprocessors

		def prefix_path_in_args(args, index=0):
			result = []
			if index == 0:
				result.append(self._prefix_path(args[0]))
				result.extend(args[1:])
			else:
				args_before = args[:index - 1]
				args_after = args[index + 1:]
				result.extend(args_before)
				result.append(self._prefix_path(args[index]))
				result.extend(args_after)
			return result

		def add_getter_kwargs(kwargs):
			if not "defaults" in kwargs and self.defaults is not None:
				kwargs.update(defaults=self.defaults)
			if not "preprocessors" in kwargs:
				kwargs.update(preprocessors=self.get_preprocessors)
			return kwargs

		def add_setter_kwargs(kwargs):
			if not "defaults" in kwargs and self.defaults is not None:
				kwargs.update(defaults=self.defaults)
			if not "preprocessors" in kwargs:
				kwargs.update(preprocessors=self.set_preprocessors)
			return kwargs

		self.access_methods = dict(
			has        =("has",        prefix_path_in_args, add_getter_kwargs),
			get        =("get",        prefix_path_in_args, add_getter_kwargs),
			get_int    =("getInt",     prefix_path_in_args, add_getter_kwargs),
			get_float  =("getFloat",   prefix_path_in_args, add_getter_kwargs),
			get_boolean=("getBoolean", prefix_path_in_args, add_getter_kwargs),
			set        =("set",        prefix_path_in_args, add_setter_kwargs),
			set_int    =("setInt",     prefix_path_in_args, add_setter_kwargs),
			set_float  =("setFloat",   prefix_path_in_args, add_setter_kwargs),
			set_boolean=("setBoolean", prefix_path_in_args, add_setter_kwargs),
			remove     =("remove",     prefix_path_in_args, lambda x: x)
		)
		self.deprecated_access_methods = dict(
			getInt    ="get_int",
			getFloat  ="get_float",
			getBoolean="get_boolean",
			setInt    ="set_int",
			setFloat  ="set_float",
			setBoolean="set_boolean"
		)

	def _prefix_path(self, path=None):
		if path is None:
			path = list()
		return ['plugins', self.plugin_key] + path

	def global_has(self, path, **kwargs):
		return self.settings.has(path, **kwargs)

	def global_remove(self, path, **kwargs):
		return self.settings.remove(path, **kwargs)

	def global_get(self, path, **kwargs):
		"""
		Getter for retrieving settings not managed by the plugin itself from the core settings structure. Use this
		to access global settings outside of your plugin.

		Directly forwards to :func:`octoprint.settings.Settings.get`.
		"""
		return self.settings.get(path, **kwargs)

	def global_get_int(self, path, **kwargs):
		"""
		Like :func:`global_get` but directly forwards to :func:`octoprint.settings.Settings.getInt`.
		"""
		return self.settings.getInt(path, **kwargs)

	def global_get_float(self, path, **kwargs):
		"""
		Like :func:`global_get` but directly forwards to :func:`octoprint.settings.Settings.getFloat`.
		"""
		return self.settings.getFloat(path, **kwargs)

	def global_get_boolean(self, path, **kwargs):
		"""
		Like :func:`global_get` but directly orwards to :func:`octoprint.settings.Settings.getBoolean`.
		"""
		return self.settings.getBoolean(path, **kwargs)

	def global_set(self, path, value, **kwargs):
		"""
		Setter for modifying settings not managed by the plugin itself on the core settings structure. Use this
		to modify global settings outside of your plugin.

		Directly forwards to :func:`octoprint.settings.Settings.set`.
		"""
		self.settings.set(path, value, **kwargs)

	def global_set_int(self, path, value, **kwargs):
		"""
		Like :func:`global_set` but directly forwards to :func:`octoprint.settings.Settings.setInt`.
		"""
		self.settings.setInt(path, value, **kwargs)

	def global_set_float(self, path, value, **kwargs):
		"""
		Like :func:`global_set` but directly forwards to :func:`octoprint.settings.Settings.setFloat`.
		"""
		self.settings.setFloat(path, value, **kwargs)

	def global_set_boolean(self, path, value, **kwargs):
		"""
		Like :func:`global_set` but directly forwards to :func:`octoprint.settings.Settings.setBoolean`.
		"""
		self.settings.setBoolean(path, value, **kwargs)

	def global_get_basefolder(self, folder_type, **kwargs):
		"""
		Retrieves a globally defined basefolder of the given ``folder_type``. Directly forwards to
		:func:`octoprint.settings.Settings.getBaseFolder`.
		"""
		return self.settings.getBaseFolder(folder_type, **kwargs)

	def get_plugin_logfile_path(self, postfix=None):
		"""
		Retrieves the path to a logfile specifically for the plugin. If ``postfix`` is not supplied, the logfile
		will be named ``plugin_<plugin identifier>.log`` and located within the configured ``logs`` folder. If a
		postfix is supplied, the name will be ``plugin_<plugin identifier>_<postfix>.log`` at the same location.

		Plugins may use this for specific logging tasks. For example, a :class:`~octoprint.plugin.SlicingPlugin` might
		want to create a log file for logging the output of the slicing engine itself if some debug flag is set.

		Arguments:
		    postfix (str): Postfix of the logfile for which to create the path. If set, the file name of the log file
		        will be ``plugin_<plugin identifier>_<postfix>.log``, if not it will be
		        ``plugin_<plugin identifier>.log``.

		Returns:
		    str: Absolute path to the log file, directly usable by the plugin.
		"""
		filename = "plugin_" + self.plugin_key
		if postfix is not None:
			filename += "_" + postfix
		filename += ".log"
		return os.path.join(self.settings.getBaseFolder("logs"), filename)

	@deprecated("PluginSettings.get_plugin_data_folder has been replaced by OctoPrintPlugin.get_plugin_data_folder",
	            includedoc="Replaced by :func:`~octoprint.plugin.types.OctoPrintPlugin.get_plugin_data_folder`",
	            since="1.2.0")
	def get_plugin_data_folder(self):
		path = os.path.join(self.settings.getBaseFolder("data"), self.plugin_key)
		if not os.path.isdir(path):
			os.makedirs(path)
		return path

	def get_all_data(self, **kwargs):
		merged = kwargs.get("merged", True)
		asdict = kwargs.get("asdict", True)
		defaults = kwargs.get("defaults", self.defaults)
		preprocessors = kwargs.get("preprocessors", self.get_preprocessors)

		kwargs.update(dict(
			merged=merged,
			asdict=asdict,
			defaults=defaults,
			preprocessors=preprocessors
		))

		return self.settings.get(self._prefix_path(), **kwargs)

	def clean_all_data(self):
		self.settings.remove(self._prefix_path())

	def __getattr__(self, item):
		all_access_methods = self.access_methods.keys() + self.deprecated_access_methods.keys()
		if item in all_access_methods:
			decorator = None
			if item in self.deprecated_access_methods:
				new = self.deprecated_access_methods[item]
				decorator = deprecated("{old} has been renamed to {new}".format(old=item, new=new), stacklevel=2)
				item = new

			settings_name, args_mapper, kwargs_mapper = self.access_methods[item]
			if hasattr(self.settings, settings_name) and callable(getattr(self.settings, settings_name)):
				orig_func = getattr(self.settings, settings_name)
				if decorator is not None:
					orig_func = decorator(orig_func)

				def _func(*args, **kwargs):
					return orig_func(*args_mapper(args), **kwargs_mapper(kwargs))
				_func.__name__ = item
				_func.__doc__ = orig_func.__doc__ if "__doc__" in dir(orig_func) else None

				return _func

		return getattr(self.settings, item)

	##~~ deprecated methods follow

	# TODO: Remove with release of 1.3.0

	globalGet            = deprecated("globalGet has been renamed to global_get",
	                                  includedoc="Replaced by :func:`global_get`",
	                                  since="1.2.0-dev-546")(global_get)
	globalGetInt         = deprecated("globalGetInt has been renamed to global_get_int",
	                                  includedoc="Replaced by :func:`global_get_int`",
	                                  since="1.2.0-dev-546")(global_get_int)
	globalGetFloat       = deprecated("globalGetFloat has been renamed to global_get_float",
	                                  includedoc="Replaced by :func:`global_get_float`",
	                                  since="1.2.0-dev-546")(global_get_float)
	globalGetBoolean     = deprecated("globalGetBoolean has been renamed to global_get_boolean",
	                                  includedoc="Replaced by :func:`global_get_boolean`",
	                                  since="1.2.0-dev-546")(global_get_boolean)
	globalSet            = deprecated("globalSet has been renamed to global_set",
	                                  includedoc="Replaced by :func:`global_set`",
	                                  since="1.2.0-dev-546")(global_set)
	globalSetInt         = deprecated("globalSetInt has been renamed to global_set_int",
	                                  includedoc="Replaced by :func:`global_set_int`",
	                                  since="1.2.0-dev-546")(global_set_int)
	globalSetFloat       = deprecated("globalSetFloat has been renamed to global_set_float",
	                                  includedoc="Replaced by :func:`global_set_float`",
	                                  since="1.2.0-dev-546")(global_set_float)
	globalSetBoolean     = deprecated("globalSetBoolean has been renamed to global_set_boolean",
	                                  includedoc="Replaced by :func:`global_set_boolean`",
	                                  since="1.2.0-dev-546")(global_set_boolean)
	globalGetBaseFolder  = deprecated("globalGetBaseFolder has been renamed to global_get_basefolder",
	                                  includedoc="Replaced by :func:`global_get_basefolder`",
	                                  since="1.2.0-dev-546")(global_get_basefolder)
	getPluginLogfilePath = deprecated("getPluginLogfilePath has been renamed to get_plugin_logfile_path",
	                                  includedoc="Replaced by :func:`get_plugin_logfile_path`",
	                                  since="1.2.0-dev-546")(get_plugin_logfile_path)


import numpy as np
import cv2

def HH():
    img = cv2.imread("img/1.jpg", 0)

    # this is numpy array [] operator
    img[img < 2] = 255
    img[img < 255] = 0

    distance = cv2.distanceTransform(
        img, cv2.DIST_L2, 5, dstType=cv2.CV_32F)
    cv2.normalize(distance, distance, 0, 255, cv2.NORM_MINMAX)
    distance = np.uint8(distance)
    _, distance = cv2.threshold(distance, 127, 255, cv2.THRESH_BINARY)

    # find contours :
    #   RETR_LIST : to find all contours without establishing any hierarchical relationships.
    #   see : https://docs.opencv.org/3.4.1/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71
    #   CHAIN_APPROX_SIMPLE : to get less points from contours.
    #   see : https://docs.opencv.org/3.4.1/d3/dc0/group__imgproc__shape.html#ga4303f45752694956374734a03c54d5ff
    contours, hierarchy = cv2.findContours(
        distance, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    # convect grey to bgr for drawing color in the image.
    drawing = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

    #
    cv2.drawContours(drawing, contours, -1, (0, 0, 255), -1)

    print(len(contours))

    while True:
        cv2.imshow("drawing", drawing)

        if cv2.waitKey(1) == 27:
            break

    cv2.destroyAllWindows()

if __name__ == '__main__':
    HH()


import os.path
import pickle
import math
# from cv2 import cv2
import cv2
import face_recognition
import numpy as np
from Voice_Speaker import speak
import datetime
import train_module_by_video_screenshot as train
from Analize_Unknown_person import analyze_unknown_person as analyze
from PIL import Image

def face_confidence(face_distance, face_match_threshold = 0.6):
    range = (1.0 - face_match_threshold)
    linear_val = (1.0 - face_distance) / (range * 2.0)

    if face_distance > face_match_threshold:
        return str(round(linear_val * 100, 2)) + "%"
    else:
        value = (linear_val + ((1.0 - linear_val) * math.pow((linear_val - 0.5) * 2, 0.2))) * 100
        return str(round(value, 2)) + '%'

def detect_person_on_video():
    # Get a reference to webcam #0 (the default one)
    video_capture = cv2.VideoCapture(0)
    video_capture.set(3, 1280)
    video_capture.set(4, 720)
    if not os.path.exists('Data'):
        os.mkdir('Data')
    # Initialize some variables
    data_path = os.listdir(f"Data/")
    face_locations = []
    face_encodings = []
    face_names = []
    bodies_arr = []
    unknown_found = False
    known_count = 0
    unknown_count = 0
    process_this_frame = True
    known_encodings = []
    known_names = []
    speak('Welcome to Face Recognition Aplication, I hope you will enjoy!')
    # Prepare DATA for Work
    for file in data_path:
        if file.endswith(".pkl"):
            data = pickle.loads(open(f"Data/{file}", 'rb').read())
            known_encodings.append(data['encodings'][0])
            known_names.append(data['name'])
    while True:
        # Grab a single frame of video
        ret, frame = video_capture.read()
        fps = video_capture.get(cv2.CAP_PROP_FPS)
        multiplier = fps * 5
        frame_id = int(round(video_capture.get(cv2.CAP_PROP_POS_FRAMES)))
        timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S')
        # Load the classifier
        classifier = cv2.CascadeClassifier('Assets/haarcascade_fullbody.xml')
        # Detect the person body
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        bodies = classifier.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=5)
        if len(bodies) > 0:
            for (i, body) in enumerate(bodies):
                bodies_arr.append(f"{body}_{i}")
            if len(bodies_arr) > 0:
                for (i, body) in enumerate(bodies_arr):
                    speak(f"Person {i+1} was detected")
        else: bodies_arr = []

        # Draw a bounding box around the person
        for (x, y, w, h) in bodies:
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
            cv2.putText(frame, 'Person', (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)

        # Only process every other frame of video to save time
        if process_this_frame:
            # Resize frame of video to 1/4 size for faster face recognition processing
            small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
            # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
            rgb_small_frame = small_frame[:, :, ::-1]
            # Find all the faces and face encodings in the current frame of video
            face_locations = face_recognition.face_locations(rgb_small_frame)
            for face_location in face_locations:

                face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)
                face_names = []
                if len(face_encodings) > 0:
                    for face_encoding in face_encodings:
                        matches = face_recognition.compare_faces(known_encodings, face_encoding)
                        face_distances = face_recognition.face_distance(known_encodings, face_encoding)
                        if len(face_distances) > 0:
                            best_match_index = np.argmin(face_distances)
                            confidence = face_confidence(face_distances[best_match_index])
                            name = 'Unknown'
                            # See if the face is a match for the known face(s)
                            if matches[best_match_index]:
                                idx = matches.index(True)
                                k_name = known_names[idx]
                                name = f"{k_name} ({confidence})"
                                if frame_id % multiplier == 10:
                                    if not os.path.exists('DataSet_from_Video'):
                                        os.mkdir('DataSet_from_Video')
                                    if known_count < 15:
                                        cv2.imwrite(f"DataSet_from_Video/{known_count}_{timestamp}_screen.jpeg", frame)
                                        known_count += 1
                                        print(f"Take screenshot {known_count}")
                                        train.train_module_by_video_screenshots()
                                        # Update DATA for Work
                                        for file in data_path:
                                            if file.endswith(".pkl"):
                                                data = pickle.loads(open(f"Data/{file}", 'rb').read())
                                                known_encodings.append(data['encodings'][0])
                                                known_names.append(data['name'])

                            else:
                                if filter(lambda n:n == "Unknown", face_names):
                                    if not os.path.exists('Unknown_DataSet_from_video'):
                                        os.mkdir('Unknown_DataSet_from_video')
                                    speak("Unknown Person detected")

                                    print(face_locations)
                                    print(f"Took screenshot of Unknown Person {unknown_count}")
                                    top, right, bottom, left = face_location
                                    face_img = rgb_small_frame[top - 40:bottom + 20, left - 20:right + 20]
                                    pil_img = Image.fromarray(face_img)
                                    pil_img.save(f'Unknown_DataSet_from_video/{unknown_count}_unknown.jpg')
                                    img = f'Unknown_DataSet_from_video/{unknown_count}_unknown.jpg'
                                    print(img)
                                    analyze(img)
                                    unknown_count += 1

                            face_names.append(name)
                        print(face_names)

        process_this_frame = not process_this_frame
        # Display the results
        for (top, right, bottom, left), name in zip(face_locations, face_names):
            # Scale back up face locations since the frame we detected in was scaled to 1/4 size
            top *= 4
            right *= 4
            bottom *= 4
            left *= 4

            # Draw a box around the face
            cv2.rectangle(frame, (left, top), (right, bottom), (50, 249, 42), 2)
            # Draw a label with a name below the face
            cv2.rectangle(frame, (left, bottom - 30), (right, bottom), (50, 249, 42), cv2.FILLED)
            font = cv2.FONT_HERSHEY_PLAIN
            cv2.putText(frame, f"{name}", (left + 6, bottom - 6), font, 1, (255, 255, 255), 1, cv2.LINE_AA)

            if name == "Unknown":
                # Draw a box around the face
                cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
                # Draw a label with a name below the face
                cv2.rectangle(frame, (left, bottom - 30), (right, bottom), (0, 0, 255), cv2.FILLED)
                font = cv2.FONT_HERSHEY_PLAIN
                cv2.putText(frame, f"{name}", (left + 6, bottom - 6), font, 1, (255, 255, 255), 1, cv2.LINE_AA)

        # Display the resulting image
        cv2.imshow('Video', frame)
        # Hit 'q' on the keyboard to quit!
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    # Release handle to the webcam
    video_capture.release()
    cv2.destroyAllWindows()

def main():
    detect_person_on_video()

if __name__ == '__main__':
    main()


from exporter import config
from exporter.utils import func
from exporter.utils import export_writer

class Executor:
    android_field_list_params = config.COUNTRIES
    ios_field_list_params = config.COUNTRIES
    export_writer_class = export_writer.ExportWriter
    aggregate = True

    def __init__(self, exporter):
        self.exporter = exporter
        self.writer = self.export_writer_class()

    @property
    def aggregate_func(self):
        return sum

    @property
    def apps(self):
        raise NotImplementedError

    @property
    def kpi(self):
        raise NotImplementedError

    @property
    def source_name(self):
        raise NotImplementedError

    def execute(self, export_from, export_to):
        if config.OPTIMIZE_EXPORT_FROM:
            export_from = self.get_last_date(export_from)
        params_list = self.get_params_list(export_from, export_to)
        exported_data = self.get_export_data(params_list, self.exporter)
        proccessed_data = self.get_proccessed_data(exported_data)
        self.write_export(proccessed_data)
        self.writer.upload_files()

    def get_params(self, *args, **kwargs):
        raise NotImplementedError

    def get_export_data(self, params_list, exporter):
        raise NotImplementedError

    def get_proccessed_data(self, exported_data):
        raise NotImplementedError

    def get_export_field_list(self, filed_list_params):
        raise NotImplementedError

    def get_params_list(self, export_from, export_to):
        params_list = []
        for country in config.COUNTRIES:
            for app_id, platform in self.apps.items():
                params = self.get_params(
                    export_from, export_to, app_id, platform, country
                )
                params_list.append(params)
        return params_list

    def write_export(self, data):
        self.write_export_for_platform(
            data, "ios", self.ios_field_list_params
        )
        self.write_export_for_platform(
            data, "android", self.android_field_list_params
        )

    def write_export_for_platform(self, data, platform_name, filed_list_params):
        filename = self.get_filename(platform_name, self.kpi, "days")
        filtered_data = self.filter_data_for_platform(data, platform_name)
        field_list = self.get_export_field_list(filed_list_params)
        self.writer.export_data(filtered_data, filename, field_list)
        if self.aggregate:
            self.write_aggregated_exports(filename, field_list, platform_name, self.kpi)

    def write_aggregated_exports(self, filename, field_list, *filename_params):
        exported_data = self.writer.get_exported_data(filename)
        for filename, date_func in [
            (
                self.get_filename(*filename_params, "months"),
                self.writer.get_first_day_of_the_month,
            ),
            (
                self.get_filename(*filename_params, "weeks"),
                self.writer.get_first_day_of_the_week,
            ),
        ]:
            self.writer.export_aggregated(
                filename, date_func, exported_data, self.aggregate_func, int, field_list
            )

    def filter_data_for_platform(self, data, platform_name):
        return {
            (date, platform): value
            for (date, platform), value in data.items()
            if platform == platform_name
        }

    def get_filename(self, platform_name, kpi, aggregate):
        return f"{config.EXPORTED_DATA_DIR}/{self.source_name}_{kpi}_{platform_name}_{aggregate}.csv"

    def get_last_date(self, export_from):
        return max(
            [
                func.get_last_date(
                    export_from, self.get_filename(platform, self.kpi, "days")
                )
                for platform in self.apps.values()
            ]
        )


import numpy as np
from scipy.linalg import sqrtm

def EnKF(prior, observations, prediction, DA_alpha, DA_error, seed=None):
    """
    Ensemble Kalman Filter

    Parameters
    ----------
    prior: array (n_vars x ensemble_size)
        prior ensemble matrix
        assumed to be a Gaussian probability distribtion
    observations: array (n_obs)
        observation vector
        assumed to be a Gaussian probability distribtion
    prediction: array (n_obs x ensemble_size)
        predicted observation ensemble matrix
        follows from the forward model with prior input values
    DA_alpha: scalar
        observation error inflation coefficient
    DA_error: scalar or array (n_obs or n_obs x n_obs)
        observation error covariance matrix
    seed: scalar (int)
        included for testing purposes only

    n_vars: number of variables
    n_obs: number of observations
    ensemble_size: number of ensemble members

    Returns
    -------
    posterior: array (n_vars x ensemble_size)
        posterior ensemble matrix
        assumed to be a Gaussian probability distribtion
    """

    # Get dimensions of input arrays: n_obs, n_vars, ensemble_size
    n_obs = np.size(observations)
    if prior.ndim == 1:
        n_vars = 1
        ensemble_size = prior.size
    elif prior.ndim == 2:
        n_vars = prior.shape[0]
        ensemble_size = prior.shape[1]
    else:
        raise Exception("Prior should be 1D or 2D array")

    # Check if dimensions of observations, prior and prediction match
    if n_obs == 1:
        if prediction.size != ensemble_size:
            raise Exception(
                f"- n_obs = {n_obs} - Dimensions of observations (n_obs = {n_obs}), prior (n_vars x ensemble_size = {n_vars} x {ensemble_size}) \
                and prediction (n_obs x ensemble_size = {n_obs} x {ensemble_size}) do not match"
            )
    else:  # n_obs > 1
        if (prediction.shape[0] != n_obs) or (prediction.shape[1] != ensemble_size):
            raise Exception(
                f"- n_obs = {n_obs} - Dimensions of observations (n_obs = {n_obs}), prior (n_vars x ensemble_size = {n_vars} x {ensemble_size}) \
                and prediction (n_obs x ensemble_size = {n_obs} x {ensemble_size}) do not match"
            )

    # Reshape prior and prediction to explicitly create 2D arrays if 1D arrays are given
    prior = prior.reshape(n_vars, ensemble_size)
    prediction = prediction.reshape(n_obs, ensemble_size)

    # Check dimension of input variable DA_error
    # and create observation error covariance matrix with dimensions n_obs x n_obs
    if np.isscalar(DA_error):
        R = DA_error * np.identity(n_obs)
    elif DA_error.ndim == 1:
        if DA_error.size == n_obs:
            R = np.diag(DA_error)
        else:
            raise Exception(f"Dimension of 1D DA_error array should be n_obs = {n_obs}")
    elif DA_error.ndim == 2:
        if DA_error.shape[0] == DA_error.shape[1] == n_obs:
            pass
        else:
            raise Exception(
                f"Dimension of 2D DA_error array should be n_obs x n_obs = {n_obs} x {n_obs}"
            )
    else:
        raise Exception("Error should be scalar, 1D or 2D array")

    # Anomaly calculations
    prior_mean = np.mean(prior, axis=1)  # ensemble mean (for each variable)
    A = prior - prior_mean.reshape(n_vars, 1)

    prediction_mean = np.mean(prediction, axis=1)  # ensemble mean (for each observation)
    B = prediction - prediction_mean.reshape(n_obs, 1)
    B_transpose = B.T

    C_AB = A @ B_transpose
    C_BB = B @ B_transpose

    # Create perturbed observations
    R_sqrt = sqrtm(R)  # Square root of the observation error covariance matrix
    rng = np.random.default_rng(seed)
    perturbations = (
        np.sqrt(DA_alpha) * R_sqrt @ rng.standard_normal((n_obs, ensemble_size))
    )
    observations_perturbed = (
        np.outer(observations, np.ones(ensemble_size)) + perturbations
    )

    # Create scaled observation error covariance matrix with dimensions n_obs x n_obs
    R_scaled = (ensemble_size * DA_alpha) * R

    # Analysis step
    Kalman_gain = C_AB @ (np.linalg.inv(C_BB + R_scaled))
    innovation = observations_perturbed - prediction
    posterior = prior + Kalman_gain @ innovation

    return posterior


#!/usr/local/bin/python -u

import os
import re
import sys

REGEXES = [
    r"""^
    (\d\d?)            # day
       (?:\s+|[-\/])
    (\w+)              # month
        (?:\s+|[-\/])
    (\d+)              # year
    (?:
          (?:\s+|:)    # separator before clock
       (\d\d?):(\d\d)  # hour:min
       (?::(\d\d))?    # optional seconds
    )?                 # optional clock
       \s*
    ([-+]?\d{2,4}|(?![APap][Mm]\b)[A-Za-z]+)? # timezone
       \s*
    (?:\(\w+\))?       # ASCII representation of timezone in parens.
       \s*$""",
]

if __name__ == '__main__':
    r = REGEXES[int(sys.argv[1])]
    REGEX = re.compile(r, re.X if '\n' in r else 0)
    if len(sys.argv) != 3:
        print(REGEX.pattern)
    else:
        attempt = os.environ["ATTEMPT"]
        print(">>>", attempt)
        if re.search(REGEX, attempt):
            print('Regex satisfied :)')
            print("You might expire then, but you aren't getting a flag.")
        else:
            print('Regex unsatisfied :(')


import asyncio
from datetime import datetime
from typing import Optional

from flask.ctx import AppContext

from repositories.tcgplayer_catalog_repository import TCGPlayerCatalogRepository
from services import paginate

class DatabaseSyncWorker:
    def __init__(self):
        self.app_context = None
        self.catalog_repository: Optional[TCGPlayerCatalogRepository] = None

    def init(self, catalog_repository: TCGPlayerCatalogRepository, app_context: AppContext):
        self.catalog_repository = catalog_repository
        self.app_context = app_context

    def _add_updated_set_models(self, outdated_sets: list, set_responses: list[dict]):
        from models.set import Set

        with self.app_context:
            for set_response in set_responses:
                response_set_model = Set.from_tcgplayer_response(set_response)
                existing_set_model: Set = Set.query.get(response_set_model.id)

                if (existing_set_model is None or
                        existing_set_model.modified_date < response_set_model.modified_date):
                    outdated_sets.append(response_set_model)

    def _convert_and_insert_cards_and_skus(self, card_responses):
        from models.card import Card
        from models.sku import Sku

        self.catalog_repository.insert_cards(
            map(lambda card_response: Card.from_tcgplayer_response(card_response), card_responses)
        )

        for card_response in card_responses:
            self.catalog_repository.insert_skus(
                map(lambda sku_response: Sku.from_tcgplayer_response(sku_response), card_response['skus'])
            )

    async def _fetch_cards_in_set(self, set_id: int) -> list[dict]:
        print(set_id)
        set_card_count = await self.catalog_repository.fetch_total_card_count(set_id)
        set_cards = []

        await paginate(
            total=set_card_count,
            async_paginate_fn=lambda offset, limit: self.catalog_repository.fetch_cards(offset, limit, set_id),
            on_paginated=lambda card_responses: set_cards.extend(card_responses)
        )

        return set_cards

    async def update_card_database(self):
        from models.condition import Condition
        from models.printing import Printing

        print(f'{self.__class__.__name__} started at {datetime.now()}')

        printing_responses, condition_responses = await asyncio.gather(*[
            self.catalog_repository.fetch_card_printings(),
            self.catalog_repository.fetch_card_conditions(),
        ])

        condition_models = list(map(lambda x: Condition.from_tcgplayer_response(x), condition_responses))
        printing_models = list(map(lambda x: Printing.from_tcgplayer_response(x), printing_responses))

        self.catalog_repository.insert_conditions(condition_models)
        self.catalog_repository.insert_printings(printing_models)

        set_total_count = await self.catalog_repository.fetch_total_card_set_count()

        outdated_sets = []
        await paginate(
            total=set_total_count,
            async_paginate_fn=self.catalog_repository.fetch_card_sets,
            on_paginated=lambda set_responses: self._add_updated_set_models(outdated_sets, set_responses)
        )

        print(f'{len(outdated_sets)} sets are oudated: {[outdated_set.name for outdated_set in outdated_sets]}')

        self.catalog_repository.insert_sets(outdated_sets)

        # We want to "paginate" on all the card sets and fetch the cards in each set. Hence, we call paginate
        # with pagination_size=1.
        await paginate(
            total=len(outdated_sets),
            async_paginate_fn=lambda offset, limit: self._fetch_cards_in_set(outdated_sets[offset].id),
            on_paginated=lambda card_responses: self._convert_and_insert_cards_and_skus(card_responses),
            num_parallel_requests=1,
            pagination_size=1,
        )

        print(f'{self.__class__.__name__} done at {datetime.now()}')


from tkinter import *
from tkinter import messagebox

window = Tk()

# print(coord_cases["A1"][0])
cases_attaque = ["a3","a4","a5","a6","a7","b5","d0","e0","f0","g0","h0","f1","f9","d10","e10","f10","g10","h10","j5","k3","k4","k5","k6","k7"]
cases_defense = ["d5","e4","e5","e6",'f3',"f4","f6","f7","g4","g5","g6","h5"]
case_king = ["f5"]
grid = {}
current_game = {}
turn = 0
letters = []
player = "Red plays"

def index_cases():
    global letters
    values = list(range(20,440,40))
    letters = ["a","b","c","d","e","f","g","h","i","j","k"]

    for i,v in enumerate(letters):
        for j,w in enumerate(values):
            grid[v + str(j)] = (values[i],w)

    return grid

coord_cases = index_cases()
# print(coord_cases)

def draw_grid():
    for line in range(13):
        canvas.create_line(line*40,0,line*40,440, width= 2, fill="blue")
        canvas.create_line(0,line*40,440,line*40, width=2, fill="red")

def set_pawns():
    rayon = 15

    for item in cases_attaque:
            x = coord_cases[item][0]
            y = coord_cases[item][1]
            canvas.create_oval(x-rayon,y-rayon,x+rayon,y+rayon,outline='black', fill='black')

    for item in cases_defense:
        x = coord_cases[item][0]
        y = coord_cases[item][1]
        canvas.create_oval(x-rayon,y-rayon,x+rayon,y+rayon,outline='red', fill='red')

    for item in case_king:
        x = coord_cases[item][0]
        y = coord_cases[item][1]
        canvas.create_oval(x-rayon,y-rayon,x+rayon,y+rayon,outline='blue', fill='blue')

def display_player_turn(newName):
    w.config(text=newName)

def authorized_moves(data):
    temp_auth_moves = []
    # define authorized pawn moves
    if data[0:1] in letters:
        prev = letters.index(data[0:1])-1
        # exception for pawns on row "k"
        if data[0:1] == "k":
            next = letters.index(data[0:1])
        else:
            next = letters.index(data[0:1])+1

        temp_auth_moves.append(data[0:1] + str(int(data[1:])-1))
        temp_auth_moves.append(data[0:1] + str(int(data[1:])+1))
        temp_auth_moves.append(letters[prev] + data[1:])
        temp_auth_moves.append(letters[next] + data[1:])

    actual_turn_pawn_cases = case_king + cases_attaque + cases_defense
    real_arr = [x for x in temp_auth_moves if x not in actual_turn_pawn_cases]
    # print("You can't go here, authorized moves are : ",real_arr, actual_turn_pawn_cases)
    return real_arr

def take_pawns(value, player, players_cases):

    print("la value est ", value)
    prev = letters.index(value[0:1])-1
    next = letters.index(value[0:1])+1
    prev1 = letters.index(value[0:1])-2
    next1 = letters.index(value[0:1])+2
    print("next", next, next1, prev, prev1, str(value[0:1]))

    if value[0:1] == "k" or value[0:1] == "j":
        next = 10
        next1 = 10
    else:
        next = letters.index(value[0:1])+1
        next1 = letters.index(value[0:1])+2

    print("next2", next, next1, prev, prev1)

    close_arr = []
    up = value[0:1] + str(int(value[1:])-1)
    down = value[0:1] + str(int(value[1:])+1)
    left = letters[prev] + value[1:]
    right = letters[next] + value[1:]
    close_arr.append(up)
    close_arr.append(down)
    close_arr.append(left)
    close_arr.append(right)

    up1 = value[0:1] + str(int(value[1:])-2)
    down1 = value[0:1] + str(int(value[1:])+2)
    left1 = letters[prev1] + value[1:]
    right1 = letters[next1] + value[1:]

    if player == "Red plays":
        if up in cases_attaque and up1 in cases_defense:
            cases_attaque.remove(up)
        if down in cases_attaque and down1 in cases_defense:
            cases_attaque.remove(down)
        if left in cases_attaque and left1 in cases_defense:
            cases_attaque.remove(left)
        if right in cases_attaque and right1 in cases_defense:
            cases_attaque.remove(right)

    if player == "Black plays":
        if up in cases_defense and up1 in cases_attaque:
            cases_defense.remove(up)
        if down in cases_defense and down1 in cases_attaque:
            cases_defense.remove(down)
        if left in cases_defense and left1 in cases_attaque:
            cases_defense.remove(left)
        if right in cases_defense and right1 in cases_attaque:
            cases_defense.remove(right)

def red_wins(king_case):
    print("king", king_case[0])
    escape_cases = ["a0", "k0", "a10", "K10"]

    for y in escape_cases:
        print(y)
        if y == king_case[0]:
            messagebox.showinfo("Red Wins", "Les Rouges ont gagné ")
            window.destroy()

def black_wins(kvalue):
    kvalue = str(kvalue[0])
    prev = letters.index(kvalue[0:1])-1

#   # exception for pawns on row "k"
#     if kvalue[0:1] == "k":
#         next = letters.index(kvalue[0:1])
#     else:
#         next = letters.index(kvalue[0:1])+1

    next = letters.index(kvalue[0:1])+1
    closest_arr = []
    up = kvalue[0:1] + str(int(kvalue[1:])-1)
    down = kvalue[0:1] + str(int(kvalue[1:])+1)
    left = letters[prev] + kvalue[1:]
    right = letters[next] + kvalue[1:]
    closest_arr.append(up)
    closest_arr.append(down)
    closest_arr.append(left)
    closest_arr.append(right)

    # result = all(x in cases_attaque for x in closest_arr)
    # print(result, closest_arr, cases_attaque, case_king)
    if all(x in cases_attaque for x in closest_arr):
        print("Black wins")
        messagebox.showwarning("Black Wins", "Les Noires ont gagné, vouslez-vous rejouer ? ")

def move_pion(data,target,case_lists):
    # effectively move the pawn
    for el in case_lists:
        auth_arr = authorized_moves(data) # check authorized moves
        global player

        if el == data and target in auth_arr: # if case is found by mouse_point and target is in authorized cases for move
        # the we move the pawn on the proper case list
            case_lists[case_lists.index(data)] = target

            # add take pawn function to remove opponent pawns if they are framed by two pawns
            take_pawns(target, player, case_lists)

            # switch player after effective move
            if player == "Red plays":
                player = "Black plays"
            else:
                player = "Red plays"
            canvas.delete("all")
            red_wins(case_king)
            black_wins(case_king)
            return set_pawns(), draw_grid(),display_player_turn(player)

def selected_case(d):
    if d in cases_defense:
        selected = canvas.create_rectangle(grid[d][0]-20, grid[d][1]-20, grid[d][0]+20, grid[d][1]+20,fill='green')
        canvas.tag_lower(selected)
    elif d in cases_attaque:
        selected = canvas.create_rectangle(grid[d][0]-20, grid[d][1]-20, grid[d][0]+20, grid[d][1]+20,fill='green')
        canvas.tag_lower(selected)
    elif d in case_king:
        selected = canvas.create_rectangle(grid[d][0]-20, grid[d][1]-20, grid[d][0]+20, grid[d][1]+20,fill='green')
        canvas.tag_lower(selected)
    else:
        # print(d + " not in selected cases")
        turn -=1

def mouse_coords(event):
    global turn # need to be a global variable
    global player
    # retreive x and y coordinates from the mouse
    mouse_point = (event.x, event.y)

    for x in grid:
        # print(mouse_point[0], grid[x][0])
        varAbs  = mouse_point[0] - grid[x][0]
        varOrd = mouse_point[1] - grid[x][1]
        abs_min = grid[x][0] - 15
        abs_max = grid[x][0] + 15
        ord_min = grid[x][1] - 15
        ord_max = grid[x][1] + 15
        # mouse selected point is converted in a case from the grid
        if mouse_point[0] > abs_min and mouse_point[0] < abs_max :
            if mouse_point[1] > ord_min and mouse_point[1] < ord_max :
                current_game[turn]= x
                # if turn is even
                if turn % 2 == 0:
                    selected_case(x)
                    # print(" even " + str(turn))
                    turn += 1
                # else turn is odd
                else:
                    print(mouse_point, grid[x])
                    #the case is sent to move_pion function as input
                    val = current_game[turn-1]
                    if val in cases_defense and player == "Red plays":
                        move_pion(str(val),str(x),cases_defense)
                    elif val in case_king and player == "Red plays":
                        move_pion(str(val),str(x),case_king)
                    elif val in cases_attaque and player =="Black plays":
                        move_pion(str(val),str(x),cases_attaque)
                    turn += 1
                    print(turn, current_game)

canvas = Canvas(window, height= 440, width= 440, background= "grey")
canvas.pack()
bouton1 = Button(window, text="Quitter", command = window.destroy)
bouton1.pack()
w = Label(window, text="Red plays")
w.pack()

canvas.bind("<1>",mouse_coords)
# print(current_game)
# print("resultat = " + str(mouse_coords))

# creation d'une window de saisie
# player1_label = Label(window, text="enter player's one name")
# player1_label.pack()
# player1_name = Entry(window)
# player1_name.pack()

draw_grid()
set_pawns()

window.mainloop()


from requests import get

def get_section(sections, section_name: str, index: int) -> dict:
    section = list(filter(lambda x: x["section_name"] == section_name, sections))
    try:
        return section[0]['widgets'][index]['data'] if section else {}
    except KeyError:
        return {}

def get_attr(sections):
    section = list(filter(lambda x: x["section_name"] == 'LIST_DATA', sections))[0]

    attributes = []
    for i in section["widgets"]:
        data = i.get("data", {})

        if data.get("@type") == "type.googleapis.com/widgets.GroupFeatureRow":
            items = list(map(lambda x: {x["title"]:x.get("available", False)}, data["items"]))
            attributes.extend(items)

        elif data.get("@type") == "type.googleapis.com/widgets.UnexpandableRowData":
            attributes.append({data["title"]:data["value"]})

        elif data.get("@type") == "type.googleapis.com/widgets.GroupInfoRow":
            attributes.extend(data["items"])

        else:
            continue
    return attributes

def extract_detail_product(post_id:str) -> dict:
    response = get(f"https://api.divar.ir/v8/posts-v2/web/{post_id}").json()
    sections = response["sections"]
    titleinfo = get_section(sections, "TITLE", 0)
    title = titleinfo.get('title')
    subtitle = titleinfo.get('subtitle')
    images = get_section(sections, "IMAGE", 0).get('items')
    description = get_section(sections, "DESCRIPTION", 1).get('text') or response["seo"].get("description")
    location = get_section(sections, "MAP", 0).get("location", {}).get('exact_data', {}).get('point')
    return {
        "title": title,
        "subtitle": subtitle,
        "images": images,
        "description": description,
        "location": location,
        "attributes": get_attr(sections)
    }

agahis = ['AZVdzuJv', 'AZVSVRUc', 'AZVS1TX7', 'AZVSFDaZ', 'AZ1JbQV9', 'AZQexa7M', 'AZ5hvatY', 'AZVSVNdn', 'AZ_tI5hy', 'AZIODl9d', 'gYnGjAcr', 'AZVS1fKx', 'AZ31XrZ1', 'AZVSlIV3', 'AZVS1Gsl', 'AZQixUAb', 'AZVSFB-K', 'AZVSVCjw', 'AZVSVAZh', 'AZ75WT35']
for i in agahis:
    data = extract_detail_product(i)
    print(data)


with open('./day5-input.txt', 'r') as f:
    coordinate = []
    for line in f:
        coordinate.append([[int(c) for c in coordinates.split(',')] for coordinates in line.replace('\n', '').split(' -> ')])

    dot_count = 0
    matrix = {}
    for co in coordinate:
        x1 = co[0][0]
        y1 = co[0][1]
        x2 = co[1][0]
        y2 = co[1][1]

        if x1 == x2: # vertical line
            for y in range(min(y1, y2), max(y1, y2) + 1):
                vertical_line = matrix.get(y, {})
                vertical_line[x1] = vertical_line.get(x1, 0)
                vertical_line[x1] += 1
                if vertical_line[x1] == 2:
                    dot_count += 1
                matrix[y] = vertical_line
        elif y1 == y2: # horizontal line
            horizontal_line = matrix.get(y1, {})

            for x in range(min(x1, x2), max(x1, x2) + 1):
                horizontal_line[x] = horizontal_line.get(x, 0)
                horizontal_line[x] += 1
                if horizontal_line[x] == 2:
                    dot_count += 1

            matrix[y1] = horizontal_line

    print(dot_count)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue May 31 10:37:32 2022

@author: student
"""
import rospy
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist, Pose, Point, Vector3, Quaternion, PoseStamped
from std_msgs.msg import Bool
import numpy as np
from nav_msgs.msg import Path, Odometry
from visualization_msgs.msg import Marker
from std_msgs.msg import Header, ColorRGBA, String
from tf.transformations import quaternion_from_euler

aeb_distance = 0.7
TTC_threshold = 0.6

wb = 0.257
alpha = 0
velocity = 0.1

def cmd_callback(data):
    global alpha
    steer = data.angular.z
    try:
        aeb_distance = rospy.get_param('joy_to_cmd_vel/aeb_distance')
    except:
        aeb_distance = 0.7
    alpha = np.arcsin(aeb_distance*np.tan(steer)/(2*wb))

def odom_callback(data):

    global velocity
    velocity = -data.twist.twist.linear.x

def main():

    global pub
    global pub_marker

    # init node
    rospy.init_node('lidar')
    rate = rospy.Rate(100) # hz

    pub = rospy.Publisher('AEB', Bool, queue_size=1)
    rospy.Subscriber('scan', LaserScan, lidar_callback, queue_size=10)
    rospy.Subscriber('cmd_vel', Twist, cmd_callback, queue_size=10)
    rospy.Subscriber('camera/odom/sample', Odometry, odom_callback, queue_size=10)
    pub_marker = rospy.Publisher('aeb_marker', Marker, queue_size=10)

    rospy.spin()

def publish_marker(pub, x, y, theta, collide=False):
    marker = Marker()
    marker.header=Header(frame_id='laser')
    marker.type=Marker.CUBE
    marker.scale=Vector3(0.2, 0.2, 0.2)
    a,b,c,d = quaternion_from_euler(0,0,theta)
    marker.pose=Pose(Point(x,y,0), Quaternion(a,b,c,d))
    if collide :
        marker.color = ColorRGBA(1,0,0,1)
    else :
        marker.color = ColorRGBA(0,1,0,1)
    marker.lifetime = rospy.Duration(100)

    pub.publish(marker)

def lidar_callback(data):
    global pub_marker

    angle_min = data.angle_min
    angle_max = data.angle_max
    angle_increment = data.angle_increment
    ranges = data.ranges
    # print(angle_min, angle_max)
    laser_number = int(alpha/angle_increment)
    try :
        TTC_threshold = rospy.get_param('joy_to_cmd_vel/TTC_threshold')
    except:
        TTC_threshold = 0.6

    # print(ranges[laser_number],alpha,laser_number)
    b = Bool()
    if velocity != 0:
        TTC = (ranges[laser_number]-0.1)/velocity
    else :
        TTC = np.inf
    # print(TTC)
    if TTC<TTC_threshold and TTC >0:
        print('break',TTC)
        b.data = True
    else :
        b.data = False

    d = min(aeb_distance,ranges[laser_number])

    # x = -ranges[laser_number]*np.cos(alpha)
    # y = -ranges[laser_number]*np.sin(alpha)

    x = -d*np.cos(alpha)
    y = -d*np.sin(alpha)

    publish_marker(pub_marker, x, y, alpha, collide=b.data )

    pub.publish(b)

if __name__ == '__main__':
    try:
        main()
    except rospy.ROSInterruptException:
        pass


import bpy
import json
from math import degrees

path = bpy.path.abspath("//")

# SCENE OBJECTS

# selection_names = [obj.name for obj in bpy.context.selected_objects]

frameStart = 765
frameEnd = 820

sceneName = 'game-001' + '_' + str(frameStart) + '-' + str(frameEnd)

players = [
    'PUCK',
    'P_LOC_1','P_LOC_2','P_LOC_3','P_LOC_4','P_LOC_5',
    'P_VIS_1','P_VIS_2','P_VIS_3','P_VIS_4','P_VIS_5','P_VIS_6',
]

trackers = [
    'C1',    'C2',    'C3',
    'L11',    'L12',    'L13',    'L14',
    'L21',    'L22',    'L23',    'L24',    'L25',
    'L31',    'L32',    'L33',    'L34',    'L35',    'L36',
    'L41',    'L42',
    'L51',    'L52',    'L53',    'L54',
]

data = {
    'meta':{
       'name': sceneName
    },
    'limits':[5.04488, 2.77683],
    'ref_objects_location': [],
    'ref_players_location': [],
    'ref_objects': trackers,
    'ref_players': players
}

# ANIMATION

for tracker in trackers:

    objData = {
        'name': tracker,
        'coors': [],
    }

    for frameIdx in range(frameStart,frameEnd):

        bpy.context.scene.frame_set(frameIdx)

        obj = bpy.data.objects[tracker]
        loc = obj.location

        x = round(loc.x, 3)
        y = round(loc.y, 3)

        objData['coors'].append([x, y, frameIdx])

    data['ref_objects_location'].append(objData)

for player in players:

    obj = bpy.data.objects[player]

    objData = {
        'name': player,
        'num':  int(obj["NUM"]) if player is not 'PUCK' else '',
        'coors': [],
    }

    for frameIdx in range(frameStart,frameEnd):

        bpy.context.scene.frame_set(frameIdx)

        loc = obj.location

        x = round(loc.x, 2)
        y = round(loc.y, 2)

        objData['coors'].append([x, y, frameIdx])

    data['ref_players_location'].append(objData)

content = f"""
{json.dumps(data, indent=4)}
"""

with open(path + "../data/game-tracking/" + sceneName +".json",'w') as f:
    f.write(content)