import numpy as np
import time
import pickle
import json
from copy import deepcopy
from pycvi.cluster import compute_cluster_params, prepare_data

from typing import List, Sequence, Tuple, Union, Any, Dict

from . import Vertex
from . import Edge
from . import Component
from ._set_default_properties import (
    _set_members, _set_zero, _set_model_class, _set_score, _set_k_range,
    _set_sliding_window, _set_transformer, _set_scaler
)
from ._clustering_model import generate_all_clusters, merge_clusters
from ._scores import _compute_ratio_scores, _compute_score_bounds
from ._analysis import k_info, get_relevant_k

from ..utils.sorted_lists import ( has_element )
from ..utils.d3 import jsonify

class PersistentGraph():

    def __init__(
        self,
        members: np.ndarray = None,
        time_axis: np.ndarray = None,
        w: int = 1,
        weights: np.ndarray = None,
        precision: int = 13,
        score: str = None,
        transformer = None,
        scaler = None,
        DTW: bool = False,
        zero_type: str = 'variance',
        model_class = None,
        k_max : int = 5,
        name: str = None,
        model_kw: dict = {},
        fit_predict_kw: dict = {},
        model_class_kw: dict = {},
    ):
        """
        Initialize an empty graph

        :param members: (N, T, d)-array. Original data, ensemble of time series

        - N: number of members
        - d: number of variables (default: 1)
        - T: length of the time series (default: 1)

        :type members: np.ndarray

        :param time_axis: (T)-array. Time axis, mostly used for plotting,
        defaults to None, in that case, ``time_axis = np.arange(T)``
        :type time_axis: np.ndarray, optional

        :param weights: Only here for parameter compatibility with the naive
        version, defaults to None
        :type weights: np.ndarray, optional

        :param precision: Score precision, defaults to 13
        :type precision: int, optional

        :param score: Define how the score is computed,
        defaults to 'inertia'. Scores available:

        - inertia
        - min / max_inertia
        - variance
        - min / max_variance

        :type score: optional
        :param zero_type: Define the "0" cluster, defaults to 'uniform'
        :type zero_type: str, optional
        :param verbose: Level of verbosity (defaults to False):

        - 0 (False) Nothing
        - 1 (True) Steps overview
        - 2 Steps overview + more details about vertices and edges
        :type verbose: Union[bool,int], optional

        :param name: Name of the graph object, mostly used for saving,
        defaults to None
        :type name: str, optional
        :raises ValueError: [description]
        """

        # --------------------------------------------------------------
        # ---------------------- About the data ------------------------
        # --------------------------------------------------------------

        if members is not None:
            # After that pg._members is of shape (N, T, d) even if
            # d and T were initially omitted
            _set_members(self, members)
            _set_sliding_window(self, w)
            _set_zero(self, zero_type)

            # Determines how to scale the clustering data
            _set_scaler(self, scaler)
            # Determines how to transform the clustering data
            _set_transformer(self, transformer)

            # Shared x-axis values among the members
            if time_axis is None:
                self._time_axis = np.arange(self.T)
            else:
                self._time_axis = np.copy(time_axis)

            if weights is None:
                self._weights = np.ones((self.T, self.d), dtype = float)
            else:
                self._weights = np.array(weights)
                if len(self._weights.shape) < 2:
                    self._weights = np.expand_dims(self._weights, axis=0)

            # --------------------------------------------------------------
            # ---------------- About the clustering method  ----------------
            # --------------------------------------------------------------

            # Determines how to cluster the members
            _set_model_class(
                self, model_class, DTW, model_kw, fit_predict_kw,
                model_class_kw)
            # Score type, determines how to measure how good a model is
            _set_score(self, score)
            # Set k_range while taking into account the condition on k
            # of the given score.
            _set_k_range(self, k_max)

            if name is None:
                self._name = self._model_type + "_" + str(self._score)
            else:
                self._name = name

            # --------------------------------------------------------------
            # --------- About the graph:   graph's attributes --------------
            # --------------------------------------------------------------

            # Total number of vertices/edges created at each time step
            self._nb_vertices = np.zeros((self.T), dtype=int)
            self._nb_edges = np.zeros((self.T-1), dtype=int)
            # Nested list (time, nb_vertices/edges) of vertices/edges
            # Here are stored Vertices/Edges themselves, not only their num
            self._vertices = [[] for _ in range(self._T_w)]
            self._edges = [[] for _ in range(self._T_w-1)]
            # Nested list (time, nb_local_steps) of dict storing info about
            # The successive steps
            self._local_steps = [[] for _ in range(self._T_w)]
            self._k_info = None
            self._life_span_max = None
            self._life_span_min = None
            self._relevant_k = None
            self._max = None
            self._min = None

            # Score precision
            if precision <= 0:
                raise ValueError("precision must be a > 0 int")
            else:
                self._precision = int(precision)

            # --------------------------------------------------------------
            # --------- About the graph:   algo's helpers ------------------
            # --------------------------------------------------------------

            # To find members' vertices and edges more efficiently
            #
            # List of dictionary of arrays of length N
            # members_v_distrib[t][k][i] is the vertex num of the
            # ith member at t for the assumption k.
            self._members_v_distrib = [
                {
                    k: np.zeros(self.N)
                    # Take into account the available k and ignore the
                    # case k=0
                    for k in self._k_range if k>=1
                }
                for _ in range(self._T_w)
            ]

            # List of length T_w of scores
            self._best_scores = None
            self._worst_scores = None

            self._verbose = False
            self._quiet = False

    def _add_vertex(
        self,
        list_k: List[int],
        info: Dict[str, Any],
        t: int,
        members: List[int],
    ):
        """
        Add a vertex to the current graph

        :param list_k: Number(s) of clusters in which this vertex exist
        :type list_k: List[int]
        :param t: time step at which the vertex should be added
        :type t: int
        :param members: Ordered list of members indices represented by the
        vertex
        :type members: List[int]
        :return: The newly added vertex
        :rtype: Vertex
        """

        info_complete = {
            **{"k": list_k},
            **info
        }

        # Compute the ratio intervals
        score_ratios = []
        for k in list_k:
            score_ratios = Component.ratio_union(
                score_ratios, [self._k_info[k]["score_ratios"][t]]
            )

        # Create the vertex
        num = self._nb_vertices[t]
        v = Vertex(
            info = info_complete,
            t = t,
            num = num,
            members = members,
            score_ratios = score_ratios,
            total_nb_members = self.N,
        )

        # Update the graph with the new vertex
        self._nb_vertices[t] += 1
        self._vertices[t].append(v)
        for k in info_complete["k"]:
            self._members_v_distrib[t][k][members] = num

        return v

    def _add_edge(
        self,
        v_start: Vertex,
        v_end: Vertex,
    ):
        """
        Add an edge to the current graph

        Return None if the edge is malformed

        :param v_start: Vertex from which the edge comes
        :type v_start: Vertex
        :param v_end: Vertex to which the edge goes
        :type v_end: Vertex
        :return: The newly added edge
        :rtype: Edge
        """
        t = v_start.time_step

        members = v_start.get_common_members(v_end)
        if not members:
            if not self._quiet:
                print("WARNING: No members in edge")
            return None

        # --------- Compute scores and ratios info --------------
        ratios = Component.ratio_intersection(
            v_start.score_ratios, v_end.score_ratios
        )

        # -------------- Compute edge info --------------

        # Compute info (mean, std inf/sup at start and end)
        # Option 1: Re-compute cluster params naively from original members
        # Note that this is not very consistent with DTW.....
        # X_start = self._members[members, :, t]
        # info_start = compute_cluster_params(X_start)
        # X_end = self._members[members, :, t+1]
        # info_end = compute_cluster_params(X_end)

        # Option 2: Use v.info["X"] that we are now storing!
        info_start = Edge.info(v_start, members)
        info_end = Edge.info(v_end, members)

        e = Edge(
            info_start = info_start,
            info_end = info_end,
            v_start = v_start.num,
            v_end = v_end.num,
            t = t,
            num = self._nb_edges[t],
            members = members,
            score_ratios = ratios,
            total_nb_members = self.N,
        )

        # Add edge number to v_start and v_end
        v_start.add_edge_from(e.num)
        v_end.add_edge_to(e.num)

        # Update the graph with the new edge
        self._nb_edges[t] += 1
        self._edges[t].append(e)
        return e

    def get_alive_vertices(
        self,
        ratio: float = None,
        t: Union[Sequence[int],int] = None,
        get_only_num: bool = True
    ) -> Union[List[Vertex], List[int]]:
        """
        Extract alive vertices

        If ``t`` is not specified then returns a nested list of
        alive vertices for each time steps

        :param ratio: Ratio at which vertices should be alive
        :type ratio: float, optional
        :param t: Time step from which the vertices should be extracted
        :type t: int, optional
        :param get_only_num: Return the component or its num only?
        :type get_only_num: bool, optional
        """
        # -------------------- Initialization --------------------------
        v_alive = []
        if t is None:
            return_nested_list = True
            t_range = range(self._T_w)
        else:
            if isinstance(t, int):
                return_nested_list = False
                t_range = [t]
            else:
                return_nested_list = True
                t_range = t

        # -------------------- Using ratio ---------------------------
        for t in t_range:

            v_alive_t = [
                v for v in self._vertices[t] if v.is_alive(ratio)
            ]

            if not get_only_num:
                v_alive_t = [
                    self._vertices[t][v_num] for v_num in v_alive_t
                ]
            v_alive.append(v_alive_t)

        if not return_nested_list:
            v_alive = v_alive[0]
        return v_alive

    def get_alive_edges(
        self,
        ratio: float = None,
        t: Union[Sequence[int],int] = None,
        get_only_num: bool = True
    ) -> Union[List[Vertex], List[int]]:
        """
        Extract alive edges

        If ``t`` is not specified then returns a nested list of
        alive edges for each time steps

        :param ratio: Ratio at which edges should be alive
        :type ratio: float, optional
        :param t: Time step from which the edges should be extracted
        :type t: int, optional
        :param get_only_num: Return the component or its num only?
        :type get_only_num: bool, optional
        """
        # -------------------- Initialization --------------------------
        e_alive = []
        if t is None:
            return_nested_list = True
            t_range = range(self._T_w-1)
        else:
            if isinstance(t, int):
                return_nested_list = False
                t_range = [t]
            else:
                return_nested_list = True
                t_range = t

        # -------------------- Using ratio ---------------------------
        for t in t_range:

            e_alive_t = [
                e for e in self._edges[t] if e.is_alive(ratio)
            ]

            if not get_only_num:
                e_alive_t = [
                    self._edges[t][e_num] for e_num in e_alive_t
                ]
            e_alive.append(e_alive_t)

        if not return_nested_list:
            e_alive = e_alive[0]
        return e_alive

    def _construct_vertices(
        self,
        clusterings_t_k: List[Dict[int, List[List[int]]]],
        listk_t_k: List[Dict[int, List[List[int]]]],
    ) -> None:
        # list of T (if sliding window) or 1 array(s) of shape:
        # (N, T|w_t, d) if DTW
        # (N, (T|w_t)*d) if not DTW
        data_params = prepare_data(
            self._members, DTW=self._DTW, window=self._sliding_window,
            transformer=None, scaler=None)

        # `clusterings_t_k[t_w][k][i]` is a list of members indices
        # contained in cluster i for the clustering assuming k clusters
        # for the extracted time window t_w.
        for t_w in range(len(clusterings_t_k)):
            if self._sliding_window is None:
                midpoint = None
            else:
                midpoint = self._sliding_window["midpoint_w"][t_w]
            for k, clustering_t in clusterings_t_k[t_w].items():
                    # create all v
                    new_vertices = [
                        self._add_vertex(
                                list_k=listk_t_k[t_w][k][i_c],
                                info=compute_cluster_params(
                                    data_params[t_w][cluster],
                                    midpoint
                                ),
                                t=t_w,
                                members=cluster,
                            ) for i_c, cluster in enumerate(clustering_t)
                        ]

    def _construct_edges(self):
        for t in range(self._T_w-1):
            v_starts = self._vertices[t]
            v_ends = self._vertices[t+1]

            for v_start in v_starts:
                nb_new_edges = 0
                # Take all vertices at t and all at t+1 If they are
                # contemporaries and have common members then draw an
                # edge
                v_ends_contemp = [
                    v_end for v_end in v_ends
                    if (Component.contemporaries(v_start, v_end)
                    and Component.have_common_members(v_start, v_end))
                ]
                for v_end in v_ends_contemp:

                    e = self._add_edge(
                        v_start = v_start,
                        v_end = v_end,
                    )
                    if e is not None:
                        nb_new_edges += 1

                if self._verbose == 2 and nb_new_edges == 0:
                    print(
                        "WARNING! \nt: {}, no new edges going from v: {}"
                        %(t, v_start.num)
                    )

    def _compute_statistics(self):
        # Max/min (N, T, d)
        self._max = np.amax(self._members, axis=0)
        self._min = np.amin(self._members, axis=0)
        # max/min life span
        life_spans = []
        for v_t in self._vertices:
            life_spans += [v.life_span for v in v_t]
        self._life_span_max = np.amax(life_spans)
        self._life_span_min = np.amin(life_spans)
        return None

    def construct_graph(
        self,
        k_max : int = None,
        pre_prune: bool = False,
        pre_prune_threshold: float = 0.30,
        post_prune: bool = False,
        post_prune_threshold: float = 0.05,
        verbose: Union[bool,int] = False,
        quiet: bool = False,
    ):

        self._verbose = verbose
        self._quiet = (quiet or not verbose)
        self._pre_prune = pre_prune
        self._pre_prune_threshold = pre_prune_threshold
        self._post_prune = post_prune
        self._post_prune_threshold = post_prune_threshold

        # ================== Cluster all the data ======================
        t_start = time.time()
        if self._verbose:
            print("Clustering data...")
        clusterings_t_k = generate_all_clusters(self)
        t_end = time.time()
        if self._verbose:
            print('Data clustered in %.2f s' %(t_end - t_start))

        # ================== Compute score bounds ======================
        _compute_score_bounds(self)

        # ================= Compute ratio scores =======================
        _compute_ratio_scores(self)

        # =================== default k values =========================
        self._k_info = k_info(self)
        relevant_k = get_relevant_k(self)
        dict_relevant_k = {}
        dict_relevant_k["k"] = [x[0] for x in relevant_k]
        dict_relevant_k["life_span"] = [x[1] for x in relevant_k]
        self._relevant_k = dict_relevant_k

        # ================== Construct vertices ========================
        # Remove the case k=0 from the clusters and list_k now that
        # k_info, and ratios and score bounds have been computed
        for clusterings_t in clusterings_t_k:
            clusterings_t.pop(0, None)

        # Merge clusters that are identical
        clusterings_t_k, listk_t_k = merge_clusters(clusterings_t_k)

        t_start = time.time()
        if self._verbose:
            print("Construct vertices...")
        self._construct_vertices(clusterings_t_k, listk_t_k)
        t_end = time.time()
        if self._verbose:
            print('Vertices constructed in %.2f s' %(t_end - t_start))

        # =================== Construct edges ==========================
        if self._verbose:
            print("Construct edges...")
        t_start = time.time()
        self._construct_edges()
        t_end = time.time()
        if self._verbose:
            print('Edges constructed in %.2f s' %(t_end - t_start))

        # =================== Compute statistics =======================
        self._compute_statistics()

    def get_relevant_components(
        self,
        selected_k: List[int] = None,
        k_max: int = 8,
        fill_holes: bool = True,
    ) -> Tuple[List[Vertex], List[Edge]]:
        """
        Return a deep copy of most relevant vertices and edges

        Potentially fills holes in edges

        :type selected_k: List[int], optional
        :param k_max: Max value of k considered, defaults to 8
        :type k_max: int, optional
        :return: Relevant vertices and edges
        :rtype: Tuple[List[Vertex], List[Edge]]
        """
        k_max = min(k_max, self.k_max)
        # For each time step, get the most relevant number of clusters
        if selected_k is None:
            selected_k = self._relevant_k["k"]

        # ------------ Find vertices that represent such a k -----------
        relevant_vertices = [
            [
                deepcopy(v) for v in self._vertices[t]
                if has_element(v.info['k'], selected_k[t])
            ] for t in range(self._T_w)
        ]

        # ------------- Find edges between 2 relevant k ----------------
        if not fill_holes:
            relevant_edges = [
                [
                    deepcopy(e) for e in self._edges[t]
                    if (
                        has_element(
                            self._vertices[e.time_step][e.v_start].info['k'],
                            selected_k[t]
                        ) and has_element(
                            self._vertices[e.time_step + 1][e.v_end].info['k'],
                            selected_k[t+1]
                        ))
                ] for t in range(self._T_w-1)
            ]

        # Some edges might be non-existant (edge k1 -> k2 does not exist)
        # So we will create edges that won't be attached to the graph but with
        # the necessary information so that they can be visualized
        else:
            relevant_edges = []
            for t in range(self._T_w-1):
                edges = []

                # keep track of edge num for each t
                e_num = self._nb_edges[t]

                # Get start relevant vertices
                v_starts = relevant_vertices[t]
                v_ends = relevant_vertices[t+1]
                for v_start in v_starts:

                    # Find common members between v_start and v_end
                    v_end_members = [
                        (v, v.get_common_members(v_start)) for v in v_ends
                        if v.get_common_members(v_start) != []
                    ]
                    for (v_end, members) in v_end_members:

                        # Compute info (mean, std inf/sup at start and end)
                        info_start = Edge.info(v_start, members)
                        info_end = Edge.info(v_end, members)

                        edges.append(Edge(
                            info_start=info_start,
                            info_end=info_end,
                            v_start = v_start.num,
                            v_end = v_end.num,
                            t = t,
                            num = e_num,
                            members = members,
                            total_nb_members = self.N,
                            score_ratios = [[0, 1]],
                        ))

                        # Add edge number to v_start and v_end
                        # Potentially useful for plotting
                        v_start.add_edge_from(e_num)
                        v_end.add_edge_to(e_num)

                        e_num += 1

                relevant_edges.append(edges)

        return relevant_vertices, relevant_edges

    def save(
        self,
        filename: str = None,
        path: str = '',
        type:str = 'pg'
    ) -> None:
        """
        Save the graph (either as a class or a JSON file)

        :param filename: filename of the saved graph, defaults to None
        :type filename: str, optional
        :param path: path to the saved graph, defaults to ''
        :type path: str, optional
        :param type: type of the graph, either as a python class (.pg)
        or as a JSON file, defaults to 'pg'
        :type type: str, optional
        """
        if filename is None:
            filename = self.name
        if type == 'json':
            class_dict = jsonify(self)
            json_str = json.dumps(class_dict, indent=4)
            with open(path + filename + '.json', 'w', encoding='utf-8') as f:
                f.write(json_str)
        else:
            with open(path + filename + '.pg', 'wb') as f:
                pickle.dump(self, f)

    def load(
        self,
        filename: str = None,
        path: str = '',
    ) -> None:
        """
        Load a graph from a PersistentGraph file (.pg)

        :param filename: filename of the saved graph, defaults to None
        :type filename: str, optional
        :param path: path to the saved graph, defaults to ''
        :type path: str, optional
        """
        raise NotImplementedError("pg.load not working, use pickle instead")
        with open(path + filename, 'rb') as f:
            self = pickle.load(f)

    @property
    def name(self) -> str :
        """Name of the graph object, used as filename by default when saved

        :rtype: str
        """
        return self._name

    @property
    def N(self) -> int :
        """Number of members in the ensemble

        :rtype: int
        """
        return self._N

    @property
    def T(self) -> int :
        """Length of the time series

        :rtype: int
        """
        return self._T

    @property
    def d(self) -> int :
        """Number of variables studied
        :rtype: int
        """
        return self._d

    @property
    def w(self) -> int :
        """Size of the sliding time window
        :rtype: int
        """
        return self._w

    @property
    def k_max(self) -> int:
        """
        Max value of k considered

        :rtype: int
        """
        return self._k_max

    @property
    def members(self) -> np.ndarray:
        """Original data, ensemble of time series

        :rtype: np.ndarray[float], shape: (N, T, d)
        """
        return np.copy(self._members)

    @property
    def time_axis(self) -> np.ndarray:
        """
        Time axis, mostly used for plotting

        :rtype: np.ndarray[float], shape: T
        """
        return self._time_axis

    @property
    def max(self) -> np.ndarray:
        """
        Max member values for each variable and each t

        :rtype: np.ndarray[float], shape: (T, d)
        """
        return self._max

    @property
    def min(self) -> np.ndarray:
        """
        Min member values for each variable and each t

        :rtype: np.ndarray[float], shape: (T, d)
        """
        return self._min

    @property
    def k_info(self) -> Dict[int, List[float]]:
        """
        Life span and ratios of each assumptions k for all k and t.

        Available keys:

        - "score_ratios"
        - "life_span"

        Example: k_info[3]["life_span"][10] is the life span of k=3 and
        t = 10

        :rtype: Dict[int, List[float]]
        """
        return self._k_info

    @property
    def life_span_max(self) -> float:
        """
        Max life span

        :rtype: float
        """
        return self._life_span_max

    @property
    def life_span_min(self) -> float:
        """
        Min life span

        :rtype: float
        """
        return self._life_span_min

    @property
    def relevant_k(self) -> Dict[str, List]:
        """
        Dict of lists with 2 keys, "k" and "life_span", both lists
        are of length T and represent respectively the most relevant k
        and its life span at each time step.

        :rtype: Dict[str, List]
        """
        return self._relevant_k

    @property
    def nb_vertices(self) -> np.ndarray:
        """
        Total number of vertices created at each time step (T)

        :rtype: np.ndarray[int], shape: T
        """
        return np.copy(self._nb_vertices)

    @property
    def nb_edges(self) -> np.ndarray:
        """
        Total number of edges created at each time step (T-1)

        ..note::
          ``nb_edges[t]`` are the edges going from vertices at ``t`` to
          vertices at ``t+1``

        :rtype: np.ndarray[int], shape: T-1
        """
        return np.copy(self._nb_edges)

    @property
    def edges(self) -> List[List[Edge]]:
        """
        Nested list of edges of the graph (T-1, nb_edges[t])

        .. note::
          This includes dead and alive vertices

        :rtype: List[List[Edge]]
        """
        return self._edges

    @property
    def vertices(self) -> List[List[Vertex]]:
        """
        Nested list of vertices of the graph (T, nb_vertices[t])

        .. note::
          This includes dead and alive vertices

        :rtype: List[List[Vertex]]
        """
        return self._vertices

    @property
    def local_steps(self) -> List[List[dict]]:
        """
        Nested list (time and steps) of information on local steps.

        Available keys of self._local_steps[t][s]:
        - `k`
        - `score`
        - `ratio_score`

        :rtype: List[List[dict]]
        """
        return self._local_steps

    @property
    def k_range(self) -> Sequence[int]:
        """
        Ordered range of number of clusters studied, satisfying the
        condition of the score and the condition on k_max

        :rtype: Sequence[int]
        """
        return self._k_range

    @property
    def parameters(self) -> dict:
        """
        Parameters of the graph

        :rtype: dict
        """
        dic = {
            "model_type" : self._model_type,
            "zero_type" : self._zero_type,
            "score" : self._score,
            "transformer" : self._transformer.__name__,
            "scaler" : self._scaler.__class__,
            "DTW" : self._DTW,
            "model_class_kw" : self._model_class_kw,
            "model_kw" : self._model_kw,
            "fit_predict_kw" : self._fit_predict_kw,
        }
        return dic


from django.shortcuts import render, redirect
from django.core.exceptions import ObjectDoesNotExist

# Translate
from django.utils.translation import gettext as _

from .models import Product, Shoe, Accessorie

# Create your views here.
def index(request):

    user = request.user

    last_added = Product.objects.all().order_by('-pk')[:3]

    men_shoes = Shoe.objects.filter(sex='male').order_by('-pk')[:5]
    women_shoes = Shoe.objects.filter(sex='female').order_by('-pk')[:5]

    accessories = Accessorie.objects.all().order_by('-pk')[:5]

    context = {
        'last_added': last_added,
        'men_shoes': men_shoes,
        'women_shoes': women_shoes,
        'accessories': accessories,
    }

    return render(request, "core/index.html", context)

def assort(request, param):

    if param == "shoes":
        products = Shoe.objects.all()
    elif param == "accessories":
        products = Accessorie.objects.all()
    else:
        return redirect('core:index')

    context = {
        "products": products,
    }

    return render(request, "core/assort.html", context)

def product(request, model_code):

    try:
        product = Shoe.objects.get(model_code=model_code)
    except ObjectDoesNotExist:
        product = Accessorie.objects.get(model_code=model_code)

    context = {
        "product": product,
    }

    return render(request, "core/product.html", context)


"""Define data models for various Ridwell objects."""
from __future__ import annotations

from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field
from datetime import date, datetime
from enum import Enum
from typing import Any, TypedDict, cast

from titlecase import titlecase

from .const import LOGGER
from .errors import RidwellError
from .query import (
    QUERY_SUBSCRIPTION_PICKUP_QUOTE,
    QUERY_SUBSCRIPTION_PICKUPS,
    QUERY_UPDATE_SUBSCRIPTION_PICKUP,
)

class PickupCategory(Enum):
    """Define a representation of a pickup category."""

    ADD_ON = "add_on"
    ROTATING = "rotating"
    STANDARD = "standard"

PICKUP_CATEGORIES_MAP = {
    "Batteries": PickupCategory.STANDARD,
    "Beyond the Bin": PickupCategory.ADD_ON,
    "Fluorescent Light Tubes": PickupCategory.ADD_ON,
    "Latex Paint": PickupCategory.ADD_ON,
    "Light Bulbs": PickupCategory.STANDARD,
    "Multi-Layer Plastic": PickupCategory.STANDARD,
    "Paint": PickupCategory.ADD_ON,
    "Plastic Film": PickupCategory.STANDARD,
    "Styrofoam": PickupCategory.ADD_ON,
    "Threads": PickupCategory.STANDARD,
}

class AddressType(TypedDict):
    """Define a type to represent an address."""

    street1: str
    city: str
    state: str
    postal_code: str

class EventState(Enum):
    """Define a representation of an event state."""

    INITIALIZED = "initialized"
    NOTIFIED = "notified"
    SCHEDULED = "scheduled"
    SKIPPED = "skipped"
    UNKNOWN = "unknown"

def convert_pickup_event_state(state: str) -> EventState:
    """Convert a raw pickup event state string into an EventState.

    Args:
        state: A raw event state.

    Returns:
        A parsed EventState object.
    """
    try:
        return EventState(state)
    except ValueError:
        LOGGER.warning("Unknown pickup event state: %s", state)
        return EventState.UNKNOWN

@dataclass(frozen=True)
class RidwellAccount:
    """Define a Ridwell account."""

    _async_request: Callable[..., Awaitable[dict[str, Any]]] = field(compare=False)

    account_id: str
    address: AddressType
    email: str
    full_name: str
    phone: str
    subscription_id: str
    subscription_active: bool

    async def async_get_next_pickup_event(self) -> RidwellPickupEvent:
        """Get the next pickup event based on today's date.

        Returns:
            A RidwellPickupEvent object.

        Raises:
            RidwellError: Raised when no valid pickup events are found.
        """
        pickup_events = await self.async_get_pickup_events()
        for event in pickup_events:
            if event.pickup_date >= date.today():
                return event
        raise RidwellError("No pickup events found after today")

    async def async_get_pickup_events(self) -> list[RidwellPickupEvent]:
        """Get pickup events for this subscription.

        Returns:
            A list of RidwellPickupEvent objects.
        """
        resp = await self._async_request(
            json={
                "operationName": "upcomingSubscriptionPickups",
                "variables": {"subscriptionId": self.subscription_id},
                "query": QUERY_SUBSCRIPTION_PICKUPS,
            },
        )

        return [
            RidwellPickupEvent(
                self._async_request,
                event_data["id"],
                datetime.strptime(event_data["pickupOn"], "%Y-%m-%d").date(),
                [
                    RidwellPickup(
                        titlecase(
                            pickup["pickupOfferPickupProduct"]["pickupOffer"][
                                "category"
                            ]["name"]
                        ),
                        pickup["pickupOfferPickupProduct"]["pickupOffer"]["id"],
                        pickup["pickupOfferPickupProduct"]["pickupOffer"]["priority"],
                        pickup["pickupOfferPickupProduct"]["pickupProduct"]["id"],
                        pickup["quantity"],
                    )
                    for pickup in event_data["pickupProductSelections"]
                ],
                convert_pickup_event_state(event_data["state"]),
            )
            for event_data in resp["data"]["upcomingSubscriptionPickups"]
        ]

@dataclass(frozen=True)
class RidwellPickup:
    """Define a Ridwell pickup (i.e., the thing being picked up)."""

    name: str
    offer_id: str
    priority: int
    product_id: str
    quantity: int

    category: PickupCategory = field(init=False)

    def __post_init__(self) -> None:
        """Perform some post-init init."""
        category = PICKUP_CATEGORIES_MAP.get(self.name, PickupCategory.ROTATING)

        if category == PickupCategory.ROTATING:
            # Our method of detecting rotating pickups is pretty "loose" (i.e., if a
            # category isn't a known standard or add-on, we assume it's rotating). This
            # could lead to future issues if Ridwell introduces a new standard or add-on
            # type that isn't mapped here. We try to be unintrusive and merely log that
            # we've found a "rotating" pickup so that it can later tell the full story:
            LOGGER.info("Detected assumed rotating pickup: %s", self.name)

        object.__setattr__(
            self,
            "category",
            PICKUP_CATEGORIES_MAP.get(self.name, PickupCategory.ROTATING),
        )

@dataclass(frozen=True)
class RidwellPickupEvent:
    """Define a Ridwell pickup event."""

    _async_request: Callable[..., Awaitable[dict[str, Any]]] = field(compare=False)

    event_id: str
    pickup_date: date
    pickups: list[RidwellPickup]
    state: EventState

    async def _async_opt(self, state: EventState) -> None:
        """Define a helper to opt in/out to/from the pickup event.

        Args:
            state: An EventState object denoting the desired state.
        """
        data = await self._async_request(
            json={
                "operationName": "updateSubscriptionPickup",
                "variables": {
                    "input": {
                        "subscriptionPickupId": self.event_id,
                        "state": state.value,
                    }
                },
                "query": QUERY_UPDATE_SUBSCRIPTION_PICKUP,
            },
        )

        object.__setattr__(
            self,
            "state",
            convert_pickup_event_state(
                data["data"]["updateSubscriptionPickup"]["subscriptionPickup"]["state"]
            ),
        )

    async def async_get_estimated_cost(self) -> float:
        """Get the estimated cost (USD) of this pickup based on its pickup types.

        Returns:
            A float denoting the estimated cost.
        """
        if not self.pickups:
            return 0.0

        resp = await self._async_request(
            json={
                "operationName": "subscriptionPickupQuote",
                "variables": {
                    "input": {
                        "subscriptionPickupId": self.event_id,
                        "addOnSelections": [
                            {
                                "productId": pickup.product_id,
                                "offerId": pickup.offer_id,
                                "quantity": pickup.quantity,
                            }
                            for pickup in self.pickups
                        ],
                    }
                },
                "query": QUERY_SUBSCRIPTION_PICKUP_QUOTE,
            },
        )

        return cast(float, resp["data"]["subscriptionPickupQuote"]["totalCents"] / 100)

    async def async_opt_in(self) -> None:
        """Opt in to the pickup event."""
        await self._async_opt(EventState.SCHEDULED)

    async def async_opt_out(self) -> None:
        """Opt out from the pickup event."""
        await self._async_opt(EventState.SKIPPED)


from model import (articles, check_choice)

def create_article():
    article = {"title": input("title: "),
                           "text": input("text: "),
                           "writer": input("writer: ")}
    articles.append(article)
    print("Article created!")

@check_choice
def read_article(choice2):
    print(articles[choice2-1])

@check_choice
def edit_article(choice2):
    edit_options = {
        "1": "title",
        "2": "text",
        "3": "writer"
    }
    edit_choice = input("Що бажаєте змінити? 1 - Заголовок, 2 - Текст, 3 - Автор: ")
    if edit_choice in edit_options:
        edit_key = edit_options[edit_choice]
        articles[choice2 - 1][edit_key] = input(f"Нове значення для {edit_key}: ")
        print("Статтю відредаговано!")
    else:
        print("Невірний вибір. Будь ласка, спробуйте знову.")

@check_choice
def del_article(choice2):
    del articles[choice2 - 1]


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
import glob
import cv2
cwd = os.getcwd()

def Seperate(loadfile,savefolder):
    img = cv2.imread(loadfile + ".jpg",0)
    img = 255 - img
    ydone = False
    start_ypoints_crop = []
    end_ypoints_crop = []
    for row in range(len(img)):
        if any([ind < 200 for ind in iter(img[row])]) and not ydone:
            start_ypoints_crop.append(row)
            ydone = True
        if all([ind > 200 for ind in iter(img[row])]) and ydone:
            end_ypoints_crop.append(row)
            ydone = False

    CroppedImages= []
    for top,bot in zip(start_ypoints_crop,end_ypoints_crop):
        CroppedImages.append(img[top:bot,...])

    xdone = False
    start_xpoints_crop = []
    end_xpoints_crop = []
    for k in range(len(CroppedImages)):
        start_xpoints_crop.append([])
        end_xpoints_crop.append([])
    for i in range(len(CroppedImages)):
        for col in range(len(CroppedImages[0][0])):
            if any([ind < 200 for ind in iter(CroppedImages[i][...,col])]) and not xdone:
                start_xpoints_crop [i].append(col)
                xdone = True
            if all([ind > 200 for ind in iter(CroppedImages[i][...,col])]) and xdone:
                end_xpoints_crop [i].append(col)
                xdone = False

    Numbers=[]
    for i in range(len(CroppedImages)):
        for left,right in zip(start_xpoints_crop[i],end_xpoints_crop[i]):
            Numbers.append(CroppedImages[i][...,left-2:right+2])

    for i in range(len(Numbers)):
        cv2.copyMakeBorder(Numbers[i],4,4,10,10,cv2.BORDER_CONSTANT,value=255)
        cv2.imwrite(f'Images/{savefolder}/{i}.png',Numbers[i])

folders = ['Zeros','Ones','Twos','Threes','Fours','Fives','Sixes','Sevens','Eights','Nines']
final_images = []
for n in range(len(folders)):
    for file in glob.glob(f"{cwd}RawImages/{folders[n]}/*.jpg"):
        image = cv2.imread(file,0)
        image = cv2.copyMakeBorder(image,2,2,10,10,cv2.BORDER_CONSTANT,value=255)
        image = cv2.resize(image,(28,28))
        image = image / np.max(image)
        final_images.append(image)

from keras.utils import to_categorical

labels = []
final_labels = []
for n in range(len(folders)):
    for file in glob.glob(f"{cwd}{folders[n]}/*.jpg"):
        labels.append(n)
final_labels = to_categorical(labels,num_classes = 10)

from sklearn.model_selection import train_test_split
img_train, img_test, lbl_train, lbl_test = train_test_split(final_images, final_labels,
                                                            test_size=0.2, random_state=42, stratify=final_labels)

import keras
from keras.models import Sequential
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Flatten
from keras.layers import Dense
from keras.layers import Dropout

CNT_Model = Sequential()

CNT_Model.add(Conv2D(20,5, activation = 'relu', padding = 'same',kernel_regularizer = None, input_shape =(28,28,1)))
CNT_Model.add(MaxPooling2D(pool_size = (2,2)))
CNT_Model.add(Conv2D(20,5, activation = 'relu', padding = 'same', kernel_regularizer = None))
CNT_Model.add(MaxPooling2D(pool_size = (2,2)))
#CNT_Model.add(Dropout(0.5))
CNT_Model.add(Flatten())
CNT_Model.add(Dropout(0.5))
#CNT_Model.add(BatchNormalization())
CNT_Model.add(Dense(64,activation = 'relu', kernel_regularizer = None))
#CNT_Model.add(BatchNormalization())
CNT_Model.add(Dense(10,activation='sigmoid'))

Optimizer = keras.optimizers.SGD(lr=0.01, momentum = 0.5, decay = 1e-5, nesterov = True)

CNT_Model.compile(optimizer=Optimizer,
              loss='categorical_crossentropy',
              metrics=['accuracy'])
CNT_Model.summary()

img_train = np.reshape(img_train,(len(img_train),28,28,1))

from keras.callbacks import ModelCheckpoint
ann_hist = CNT_Model.fit(np.array(img_train), np.array(lbl_train), validation_split = 0.1, batch_size = 10, epochs = 2)

img_test = np.reshape(img_test,(len(img_test),28,28,1))

test_loss, test_acc = CNT_Model.evaluate(np.array(img_test), np.array(lbl_test), batch_size = 3)
CNT_Model.save('Model1.h5')
labels_predicted = CNT_Model.predict(np.array(img_test))
lbl= np.argmax(labels_predicted, axis=1)
print('Test loss is',test_loss)
print('Test accuracy is',test_acc)


import os
import json

from _thread import start_new_thread

from gi.repository import GLib, Gtk, GdkPixbuf, Notify
from .ChatController import ChatController
from .CreateTeamController import CreateTeamController
from .SearchChannelsController import SearchChannelsController
from Mattermost.ChannelModel import ChannelModel

class TeamController:
    __gladeBuilder = None           # Gtk.Builder
    __application = None            # Application
    __window = None                 # Gtk.Window
    __windowTitleTemplate = None    # string
    __serverModel = None            # Mattermost.ServerModel
    __loggedInModel = None          # Mattermost.ServerLoggedInModel
    __teamModel = None              # Mattermost.TeamModel
    __channelControllers = {}       # dict(Mattermost.ChatController)
    __channelTreeIterMap = {}       # dict(Gtk.TreeIter)
    __privateGroupTreeIterMap = {}  # dict(Gtk.TreeIter)
    __directMessageTreeIterMap = {} # dict(Gtk.TreeIter)

    FONT_WEIGHT_NORMAL=400
    FONT_WEIGHT_BOLD=700

    def __init__(self, application, url, username, password, teamName):
        self.__application = application

        self.__gladeBuilder = application.createGladeBuilder('team')
        self.__gladeBuilder.connect_signals(self)

        self.__window = self.__gladeBuilder.get_object('windowTeam')
        self.__windowTitleTemplate = self.__window.get_title()

        self.__serverModel = application.getServerModel(url)

        self.__loggedInModel = self.__serverModel.login(username, password, self.__onConnectionClosed)

        self.__teamModel = self.__loggedInModel.getTeam(teamName)

        self.__loggedInModel.registerPostedListener(self.onMessagePosted)

        # Mattermost.UserModel
        selfUser = self.__loggedInModel.getSelfUser()

        self.__teamModel.registerChannelCreatedListener(self.onChannelCreated, {
            'user_id': selfUser.getId()
        })

    def show(self):
        self.__reload()
        self.__window.show_all()

    def getChatController(self, channel):
        channelId = channel
        if type(channel) == ChannelModel:
            channelId = channel.getId()
        if channelId not in self.__channelControllers:
            if type(channel) != ChannelModel:
                # Mattermost.ChannelModel
                channel = self.__teamModel.getChannel(channelId)

            # ChatController
            chatController = None

            if channel != None:
                chatController = ChatController(self.__application, channel)

            self.__channelControllers[channelId] = chatController
        return self.__channelControllers[channelId]

    def onChannelCreated(self, data=None):
        channelId = data['channel_id']
        teamId = data['team_id']

        # Mattermost.TeamModel
        teamModel = self.__teamModel

        if teamId == teamModel.getId():
            # Mattermost.ChannelModel
            channelModel = teamModel.getChannel(channelId)

            self.__addChannelToView(channelModel)

    def onMessagePosted(self, data=None):

        # Gtk.Builder
        gladeBuilder = self.__gladeBuilder

        # Gtk.ListStore
        liststoreTeamChannels = gladeBuilder.get_object('liststoreTeamChannels')

        # Gtk.ListStore
        liststoreTeamPrivateGroups = gladeBuilder.get_object('liststoreTeamPrivateGroups')

        # Gtk.ListStore
        liststoreTeamDirectMessages = gladeBuilder.get_object('liststoreTeamDirectMessages')

        postJson = data['post']
        postData = json.loads(postJson)

        channelId = postData['channel_id']

        # Mattermost.ChatController
        chatController = self.getChatController(channelId)

        # Mattermost.ChannelModel
        channel = self.__teamModel.getChannel(channelId)

        # Mattermost.UserModel
        user = self.__loggedInModel.getUserById(postData['user_id'])

        # Mattermost.UserModel
        selfUser = self.__loggedInModel.getSelfUser()

        GLib.idle_add(chatController.show)

        if selfUser.getId() != user.getId():
            # Notify.Notification
            notification = Notify.Notification.new("gMattermost", "%s - %s: %s" % (
                channel.getDisplayName(),
                user.getUseName(),
                postData['message']
            ))
            success = notification.show()

        if postData['channel_id'] in self.__channelTreeIterMap:
            # Gtk.TreeIter
            treeIter = self.__channelTreeIterMap[channelId]

            liststoreTeamChannels.set_value(treeIter, 2, self.FONT_WEIGHT_BOLD)

        if postData['channel_id'] in self.__privateGroupTreeIterMap:
            # Gtk.TreeIter
            treeIter = self.__privateGroupTreeIterMap[channelId]

            liststoreTeamPrivateGroups.set_value(treeIter, 2, self.FONT_WEIGHT_BOLD)

        if postData['channel_id'] in self.__directMessageTreeIterMap:
            # Gtk.TreeIter
            treeIter = self.__directMessageTreeIterMap[channelId]

            liststoreTeamDirectMessages.set_value(treeIter, 2, self.FONT_WEIGHT_BOLD)

    def onSearchMoreChannelsClicked(self, button, data=None):
        searchChannels = SearchChannelsController(self.__application, self.__teamModel)
        searchChannels.show()

    def onCreateChannelClicked(self, button, data=None):
        createChannel = CreateTeamController(self.__application, self.__teamModel)
        createChannel.show()

    def onSearchMoreDirectMessagesClicked(self, button, data=None):
        searchChannels = SearchChannelsController(self.__application, self.__teamModel, "direct-message")
        searchChannels.show()

    def onCreatePrivateGroupClicked(self, button, data=None):
        createChannel = CreateTeamController(self.__application, self.__teamModel, "private-group")
        createChannel.show()

    def onTeamChannelRowActivated(self, treeView, treePath, column, data=None):
        # Gtk.TreeView
        # Gtk.TreePath
        # Gtk.TreeViewColumn

        # Gtk.Builder
        gladeBuilder = self.__gladeBuilder

        # Gtk.ListStore
        liststoreTeamChannels = gladeBuilder.get_object('liststoreTeamChannels')

        # Gtk.TreeIter
        treeIter = liststoreTeamChannels.get_iter(treePath)

        channelId = liststoreTeamChannels.get_value(treeIter, 1)

        # ChatController
        chatController = self.getChatController(channelId)
        chatController.show()

    def onTeamDirectMessageRowActivated(self, treeView, treePath, column, data=None):
        # Gtk.TreeView
        # Gtk.TreePath
        # Gtk.TreeViewColumn

        # Gtk.Builder
        gladeBuilder = self.__gladeBuilder

        # Gtk.ListStore
        liststoreTeamDirectMessages = gladeBuilder.get_object('liststoreTeamDirectMessages')

        # Gtk.TreeIter
        treeIter = liststoreTeamDirectMessages.get_iter(treePath)

        remoteUserId = liststoreTeamDirectMessages.get_value(treeIter, 1)

        for channelModelCandidate in self.__teamModel.getChannels():
            if channelModelCandidate.isDirectMessage():
                remoteUser = channelModelCandidate.getDirectMessageRemoteUser()
                if remoteUser != None and remoteUser.getId() == remoteUserId:
                    channelModel = channelModelCandidate

        if channelModel != None:
            # ChatController
            chatController = self.getChatController(channelModel)
            chatController.show()

    def onTeamPrivateGroupRowActivated(self, treeView, treePath, column, data=None):
        # Gtk.TreeView
        # Gtk.TreePath
        # Gtk.TreeViewColumn

        # Gtk.Builder
        gladeBuilder = self.__gladeBuilder

        # Gtk.ListStore
        liststoreTeamPrivateGroups = gladeBuilder.get_object('liststoreTeamPrivateGroups')

        # Gtk.TreeIter
        treeIter = liststoreTeamPrivateGroups.get_iter(treePath)

        channelId = liststoreTeamPrivateGroups.get_value(treeIter, 1)

        # ChatController
        chatController = self.getChatController(channelId)
        chatController.show()

    def __reload(self):
        # MattermostTeamModel
        teamModel = self.__teamModel

        # Gtk.Builder
        gladeBuilder = self.__gladeBuilder

        # Gtk.Image
        imageTeamAvatar = gladeBuilder.get_object('imageTeamAvatar')

        # Gtk.Window
        window = self.__window

        # UserModel
        selfUser = self.__loggedInModel.getSelfUser()

        cacheId = "avatar." + selfUser.getId() + ".png"
        avatarImagePath = self.__application.getCacheFilePath(cacheId)

        if not os.path.exists(avatarImagePath):
            avatarImageData = selfUser.getImage()
            self.__application.putCache(cacheId, avatarImageData)

        if os.path.exists(avatarImagePath):
            # GdkPixbuf.Pixbuf
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
                avatarImagePath,
                width=48,
                height=48,
                preserve_aspect_ratio=False
            )

            imageTeamAvatar.set_from_pixbuf(pixbuf)

        variables = {
            'USERNAME': selfUser.getUseName(),
            'TEAMNAME': teamModel.getName()
        }

        windowTitle = self.__windowTitleTemplate
        for variableKey in variables:
            windowTitle = windowTitle.replace("%"+variableKey+"%", variables[variableKey])
        window.set_title(windowTitle)

        for labelId in ["labelTeamNamesUsername", "labelTeamNamesTeam"]:
            # Gtk.Label
            label = gladeBuilder.get_object(labelId)

            templateText = label.get_text()
            for variableKey in variables:
                templateText = templateText.replace("%"+variableKey+"%", variables[variableKey])
            label.set_text(templateText)

        self.__addChannelsToView(teamModel.getChannels())

    def __addChannelsToView(self, channels):

        # Gtk.Builder
        gladeBuilder = self.__gladeBuilder

        # Gtk.ListStore
        liststoreTeamDirectMessages = gladeBuilder.get_object('liststoreTeamDirectMessages')

        directMessageUserIdToTreeIterMap = {}

        for channel in channels:
            # Mattermost.ChannelModel

            self.__addChannelToView(channel, directMessageUserIdToTreeIterMap)

        # Load all direct-message remote users at once
        # list(Mattermost.UserModel)
        remoteUsers = self.__loggedInModel.getUsersByIds(list(directMessageUserIdToTreeIterMap))

        for remoteUser in remoteUsers:
            # Mattermost.UserModel

            treeIter = directMessageUserIdToTreeIterMap[remoteUser.getId()]
            liststoreTeamDirectMessages.set_value(treeIter, 0, remoteUser.getUseName())

    def __addChannelToView(self, channel, directMessageUserIdToTreeIterMap=None):
        # Mattermost.ChannelModel

        # Gtk.Builder
        gladeBuilder = self.__gladeBuilder

        # Gtk.ListStore
        liststoreTeamChannels = gladeBuilder.get_object('liststoreTeamChannels')

        # Gtk.ListStore
        liststoreTeamPrivateGroups = gladeBuilder.get_object('liststoreTeamPrivateGroups')

        # Gtk.ListStore
        liststoreTeamDirectMessages = gladeBuilder.get_object('liststoreTeamDirectMessages')

        doSetDirectMessageNames = False
        if directMessageUserIdToTreeIterMap == None:
            directMessageUserIdToTreeIterMap = {}
            doSetDirectMessageNames = True

        # TODO: find out if channel's have unread messages

        if channel.isOpen():
            # Gtk.TreeIter
            treeIter = liststoreTeamChannels.append()

            self.__channelTreeIterMap[channel.getId()] = treeIter

            liststoreTeamChannels.set_value(treeIter, 0, channel.getDisplayName())
            liststoreTeamChannels.set_value(treeIter, 1, channel.getId())
            liststoreTeamChannels.set_value(treeIter, 2, self.FONT_WEIGHT_NORMAL) # hasUnreadMessages

        if channel.isDirectMessage():
            # Gtk.TreeIter
            treeIter = liststoreTeamDirectMessages.append()

            self.__directMessageTreeIterMap[channel.getId()] = treeIter

            remoteUserId = channel.getDirectMessageRemoteUserId()

            directMessageUserIdToTreeIterMap[remoteUserId] = treeIter

            if doSetDirectMessageNames:
                # Mattermost.UserModel
                remoteUser = self.__loggedInModel.getUserById(remoteUserId)

                liststoreTeamDirectMessages.set_value(treeIter, 0, remoteUser.getUseName())

            liststoreTeamDirectMessages.set_value(treeIter, 1, remoteUserId)
            liststoreTeamDirectMessages.set_value(treeIter, 2, self.FONT_WEIGHT_NORMAL) # hasUnreadMessages

        if channel.isPrivateGroup():
            # Gtk.TreeIter
            treeIter = liststoreTeamPrivateGroups.append()

            self.__privateGroupTreeIterMap[channel.getId()] = treeIter

            liststoreTeamPrivateGroups.set_value(treeIter, 0, channel.getDisplayName())
            liststoreTeamPrivateGroups.set_value(treeIter, 1, channel.getId())
            liststoreTeamPrivateGroups.set_value(treeIter, 2, self.FONT_WEIGHT_NORMAL) # hasUnreadMessages

    def __onConnectionClosed(self, serverLoggedInModel, exception):
        GLib.idle_add(self.__handleLostConnectionUI, serverLoggedInModel, exception)
        return False

    def __handleLostConnectionUI(self, serverLoggedInModel, exception):
        url = self.__serverModel.getUrl()

        self.__window.hide()

        dialog = Gtk.Dialog(
            "Lost connection to mattermost-server %s" % (url),
            self.__window,
            0,
            (Gtk.STOCK_OK, Gtk.ResponseType.OK)
        )
        dialog.get_content_area().add(Gtk.Label(str(exception)))
        dialog.show_all()
        dialog.run()
        dialog.destroy()

#


#!/usr/bin/env python
# -*- coding:utf-8 -*-

# django项目搭建与Session使用详解
# 前言
# Django完全支持也匿名会话，简单说就是使用跨网页之间可以进行通讯，比如显示用户名，用户是否已经发表评论。session框架让你存储和获取访问者的数据信息，这些信息保存在服务器上（默认是数据库中），以 cookies 的方式发送和获取一个包含 session ID的值，并不是用cookies传递数据本身。
# 本文给大家详细介绍了关于django项目搭建与Session使用的相关内容，分享出来供大家参考学习，下面话不多说了，来一起看看详细的介绍吧
# django+session+中间件
# 配置session
# django的session可以分为内存缓存存储、文件存储、数据库存储。这里我们采用数据库存储。
# django项目默认是开启session的，默认存储方式为：SESSION_ENGINE = 'django.contrib.sessions.backends.db'
# （本地缓存：SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
# 混合缓存：SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'）
# 当然也可以是redis缓存：
#
# CACHES = {"default": {"BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://ip:端口/",
#                       "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient", }}}
# SESSION_ENGINE = "django.contrib.sessions.backends.cache"
# SESSION_CACHE_ALIAS = "default"
# 我们这里使用django自带的sqlite存储session，settings配置如下：
# SESSION_COOKIE_SECURE = False
# SESSION_COOKIE_HTTPONLY = True
# SESSION_COOKIE_NAME = 'xxxx'
# SEESION_EXPIRE_AT_BROWSER_CLOSE = True
# SESSION_COOKIE_AGE = 18000
# 这里的设定的时间作用不大，可以直接在代码里指定时间：request.session.set_expiry(0) 参数0代表退出浏览器session即失效，单位均为毫秒
# 中间件MIDDLEWARE_CLASSES中查看是否有'django.contrib.sessions.middleware.SessionMiddleware' 没有的话添加即可
# 这里我们已经设置好了django session的环境，我们需要使用django自带的模块处理session值
# 这里需要强调一点，用sqlite数据库存储session需要初始化下数据库，在manage.py文件所在目录下执行命令：
# python manage.py makemigrations（python manage.py migrate）
# 至此，session准备工作已经完成。但是需要注意的是session失效，只是时间上失效，其值还是存在库中，因此需要定期删除，也可以在代码中逻辑删除，具体代码如下：
import sys

reload(sys)
sys.setdefaultencoding('utf-8')
import datetime
from django.contrib.sessions.backends.db import SessionStore
from django.contrib.sessions.models import Session

def delsession(key):
    if key is None:
        return
    store = SessionStore()
    store.delete(key)

count = Session.objects.all().count()
if count > 20:
    nowtime = datetime.datetime.now()
    outdatesession = Session.objects.filter(expire_date__lt=nowtime)
    for item in outdatesession:
        store.delete(item.session_key)

def delovertimesession():
    store = SessionStore()

count = Session.objects.all().count()
if count > 20:
    nowtime = datetime.datetime.now()
    outdatesession = Session.objects.filter(expire_date__lt=nowtime)
    for item in outdatesession:
        store.delete(item.session_key)

# 三、请求的中间件
# 安全认证、请求过滤以及session的初始值 我们可以在中间件中处理。
# 在djangocommon项目下，新增middlehttp.py文件，代码大致如下：
reload(sys)
sys.setdefaultencoding('utf-8')

from django.shortcuts import render_to_response

# process_request -------- 接受request之后确定view之前执行
# process_view 确定view之后 并且在view真正执行之前执行
# process_response view执行之后
# process_exception(self, request, exception) view抛出异常

class LoginRequiredMiddleware:
    def process_request(self, request):
        path = request.path_info.strip('/')
        # 这里写处理逻辑和请求控制

# 在settings文件中的中间件配置MIDDLEWARE_CLASSES中添加'djangocommon.middlehttp.LoginRequiredMiddleware' 如下：
# MIDDLEWARE_CLASSES = [
#  'django.middleware.security.SecurityMiddleware',
#  'django.contrib.sessions.middleware.SessionMiddleware',
#  'django.middleware.common.CommonMiddleware',
#  'django.middleware.csrf.CsrfViewMiddleware',
#  'django.contrib.auth.middleware.AuthenticationMiddleware',
#  'django.contrib.auth.middleware.SessionAuthenticationMiddleware',　　 'djangocommon.middlehttp.LoginRequiredMiddleware',
#  'django.contrib.messages.middleware.MessageMiddleware',
#  'django.middleware.clickjacking.XFrameOptionsMiddleware',
# ]
# 注意顺序，必须在SessionMiddleware之后。
# 至此，一个完整的django+session+中间件 项目搭建完成。前后端分离的项目，也可以以此为基础，通过webpack或cli 搭建 编译生成的templates及静态文件即可。


# 模组区 -------------
from 海龟模组 import *
import random as 随机

# 全域变数 -------------
最大层数 = 11
开始长度 = 300
长缩减最小 = 0.6
长缩减最大 = 0.8
开始宽度 = 40
宽缩减率 = 0.7
转角最小 = 10
转角最大 = 40
夹角最小 = 30
夹角最大 = 70
叶直径 = 3
叶层数 = 1

# 函式区 -------------
def 初始设定() :
    左转(90)
    速度('fastest')
    tracer(50, 0)
    视窗设定(1000, 1000)
    画笔颜色('black')
    停笔()
    走到(0,-500)
    bgpic('sunset1.gif')
    随机.seed(1234)

def 叶() :
    画点(叶直径, 'black')
    for 数 in range(4) :
        向前(叶直径 * 2)
        画点(叶直径, 'black')
        向后(叶直径 * 2)
        右转(90)

def 递回(层, 长, 宽) :
    if 层 > 最大层数 :
        return
    elif 层 > 最大层数 - 叶层数 :
        叶()

    下笔()
    画笔尺寸(宽)
    向前(长)
    停笔()

    转角 = 随机.randint(转角最小,转角最大)
    右转(转角)
    长缩减率 = 随机.uniform(长缩减最小, 长缩减最大)
    递回(层 + 1, 长 * 长缩减率, 宽 * 宽缩减率)

    夹角 = 随机.randint(夹角最小,夹角最大)
    左转(夹角)
    长缩减率 = 随机.uniform(长缩减最小, 长缩减最大)
    递回(层 + 1, 长 * 长缩减率, 宽 * 宽缩减率)

    右转(夹角 - 转角)
    向后(长)

# 主程式 -------------
初始设定()

递回(1, 开始长度, 开始宽度)
update()

完成()

課程資料/專題-海龜與樹/try.py


# -*- coding: utf-8 -*-
"""
Created on Wed Dec 14 20:53:05 2016

@author: yamane
"""

import numpy as np
from skimage import draw

class RandomCircleSquareDataset(object):
    def __init__(self, image_size=500, circle_r_min=50, circle_r_max=150,
                 size_min=50, size_max=200, p=[0.3, 0.3, 0.4], output_size=224,
                 aspect_ratio_max=4, aspect_ratio_min=1, batch_size=100):
        self.image_size = image_size
        self.cr_min = circle_r_min
        self.cr_max = circle_r_max
        self.size_min = size_min
        self.size_max = size_max
        self.p = p
        self.output_size = output_size
        self.ar_max = aspect_ratio_max
        self.ar_min = aspect_ratio_min
        self.batch_size = batch_size

    def __iter__(self):
        return self

    def next(self):
        images = []
        for i in range(self.batch_size):
            image = self.create_image()
            images.append(image)
        batch = np.stack(images, axis=0)
        return batch

    def create_image(self):
        case = np.random.choice(3, p=self.p)
        if case == 0:
            image = self.create_random_circle(
                self.image_size, self.cr_min, self.cr_max)
        elif case == 1:
            image = self.create_random_square(
                self.image_size, self.size_min, self.size_max)
        else:
            image = self.create_random_circle_square(
                self.image_size, self.cr_min, self.cr_max,
                self.size_min, self.size_max)
        return image

    def create_random_circle(self, image_size, r_min, r_max):
        image = np.zeros((image_size, image_size), dtype=np.float64)
        r = np.random.randint(r_min, r_max)
        x = np.random.randint(r-1, image_size - r + 1)
        y = np.random.randint(r-1, image_size - r + 1)

        rr, cc = draw.circle(x, y, r)
        image[rr, cc] = 1

        image = np.reshape(image, (image_size, image_size, 1))
        return image

    def create_random_square(self, image_size, size_min, size_max):
        image = np.zeros((image_size, image_size), dtype=np.float64)
        size = np.random.randint(size_min, size_max)
        x = np.random.randint(0, image_size-size+1)
        y = np.random.randint(0, image_size-size+1)

        for i in range(0, size):
            rr, cr = draw.line(y, x+i, y+size-1, x+i)
            image[rr, cr] = 1

        image = np.reshape(image, (image_size, image_size, 1))
        return image

    def create_random_circle_square(
            self, image_size, r_min, r_max, size_min, size_max):
        circle = self.create_random_circle(image_size, r_min, r_max)
        square = self.create_random_square(image_size, size_min, size_max)
        image = np.logical_or(circle, square)
        image = image.astype(np.float64)
        return image

    def __repr__(self):
        template = """image_size:{}
output_size:{}
circle_min:{}
circle_max:{}
size_min:{}
size_max:{}
p:{}
aspect_ratio_min:{}
aspect_ratio_max:{}"""
        return template.format(self.image_size, self.output_size, self.cr_min,
                               self.cr_max, self.size_min, self.size_max,
                               self.p, self.ar_min, self.ar_max)


r"""Utility functions to mock SCPI device over TCP.

Example Usage:
  MockServerHandler.AddLookup(r'\*CLS', None)
  SetupLookupTable()
  SERVER_PORT = 5025
  MockTestServer(('0.0.0.0', SERVER_PORT), MockServerHandler).serve_forever()
"""

import inspect
import logging
import re
import socketserver

class MockTestServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
  allow_reuse_address = True

class MockServerHandler(socketserver.StreamRequestHandler):
  """A mocking handler for socket.

  This handler responses client based on its pre-defined lookup table.
  Lookup table is a list of tuple where the first of each tuple is a regular
  expression and the second is response. Response could be one of None, string
  or function.

  Exceptions will be raised if input cannot match any of the regular expression
  from keys.
  """
  responses_lookup = []

  @classmethod
  def AddLookup(cls, input_line, response):
    # Check if the response is one of the known types.
    is_known_types = False
    if isinstance(response, (bytes, type(None))):
      is_known_types = True
    elif inspect.isfunction(response) or inspect.ismethod(response):
      is_known_types = True
    assert is_known_types, (
        'type %r of response is not supported' % type(response))
    cls.responses_lookup.append((input_line, response))

  def __init__(self, *args, **kwargs):
    self.lookup = list(self.responses_lookup)
    socketserver.StreamRequestHandler.__init__(self, *args, **kwargs)

  def handle(self):
    while True:
      line = self.rfile.readline().rstrip('\r\n')
      if not line:
        break
      matched = False
      for regexp, response in self.lookup:
        if not re.search(regexp, line):
          continue
        matched = True
        logging.info('Input %r matched with regexp %r', line, regexp)
        if inspect.isfunction(response) or inspect.ismethod(response):
          response = response(line)

        if isinstance(response, bytes):
          self.wfile.write(response)
        elif isinstance(response, type(None)):
          pass
        # Only the first match will be used.
        break
      if not matched:
        raise ValueError('Input %r is not matching any.' % line)


# -*- coding: utf-8 -*-
# lista2=[]
# global N
"""
Created on Sat Jul 25 18:03:22 2020

@author: rawre
"""
import pandas as pd
from tkinter import *
import Funciones as f
import Data as dt

# personaje
# puntosdevida
# iniciativa
# name
# danoacumulado
lista=[]
lista_de_iniciativa=[]
lista_personajes=[]
lista_enemigos=[]
lista_aliados=[]
class personaje:

    def __init__(self,iniciativa,name):
        self.iniciativa=iniciativa
        self.name=name
        self.id=id(self)
        self.rep=""
        lista.append(id(self))
        lista_de_iniciativa.append(iniciativa)
        lista_personajes.append(self)
        self.type_damage=dict()
        for i in dt.tipe_attack:
            p=i
            self.type_damage[i]=0
        self.altered=[]
    def cambiariniciativa(self,l):
        self.iniciativa=l
    def ponerImagen(self,imagen):
        self.jpg =imagen
        # self.foto = ImageTk.PhotoImage(Image.open(self.jpg))
    def repe(self,lista):
        t=f.saberRepeticion(lista,self.name)
        self.rep=t
class enemigo(personaje):
    def __init__(self,iniciativa,name):
        self.danoacumulado=0
        self.tipo=1
        self.jpg="None"
        personaje.__init__(self,iniciativa,name)
        lista_enemigos.append(self)

        # msg=('Se ha anadido a {0} con {1} de iniciativa')
        # print(msg.format(self.name,self.iniciativa))
    def __init__(self,iniciativa,name,ps):
        # self.danoacumulado=0
        self.tipo=1
        self.jpg="None"

        self.danoacumulado=ps
        personaje.__init__(self,iniciativa,name)
        lista_enemigos.append(self)
        # msg=('Se ha anadido a {0} con {1} de iniciativa')
        # print(msg.format(self.name,self.iniciativa))

    def atacar(self,ps):
        self.danoacumulado+=ps
    def curar(self,ps):
        self.atacar(-ps)
        if self.danoacumulado<0:
            self.danoacumulado=0
class aliado(personaje):
    def __init__(self,iniciativa,name,vida):
        self.tipo=0
        self.vida=vida
        self.jpg="None"
        self.rep=""
        personaje.__init__(self,iniciativa,name)
        lista_aliados.append(self)
        # msg=('Se ha anadido a {0} con {1} de iniciativa y {2}')
        # print(msg.format(self.name,self.iniciativa,self.vida))
    def atacar(self,ps):
        self.vida=self.vida-ps
        if self.vida<1:
            lista_aliados.remove(self)
            lista_personajes.remove(self)
    def curar(self,ps):
        self.atacar(-ps)

# f.menu(lista_personajes,lista_de_iniciativa,lista_aliados,lista_enemigos)


import importlib
import time
import re
from sys import argv
from typing import Optional

from LaylaRobot import (
    ALLOW_EXCL,
    CERT_PATH,
    DONATION_LINK,
    LOGGER,
    OWNER_ID,
    PORT,
    SUPPORT_CHAT,
    TOKEN,
    URL,
    WEBHOOK,
    SUPPORT_CHAT,
    dispatcher,
    StartTime,
    telethn,
    pbot,
    updater,
)

# needed to dynamically load modules
# NOTE: Module order is not guaranteed, specify that in the config file!
from LaylaRobot.modules import ALL_MODULES
from LaylaRobot.modules.helper_funcs.chat_status import is_user_admin
from LaylaRobot.modules.helper_funcs.misc import paginate_modules
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ParseMode, Update
from telegram.error import (
    BadRequest,
    ChatMigrated,
    NetworkError,
    TelegramError,
    TimedOut,
    Unauthorized,
)
from telegram.ext import (
    CallbackContext,
    CallbackQueryHandler,
    CommandHandler,
    Filters,
    MessageHandler,
)
from telegram.ext.dispatcher import DispatcherHandlerStop, run_async
from telegram.utils.helpers import escape_markdown

def get_readable_time(seconds: int) -> str:
    count = 0
    ping_time = ""
    time_list = []
    time_suffix_list = ["s", "m", "h", "days"]

    while count < 4:
        count += 1
        remainder, result = divmod(seconds, 60) if count < 3 else divmod(seconds, 24)
        if seconds == 0 and remainder == 0:
            break
        time_list.append(int(result))
        seconds = int(remainder)

    for x in range(len(time_list)):
        time_list[x] = str(time_list[x]) + time_suffix_list[x]
    if len(time_list) == 4:
        ping_time += time_list.pop() + ", "

    time_list.reverse()
    ping_time += ":".join(time_list)

    return ping_time

PM_START_TEXT = """
Hello, I'M Layla
`ɪ'ᴍ ʜᴇʀᴇ ᴛᴏ ʜᴇʟᴘ ʏᴏᴜ ᴍᴀɴᴀɢᴇ ʏᴏᴜʀ ɢʀᴏᴜᴘꜱ! ʜɪᴛ` /help
Maintained by @HEROGAMERS1 ❤
"""

buttons = [
    [
        InlineKeyboardButton(
            text="➕ ᴀᴅᴅ ᴛᴏ ʏᴏᴜʀ ɢʀᴏᴜᴘ ➕", url="t.me/LaylaRobot?startgroup=true"),
    ],
    [
        InlineKeyboardButton(text="ᴀʙᴏᴜᴛ", callback_data="layla_"),
        InlineKeyboardButton(
            text="ꜱᴜᴘᴘᴏʀᴛ", url=f"https://t.me/{SUPPORT_CHAT}"
        ),
    ],
    [
        InlineKeyboardButton(text="ʟᴏɢꜱ", url=f"https://t.me/laylalogs"),
        InlineKeyboardButton(
            text="ꜰᴇᴅᴇʀᴀᴛɪᴏɴ", url=f"https://t.me/AntiRippingOrganization/410"
        ),
    ],
    [
        InlineKeyboardButton(text="ʜᴇʟᴘ & ᴄᴏᴍᴍᴀɴᴅꜱ❔", callback_data="help_back"),
    ],
]

HELP_STRINGS = """
`Hi.. I'M` Layla
`ᴄʟɪᴄᴋ ᴏɴ ᴛʜᴇ ʙᴜᴛᴛᴏɴꜱ ʙᴇʟᴏᴡ ᴛᴏ ɢᴇᴛ ᴅᴏᴄᴜᴍᴇɴᴛᴀᴛɪᴏɴ ᴀʙᴏᴜᴛ ꜱᴘᴇᴄɪꜰɪᴄ ᴍᴏᴅᴜʟᴇꜱ..`
Powered by :- [Awesome Bots](t.me/Laylalist)"""

layla_IMG = "https://telegra.ph/file/524b78577a42b02b2f074.jpg"

DONATE_STRING = """Heya, glad to hear you want to donate!
 You can support the project [Hero](t.me/HEROGAMERS1) \
 Supporting isnt always financial! [AwesomeSupport](t.me/LaylaList) \
 Those who cannot provide monetary support are welcome to help us develop the bot at ."""

IMPORTED = {}
MIGRATEABLE = []
HELPABLE = {}
STATS = []
USER_INFO = []
DATA_IMPORT = []
DATA_EXPORT = []
CHAT_SETTINGS = {}
USER_SETTINGS = {}

for module_name in ALL_MODULES:
    imported_module = importlib.import_module("LaylaRobot.modules." + module_name)
    if not hasattr(imported_module, "__mod_name__"):
        imported_module.__mod_name__ = imported_module.__name__

    if imported_module.__mod_name__.lower() not in IMPORTED:
        IMPORTED[imported_module.__mod_name__.lower()] = imported_module
    else:
        raise Exception("Can't have two modules with the same name! Please change one")

    if hasattr(imported_module, "__help__") and imported_module.__help__:
        HELPABLE[imported_module.__mod_name__.lower()] = imported_module

    # Chats to migrate on chat_migrated events
    if hasattr(imported_module, "__migrate__"):
        MIGRATEABLE.append(imported_module)

    if hasattr(imported_module, "__stats__"):
        STATS.append(imported_module)

    if hasattr(imported_module, "__user_info__"):
        USER_INFO.append(imported_module)

    if hasattr(imported_module, "__import_data__"):
        DATA_IMPORT.append(imported_module)

    if hasattr(imported_module, "__export_data__"):
        DATA_EXPORT.append(imported_module)

    if hasattr(imported_module, "__chat_settings__"):
        CHAT_SETTINGS[imported_module.__mod_name__.lower()] = imported_module

    if hasattr(imported_module, "__user_settings__"):
        USER_SETTINGS[imported_module.__mod_name__.lower()] = imported_module

# do not async
def send_help(chat_id, text, keyboard=None):
    if not keyboard:
        keyboard = InlineKeyboardMarkup(paginate_modules(0, HELPABLE, "help"))
    dispatcher.bot.send_message(
        chat_id=chat_id,
        text=text,
        parse_mode=ParseMode.MARKDOWN,
        disable_web_page_preview=True,
        reply_markup=keyboard,
    )

@run_async
def test(update: Update, context: CallbackContext):
    # pprint(eval(str(update)))
    # update.effective_message.reply_text("Hola tester! _I_ *have* `markdown`", parse_mode=ParseMode.MARKDOWN)
    update.effective_message.reply_text("This person edited a message")
    print(update.effective_message)

@run_async
def start(update: Update, context: CallbackContext):
    args = context.args
    uptime = get_readable_time((time.time() - StartTime))
    if update.effective_chat.type == "private":
        if len(args) >= 1:
            if args[0].lower() == "help":
                send_help(update.effective_chat.id, HELP_STRINGS)
            elif args[0].lower().startswith("ghelp_"):
                mod = args[0].lower().split("_", 1)[1]
                if not HELPABLE.get(mod, False):
                    return
                send_help(
                    update.effective_chat.id,
                    HELPABLE[mod].__help__,
                    InlineKeyboardMarkup(
                        [[InlineKeyboardButton(text="⬅ BACK", callback_data="help_back")]]
                    ),
                )

            elif args[0].lower().startswith("stngs_"):
                match = re.match("stngs_(.*)", args[0].lower())
                chat = dispatcher.bot.getChat(match.group(1))

                if is_user_admin(chat, update.effective_user.id):
                    send_settings(match.group(1), update.effective_user.id, False)
                else:
                    send_settings(match.group(1), update.effective_user.id, True)

            elif args[0][1:].isdigit() and "rules" in IMPORTED:
                IMPORTED["rules"].send_rules(update, args[0], from_pm=True)

        else:
            update.effective_message.reply_text(
                PM_START_TEXT,
                reply_markup=InlineKeyboardMarkup(buttons),
                parse_mode=ParseMode.MARKDOWN,
                timeout=60,
            )
    else:
        update.effective_message.reply_text(
            "I'm awake already!\nHaven't slept since: {}".format(
                uptime
            ),
            parse_mode=ParseMode.HTML,
        )

def error_handler(update, context):
    """Log the error and send a telegram message to notify the developer."""
    # Log the error before we do anything else, so we can see it even if something breaks.
    LOGGER.error(msg="Exception while handling an update:", exc_info=context.error)

    # traceback.format_exception returns the usual python message about an exception, but as a
    # list of strings rather than a single string, so we have to join them together.
    tb_list = traceback.format_exception(
        None, context.error, context.error.__traceback__
    )
    tb = "".join(tb_list)

    # Build the message with some markup and additional information about what happened.
    message = (
        "An exception was raised while handling an update\n"
        "
update = {}
\n\n"
        "
{}
"
    ).format(
        html.escape(json.dumps(update.to_dict(), indent=2, ensure_ascii=False)),
        html.escape(tb),
    )

    if len(message) >= 4096:
        message = message[:4096]
    # Finally, send the message
    context.bot.send_message(chat_id=OWNER_ID, text=message, parse_mode=ParseMode.HTML)

# for test purposes
def error_callback(update: Update, context: CallbackContext):
    error = context.error
    try:
        raise error
    except Unauthorized:
        print("no nono1")
        print(error)
        # remove update.message.chat_id from conversation list
    except BadRequest:
        print("no nono2")
        print("BadRequest caught")
        print(error)

        # handle malformed requests - read more below!
    except TimedOut:
        print("no nono3")
        # handle slow connection problems
    except NetworkError:
        print("no nono4")
        # handle other connection problems
    except ChatMigrated as err:
        print("no nono5")
        print(err)
        # the chat_id of a group has changed, use e.new_chat_id instead
    except TelegramError:
        print(error)
        # handle all other telegram related errors

@run_async
def help_button(update, context):
    query = update.callback_query
    mod_match = re.match(r"help_module\((.+?)\)", query.data)
    prev_match = re.match(r"help_prev\((.+?)\)", query.data)
    next_match = re.match(r"help_next\((.+?)\)", query.data)
    back_match = re.match(r"help_back", query.data)

    print(query.message.chat.id)

    try:
        if mod_match:
            module = mod_match.group(1)
            text = (
                "Here is the help for the *{}* module:\n".format(
                    HELPABLE[module].__mod_name__
                )
                + HELPABLE[module].__help__
            )
            query.message.edit_text(
                text=text,
                parse_mode=ParseMode.MARKDOWN,
                disable_web_page_preview=True,
                reply_markup=InlineKeyboardMarkup(
                    [[InlineKeyboardButton(text="Back", callback_data="help_back")]]
                ),
            )

        elif prev_match:
            curr_page = int(prev_match.group(1))
            query.message.edit_text(
                text=HELP_STRINGS,
                parse_mode=ParseMode.MARKDOWN,
                reply_markup=InlineKeyboardMarkup(
                    paginate_modules(curr_page - 1, HELPABLE, "help")
                ),
            )

        elif next_match:
            next_page = int(next_match.group(1))
            query.message.edit_text(
                text=HELP_STRINGS,
                parse_mode=ParseMode.MARKDOWN,
                reply_markup=InlineKeyboardMarkup(
                    paginate_modules(next_page + 1, HELPABLE, "help")
                ),
            )

        elif back_match:
            query.message.edit_text(
                text=HELP_STRINGS,
                parse_mode=ParseMode.MARKDOWN,
                reply_markup=InlineKeyboardMarkup(
                    paginate_modules(0, HELPABLE, "help")
                ),
            )

        # ensure no spinny white circle
        context.bot.answer_callback_query(query.id)
        # query.message.delete()

    except BadRequest:
        pass

@run_async
def layla_about_callback(update, context):
    query = update.callback_query
    if query.data == "layla_":
        query.message.edit_text(
            text=""" ℹ I'm *Layla*, a powerful group management bot built to help you manage your group easily.
                 \n❍ I can restrict users.
                 \n❍ I can greet users with customizable welcome messages and even set a group's rules.
                 \n❍ I have an advanced anti-flood system.
                 \n❍ I can warn users until they reach max warns, with each predefined actions such as ban, mute, kick, etc.
                 \n❍ I have a note keeping system, blacklists, and even predetermined replies on certain keywords.
                 \n❍ I check for admins' permissions before executing any command and more stuffs
                 \n\n_Layla's licensed under the GNU General Public License v3.0_
                 \n❍ Awesome Bots @LaylaList
                 \n❍ Support Group @AwesomeSupport
                 \n❍ Assistant @LaylaAssistant.
                 \nHere is the [💾Repository](https://github.com/QueenArzoo/LaylaRobot).
                 \n\nIf you have any question about Layla, let us know at .""",
            parse_mode=ParseMode.MARKDOWN,
            disable_web_page_preview=True,
            reply_markup=InlineKeyboardMarkup(
                [
                 [
                    InlineKeyboardButton(text="Back", callback_data="layla_back")
                 ]
                ]
            ),
        )
    elif query.data == "layla_back":
        query.message.edit_text(
                PM_START_TEXT,
                reply_markup=InlineKeyboardMarkup(buttons),
                parse_mode=ParseMode.MARKDOWN,
                timeout=60,
                disable_web_page_preview=False,
        )

@run_async
def Source_about_callback(update, context):
    query = update.callback_query
    if query.data == "source_":
        query.message.edit_text(
            text=""" Hi..🤗 I'm *Layla*
                 \nHere is the [Source Code](https://github.com/QueenArzoo/LaylaRobot) .""",
            parse_mode=ParseMode.MARKDOWN,
            disable_web_page_preview=True,
            reply_markup=InlineKeyboardMarkup(
                [
                 [
                    InlineKeyboardButton(text="Go Back", callback_data="source_back")
                 ]
                ]
            ),
        )
    elif query.data == "source_back":
        query.message.edit_text(
                PM_START_TEXT,
                reply_markup=InlineKeyboardMarkup(buttons),
                parse_mode=ParseMode.MARKDOWN,
                timeout=60,
                disable_web_page_preview=False,
        )

@run_async
def get_help(update: Update, context: CallbackContext):
    chat = update.effective_chat  # type: Optional[Chat]
    args = update.effective_message.text.split(None, 1)

    # ONLY send help in PM
    if chat.type != chat.PRIVATE:
        if len(args) >= 2 and any(args[1].lower() == x for x in HELPABLE):
            module = args[1].lower()
            update.effective_message.reply_text(
                f"Contact me in PM to get help of {module.capitalize()}",
                reply_markup=InlineKeyboardMarkup(
                    [
                        [
                            InlineKeyboardButton(
                                text="Help",
                                url="t.me/{}?start=ghelp_{}".format(
                                    context.bot.username, module
                                ),
                            )
                        ]
                    ]
                ),
            )
            return
        update.effective_message.reply_text(
            "Contact me in PM to get the list of possible commands.",
            reply_markup=InlineKeyboardMarkup(
                [
                    [
                        InlineKeyboardButton(
                            text="Help",
                            url="t.me/{}?start=help".format(context.bot.username),
                        )
                    ]
                ]
            ),
        )
        return

    elif len(args) >= 2 and any(args[1].lower() == x for x in HELPABLE):
        module = args[1].lower()
        text = (
            "Here is the available help for the *{}* module:\n".format(
                HELPABLE[module].__mod_name__
            )
            + HELPABLE[module].__help__
        )
        send_help(
            chat.id,
            text,
            InlineKeyboardMarkup(
                [[InlineKeyboardButton(text="Back", callback_data="help_back")]]
            ),
        )

    else:
        send_help(chat.id, HELP_STRINGS)

def send_settings(chat_id, user_id, user=False):
    if user:
        if USER_SETTINGS:
            settings = "\n\n".join(
                "*{}*:\n{}".format(mod.__mod_name__, mod.__user_settings__(user_id))
                for mod in USER_SETTINGS.values()
            )
            dispatcher.bot.send_message(
                user_id,
                "These are your current settings:" + "\n\n" + settings,
                parse_mode=ParseMode.MARKDOWN,
            )

        else:
            dispatcher.bot.send_message(
                user_id,
                "Seems like there aren't any user specific settings available :'(",
                parse_mode=ParseMode.MARKDOWN,
            )

    else:
        if CHAT_SETTINGS:
            chat_name = dispatcher.bot.getChat(chat_id).title
            dispatcher.bot.send_message(
                user_id,
                text="Which module would you like to check {}'s settings for?".format(
                    chat_name
                ),
                reply_markup=InlineKeyboardMarkup(
                    paginate_modules(0, CHAT_SETTINGS, "stngs", chat=chat_id)
                ),
            )
        else:
            dispatcher.bot.send_message(
                user_id,
                "Seems like there aren't any chat settings available :'(\nSend this "
                "in a group chat you're admin in to find its current settings!",
                parse_mode=ParseMode.MARKDOWN,
            )

@run_async
def settings_button(update: Update, context: CallbackContext):
    query = update.callback_query
    user = update.effective_user
    bot = context.bot
    mod_match = re.match(r"stngs_module\((.+?),(.+?)\)", query.data)
    prev_match = re.match(r"stngs_prev\((.+?),(.+?)\)", query.data)
    next_match = re.match(r"stngs_next\((.+?),(.+?)\)", query.data)
    back_match = re.match(r"stngs_back\((.+?)\)", query.data)
    try:
        if mod_match:
            chat_id = mod_match.group(1)
            module = mod_match.group(2)
            chat = bot.get_chat(chat_id)
            text = "*{}* has the following settings for the *{}* module:\n\n".format(
                escape_markdown(chat.title), CHAT_SETTINGS[module].__mod_name__
            ) + CHAT_SETTINGS[module].__chat_settings__(chat_id, user.id)
            query.message.reply_text(
                text=text,
                parse_mode=ParseMode.MARKDOWN,
                reply_markup=InlineKeyboardMarkup(
                    [
                        [
                            InlineKeyboardButton(
                                text="Back",
                                callback_data="stngs_back({})".format(chat_id),
                            )
                        ]
                    ]
                ),
            )

        elif prev_match:
            chat_id = prev_match.group(1)
            curr_page = int(prev_match.group(2))
            chat = bot.get_chat(chat_id)
            query.message.reply_text(
                "Hi there! There are quite a few settings for {} - go ahead and pick what "
                "you're interested in.".format(chat.title),
                reply_markup=InlineKeyboardMarkup(
                    paginate_modules(
                        curr_page - 1, CHAT_SETTINGS, "stngs", chat=chat_id
                    )
                ),
            )

        elif next_match:
            chat_id = next_match.group(1)
            next_page = int(next_match.group(2))
            chat = bot.get_chat(chat_id)
            query.message.reply_text(
                "Hi there! There are quite a few settings for {} - go ahead and pick what "
                "you're interested in.".format(chat.title),
                reply_markup=InlineKeyboardMarkup(
                    paginate_modules(
                        next_page + 1, CHAT_SETTINGS, "stngs", chat=chat_id
                    )
                ),
            )

        elif back_match:
            chat_id = back_match.group(1)
            chat = bot.get_chat(chat_id)
            query.message.reply_text(
                text="Hi there! There are quite a few settings for {} - go ahead and pick what "
                "you're interested in.".format(escape_markdown(chat.title)),
                parse_mode=ParseMode.MARKDOWN,
                reply_markup=InlineKeyboardMarkup(
                    paginate_modules(0, CHAT_SETTINGS, "stngs", chat=chat_id)
                ),
            )

        # ensure no spinny white circle
        bot.answer_callback_query(query.id)
        query.message.delete()
    except BadRequest as excp:
        if excp.message not in [
            "Message is not modified",
            "Query_id_invalid",
            "Message can't be deleted",
        ]:
            LOGGER.exception("Exception in settings buttons. %s", str(query.data))

@run_async
def get_settings(update: Update, context: CallbackContext):
    chat = update.effective_chat  # type: Optional[Chat]
    user = update.effective_user  # type: Optional[User]
    msg = update.effective_message  # type: Optional[Message]

    # ONLY send settings in PM
    if chat.type != chat.PRIVATE:
        if is_user_admin(chat, user.id):
            text = "Click here to get this chat's settings, as well as yours."
            msg.reply_text(
                text,
                reply_markup=InlineKeyboardMarkup(
                    [
                        [
                            InlineKeyboardButton(
                                text="Settings",
                                url="t.me/{}?start=stngs_{}".format(
                                    context.bot.username, chat.id
                                ),
                            )
                        ]
                    ]
                ),
            )
        else:
            text = "Click here to check your settings."

    else:
        send_settings(chat.id, user.id, True)

@run_async
def donate(update: Update, context: CallbackContext):
    user = update.effective_message.from_user
    chat = update.effective_chat  # type: Optional[Chat]
    bot = context.bot
    if chat.type == "private":
        update.effective_message.reply_text(
            DONATE_STRING, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True
        )

        if OWNER_ID != 254318997 and DONATION_LINK:
            update.effective_message.reply_text(
                "You can also donate to the person currently running me "
                "[here]({})".format(DONATION_LINK),
                parse_mode=ParseMode.MARKDOWN,
            )

    else:
        try:
            bot.send_message(
                user.id,
                DONATE_STRING,
                parse_mode=ParseMode.MARKDOWN,
                disable_web_page_preview=True,
            )

            update.effective_message.reply_text(
                "I've PM'ed you about donating to my creator!"
            )
        except Unauthorized:
            update.effective_message.reply_text(
                "Contact me in PM first to get donation information."
            )

def migrate_chats(update: Update, context: CallbackContext):
    msg = update.effective_message  # type: Optional[Message]
    if msg.migrate_to_chat_id:
        old_chat = update.effective_chat.id
        new_chat = msg.migrate_to_chat_id
    elif msg.migrate_from_chat_id:
        old_chat = msg.migrate_from_chat_id
        new_chat = update.effective_chat.id
    else:
        return

    LOGGER.info("Migrating from %s, to %s", str(old_chat), str(new_chat))
    for mod in MIGRATEABLE:
        mod.__migrate__(old_chat, new_chat)

    LOGGER.info("Successfully migrated!")
    raise DispatcherHandlerStop

def main():

    if SUPPORT_CHAT is not None and isinstance(SUPPORT_CHAT, str):
        try:
            dispatcher.bot.sendMessage(f"@{SUPPORT_CHAT}", "Yes I'm alive 😹")
        except Unauthorized:
            LOGGER.warning(
                "Bot isnt able to send message to support_chat, go and check!"
            )
        except BadRequest as e:
            LOGGER.warning(e.message)

    test_handler = CommandHandler("test", test)
    start_handler = CommandHandler("start", start)

    help_handler = CommandHandler("help", get_help)
    help_callback_handler = CallbackQueryHandler(help_button, pattern=r"help_.*")

    settings_handler = CommandHandler("settings", get_settings)
    settings_callback_handler = CallbackQueryHandler(settings_button, pattern=r"stngs_")

    about_callback_handler = CallbackQueryHandler(layla_about_callback, pattern=r"layla_")
    source_callback_handler = CallbackQueryHandler(Source_about_callback, pattern=r"source_")

    donate_handler = CommandHandler("donate", donate)
    migrate_handler = MessageHandler(Filters.status_update.migrate, migrate_chats)

    # dispatcher.add_handler(test_handler)
    dispatcher.add_handler(start_handler)
    dispatcher.add_handler(help_handler)
    dispatcher.add_handler(about_callback_handler)
    dispatcher.add_handler(source_callback_handler)
    dispatcher.add_handler(settings_handler)
    dispatcher.add_handler(help_callback_handler)
    dispatcher.add_handler(settings_callback_handler)
    dispatcher.add_handler(migrate_handler)
    dispatcher.add_handler(donate_handler)

    dispatcher.add_error_handler(error_callback)

    if WEBHOOK:
        LOGGER.info("Using webhooks.")
        updater.start_webhook(listen="0.0.0.0", port=PORT, url_path=TOKEN)

        if CERT_PATH:
            updater.bot.set_webhook(url=URL + TOKEN, certificate=open(CERT_PATH, "rb"))
        else:
            updater.bot.set_webhook(url=URL + TOKEN)

    else:
        LOGGER.info("Using long polling.")
        updater.start_polling(timeout=15, read_latency=4, clean=True)

    if len(argv) not in (1, 3, 4):
        telethn.disconnect()
    else:
        telethn.run_until_disconnected()

    updater.idle()

if __name__ == "__main__":
    LOGGER.info("Successfully loaded modules: " + str(ALL_MODULES))
    telethn.start(bot_token=TOKEN)
    pbot.start()
    main()


from DAO import NaturezaDAO as n

#OBJETO NATUREZA
class Natureza():
    def __init__(self, id_natureza):
        #INSTÂNCIA DA BUSCA NA BASE DE DADOS
        naturezaDAO = n.NaturezaDAO()
        self.idNatureza, self.nomeNatureza, self.descricaoNatureza  = naturezaDAO.getNatureza(id_natureza) #ID DA NATUREZA, NOME NATUREZA E DESCRIÇÃO NATUREZA
        self.naturezas                                              = naturezaDAO.getTodasNaturezas() #TODAS AS NATUREZAS


def fibnumber(n, k):
	a, b = 1, 1
	for i in range(2, n ):
		a, b = b, k*a+b
	return b

#this example you can use the true value of n.

#this is way faster than recursion