import json
import os
import random
from nltk.tokenize import word_tokenize
from keras.preprocessing.sequence import pad_sequences
from collections import Counter
import pandas as pd
import _pickle as pickle
import numpy as np
from nltk.corpus import stopwords
from string import punctuation
from lemmatization import lemmatize
from tqdm import tqdm
from valence import assign_valence
from dependency_parsing import shift_valence, dependency_parse

def load_embeddings(file_name, vocabulary):
    """
    Loads word embeddings from the file with the given name.
    :param file_name: name of the file containing word embeddings
    :type file_name: str
    :param vocabulary: captions vocabulary
    :type vocabulary: numpy.array
    :return: word embeddings
    :rtype: dict
    """
    embeddings = dict()
    with open(file_name, 'r', encoding='utf-8') as doc:
        line = doc.readline()
        while line != '':
            line = line.rstrip('\n').lower()
            parts = line.split(' ')
            vals = np.array(parts[1:], dtype=np.float)
            if parts[0] in vocabulary:
                embeddings[parts[0]] = vals
            line = doc.readline()
    return embeddings

def load_embedding_weights(vocabulary, embedding_size, embedding_source, r1, r2):
    """
    Creates and loads embedding weights.
    :param vocabulary: captions vocabulary
    :type vocabulary: numpy.array
    :param embedding_size: embedding size
    :type embedding_size: int
    :param embedding_source: source of the pre-trained embeddings
    :type embedding_source: string
    :param r1: lower range boundary of reviews
    :type r1: int
    :param r2: upper range boundary of reviews
    :type r2: int
    :return: embedding weights
    :rtype: numpy.array
    """
    assert embedding_source in ['wikipedia', 'twitter']
    if os.path.exists(f'data/embedding_matrix_{embedding_source}_{embedding_size}_{r1}_{r2}.pkl'):
        with open(f'data/embedding_matrix_{embedding_source}_{embedding_size}_{r1}_{r2}.pkl', 'rb') as f:
            embedding_matrix = pickle.load(f)
    else:
        print('Creating embedding weights...')
        if embedding_source == 'wikipedia':
            embeddings = load_embeddings(f'data/glove.6B.{embedding_size}d.txt', vocabulary)
        else:
            embeddings = load_embeddings(f'data/glove.twitter.27B.{embedding_size}d.txt', vocabulary)
        embedding_matrix = np.zeros((len(vocabulary), embedding_size))
        for i in range(len(vocabulary)):
            if vocabulary[i] in embeddings.keys():
                embedding_matrix[i] = embeddings[vocabulary[i]]
            else:
                embedding_matrix[i] = np.random.standard_normal(embedding_size)
        with open(f'data/embedding_matrix_{embedding_source}_{embedding_size}_{r1}_{r2}.pkl', 'wb') as f:
            pickle.dump(embedding_matrix, f)
    return embedding_matrix

def load_word_mappings(vocabulary, r1, r2):
    """
    Loads word_to_id and id_to_word according to the given vocabulary. They are created if they do not exist.
    :param vocabulary: reviews vocabulary
    :type vocabulary: numpy.array
    :param r1: lower range boundary of reviews
    :type r1: int
    :param r2: upper range boundary of reviews
    :type r2: int
    :return: word mappings
    :rtype: dict, dict
    """
    if os.path.exists(f'data/word_to_id_{r1}_{r2}.pkl') and os.path.exists(f'data/id_to_word_{r1}_{r2}.pkl'):
        with open(f'data/id_to_word_{r1}_{r2}.pkl', 'rb') as f:
            id_to_word = pickle.load(f)
        with open(f'data/word_to_id_{r1}_{r2}.pkl', 'rb') as f:
            word_to_id = pickle.load(f)
    else:
        id_to_word = dict()
        word_to_id = dict()
        for i, word in zip(range(len(vocabulary)), vocabulary):
            id_to_word[i + 1] = word
            word_to_id[word] = i + 1
        with open(f'data/id_to_word_{r1}_{r2}.pkl', 'wb') as f:
            pickle.dump(id_to_word, f)
        with open(f'data/word_to_id_{r1}_{r2}.pkl', 'wb') as f:
            pickle.dump(word_to_id, f)
    return word_to_id, id_to_word

def load_train_val_test_subsets(r1, r2):
    if not os.path.exists(f'data/yelp_reviews_train_ids_{r1}_{r2}.csv'):
        review_ids = list()
        with open(f'data/yelp_reviews_filtered_{r1}_{r2}.json', 'r', encoding='utf-8') as doc_r:
            line = doc_r.readline()
            while line != '':
                review_id = json.loads(line)['review_id']
                review_ids.append(review_id)
                line = doc_r.readline()
        random.shuffle(review_ids)
        train_ids = review_ids[:int(0.7 * len(review_ids))]
        val_ids = review_ids[int(0.7 * len(review_ids)):int(0.85 * len(review_ids))]
        test_ids = review_ids[int(0.85 * len(review_ids)):]
        pd.DataFrame(train_ids).to_csv(f'data/yelp_reviews_train_ids_{r1}_{r2}.csv', index=None, header=None)
        pd.DataFrame(val_ids).to_csv(f'data/yelp_reviews_val_ids_{r1}_{r2}.csv', index=None, header=None)
        pd.DataFrame(test_ids).to_csv(f'data/yelp_reviews_test_ids_{r1}_{r2}.csv', index=None, header=None)
    else:
        train_ids = pd.read_csv(f'data/yelp_reviews_train_ids_{r1}_{r2}.csv').get_values().flatten()
        val_ids = pd.read_csv(f'data/yelp_reviews_val_ids_{r1}_{r2}.csv').get_values().flatten()
        test_ids = pd.read_csv(f'data/yelp_reviews_test_ids_{r1}_{r2}.csv').get_values().flatten()
    return train_ids, val_ids, test_ids

def load_sequences(r1, r2, review_ids, word_to_id, pad_size, pad=True):
    with open(f'data/yelp_reviews_lemmas_{r1}_{r2}.pkl', 'rb') as doc_r:
        data = pickle.load(doc_r)
    stars = pd.read_csv(f'data/yelp_reviews_stars_{r1}_{r2}.csv', index_col=[0]).to_dict(orient='index')
    sequences = list()
    classes = list()
    for review_id in review_ids:
        sequences.append(np.array([word_to_id[d] for d in data[review_id] if d in word_to_id.keys()]))
        classes.append(stars[review_id]['0'])
    if pad:
        return pad_sequences(sequences, pad_size), classes
    else:
        return sequences, classes

def tokenize_review(review):
    tokens = word_tokenize(review.lower())
    return tokens

def parse_json_review(review):
    review_json = json.loads(review)
    review_id = review_json['review_id']
    review_text = tokenize_review(review_json['text'])
    # return {'Review_ID': review_id, 'Text': review_text}
    return review_id, review_text

def filter_reviews(file_name):
    frequencies = pd.read_csv('data/user_review_frequencies.csv', sep=',', index_col=0)
    lengths = pd.read_csv('data/reviews_length.csv', sep=',', index_col=0)
    p_25 = np.percentile(lengths.get_values(), 25)
    p_75 = np.percentile(lengths.get_values(), 75)
    total = 0
    filtered_50_500 = 0
    filtered_10_500 = 0
    with open(file_name, 'r', encoding='utf-8') as doc_r, \
            open('data/yelp_reviews_filtered_50_500.json', 'w+', encoding='utf-8') as doc_w1, \
            open('data/yelp_reviews_filtered_10_500.json', 'w+', encoding='utf-8') as doc_w2:
        line = doc_r.readline()
        i = 0
        while line != '':
            i += 1
            if i % 100000 == 0:
                print(str(i))
            review = json.loads(line)
            total += 1
            f = frequencies.loc[review['user_id']].values[0]
            l = lengths.loc[review['review_id']].values[0]
            if f in range(50, 500) and l in range(int(p_25), int(p_75)):
                filtered_50_500 += 1
                doc_w1.write(line)
            if f in range(10, 500) and l in range(int(p_25), int(p_75)):
                filtered_10_500 += 1
                doc_w2.write(line)
            line = doc_r.readline()
    print('Total: ' + str(total))
    print('Filtered 50-500: ' + str(filtered_50_500))
    print('Percentage 50-500: ' + str(filtered_50_500 * 100.0 / total))
    print('Filtered 10-500: ' + str(filtered_10_500))
    print('Percentage 10-500: ' + str(filtered_10_500 * 100.0 / total))

def tokenize_reviews(r1, r2):
    reviews = dict()
    with open(f'data/yelp_reviews_filtered_{r1}_{r2}.json', 'r', encoding='utf-8') as doc_r:
        line = doc_r.readline()
        i = 0
        while line != '':
            i += 1
            if i % 100000 == 0:
                print(str(i))
            review_id, review_text = parse_json_review(line)
            reviews[review_id] = review_text
            line = doc_r.readline()
    with open(f'data/yelp_reviews_tokens_{r1}_{r2}.pkl', 'wb') as doc_w:
        pickle.dump(reviews, doc_w)

def lemmatize_reviews(r1, r2):
    with open(f'data/yelp_reviews_tokens_{r1}_{r2}.pkl', 'rb') as doc_r:
        data = pickle.load(doc_r)
    reviews = dict()
    for _, review_id in zip(tqdm(list(range(len(list(data.keys()))))), list(data.keys())):
        review_tokens = data[review_id]
        review_lemmas = lemmatize(review_tokens)
        reviews[review_id] = review_lemmas
    with open(f'data/yelp_reviews_lemmas_{r1}_{r2}.pkl', 'wb') as doc_w:
        pickle.dump(reviews, doc_w)

def create_reviews_subset(file_name):
    reviews = list()
    s = [0, 0, 0, 0, 0]
    with open(file_name, 'r', encoding='utf-8') as doc:
        line = doc.readline()
        while line != '':
            review = json.loads(line)
            stars = review['stars']
            if s[int(stars) - 1] < 500:
                s[int(stars) - 1] += 1
                review_id = review['review_id']
                review_text = review['text']
                reviews.append({'Review_ID': review_id, 'Text': review_text, 'Stars': stars})
            line = doc.readline()
    with open('data/yelp_reviews_subset.json', 'w+', encoding='utf-8') as doc:
        json.dump(reviews, doc)

def load_vocabulary(r1, r2):
    return pd.read_csv(f'data/vocabulary_{r1}_{r2}.csv', index_col=[0]).get_values().flatten()

def filter_vocabulary(vocab, frequencies):
    allowed_tokens = [f[0] for f in frequencies if f[1] > 15]
    return [w for w in vocab if w in allowed_tokens and w not in stopwords.words('english') and w not in punctuation]

def create_vocabulary(r1, r2):
    with open(f'data/yelp_reviews_lemmas_{r1}_{r2}.pkl', 'rb') as doc_r:
        data = pickle.load(doc_r)
    vocabulary = list()
    for _, review_id in zip(tqdm(list(range(len(list(data.keys()))))), list(data.keys())):
        review_lemmas = data[review_id]
        vocabulary.extend(review_lemmas)
    pd.DataFrame(list(set(vocabulary))).to_csv(f'data/vocabulary_full_{str(r1)}_{str(r2)}.csv')
    frequencies = sorted(Counter(list(vocabulary)).items(), key=lambda x: x[1], reverse=True)
    with open(f'data/vocabulary_full_frequencies_{str(r1)}_{str(r2)}.pkl', 'wb') as f:
        pickle.dump(frequencies, f)
    filtered_vocabulary = filter_vocabulary(list(set(vocabulary)), frequencies)
    pd.DataFrame(list(set(filtered_vocabulary))).to_csv(f'data/vocabulary_{str(r1)}_{str(r2)}.csv')

def assign_valence_vocabulary(r1, r2):
    vocabulary = pd.read_csv(f'data/vocabulary_{str(r1)}_{str(r2)}.csv', index_col=[0]).get_values().flatten()
    valences_nrc = assign_valence(vocabulary, ['afinn', 'nrc-hashtag', 'nrc-vad'])
    pd.DataFrame().from_dict(valences_nrc, orient='index').to_csv(f'data/vocabulary_{str(r1)}_{str(r2)}_val_nrc.csv')
    valences_yelp = assign_valence(vocabulary, ['yelp-sentiment'])
    pd.DataFrame().from_dict(valences_yelp, orient='index').to_csv(f'data/vocabulary_{str(r1)}_{str(r2)}_val_yelp.csv')

def assign_valence_reviews(r1, r2):
    with open(f'data/yelp_reviews_lemmas_{r1}_{r2}.pkl', 'rb') as doc_r:
        data = pickle.load(doc_r)
    val_nrc = pd.read_csv(f'data/vocabulary_{str(r1)}_{str(r2)}_val_nrc.csv', index_col=[0])
    val_y = pd.read_csv(f'data/vocabulary_{str(r1)}_{str(r2)}_val_yelp.csv', index_col=[0])
    lemmas_nrc = dict()
    values_nrc = dict()
    valence_nrc = dict()
    lemmas_yelp = dict()
    values_yelp = dict()
    valence_yelp = dict()
    for _, review_id in zip(tqdm(list(range(len(list(data.keys()))))), list(data.keys())):
        lemmas = list(set(data[review_id]))
        lemmas_list_nrc = sorted([(lemma, round(val_nrc.loc[lemma][0], 5)) for lemma in lemmas
                                  if lemma in val_nrc.index], key=lambda x: x[1], reverse=True)
        valence_nrc[review_id] = lemmas_list_nrc
        lemmas_nrc[review_id] = [x[0] for x in lemmas_list_nrc]
        values_nrc[review_id] = [x[1] for x in lemmas_list_nrc]
        lemmas_list_yelp = sorted([(lemma, round(val_y.loc[lemma][0], 5)) for lemma in lemmas
                                   if lemma in val_y.index], key=lambda x: x[1], reverse=True)
        valence_yelp[review_id] = lemmas_list_yelp
        lemmas_yelp[review_id] = [x[0] for x in lemmas_list_yelp]
        values_yelp[review_id] = [x[1] for x in lemmas_list_yelp]
    with open(f'data/yelp_reviews_lemmas_valence_nrc_{r1}_{r2}.pkl', 'wb') as doc_w:
        pickle.dump(valence_nrc, doc_w)
    pd.DataFrame().from_dict(lemmas_nrc, orient='index').to_csv(
        f'data/yelp_reviews_lemmas_val_nrc_words_{r1}_{r2}.csv')
    pd.DataFrame().from_dict(values_nrc, orient='index').to_csv(
        f'data/yelp_reviews_lemmas_val_nrc_values_{r1}_{r2}.csv')
    with open(f'data/yelp_reviews_lemmas_valence_yelp_{r1}_{r2}.pkl', 'wb') as doc_w:
        pickle.dump(valence_yelp, doc_w)
    pd.DataFrame().from_dict(lemmas_yelp, orient='index').to_csv(
        f'data/yelp_reviews_lemmas_val_yelp_words_{r1}_{r2}.csv')
    pd.DataFrame().from_dict(values_yelp, orient='index').to_csv(
        f'data/yelp_reviews_lemmas_val_yelp_values_{r1}_{r2}.csv')

def parse_dependencies():
    if os.path.exists('data/yelp_reviews_dependencies.pkl'):
        with open('data/yelp_reviews_dependencies.pkl', 'rb') as doc_r:
            dependencies = pickle.load(doc_r)
    else:
        dependencies = dict()
    with open('data/yelp_reviews_filtered_50_500.json', 'r', encoding='utf-8') as doc_r:
        line = doc_r.readline()
        i = 0
        while line != '':
            i += 1
            if i % 10 == 0:
                print(str(i))
            review = json.loads(line)
            review_id = review['review_id']
            if review_id not in dependencies.keys():
                review_text = review['text']
                try:
                    rel = dependency_parse(review_text.replace('/', ' '))
                except AssertionError:
                    rel = []
                    print(review_text)
                    with open('data/yelp_reviews_dependencies.pkl', 'wb') as doc_w:
                        pickle.dump(dependencies, doc_w)
                dependencies[review_id] = rel
                if i % 2000 == 0:
                    with open(f'data/yelp_reviews_dependencies_{i}.pkl', 'wb') as doc_w:
                        pickle.dump(dependencies, doc_w)
            line = doc_r.readline()
    with open('data/yelp_reviews_filtered_10_500.json', 'r', encoding='utf-8') as doc_r:
        line = doc_r.readline()
        i = 0
        while line != '':
            i += 1
            if i % 100 == 0:
                print(str(i))
            review = json.loads(line)
            review_id = review['review_id']
            if review_id not in dependencies.keys():
                review_text = review['text']
                try:
                    rel = dependency_parse(review_text.replace('/', ' '))
                except AssertionError:
                    rel = []
                    print(review_text)
                    with open('data/yelp_reviews_dependencies.pkl', 'wb') as doc_w:
                        pickle.dump(dependencies, doc_w)
                dependencies[review_id] = rel
                if i % 3000 == 0:
                    with open(f'data/yelp_reviews_dependencies_{i}.pkl', 'wb') as doc_w:
                        pickle.dump(dependencies, doc_w)
            line = doc_r.readline()
    with open('data/yelp_reviews_dependencies.pkl', 'wb') as doc_w:
        pickle.dump(dependencies, doc_w)

def context_shift(r1, r2):
    with open(f'data//yelp_reviews_lemmas_valence_nrc_{r1}_{r2}.pkl', 'rb') as doc_r:
        valence_nrc = pickle.load(doc_r)
    with open(f'data//yelp_reviews_lemmas_valence_yelp_{r1}_{r2}.pkl', 'rb') as doc_r:
        valence_yelp = pickle.load(doc_r)
    with open('data/yelp_reviews_dependencies_8000.pkl', 'rb') as doc_r:
        dependencies = pickle.load(doc_r)
    valence_nrc_shifted = dict()
    valence_yelp_shifted = dict()
    with open(f'data/yelp_reviews_filtered_{r1}_{r2}.json', 'r', encoding='utf-8') as doc_r:
        line = doc_r.readline()
        i = 0
        while line != '':
            i += 1
            if i % 50000 == 0:
                print(str(i))
            review = json.loads(line)
            review_id = review['review_id']
            if review_id in valence_nrc.keys() and review_id in dependencies.keys() and len(dependencies[review_id]) > 0:
                review_text = review['text']
                lemmas = [x[0] for x in valence_nrc[review_id]]
                values = [float(x[1]) for x in valence_nrc[review_id]]
                rel = dependencies[review_id]
                val_shift, rel = shift_valence(review_text.replace('/', ' '), lemmas, values, rel)
                valence_nrc_shifted[review_id] = [(l, v) for l, v in zip(lemmas, val_shift)]
                lemmas = [x[0] for x in valence_yelp[review_id]]
                values = [float(x[1]) for x in valence_yelp[review_id]]
                val_shift, rel = shift_valence(review_text.replace('/', ' '), lemmas, values, rel)
                valence_yelp_shifted[review_id] = [(l, v) for l, v in zip(lemmas, val_shift)]
            line = doc_r.readline()
    with open(f'data/yelp_reviews_lemmas_valence_shifted_nrc_{r1}_{r2}.pkl', 'wb') as doc_w:
        pickle.dump(valence_nrc_shifted, doc_w)
    with open(f'data/yelp_reviews_lemmas_valence_shifted_yelp_{r1}_{r2}.pkl', 'wb') as doc_w:
        pickle.dump(valence_yelp_shifted, doc_w)

def create_user_review_frequencies(file_name):
    frequencies = dict()
    with open(file_name, 'r', encoding='utf-8') as doc:
        line = doc.readline()
        i = 0
        while line != '':
            i += 1
            if i % 100000 == 0:
                print(str(i))
            review = json.loads(line)
            user_id = review['user_id']
            if user_id in frequencies.keys():
                frequencies[user_id] += 1
            else:
                frequencies[user_id] = 1
            line = doc.readline()
    pd.DataFrame().from_dict(frequencies, orient='index').to_csv('data/user_review_frequencies.csv')

def calculate_reviews_length(file_name):
    lengths = dict()
    with open(file_name, 'r', encoding='utf-8') as doc:
        line = doc.readline()
        i = 0
        while line != '':
            i += 1
            if i % 100000 == 0:
                print(str(i))
            review = json.loads(line)
            lengths[review['review_id']] = len(tokenize_review(review['text']))
            line = doc.readline()
    pd.DataFrame().from_dict(lengths, orient='index').to_csv('data/reviews_length.csv')

def calculate_review_length_distribution(r1, r2):
    with open(f'data/yelp_reviews_lemmas_{r1}_{r2}.pkl', 'rb') as doc_r:
        data = pickle.load(doc_r)
    vocab = load_vocabulary(r1, r2)
    lengths = dict()
    lengths_filtered = dict()
    for _, k in zip(tqdm(list(range(len(list(data.keys()))))), list(data.keys())):
        lengths[k] = len(data[k])
        lengths_filtered[k] = len([d for d in data[k] if d in vocab])
    pd.DataFrame().from_dict(lengths, orient='index').to_csv(f'data/reviews_length_{r1}_{r2}.csv')
    pd.DataFrame().from_dict(lengths_filtered, orient='index').to_csv(f'data/reviews_length_filtered_{r1}_{r2}.csv')

def create_label_data(r1, r2):
    stars = dict()
    with open(f'data/yelp_reviews_filtered_{r1}_{r2}.json', 'r', encoding='utf-8') as doc_r:
        line = doc_r.readline()
        while line != '':
            review = json.loads(line)
            review_id = review['review_id']
            review_stars = review['stars']
            stars[review_id] = review_stars
            line = doc_r.readline()
    pd.DataFrame().from_dict(stars, orient='index').to_csv(f'data/yelp_reviews_stars_{r1}_{r2}.csv')

if __name__ == '__main__':
    # create_reviews_subset('data/yelp_reviews.json')
    # create_user_review_frequencies('data/yelp_reviews.json')
    # calculate_reviews_length('data/yelp_reviews.json')
    # filter_reviews('data/yelp_reviews.json')
    # create_label_data(50, 500)
    # create_label_data(10, 500)
    # tokenize_reviews(50, 500)
    # tokenize_reviews(10, 500)
    # lemmatize_reviews(50, 500)
    # lemmatize_reviews(10, 500)
    # calculate_review_length_distribution(50, 500)
    # calculate_review_length_distribution(10, 500)
    # create_vocabulary(50, 500)
    # create_vocabulary(10, 500)
    # assign_valence_vocabulary(50, 500)
    # assign_valence_vocabulary(10, 500)
    # assign_valence_reviews(50, 500)
    # assign_valence_reviews(10, 500)
    # parse_dependencies()
    context_shift(50, 500)
    context_shift(10, 500)


import math
from typing import Callable, List, Optional, overload, Union

import numpy as np

import optcom.utils.constants as cst
import optcom.utils.utilities as util
from optcom.parameters.abstract_parameter import AbstractParameter
from optcom.utils.callable_container import CallableContainer

class NLPhaseShift(AbstractParameter):
    r"""Compute the non-linear phase shift. [14]_

    References
    ----------
    .. [14] Wise, F.W., Chong, A. and Renninger, W.H., 2008. High‐energy
            femtosecond fiber lasers based on pulse propagation at
            normal dispersion. Laser & Photonics Reviews, 2(1‐2),
            pp.58-73.

    """

    def __init__(self, nl_index: Union[float, np.ndarray, Callable],
                 eff_area: Union[float, np.ndarray, Callable],
                 power: Union[float, np.ndarray, Callable], dz: float) -> None:
        r"""
        Parameters
        ----------
        nl_index :
            The non linear index. :math:`[m^2\cdot W^{-1}]`  If a
            callable is provided, the variable must be angular
            frequency. :math:`[ps^{-1}]`
        eff_area:
            The effective area. :math:`[\mu m^2]`  If a
            callable is provided, the variable must be angular
            frequency. :math:`[ps^{-1}]`
        power :
            The powers. :math:`[W]`  If a
            callable is provided, the variable must be angular
            frequency. :math:`[ps^{-1}]`
        dz :
            The distance of propagation. :math:`[km]`

        """
        self._nl_index: Union[float, np.ndarray, Callable] = nl_index
        self._eff_area: Union[float, np.ndarray, Callable] = eff_area
        self._power: Union[float, np.ndarray, Callable] = power
        self._dz: float = dz
    # ==================================================================
    @property
    def nl_index(self) -> Union[float, np.ndarray, Callable]:

        return self._nl_index
    # ------------------------------------------------------------------
    @nl_index.setter
    def nl_index(self, nl_index: Union[float, np.ndarray, Callable]) -> None:

        self._nl_index = nl_index
    # ==================================================================
    @property
    def eff_area(self) -> Union[float, np.ndarray, Callable]:

        return self._eff_area
    # ------------------------------------------------------------------
    @eff_area.setter
    def eff_area(self, eff_area: Union[float, np.ndarray, Callable]) -> None:

        self._eff_area = eff_area
    # ==================================================================
    @property
    def power(self) -> Union[float, np.ndarray, Callable]:

        return self._power
    # ------------------------------------------------------------------
    @power.setter
    def power(self, power: Union[float, np.ndarray, Callable]) -> None:

        self._power = power
    # ==================================================================
    @property
    def dz(self) -> float:

        return self._dz
    # ------------------------------------------------------------------
    @dz.setter
    def dz(self, dz: float) -> None:

        self._dz = dz
    # ==================================================================
    @overload
    def __call__(self, omega: float) -> float: ...
    # ------------------------------------------------------------------
    @overload
    def __call__(self, omega: np.ndarray) -> np.ndarray: ...
    # ------------------------------------------------------------------
    def __call__(self, omega):
        r"""Compute the non-linear phase shift.

        Parameters
        ----------
        omega :
            The angular frequency. :math:`[rad\cdot ps^{-1}]`

        Returns
        -------
        :
            The value of the non-linear phase shift.

        """
        fct = CallableContainer(NLPhaseShift.calc_nl_phase_shift,
                                [omega, self._nl_index, self._eff_area,
                                 self._power, self._dz])

        return fct(omega)
    # ==================================================================
    @overload
    @staticmethod
    def calc_nl_phase_shift(omega: float, nl_index: float, eff_area: float,
                            power: float, dz: float) -> float: ...
    # ------------------------------------------------------------------
    @overload
    @staticmethod
    def calc_nl_phase_shift(omega: np.ndarray, nl_index: np.ndarray,
                            eff_area: np.ndarray, power: np.ndarray,
                            dz: float) -> np.ndarray: ...
    # ------------------------------------------------------------------
    @staticmethod
    def calc_nl_phase_shift(omega, nl_index, eff_area, power, dz):
        r"""Calculate the non-linear phase shift.

        Parameters
        ----------
        omega :
            The angular frequency. :math:`[rad\cdot ps^{-1}]`
        nl_index :
            The non linear index. :math:`[m^2\cdot W^{-1}]`
        eff_area:
            The effective area. :math:`[\mu m^2]`
        power :
            The powers. :math:`[W]`
        dz :
            The distance of propagation. :math:`[km]`

        Returns
        -------
        :
            Value of the non-linear phase shift.

        Notes
        -----

        .. math:: \Phi^{NL} = \frac{\omega}{c} n_2 \frac{P}{A_{eff}} dz

        """
        nl_index *=  1e-6  # m^2 W^{-1} -> km^2 W^{-1}
        eff_area *= 1e-18    # um^2 -> km^2
        c = cst.C * 1e-12    # ps/nm -> ps/km

        return (omega/c) * nl_index * (power/eff_area) * dz

if __name__ == "__main__":
    """Plot the non linear phase shift as a function of the wavelength.
    This piece of code is standalone, i.e. can be used in a separate
    file as an example.
    """

    from typing import List

    import numpy as np

    import optcom as oc

    medium: str = "SiO2"
    power: float = 0.01 # W
    dz: float = 1e-5    # km
    # With float
    omega: float = oc.lambda_to_omega(1552.0)
    core_radius: float = 5.0
    n_clad: float = 1.44
    sellmeier: oc.Sellmeier = oc.Sellmeier(medium)
    NA_inst: oc.NumericalAperture = oc.NumericalAperture(sellmeier, n_clad)
    v_nbr_inst: oc.VNumber = oc.VNumber(NA_inst, core_radius)
    eff_area_inst: oc.EffectiveArea = oc.EffectiveArea(v_nbr_inst, core_radius)
    nl_ind_inst: oc.NLIndex = oc.NLIndex(medium)
    nl_phase: oc.NLPhaseShift = oc.NLPhaseShift(nl_ind_inst, eff_area_inst,
                                                power, dz)
    print(nl_phase(omega))
    nl_ind: float = nl_ind_inst(omega)
    eff_area: float = eff_area_inst(omega)
    print(oc.NLPhaseShift.calc_nl_phase_shift(omega, nl_ind, eff_area, power,
                                              dz))
    # With np.ndarray
    lambdas: np.ndarray = np.linspace(900., 1550., 1000)
    omegas: np.ndarray = oc.lambda_to_omega(lambdas)
    res: np.ndarray = nl_phase(omegas)
    x_labels: List[str] = ['Lambda']
    y_labels: List[str] = ['Non-linear phase shift']
    plot_titles: List[str] = ["Non linear phase shift as a function of the "
                              "wavelength \n for Silica core with constant "
                              "cladding refractive index."]

    oc.plot2d([lambdas], [res], x_labels=x_labels, y_labels=y_labels,
              plot_titles=plot_titles, line_opacities=[0.0])

Quantum Cryptography/ML Kalman Filter/ml_kalman/Lib/site-packages/optcom/parameters/fiber/nl_phase_shift.py


#!/usr/bin/env python

'''
Python Implementation : Convert col and row to 0 when a 0 is encountered Algorithm
'''

__author__ = "tchaton"

from ..utils import asssert_func

import numpy as np

def test():
    ''' find 0s in a matrix and replace the col and row to 0s:'''
    m1 = [[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16]]
    asssert_func(find_0_MxN, m1 , [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
    m2 = [[1, 2, 3, 4], [0, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
    asssert_func(find_0_MxN, m2 , [[0, 2, 3, 4], [0, 0, 0, 0], [0, 10, 11, 12], [0, 14, 15, 16]])
    '''
    Print :
    before
    [1, 2, 3, 4]
    [5, 6, 7, 8]
    [9, 10, 11, 12]
    [13, 14, 15, 16]

    after
    [1, 2, 3, 4]
    [5, 6, 7, 8]
    [9, 10, 11, 12]
    [13, 14, 15, 16]

    before
    [1, 2, 3, 4]
    [0, 6, 7, 8]
    [9, 10, 11, 12]
    [13, 14, 15, 16]

    after
    [0, 2, 3, 4]
    [0, 0, 0, 0]
    [0, 10, 11, 12]
    [0, 14, 15, 16]
    '''
def find_0_MxN(matrix):
    def _find_0_MxN(matrix):
        n_row = len(matrix)
        n_col = len(matrix[0])
        pos = []
        print('before')
        for i in range(n_row):
            print(matrix[i])
        for i in range(n_row):
            for j in range(n_col):
                if matrix[i][j] == 0:
                    pos.append([i, j])
        for po in pos:
            i, j = po
            matrix[i] = [0 for i in range(n_col)]
            for i in range(n_row):
                matrix[i][j] = 0
        print()
        print('after')
        for i in range(n_row):
            print(matrix[i])
        print()
        return matrix
    matrix = _find_0_MxN(matrix)
    return matrix


"""Команда добавления новой curve по указанному сету."""
import typing
import re

import settings
from source.commands.Command import Command
from source.Keyfile import Keyfile
from source.keywords.keywords_dispatch_dict import KEYWORDS_DISPATCH_DICT
from source.input_output_interface import get_user_input, \
    get_path_by_file_explorer

class NewCurveBySetCommand(Command):
    """Комманда добавления новой curve по указанному сету."""

    def execute(
        self,
        additional_data: typing.Any,
    ):
        """Метод исполнения команды.

        Args:
            additional_data: именованый кортеж с атрибутами title lsid

        Returns:
            статус, результат команды
        """
        new_curve = KEYWORDS_DISPATCH_DICT['DEFINE_CURVE_TITLE'](
            title=additional_data.title,
            lcid=additional_data.lcid,
        )

        if get_user_input(
                'Использовать текущий кейфайл?(Y/n)',
                required=False,
        ).strip() in ['Y', 'y', 'yes', '']:
            path = settings.CONFIG_FILE.read('keyfile_path')
        else:
            path = get_path_by_file_explorer(
                title='Выбор keyfile',
                filetypes=(('Keyfiles', '*.k'),),
                message='Выберете keyfile с нужным set',
            )
            if not path:
                return True, 'Кейфайл не выбран'

        sid = get_user_input('sid', required_type=int)

        all_shell_ids = []
        with Keyfile(path) as keyfile:
            for keyword in keyfile.keywords:
                if re.match(r'SET_SHELL_LIST', keyword.name):
                    if keyword.sid == sid:
                        for shell_ids in zip(
                            keyword.eid1,
                            keyword.eid2,
                            keyword.eid3,
                            keyword.eid4,
                            keyword.eid5,
                            keyword.eid6,
                            keyword.eid7,
                            keyword.eid8,
                        ):
                            all_shell_ids += [
                                shell_id for shell_id in shell_ids
                                if shell_id != 0
                            ]

        if not all_shell_ids:
            return True, 'В указанном файле не нашлось set shell c ' \
                         'sid={sid}'.format(sid=sid)

        for a1, o1 in enumerate(all_shell_ids, start=1):
            new_curve.a1.append(a1)
            new_curve.o1.append(o1)

        with Keyfile(settings.CONFIG_FILE.read('keyfile_path')) as keyfile:
            keyfile.add(new_curve)

        return True, 'Новая curve добавлена'


import  datetime
from subprocess import call

#datos del usuario
archivo1 = open("datos_usuario.txt","r")
datos = archivo1.readlines()
lista1 = []
for j in datos:
    fix = j.strip().split(":")
    lista1.append(fix)
print("\nDatos personales del usuario en forma lista: ",lista1)
#convertiendo a diccionario
diccionario = dict(lista1)
print("\nDatos personales del usuario en forma diccionario: ",diccionario)

#lista de compras
archivo2 = open("compras.txt","r")
boleta = archivo2.readlines()
lista2 = []
for i in boleta:
    arreglo = i.strip().split(":")
    lista2.append(arreglo)
print("\nLista de su boleta de compras de productos con su precio: ",lista2)

#hora del registro
tiempo = datetime.datetime.now()
#boleta
boleta = open("boleta.txt","w",encoding="utf8")
#titulo
boleta.write(("Boleta de Tu Opcion Saludable").center(60,"="))
#hora del registro
boleta.write(tiempo.strftime("\nFecha: %y-%m-%d \nHora: %H:%M\n"))
#datos en tipo diccionario
boleta.write("\nDatos del usuario: "+str(diccionario)+"\n")
#tabla de lista de canciones con format
boleta.write("\n"+"-"*50+"\n")
cont = 0
suma = 0
boleta.write("{2:>7}{0:^25}{2}{1:^15}{2}\n".format("PRODUCTO","IMPORTE","|"))
boleta.write("-"*50+"\n")
for j in lista2:
    cont += 1
    suma += float(j[1])
    boleta.write("{3}{0:^5}{3:<2}{1:<25}{3:<2}{2:>10,.2f}{3:>4}\n".format(cont,j[0],float(j[1]),"|"))
    boleta.write("-"*50+"\n")
boleta.write("{0:>19}{2:>15}{1:>10,.2f}\n".format("TOTAL",suma,"|"))
boleta.write("-"*50+"\n")

boleta.close()


"""
Adding cryptographic proofs to our blockchain would add an additional layer of security and authenticity. Here's how we can introduce cryptographic hashing and proof of work:

Firstly, we'll need to import the hashlib module for our cryptographic needs. Each block will have a previous_hash attribute to store the hash of the previous block, and we'll create a hash method to calculate a block's hash based on its content. We'll also introduce a proof attribute and a method to calculate a proof of work.

Proof of work is a concept used in blockchains to prevent spam and abuse. It requires participants to do some work, typically solving a difficult puzzle, in order to add blocks. The most commonly used form is a hash puzzle, where the goal is to find a number that, when hashed together with the rest of the block, produces a hash with a certain number of leading zeroes. This is hard to calculate but easy to verify, providing a basic level of security for the blockchain.

In this version of the code, each block includes a proof of work, and the add_block function will not add a block until it has found a valid proof of work. The is_chain_valid function also checks whether each block's previous_hash matches the hash of the previous block, providing additional verification of the blockchain's integrity.

The difficulty level for the proof of work puzzle is set to 4, which means it requires finding a number that produces a hash with 4 leading zeroes. This might take a few seconds or more to calculate depending on your machine's capabilities.

This is still a simplified version of a blockchain. Real blockchains like Bitcoin use similar concepts, but with more complex and secure mechanisms for adding blocks, verifying transactions, handling multiple participants, and more.
"""

import hashlib
import json
from time import time

class Transaction:
    def __init__(self, from_address, to_address, amount):
        self.from_address = from_address
        self.to_address = to_address
        self.amount = amount

    def to_dict(self):
        return {
            'from_address': self.from_address,
            'to_address': self.to_address,
            'amount': self.amount,
        }

class Block:
    def __init__(self, transactions, previous_hash):
        self.timestamp = time()
        self.transactions = transactions
        self.previous_hash = previous_hash
        self.proof = 0

    def hash(self):
        """
        Here's how the hash method works:

        1. It creates a copy of the block's dictionary (self.__dict__), which includes all its properties
           (timestamp, transactions, previous hash, and proof).
        2. It replaces the list of Transaction objects with a list of dictionaries representing each
           transaction. This is done with a list comprehension ([tx.to_dict() for tx in self.transactions]),
           which calls to_dict on each transaction (tx) in the block's transactions (self.transactions).
        3. It converts the modified dictionary to a JSON string, sorts the keys to ensure consistency, and
           encodes it to bytes.
        4. It hashes the bytes using SHA-256 and returns the hexadecimal representation of the hash.
        """
        block_dict = self.__dict__.copy()
        block_dict['transactions'] = [tx.to_dict() for tx in self.transactions]
        block_string = json.dumps(block_dict, sort_keys=True)
        return hashlib.sha256(block_string.encode()).hexdigest()

class Blockchain:
    def __init__(self):
        self.blocks = [self.genesis_block()]

    def genesis_block(self):
        transactions = [Transaction("System", "Alice", 100), Transaction("System", "Bob", 100), Transaction("System", "Charlie", 100)]
        genesis_block = Block(transactions, "0")
        genesis_block.proof = self.proof_of_work(genesis_block)
        return genesis_block

    def add_block(self, transactions):
        if not self.are_transactions_valid(transactions):
            print("Invalid transactions")
            return
        previous_hash = self.blocks[-1].hash()
        new_block = Block(transactions, previous_hash)
        # new_block.proof = self.proof_of_work(new_block)
        start = time()  # Start timing
        new_block.proof = self.proof_of_work(new_block, difficulty=7)  # Increase difficulty
        end = time()  # End timing

        print(f"Proof of work solved in {end - start} seconds")
        self.blocks.append(new_block)

    def calculate_balances(self, pending_transactions=None):
        balances = {}
        for block in self.blocks:
            for transaction in block.transactions:
                if transaction.from_address not in balances:
                    balances[transaction.from_address] = 0
                if transaction.to_address not in balances:
                    balances[transaction.to_address] = 0
                balances[transaction.from_address] -= transaction.amount
                balances[transaction.to_address] += transaction.amount
        if pending_transactions:
            for transaction in pending_transactions:
                if transaction.from_address not in balances:
                    balances[transaction.from_address] = 0
                if transaction.to_address not in balances:
                    balances[transaction.to_address] = 0
                balances[transaction.from_address] -= transaction.amount
                balances[transaction.to_address] += transaction.amount
        return balances

    def are_transactions_valid(self, transactions):
        balances = self.calculate_balances(transactions)
        for transaction in transactions:
            if transaction.from_address == 'System':
                continue
            from_balance = balances.get(transaction.from_address, 0)
            if from_balance < transaction.amount:
                return False
        return True

    def proof_of_work(self, block, difficulty=4):
        block.proof = 0
        while not block.hash()[:difficulty] == "0" * difficulty:
            block.proof += 1
        return block.proof

    def is_chain_valid(self):
        for i in range(1, len(self.blocks)):
            current = self.blocks[i]
            previous = self.blocks[i-1]
            if current.hash() != current.hash():
                return False
            if current.previous_hash != previous.hash():
                return False
        return True

# Create a new Blockchain
blockchain = Blockchain()

# Add some blocks with transactions
blockchain.add_block([Transaction("Alice", "Bob", 50), Transaction("Bob", "Charlie", 25)])
blockchain.add_block([Transaction("Charlie", "Alice", 10), Transaction("Alice", "Bob", 5)])

# Verify the blockchain is valid
print(blockchain.is_chain_valid())  # Should return True

# Check the balances
print(blockchain.calculate_balances())

# Let's tamper with the second block (third, if you count the genesis block)
blockchain.blocks[2].transactions[0].amount = 1000

# Now the blockchain should be invalid
print(blockchain.is_chain_valid())  # Should return False


"""
Exports analysis data from IDA to a bnida json file
"""

import ida_struct
import ida_kernwin
import ida_segment
import ida_bytes
import idautils
import ida_funcs
import idaapi
import json
from collections import OrderedDict

def get_single_comment(regular, repeatable):
    """
    IDA has repeatable and regular comments. BN only has regular comments.
    This function constructs a single comment from both repeatable and regular
    comments
    """

    if repeatable is None and regular is None:
        return None
    elif repeatable is not None and regular is not None:
        return regular + '\n' + repeatable
    elif repeatable is not None and regular is None:
        return repeatable
    elif regular is not None and repeatable is None:
        return regular

def get_single_function_comment(ea):
    """
    Get function comment

    :param ea: Function offset
    :return: comment string or None
    """

    func = ida_funcs.get_func(ea)
    regular = ida_funcs.get_func_cmt(func, False)
    repeatable = ida_funcs.get_func_cmt(func, True)
    return get_single_comment(regular, repeatable)

def get_single_line_comment(ea):
    """
    Get line comment

    :param ea: Function offset
    :return: Comment string or None
    """

    regular = ida_bytes.get_cmt(ea, False)
    repeatable = ida_bytes.get_cmt(ea, True)
    cmt = get_single_comment(regular, repeatable)
    return cmt

def get_function_comments():
    """
    Get function comments from IDA database

    :return: Dict containing function comments
    """

    comments = {}
    for ea in idautils.Functions():
        comment = get_single_function_comment(ea)
        if comment:
            comments[ea] = comment

    return comments

def get_functions():
    """
    Get function start addresses

    :return: Array containing function addresses
    """

    func_addrs = []
    for ea in idautils.Functions():
        func_addrs.append(ea)

    return func_addrs

def get_line_comments():
    """
    Iterate through every address in a segment and check for comments

    :return: Dict containing line comments
    """

    last_comment = ''
    comments = {}
    for ea in idautils.Segments():
        segm = ida_segment.getseg(ea)
        name = ida_segment.get_segm_name(segm)
        if name == 'LOAD':
            continue

        for i in range(segm.start_ea, segm.end_ea):
            comment = get_single_line_comment(i)
            if comment and comment != last_comment:
                comments[i] = comment
                last_comment = comment

    return comments

def get_names():
    """
    Get symbols from IDA database

    :return: Dict containing symbol information
    """

    symbols = {}
    for addr, name in idautils.Names():
        symbols[addr] = name

    return symbols

def get_sections():
    """
    Get section names and start/end addrs from IDA database

    :return: Dict containing section info
    """

    sections = {}
    for ea in idautils.Segments():
        segm = ida_segment.getseg(ea)
        name = ida_segment.get_segm_name(segm)
        if name == 'LOAD':
            continue

        curr = {}
        curr['start'] = segm.start_ea
        curr['end'] = segm.end_ea
        sections[name] = curr

    return sections

def get_member_type(struct, idx):
    """
    Retrieve the type information for the struct member

    :return: Type string
    """

    member = ida_struct.get_member(struct, idx)
    tif = idaapi.tinfo_t()
    ida_struct.get_member_tinfo(tif, member)
    elements = str(tif).split(' ')
    typ = None
    if len(elements) == 2 and elements[0] == 'unsigned':
        if elements[1] == '__int8':
            typ = 'uint8_t'
        elif elements[1] == '__int16':
            typ = 'uint16_t'
        elif elements[1] == '__int32':
            typ = 'uint32_t'
        elif elements[1] == '__int64':
            typ = 'uint64_t'
        elif elements[1] != '':
            typ = elements[1]
    elif len(elements) == 1:
        if elements[0] == '__int8':
            typ = 'int8_t'
        elif elements[0] == '__int16':
            typ = 'int16_t'
        elif elements[0] == '__int32':
            typ = 'int32_t'
        elif elements[0] == '__int64':
            typ = 'int64_t'
        elif elements[0] != '':
            typ = str(tif)

    return typ

def get_struct_members(struct, sid):
    """
    Get members belonging to a structure by structure ID

    :param struct: Structure object
    :param sid: Structure ID
    :return: Dict containing structure member information
    """

    members = {}
    for offset, name, size in idautils.StructMembers(sid):
        members[name] = {}
        typ = get_member_type(struct, offset)
        if typ:
            members[name]['type'] = typ
        else:
            # Type isn't set so make it a byte array
            members[name]['type'] = 'uint8_t [{}]'.format(size)
        members[name]['offset'] = offset
        members[name]['size'] = size

    return members

def get_structs():
    """
    Get structures from IDA database

    :return: Dict containing structure info
    """

    structs = OrderedDict()
    for idx, sid, name in idautils.Structs():
        struct = ida_struct.get_struc(sid)
        structs[name] = {}
        structs[name]['size'] = ida_struct.get_struc_size(struct)
        structs[name]['members'] = get_struct_members(struct, sid)

    return structs

def main(json_file):
    """
    Construct a json file containing analysis data from an IDA database

    :param json_file: Output JSON file name
    """

    json_array = {}
    print('[*] Exporting analysis data to {}'.format(json_file))
    json_array['sections'] = get_sections()
    json_array['functions'] = get_functions()
    json_array['func_comments'] = get_function_comments()
    json_array['line_comments'] = get_line_comments()
    json_array['names'] = get_names()
    json_array['structs'] = get_structs()
    print('[+] Done exporting analysis data')

    with open(json_file, 'w') as f:
        f.write(json.dumps(json_array, indent=4))

if __name__ == '__main__':
    main(ida_kernwin.ask_file(1, '*.json', 'Export file name'))


import os

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

# 设置中文字体和负号正常显示
from util.common_function import load_json_file

def draw_best_model_hist(best_model_json_path, pic_save_dir):
    matplotlib.rcParams['font.sans-serif'] = ['SimHei']
    matplotlib.rcParams['axes.unicode_minus'] = False

    best_model_info = load_json_file(best_model_json_path)
    afm_model_info = best_model_info["afm_model"]
    sem_model_info = best_model_info["sem_model"]
    saxs_model_info = best_model_info["saxs_model"]
    waxd_model_info = best_model_info["waxd_model"]

    plt.figure(figsize=(8, 5))
    data_type_list = ['AFM', 'SEM', 'SAXS', 'WAXD']  # 横坐标刻度显示值
    accuracy_list = [afm_model_info["best_accuracy"],
                     sem_model_info["best_accuracy"],
                     saxs_model_info["best_accuracy"],
                     waxd_model_info["best_accuracy"]]  # 纵坐标值1
    val_accuracy_list = [afm_model_info["best_val_accuracy"],
                         sem_model_info["best_val_accuracy"],
                         saxs_model_info["best_val_accuracy"],
                         waxd_model_info["best_val_accuracy"]]  # 纵坐标值2
    x = range(len(accuracy_list))
    accuracy_list = np.around(accuracy_list, decimals=4)
    val_accuracy_list = np.around(val_accuracy_list, decimals=4)

    rects1 = plt.bar(x=x, height=accuracy_list, width=0.4, alpha=0.8, label="Accuracy")
    rects2 = plt.bar(x=[i + 0.4 for i in x], height=val_accuracy_list, width=0.4, label="Val Accuracy")
    plt.ylim(0, 1.09)  # y轴取值范围
    plt.ylabel("准确率")

    plt.xticks([index + 0.2 for index in x], data_type_list)
    plt.xlabel("谱图类型")
    plt.title("最佳模型准确率")
    plt.legend(loc='lower right')  # 设置题注
    # 编辑文本
    for rect in rects1:
        height = rect.get_height()
        plt.text(rect.get_x() + rect.get_width() / 2, height + 0.01, str(height), ha="center", va="bottom")
    for rect in rects2:
        height = rect.get_height()
        plt.text(rect.get_x() + rect.get_width() / 2, height + 0.01, str(height), ha="center", va="bottom")
    plt.gcf().subplots_adjust(left=0.08)
    plt.gcf().subplots_adjust(right=0.95)
    plt.gcf().subplots_adjust(top=0.92)
    # plt.show()
    pic_save_path = os.path.join(pic_save_dir, "best_model_hist.png")
    plt.savefig(pic_save_path, format='png', transparent=True)

if __name__ == '__main__':
    draw_best_model_hist("../best_model.json", "../images")


import asyncio
import uuid

from api.requests import ReportRequest
from api.responses import (ReportResponse, TaskStatusesListResponse,
                           TaskStatusResponse)
from choices import TaskStatuses
from db.clients import TasksClient
from db.collections import Task
from fastapi import HTTPException
from fastapi.requests import Request
from taskiq import AsyncBroker, AsyncTaskiqDecoratedTask

async def create_report(request: Request, report_request: ReportRequest):
    task_name = 'tasks.calls:aggregate_calls'
    broker: AsyncBroker = request.app.broker
    aggregate_calls: AsyncTaskiqDecoratedTask | None = broker.find_task(task_name)
    if aggregate_calls is None:
        raise NotImplementedError("Task aggregate_calls does not exist")
    task = Task(
        task_name=task_name,
        task_id=str(uuid.uuid4()),
        task_status=TaskStatuses.PENDING,
    )
    tasks_client: TasksClient = request.app.tasks_client
    save_in_db_task = asyncio.create_task(
        tasks_client.save_or_update_task(
            task_id=task.task_id,
            task_data=task,
        ),
    )
    await aggregate_calls.kicker().with_task_id(task.task_id).kiq(
        numbers=report_request.numbers,
        message_from='client_api',
        correlation_id=report_request.correlation_id,
    )
    await save_in_db_task
    return TaskStatusResponse(
        task_status=task.task_status,
        task_id=task.task_id,
        task_name=task_name,
        received=task.received,
    )

async def get_all_tasks_statuses(request: Request):
    tasks_client: TasksClient = request.app.tasks_client
    statuses = await tasks_client.get_statuses()
    return TaskStatusesListResponse(statuses=statuses)

async def get_task_status(request: Request, task_id: str):
    tasks_client: TasksClient = request.app.tasks_client
    task = await tasks_client.get_task_by_id(task_id)
    if task is None:
        raise HTTPException(status_code=404, detail="Task not found")
    return TaskStatusResponse(**task.model_dump())

async def get_report_task_result(request: Request, task_id):
    tasks_client: TasksClient = request.app.tasks_client
    task = await tasks_client.get_task_by_id(task_id)
    if task is None:
        raise HTTPException(status_code=404, detail="Task not found")
    if task.task_status != TaskStatuses.COMPLETED or task.result is None:
        raise HTTPException(status_code=400, detail="Result is not ready")
    return ReportResponse(**task.result)


from snake.snake_game import SnakeGame, Direction, Point, BLOCK_SIZE
import pygame
import random
from model import DateSet, DQL
from visualize_data import plot

###############################################################################
# settings
BATCH_SIZE = 1000

###############################################################################
# help function
def get_direction_index(direction):
    counter_clock_wise = [
        Direction.RIGHT, Direction.UP, Direction.LEFT, Direction.DOWN
    ]  # 逆时针排列
    return counter_clock_wise.index(direction)

def get_direction_by_index(index):
    counter_clock_wise = [
        Direction.RIGHT, Direction.UP, Direction.LEFT, Direction.DOWN
    ]  # 逆时针排列
    return counter_clock_wise[index]

class SnakeAgent:
    def __init__(self):
        self.game = None
        self.model = DQL(11, 256, 3)
        self.data_set = DateSet()
        self.trains_num = 0
        # 可视化数据
        self.width = 640
        self.height = 480
        self.right_paddle = 150
        self.total_score = 0
        self.score_list = []
        self.mean_score_list = []
        self.max_score = 0

    def get_state(self, game_data):
        head = game_data['head']
        food = game_data['food']
        point_l = Point(head.x - BLOCK_SIZE, head.y)
        point_r = Point(head.x + BLOCK_SIZE, head.y)
        point_u = Point(head.x, head.y - BLOCK_SIZE)
        point_d = Point(head.x, head.y + BLOCK_SIZE)
        counter_clock_wise_pos = [point_r, point_u, point_l, point_d]
        # world
        world_msg = [False, False, False, False]
        cur_dir_index = get_direction_index(game_data['action'])
        world_msg[cur_dir_index] = True  # 当前位置为True
        # danger
        # # straight
        nxt_dir_idx = cur_dir_index
        nxt_dir_pos = counter_clock_wise_pos[nxt_dir_idx]
        danger_straight = self.game.is_collision(nxt_dir_pos)
        # # right
        nxt_dir_idx = (cur_dir_index - 1) % 4
        nxt_dir_pos = counter_clock_wise_pos[nxt_dir_idx]
        danger_right = self.game.is_collision(nxt_dir_pos)
        # # left
        nxt_dir_idx = (cur_dir_index + 1) % 4
        nxt_dir_pos = counter_clock_wise_pos[nxt_dir_idx]
        danger_left = self.game.is_collision(nxt_dir_pos)

        danger_msg = [danger_straight, danger_right, danger_left]

        # food
        food_msg = [
            food.x > head.x,  # right
            food.y < head.y,  # up
            food.x < head.x,  # left
            food.y > head.y,  # down
        ]

        state = []
        state.extend(danger_msg)
        state.extend(world_msg)
        state.extend(food_msg)
        return state

    def get_action(self, state):
        epsilon = 100 - self.trains_num
        action = [False, False, False]
        if random.randint(0, 200) < epsilon:
            action[random.randint(0, 2)] = True
        else:
            action = self.model.predict(state)
        return action

    def interpret_action(self, action, params):
        pre_idx = get_direction_index(params['action'])
        if action[0]:  # straight
            return get_direction_by_index(pre_idx)
        elif action[1]:  # right
            return get_direction_by_index((pre_idx - 1) % 4)
        elif action[2]:  # left
            return get_direction_by_index((pre_idx + 1) % 4)

    def get_feedback(self, game_data):
        return {
            'reward': self.reward,
            'state': self.get_state(game_data),
            'game_over': game_data['game_over'],
        }

    def train_whole(self, data_set):
        """回顾整个人生历程"""
        recall_data_set = None
        if len(data_set.data) > BATCH_SIZE:
            recall_data_set = random.sample(data_set.data, BATCH_SIZE)
        else:
            recall_data_set = data_set.data
        state, action, reward, next_state, game_over = zip(*recall_data_set)
        self.model.train_whole(state, action, reward, next_state, game_over)

    def train_short(self, data_set):
        """走一步看一步的策略"""
        state, action, reward, next_state, game_over = data_set.cur_data
        self.model.train_short(state, action, reward, next_state, game_over)

    def play(self):
        self.game = SnakeGame(self.AI_hook, self.width, self.height,
                              self.right_paddle)
        while True:
            self.game.reset()
            self.game.start()
            self.train_whole(self.data_set)

    def AI_hook(self, msg, params):
        # Ai 主题逻辑
        if msg == 'msg_loop_start':
            self.state = self.get_state(params)
        elif msg == 'msg_game_action':
            self.action = self.get_action(self.state)
            params['action'] = self.interpret_action(self.action, params)
        elif msg == 'msg_loop_end':
            self.feedback = self.get_feedback(params)
            self.data_set.record(self.state, self.action, self.feedback)
            self.train_short(self.data_set)
        # 退出AI
        if msg == 'msg_game_input':
            for event in params['input']:
                if event.type == pygame.QUIT:
                    exit()
        # 采集数据
        # # 设置奖励
        # # 训练次数
        if msg == 'msg_loop_start':
            self.reward = 0
        elif msg == 'msg_snake_eat_food':
            self.reward = 10
        elif msg == 'msg_snake_hit_boundary':
            self.reward = -10
        elif msg == 'msg_snake_hit_self':
            self.reward = -10
        elif msg == 'msg_game_over':
            self.trains_num += 1
        elif msg == 'msg_train_too_long':
            self.reward = -10
        # 补充
        if msg == 'msg_game_reset':
            self.loop_num = 0
        elif msg == 'msg_loop_end':
            self.loop_num += 1
            if self.loop_num > 100 * len(params['snake']):
                self.AI_hook('msg_train_too_long', params)  # 发送消息
                params['game_over'] = True
        # 可视化数据
        if msg == 'msg_game_ui_before':
            # action
            font_color = (0, 255, 0)
            if self.action[0]:
                str_action = self.game.font.render('straight', True,
                                                   font_color)
                self.game.display.blit(str_action, (self.width + 2, 0))
            elif self.action[1]:
                str_action = self.game.font.render('turn right', True,
                                                   font_color)
                self.game.display.blit(str_action, (self.width + 2, 0))
            elif self.action[2]:
                str_action = self.game.font.render('turn left', True,
                                                   font_color)
                self.game.display.blit(str_action, (self.width + 2, 0))
            # direction
            str_dir_list = ['right', 'up', 'left', 'down']
            str_dir_idx = get_direction_index(params['action'])
            str_dir = self.game.font.render(str_dir_list[str_dir_idx], True,
                                            font_color)
            self.game.display.blit(str_dir, (self.width + 2, 20))
            # danger
            if self.state[0]:
                str_danger = self.game.font.render('danger straight', True,
                                                   font_color)
                self.game.display.blit(str_danger, (self.width + 2, 40))
            if self.state[1]:
                str_danger = self.game.font.render('danger right', True,
                                                   font_color)
                self.game.display.blit(str_danger, (self.width + 2, 60))
            if self.state[2]:
                str_danger = self.game.font.render('danger left', True,
                                                   font_color)
                self.game.display.blit(str_danger, (self.width + 2, 80))
            # food pos
            if self.state[-4]:
                str_food = self.game.font.render('food right', True,
                                                 font_color)
                self.game.display.blit(str_food, (self.width + 2, 100))
            if self.state[-3]:
                str_food = self.game.font.render('food up', True, font_color)
                self.game.display.blit(str_food, (self.width + 2, 120))
            if self.state[-2]:
                str_food = self.game.font.render('food left', True, font_color)
                self.game.display.blit(str_food, (self.width + 2, 140))
            if self.state[-1]:
                str_food = self.game.font.render('food down', True, font_color)
                self.game.display.blit(str_food, (self.width + 2, 160))
            # reward
            str_reward = self.game.font.render('reward:' + str(self.reward),
                                               True, font_color)
            self.game.display.blit(str_reward, (self.width + 2, 180))
        elif msg == 'msg_game_over':
            self.total_score += params['score']
            self.score_list.append(params['score'])
            self.mean_score_list.append(self.total_score / self.trains_num)
            plot(self.score_list, self.mean_score_list)
            # 储存模型
            if self.max_score < params['score']:
                self.max_score = params['score']
                self.model.save()
            print('Game', self.trains_num, 'Score', self.max_score)

if __name__ == '__main__':
    AI = SnakeAgent()
    AI.play()


from jinja2 import StrictUndefined
from flask import jsonify
from flask import (Flask, render_template, redirect, request, flash,
                   session, g)

import json

import requests

import os

from calworks import *

import ast

app = Flask(__name__)
app.secret_key = "ABC"

COUNTIES = ['Alameda', 'Alpine', 'Amador', 'Butte', 'Calaveras', 'Colusa',
'Contra Costa', 'Del Norte', 'El Dorado', 'Fresno', 'Glenn', 'Humboldt',
'Imperial', 'Inyo', 'Kern', 'Kings', 'Lake', 'Lassen', 'Los Angeles',
'Madera', 'Marin', 'Mariposa', 'Mendocino', 'Merced', 'Modoc', 'Mono',
'Monterey', 'Napa', 'Nevada', 'Orange', 'Placer', 'Plumas', 'Riverside',
'Sacramento', 'San Benito', 'San Bernardino', 'San Diego', 'San Francisco',
'San Joaquin', 'San Luis Obispo', 'San Mateo', 'Santa Barbara', 'Santa Clara',
'Santa Cruz', 'Shasta', 'Sierra', 'Siskiyou', 'Solano', 'Sonoma', 'Stanislaus',
'Sutter', 'Tehama', 'Trinity', 'Tulare', 'Tuolumne', 'Ventura', 'Yolo', 'Yuba']

JS_TESTING_MODE = False

@app.before_request
def add_tests():
    g.jasmine_tests = JS_TESTING_MODE

@app.route('/')
def show_homepage():
	"""Displays homepage"""

	return render_template("homepage.html")

@app.route('/intake_form')
def shot_intake_form():
	"""DIsplays intial form to be completed"""

	return render_template('intake_form.html', counties = COUNTIES)

@app.route('/passed_gross_income_test', methods=["POST"])
def check_gross_income():
	"""Checks if family passes gross_income_test and redirects user accordingly"""
	try:
		total_income = int(request.form.get('income'))
		fam_members = int(request.form.get('fammembers'))
		emp_fam_members = int(request.form.get('empmembers'))
		county_of_res = request.form.get('county')
	except ValueError:
		return redirect('/error')

	if not fam_members or county_of_res == 'other':
		return redirect('/error')

	if gross_income_test(total_income, fam_members, emp_fam_members, county_of_res):
		return render_template("fam_form.html", county= county_of_res, fam_mems_total=fam_members)
	else:
		return redirect("/sorry")

@app.route('/passed_gross_income_test', methods=["GET"])
def show_default_form():
	"""Displays generic family form"""

	return render_template("fam_form.html", county= None, fam_mems_total=1, counties = COUNTIES)

@app.route('/calc_grant', methods=["POST"])
def cal_grant():
	"""Calculates family grant and returns results"""

	family = request.form.get('family')
	try:
		family = ast.literal_eval(family)
	except:
		return redirect("/error")

	county = request.form.get('county').replace("-", " ")

	result = pass_section_a(family, county)
	if result:
		grant = grant_computation(family, county, result)
		grant = '{:20,.2f}'.format(grant)
		return render_template('grant.html', grant=grant)
	else:
		return redirect ('/sorry')

@app.route('/error')
def show_error_message():
	"""Diaplays page with error message"""

	return render_template("oops.html")

@app.route('/sorry')
def show_sorry_message():
	"""Diaplays page with sorry message"""

	return render_template("sorry.html")

if __name__ == "__main__":

	import sys
	if sys.argv[-1] == "jstest":
		JS_TESTING_MODE = True

	app.run(debug=True)
	app.debug = True
	app.run(port=5000, host='0.0.0.0')


#%%
import pickle
import numpy as np
import matplotlib.pyplot as plt
import git
import sys
sys.path.append('Josh/Clustering')
from kMeansClustering import Kmeans
import time
import os
repo=git.Repo('.', search_parent_directories=True)
cwd=repo.working_dir
os.chdir(cwd)

File=open('Josh/Processing/Processed Data/GeoDict.pkl','rb')
GeoDict=pickle.load(File)
vals=[val for val in GeoDict.values()]
for i,val in enumerate(vals):
    if (np.isnan(val)).any():
        del vals[i]
vals=np.array(vals)
Output=[]
# ks=np.arange(30,100,5)
ks=np.arange(10,500,5)
for k in ks:
# for k in [1000]:
    print(k)
    clust,cdist=Kmeans(k,vals)
    Output.append([k,len(np.unique(clust)),cdist])
Output=np.array(Output)
np.savetxt('Josh/Clustering/ClusterDataSqrd.txt',Output)

# %%


import logging
from math import ceil
from loader import dp, bot

from aiogram.types import CallbackQuery, ContentType, ReplyKeyboardRemove, Message
from aiogram import types
from keyboards.default import keyboard_library, keyboard_almaushop, keyboard_feedback, \
    keyboard_send_phone_to_register_in_db, always_stay_menu_keyboard
from keyboards.inline import main_faq_callback, inline_keyboard_menu, inline_keyboard_schedule, \
    inline_keyboard_main_faq, inline_keyboard_main_faq_back, inline_keyboard_certificate, schedule_callback, \
    inline_keyboard_nav_unifi

# Импорт текстовых кнопок
from data.button_names.main_menu_buttons import main_menu_def_buttons, schedule_button_text, faq_button_text, \
    library_button_text, \
    shop_button_text, calendar_button_text, certificate_button_text, feedback_button_text, navigation_button_text, \
    to_main_menu_button
# Импортирование функций из БД контроллера
from utils import db_api as db
from utils.delete_inline_buttons import delete_inline_buttons_in_dialogue

from utils.misc import rate_limit
from states.register_user_phone import RegisterUserPhone

from aiogram.dispatcher import FSMContext

from utils.get_linenumber import get_linenumber

_main_menu_text = 'Главное меню:\n' \
                  '- Расписание - просмотр актуального расписания\n' \
                  '- FAQ - часто задаваемые вопросы студентов и ответы на них\n' \
                  '- Библиотека - поиск книг\n' \
                  '- AlmaU Shop - просмотр мерча AlmaU и книг\n' \
                  '- Академический календарь - получение календаря на учебный год\n' \
                  '- Обратная связь с ректором - возможность написать свою жалобу или пожелания ректору\n'

# Настройка команд для бота
@dp.message_handler(commands="set_commands", state="*")
async def cmd_set_commands(message: types.Message):
    logging.info(f"User({message.chat.id}) использовал команду 'set_commands'")
    role = await db.check_role_by_id(message.chat.id)
    if role == 'admin':
        commands = [types.BotCommand(command="/menu", description="главное меню"),
                    types.BotCommand(command="/help", description="помощь"),
                    types.BotCommand(command="/admin", description="админ меню"),
                    # types.BotCommand(command="/set_commands", description="установка команд бота"),
                    types.BotCommand(command="/cancel", description="выход из диалога с ботом")
                    ]
        await bot.set_my_commands(commands)
        await message.answer("Команды настроены.")
    else:
        await bot.send_message(message.chat.id, 'Недостаточный уровень доступа')
        logging.info(f'User({message.chat.id}) попытался использовать команду set_commands')

@rate_limit(3, 'menu_old')
@dp.message_handler(commands=['menu_old'])
async def menu_handler(message):
    logging.info(f"User({message.chat.id}) вошел в меню")
    await bot.send_message(message.chat.id, _main_menu_text,
                           reply_markup=inline_keyboard_menu())

@rate_limit(5, 'menu')
@dp.message_handler(commands=['menu'])
async def menu_handler(message):
    await db.add_bot_log(message.chat.id, message.text, f"{__name__}.py [LINE:{get_linenumber()}]")
    logging.info(f"User({message.chat.id}) вошел в меню")
    await bot.send_message(message.chat.id, 'Главное меню ↘',
                           reply_markup=always_stay_menu_keyboard())

################# Регистрация номера в таблицу Users ###########################
@rate_limit(3, 'phone_reg')
@dp.message_handler(commands=['phone_reg'])
async def register_user_phone(message):
    logging.info(f"User({message.chat.id}) начал регистрацию номера телефона")
    await message.answer("Отправьте свой номер телефона", reply_markup=keyboard_send_phone_to_register_in_db())
    await RegisterUserPhone.phone.set()

@dp.message_handler(content_types=ContentType.ANY, state=RegisterUserPhone.phone)
async def register_user_phone_next(message: types.Message, state: FSMContext):
    await db.add_bot_log(message.chat.id, message.text, f"{__name__}.py [LINE:{get_linenumber()}]")
    if message.content_type == 'contact':
        if message.chat.id == message.contact.user_id:
            logging.info(f"User({message.chat.id}) ввел правильный номер {message.contact.phone_number}")
            await message.reply("✅ Номер телефона получен, и успешно зарегистрирован",
                                reply_markup=always_stay_menu_keyboard())
            phone = message.contact.phone_number
            if phone.startswith("+"):
                phone = phone
            else:
                phone = f"+{phone}"
            await db.register_user_phone(message.chat.id, phone)
            await state.reset_state()
        else:
            logging.info(f"User({message.chat.id}) ввел не правильный номер")
            await message.answer("Вы отправили не свой номер", reply_markup=ReplyKeyboardRemove())
            await message.answer("Повторите отправку номера с помощью кнопки ниже",
                                 reply_markup=keyboard_send_phone_to_register_in_db())
    else:
        if message.text == '❌ Отмена регистрации':
            logging.info(f"User({message.chat.id}) отменил регистрацию номера телефона")
            await message.answer('Отмена регистрации номера телефона.\n'
                                 'Возвращение в главное меню', reply_markup=always_stay_menu_keyboard())
            await state.reset_state()
        else:
            await message.answer("Используйте кнопки внизу экрана, для отправки номера или отмены",
                                 reply_markup=keyboard_send_phone_to_register_in_db())

################# КОНЕЦ Регистрация номера в таблицу Users КОНЕЦ ###########################

@rate_limit(1, 'Меню')
@dp.message_handler(lambda message: message.text in main_menu_def_buttons)
async def main_menu_handler(message: Message, state: FSMContext):
    logging.info(f"User({message.chat.id}) enter {message.text}")
    await db.add_bot_log(message.chat.id, message.text, f"{__name__}.py [LINE:{get_linenumber()}]")
    if message.text == schedule_button_text:
        await message.answer(text='Выберите ваш курс ↘', reply_markup=await inline_keyboard_schedule())
    elif message.text == faq_button_text:
        max_pages = (ceil(await db.main_faq_count() / 10))
        await state.update_data(page=0, max_pages=max_pages)
        data = await state.get_data()
        text = f'F.A.Q Страница {data["page"] + 1} из {data["max_pages"]}'
        await message.answer(text=text,
                             reply_markup=await inline_keyboard_main_faq(data["page"]))
    elif message.text == library_button_text:
        await message.answer(text='Библиотека ↘', reply_markup=keyboard_library())
    elif message.text == shop_button_text:
        await message.answer(text='AlmaU Shop ↘', reply_markup=keyboard_almaushop())
    elif message.text == calendar_button_text:
        file_id = await db.find_id_academic_calendar()
        await bot.send_document(message.chat.id, file_id)
    elif message.text == certificate_button_text:
        await message.answer(text='Получение справки с места учебы\n' \
                                  'Вы можете получить справку или оставить заявку на получение справки с места учебы по месту требования (военкомат и тд.) ↘',
                             reply_markup=await inline_keyboard_certificate())
    elif message.text == feedback_button_text:
        await message.answer(
            text='Вы можете написать письмо с вопросами, жалобами или предложениями адресованное в "Эдвайзинг центр" нашего университета. \n'
                 'Для этого вам нужно указать свои контактные данные и непосредственно текст самого письма.',
            reply_markup=keyboard_feedback())
    elif message.text == navigation_button_text:
        await message.answer(text='Навигация по университету', reply_markup=inline_keyboard_nav_unifi())

@dp.message_handler(lambda message: message.text in to_main_menu_button)
async def main_menu_handler(message: Message):
    logging.info(f"User({message.chat.id}) enter {message.text}")
    await db.add_bot_log(message.chat.id, message.text, f"{__name__}.py [LINE:{get_linenumber()}]")
    if message.text == to_main_menu_button:
        await message.answer('Возвращение в главное меню', reply_markup=always_stay_menu_keyboard())

@dp.callback_query_handler(text='/schedule')
async def callback_inline_schedule(call: CallbackQuery):
    logging.info(f"User({call.message.chat.id}) вошел в Расписание")
    await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
                                text='Выберите ваш курс ↘', reply_markup=await inline_keyboard_schedule())
    await call.answer()

@dp.callback_query_handler(text='/faq')
async def callback_inline_faq(call: CallbackQuery):
    logging.info(f"User({call.message.chat.id}) вошел в F.A.Q")
    await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
                                text='F.A.Q ↘', reply_markup=await inline_keyboard_main_faq())
    await call.answer()

@dp.callback_query_handler(text='/library')
async def callback_inline_library(call: CallbackQuery):
    logging.info(f"User({call.message.chat.id}) вошел в Библиотеку")
    await bot.send_message(chat_id=call.message.chat.id,
                           text='Библиотека ↘', reply_markup=keyboard_library())
    await call.answer()

@dp.callback_query_handler(text='/feedback')
async def callback_inline_feedback(call: CallbackQuery):
    logging.info(f"User({call.message.chat.id}) вошел в Обратную связь с ректором")
    await bot.send_message(chat_id=call.message.chat.id,
                           text='Обратная связь с ректором ↘')
    await bot.send_message(chat_id=call.message.chat.id,
                           text='Вы можете написать письмо с жалобами и предложениями адресованное ректору нашего университета. \n'
                                'Для этого вам нужно указать свои контактные данные и непосредственно текст самого письма.',
                           reply_markup=keyboard_feedback())
    await call.answer()

@dp.callback_query_handler(text='/almaushop')
async def callback_inline_almaushop(call: CallbackQuery):
    logging.info(f"User({call.message.chat.id}) вошел в AlmaU Shop")
    await bot.send_message(chat_id=call.message.chat.id,
                           text='AlmaU Shop ↘', reply_markup=keyboard_almaushop())
    await call.answer()

@dp.callback_query_handler(text='/certificate')
async def callback_inline_certificate(call: CallbackQuery):
    logging.info(f"User({call.message.chat.id}) вошел в Получение справки")
    if call.message.content_type == 'text':
        await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
                                    text='Получение справки с места учебы ↘ \n' \
                                         'Вы можете получить инструкции для получения справок онлайн или оставить заявку на получение справки физически с места учебы по месту требования (военкомат и тд.)',
                                    reply_markup=await inline_keyboard_certificate())
    else:
        await bot.delete_message(call.message.chat.id, call.message.message_id)
        await bot.delete_message(call.message.chat.id, call.message.message_id - 1)
        await bot.send_message(chat_id=call.message.chat.id, text='Получение справки с места учебы ↘ \n' \
                                                                  'Вы можете получить инструкции для получения справок онлайн '
                                                                  'или оставить заявку на получение справки физически с '
                                                                  'места учебы по месту требования (военкомат и тд.)',
                               reply_markup=await inline_keyboard_certificate())
    await call.answer()

@dp.callback_query_handler(text='/academ_calendar')
async def callback_academ_calendar(call: CallbackQuery):
    file_id = await db.find_id_academic_calendar()
    await bot.send_document(call.message.chat.id, file_id)
    await call.answer()

@dp.callback_query_handler(text='go_back')
async def callback_inline(call: CallbackQuery):
    logging.info(f"User({call.message.chat.id}) нажал кнопку Назад и вернулся в Главное меню")
    await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
                                text=_main_menu_text,
                                reply_markup=inline_keyboard_menu())
    await call.answer()

######################  Динамические кнопки Расписания ##################################################

@dp.callback_query_handler(schedule_callback.filter())
async def callback_inline(call: CallbackQuery, callback_data: dict):
    await db.add_bot_log(call.message.chat.id, call.data, f"{__name__}.py [LINE:{get_linenumber()}]")
    logging.info(f'call = {call.data}')
    schedule_id = callback_data.get('schedule_id')  # Получение id расписания из callback_data
    file_id = await db.find_schedule_id(schedule_id)  # Получение file_id кнопки из БД
    await bot.send_document(call.message.chat.id, file_id)  # Отправка расписания пользователю
    await call.answer()

###################### КОНЕЦ Динамические кнопки Расписания КОНЕЦ #################################################
############################ Меню F.A.Q #########################################################
@dp.callback_query_handler(main_faq_callback.filter())
async def callback_inline_faq_menu(call: CallbackQuery, callback_data: dict):
    id = callback_data.get('callback_id')
    db_request = await db.main_faq_select_question_and_answer_and_type(id)
    await db.add_bot_log(call.message.chat.id, db_request['question'], f"{__name__}.py [LINE:{get_linenumber()}]")
    if db_request['type_answer'] == 'text':
        question = db_request['question']
        answer = db_request['answer']
        text_faq = f'• Вопрос:\n' \
                   f'{question} \n\n' \
                   f'• Ответ:\n' \
                   f'{answer}'
        await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
                                    text=text_faq,
                                    reply_markup=inline_keyboard_main_faq_back(),
                                    parse_mode="HTML")
    elif db_request['type_answer'] == 'document':
        question = db_request['question']
        answer = db_request['answer']
        text_faq = f'• Вопрос:\n' \
                   f'{question} \n\n'
        await bot.delete_message(call.message.chat.id, call.message.message_id)
        await bot.send_document(call.message.chat.id, answer, caption=text_faq, parse_mode='HTML',
                                reply_markup=inline_keyboard_main_faq_back())
    elif db_request['type_answer'] == 'photo':
        question = db_request['question']
        answer = db_request['answer']
        text_faq = f'• Вопрос:\n' \
                   f'{question} \n\n'
        await bot.delete_message(call.message.chat.id, call.message.message_id)
        await bot.send_photo(call.message.chat.id, answer, caption=text_faq, parse_mode='HTML',
                             reply_markup=inline_keyboard_main_faq_back())
    await call.answer()

@dp.callback_query_handler(text='back_to_main_faq')
async def callback_inline_faq_menu_back(call: CallbackQuery, state: FSMContext):
    logging.info(f'User({call.message.chat.id}) вернулся в админ меню')
    data = await state.get_data()
    text = f'F.A.Q Страница {data["page"] + 1} из {data["max_pages"]}'
    if call.message.content_type == 'text':
        await bot.edit_message_text(chat_id=call.message.chat.id,
                                    message_id=call.message.message_id,
                                    text=text,
                                    reply_markup=await inline_keyboard_main_faq(data['page']))
    if call.message.content_type in ('document', 'photo'):
        await bot.delete_message(call.message.chat.id, call.message.message_id)
        await bot.send_message(chat_id=call.message.chat.id,
                               text=text,
                               reply_markup=await inline_keyboard_main_faq(data['page']))
    await call.answer()

# FAQ кнопки вперед и назад
@dp.callback_query_handler(text=["main_faq_previous", "main_faq_next"])
async def main_menu_faq_next_prev(call: CallbackQuery, state: FSMContext):
    logging.info(f"User({call.message.chat.id}) enter {call.data}")
    data = await state.get_data()
    if call.data == "main_faq_next" and (data['page'] + 1 < data["max_pages"]):
        await state.update_data(page=(data['page'] + 1))
        data = await state.get_data()
        text = f'F.A.Q Страница {data["page"] + 1} из {data["max_pages"]}'
        await bot.edit_message_text(chat_id=call.message.chat.id,
                                    message_id=call.message.message_id,
                                    text=text,
                                    reply_markup=await inline_keyboard_main_faq(data["page"]))
    elif call.data == "main_faq_previous" and (data['page'] != 0):
        await state.update_data(page=(data['page'] - 1))
        data = await state.get_data()
        text = f'F.A.Q Страница {data["page"] + 1} из {data["max_pages"]}'
        await bot.edit_message_text(chat_id=call.message.chat.id,
                                    message_id=call.message.message_id,
                                    text=text,
                                    reply_markup=await inline_keyboard_main_faq(data["page"]))

############################ КОНЕЦ Меню F.A.Q КОНЕЦ #########################################################

# Выход из любого admin state командой /cancel
@dp.message_handler(commands=['cancel'], state=['*'])
async def cancel_from_anywhere(message: types.Message, state: FSMContext):
    await delete_inline_buttons_in_dialogue(message)
    await bot.send_message(message.chat.id, 'Успешно отменено,\n'
                                            'Возвращение в главное меню', reply_markup=always_stay_menu_keyboard())
    await state.reset_state()


import pygame
from pygame.locals import (MOUSEBUTTONUP,
                           MOUSEBUTTONDOWN,
                           KEYDOWN,
                           K_r,
                           QUIT)
import random

def init_board(size):
    assert isinstance(size, int) and 4 <= size <= 9

    lst = []
    for i in range(1, size ** 2 + 1):
        lst.append(i)
    random.shuffle(lst)

    lst2d = []
    while lst:
        temp = []
        for i in range(size):
            temp.append(lst.pop())
        lst2d.append(temp)

    return lst2d

pygame.init()

white = (255, 255, 255)
green = (0, 255, 0)
blue = (0, 0, 128)
black = (0, 0, 0)
dark_grey = (43, 45, 47)
light_grey = (80, 80, 80)
WIDTH = 724
margin = 4
title_height = 80
HEIGHT = WIDTH + title_height
screen = pygame.display.set_mode([WIDTH, HEIGHT])
screen.fill(dark_grey)
font = pygame.font.SysFont('calibri', 35)

size_to_font_size = {
    4: 50,
    5: 42,
    6: 39,
    7: 34,
    8: 30,
    9: 28
}

def gen_text_window(text, font_size, centered_pos, font_color, bg_color):
    font = pygame.font.SysFont('calibri', font_size)
    text = font.render(text, True, font_color, bg_color)
    textrect = text.get_rect()
    textrect.center = centered_pos
    screen.blit(text, textrect)

def render_board(lst, size):
    block_size = int(round((WIDTH - margin) / size))
    block_size_no_margin = block_size - margin
    for y in range(size):
        for x in range(size):
            pygame.draw.rect(screen, black,
                             pygame.Rect(margin + block_size * x,
                                         title_height + margin + block_size * y,
                                         block_size_no_margin,
                                         block_size_no_margin))

            center = (margin + block_size * x + block_size_no_margin // 2,
                      title_height + margin + block_size * y + block_size_no_margin // 2)
            gen_text_window(str(lst[y][x]), size_to_font_size[size],
                            center, white, black)

width = 400
left = int(round((WIDTH - width) // 2))
height = 60

def select_modes():
    pygame.draw.rect(screen, black, pygame.Rect(left, 100, width, height))
    gen_text_window('4 x 4', 28, (WIDTH // 2, 100 + height // 2), white, black)

    pygame.draw.rect(screen, black, pygame.Rect(left, 200, width, height))
    gen_text_window('5 x 5', 28, (WIDTH // 2, 200 + height // 2), white, black)

    pygame.draw.rect(screen, black, pygame.Rect(left, 300, width, height))
    gen_text_window('6 x 6', 28, (WIDTH // 2, 300 + height // 2), white, black)

    pygame.draw.rect(screen, black, pygame.Rect(left, 400, width, height))
    gen_text_window('7 x 7', 28, (WIDTH // 2, 400 + height // 2), white, black)

    pygame.draw.rect(screen, black, pygame.Rect(left, 500, width, height))
    gen_text_window('8 x 8', 28, (WIDTH // 2, 500 + height // 2), white, black)

    pygame.draw.rect(screen, black, pygame.Rect(left, 600, width, height))
    gen_text_window('9 x 9', 28, (WIDTH // 2, 600 + height // 2), white, black)

def get_mode(pos):
    x, y = pos[0], pos[1]
    if x < left or x > left + width:
        return None
    elif 100 <= y <= 100 + height:
        return 4
    elif 200 <= y <= 200 + height:
        return 5
    elif 300 <= y <= 300 + height:
        return 6
    elif 400 <= y <= 400 + height:
        return 7
    elif 500 <= y <= 500 + height:
        return 8
    elif 600 <= y <= 600 + height:
        return 9

def get_block(pos, size, curr, lst):
    x, y = pos[0], pos[1] - 60
    if y < height:
        return None
    block_size = int(round((WIDTH - margin) / size))
    block_size_no_margin = block_size - margin
    i, j = y // block_size, x // block_size
    print(i, j)

    if lst[i][j] == curr:
        center = (margin + block_size * j + block_size_no_margin // 2,
                  title_height + margin + block_size * i + block_size_no_margin // 2)
        gen_text_window(str(lst[i][j]), size_to_font_size[size],
                        center, light_grey, black)
        curr += 1

    return curr

running = True
size_decided = False
rendered = False
size = None
curr = None
lst = []
time_when_finished = None
t1 = 0

while running:
    while not size_decided:
        screen.fill(dark_grey)
        select_modes()
        for event in pygame.event.get():
            if event.type == MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
                size_get = get_mode(pos)
                if size_get:
                    size = size_get
                    size_decided = True
                    curr = 1
            elif event.type == pygame.QUIT:
                pygame.quit()

        pygame.display.update()

    if not rendered:
        screen.fill(dark_grey)
        lst = init_board(size)
        print(lst)
        render_board(lst, size)
        rendered = True
        t1 = pygame.time.get_ticks()/1000

    for event in pygame.event.get():
        if event.type == MOUSEBUTTONDOWN:
            pos = pygame.mouse.get_pos()
            temp = get_block(pos, size, curr, lst)
            print(temp)
            if temp:
                curr = temp
                gen_text_window(f'current:', 25, (660, 25), white, dark_grey)
                gen_text_window(str(curr - 1), 25, (660, 50), white, dark_grey)
            if curr > size ** 2:
                time_when_finished = pygame.time.get_ticks()/1000 - t1
                center = (WIDTH // 2, HEIGHT // 2)
                gen_text_window('Finished!', 40, center, black, white)
        elif event.type == KEYDOWN:
            if event.key == K_r:
                print('reset')
                size_decided = False
                rendered = False
                size = None
                curr = None
                lst = []
                time_when_finished = None
                t1 = 0
        elif event.type == pygame.QUIT:
            pygame.quit()

    if time_when_finished:
        time_string = 'Timer: {:.3f}'.format(time_when_finished)
    else:
        time_string = 'Timer: {:.3f}'.format(pygame.time.get_ticks()/1000 - t1)

    gen_text_window(time_string, 32, (WIDTH // 2, title_height // 2),
                    white, dark_grey)
    gen_text_window('Press R to reset', 20, (80, 25), white, dark_grey)

    pygame.display.update()

pygame.quit()


import os
import os.path as osp
import logging
from comet_ml import Experiment, OfflineExperiment

class Logger:
    _level_relations = {
        'debug': logging.DEBUG,
        'info': logging.INFO,
        'warning': logging.WARNING,
        'error': logging.ERROR,
        'crit': logging.CRITICAL
    }

    def __init__(self, args: dict) -> None:
        self._timestamp = os.getenv("timestamp")
        self._project_path = os.getenv("project_path")
        self._logger_comet = None
        self._logger_console = None
        if args["comet"]["enable"]:
            if "comet_online" == args["comet"]["type"]:
                self._init_logger_online(args)
            elif "comet_offline" == args["comet"]["type"]:
                self._init_logger_offline(args, self._project_path)
        if args["logging"]["enable"]:
            self._init_logger_console(args)

        if self._logger_comet is not None:
            self._logger_comet.set_name(self._timestamp)

    def _init_logger_online(self, args: dict):
        args = args["comet"]
        self._logger_comet = Experiment(
            api_key=args["api_key"],
            project_name=args["project_name"],
            workspace=args["workspace"],
            disabled=args["test"],
            log_env_details=True,
            log_env_gpu=True,
            log_env_cpu=True,
            log_env_host=True,
            log_git_metadata=False,
            log_git_patch=False,
            auto_param_logging=False,
        )

    def _init_logger_offline(self, args: dict, project_path: str):
        args = args["comet"]
        self._logger_comet = OfflineExperiment(
            project_name=args["project_name"],
            workspace=args["workspace"],
            offline_directory=osp.join(project_path,
                                       args["offline_dir"],
                                       self._timestamp),
            disabled=args["test"],
            log_env_details=True,
            log_env_gpu=True,
            log_env_cpu=True,
            log_env_host=True,
            log_git_metadata=False,
            log_git_patch=False,
            auto_param_logging=False,
        )

    def _init_logger_console(self, args: dict):
        args = args["logging"]
        logger = logging.getLogger(self._timestamp)
        logger.setLevel(self._level_relations[args["level"]])
        # create console handler
        consoleHandler = logging.StreamHandler()
        consoleHandler.setLevel(self._level_relations[args["level"]])
        # set formatter
        formatter = logging.Formatter(args["format"],
                                      datefmt='%Y-%m-%d %H:%M:%S')
        consoleHandler.setFormatter(formatter)
        # add handler
        logger.addHandler(consoleHandler)
        self._logger_console = logger

    def broadcast_logger(self, config: dict, exclude=["logger"]) -> dict:
        config = config.copy()
        for key in config.keys():
            if key not in exclude and "logger" not in config[key].keys():
                config[key]["logger"] = (self._logger_comet, self._logger_console)
        return config

    def close_logger(self):
        if self._logger_comet is not None:
            self._logger_comet.end()
        if self._logger_console is not None:
            logging.shutdown()

    @property
    def logger_comet(self):
        return self._logger_comet

    @property
    def logger_console(self):
        return self._logger_console

    @staticmethod
    def encode_config_for_log(config: dict, character=".") -> dict:
        merge_dict = {}
        for key, value in config.items():
            if isinstance(value, dict):
                tmp_dict = Logger.encode_config_for_log(value)
                for key_, value_ in tmp_dict.items():
                    merge_dict[str(key) + character + str(key_)] = value_
            else:
                merge_dict[key] = value
        return merge_dict


# coding=utf-8
from django.conf.urls import patterns, url, include
from django.contrib.auth.decorators import login_required

from .views import CityCreateView, CityUpdateView, CityListView, CountryListView, CountryCreateView,\
    CountryUpdateView, RegionListView, RegionCreateView, RegionUpdateView
from .ajax import find_city, get_moderator_list, get_city_list
from apps.administrator.decorators import administrator_required

__author__ = '2mitrij'

city_urlpatterns = patterns(
    '',
    url(r'^$', login_required(CityListView.as_view()), name='list'),
    url(r'^add/$', administrator_required(CityCreateView.as_view()), name='add'),
    url(r'^(?P<pk>\d+)/$', login_required(CityUpdateView.as_view()), name='update'),

    url(r'^find/$', find_city, name='find'),
    url(r'^get_moderator_list/$', get_moderator_list, name='get_moderator_list'),
)

country_urlpatterns = patterns(
    '',
    url(r'^$', administrator_required(CountryListView.as_view()), name='list'),
    url(r'^add/$', administrator_required(CountryCreateView.as_view()), name='add'),
    url(r'^(?P<pk>\d+)/$', administrator_required(CountryUpdateView.as_view()), name='update'),
    url(r'^get_city_list/$', get_city_list, name='get_city_list'),
)

region_urlpatterns = patterns(
    '',
    url(r'^$', administrator_required(RegionListView.as_view()), name='list'),
    url(r'^add/$', administrator_required(RegionCreateView.as_view()), name='add'),
    url(r'^(?P<pk>\d+)/$', administrator_required(RegionUpdateView.as_view()), name='update'),
)

urlpatterns = patterns(
    '',
    url(r'^city/', include(city_urlpatterns, 'city')),
    url(r'^region/', include(region_urlpatterns, 'region')),
    url(r'^country/', include(country_urlpatterns, 'country')),
)


from kivy.uix.widget import Widget
from kivy.uix.scatter import Scatter
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.properties import BoundedNumericProperty

class Gauge(Widget):
    value = BoundedNumericProperty(0, min=-20, max=120, errorvalue=0)
    def __init__(self, **kwargs):
        super(Gauge, self).__init__(**kwargs)
        self._gauge = Image(
            size = (320, 320),
            pos_hint={'x': 0.1, 'y': .3},
            source = './merilec/temperatura_merilec.png',
        )
        self._needle = Scatter(
            size = (320, 320),
            do_rotation = False,
            do_scale = False,
            do_translation = False
        )
        self._needle.add_widget(Image(
            source = "./merilec/needle.png",
            size = (320, 320)
        ))
        self.add_widget(self._gauge)
        self.add_widget(self._needle)
        self.bind(pos=self._update)
        self.bind(size=self._update)
        self.bind(value=self._turn)

    def _update(self, *args):
        self._gauge.pos = self.pos
        self._needle.pos = (self.x, self.y)
        self._needle.center = self._gauge.center

    def _turn(self, *args):
        self._needle.center_x = self._gauge.center_x
        self._needle.center_y = self._gauge.center_y
        self._needle.rotation = (50 * 1.8) - (self.value * 1.8)