# SPDX-License-Identifier: MIT
import re
import warnings
from dataclasses import dataclass
from functools import cached_property
from itertools import chain
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, TypeVar, Union, cast
from xml.etree import ElementTree

from deprecation import deprecated

from .additionalaudience import AdditionalAudience
from .admindata import AdminData
from .companydata import CompanyData
from .comparaminstance import ComparamInstance
from .comparamspec import ComparamSpec
from .diagcomm import DiagComm
from .diagdatadictionaryspec import DiagDataDictionarySpec
from .diaglayerraw import DiagLayerRaw
from .diaglayertype import DiagLayerType
from .diagservice import DiagService
from .ecuvariantpattern import EcuVariantPattern
from .exceptions import DecodeError, OdxWarning, odxassert
from .functionalclass import FunctionalClass
from .message import Message
from .nameditemlist import NamedItemList, OdxNamed
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
from .parentref import ParentRef
from .protstack import ProtStack
from .request import Request
from .response import Response
from .servicebinner import ServiceBinner
from .singleecujob import SingleEcuJob
from .specialdatagroup import SpecialDataGroup
from .statechart import StateChart
from .table import Table
from .unitgroup import UnitGroup
from .unitspec import UnitSpec

T = TypeVar("T")
TNamed = TypeVar("TNamed", bound=OdxNamed)

PrefixTree = Dict[int, Union[List[DiagService], "PrefixTree"]]

@dataclass
class DiagLayer:
    """This class represents a "logical view" upon a diagnostic layer
    according to the ODX standard.

    i.e. it handles the value inheritance, communication parameters,
    encoding/decoding of data, etc.
    """

    diag_layer_raw: DiagLayerRaw

    @staticmethod
    def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DiagLayer":
        diag_layer_raw = DiagLayerRaw.from_et(et_element, doc_frags)

        # Create DiagLayer
        return DiagLayer(diag_layer_raw=diag_layer_raw)

    def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
        """Construct a mapping from IDs to all objects that are contained in this diagnostic layer."""
        result = self.diag_layer_raw._build_odxlinks()

        # we want to get the full diag layer, not just the raw layer
        # when referencing...
        result[self.odx_id] = self

        return result

    def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
        """Recursively resolve all references."""

        self.diag_layer_raw._resolve_odxlinks(odxlinks)

    def _finalize_init(self, odxlinks: OdxLinkDatabase) -> None:
        """This method deals with everything inheritance related and
        -- after the final set of objects covered by the diagnostic
        layer is determined -- resolves any short name references in
        the diagnostic layer.

        TODO: In some corner cases, the short name resolution is not
        correct: E.g. Given three layers A, B, and C, where B and C
        derive from A and A defines the diagnostic communication
        SA. If now B overrides SA and there are short name references
        to SA in A, the object to which this reference is resolved is
        undefined. An easy fix for this problem is to copy all
        inherited objects in derived layers, but that would lead to
        excessive memory consumption for large databases...
        """

        #####
        # fill in all applicable objects that use value inheritance
        #####

        # diagnostic communication objects with the ODXLINKs resolved
        diag_comms = self._compute_available_diag_comms(odxlinks)
        self._diag_comms = NamedItemList(diag_comms)

        # filter the diag comms for services and single-ECU jobs
        services = [dc for dc in diag_comms if isinstance(dc, DiagService)]
        single_ecu_jobs = [dc for dc in diag_comms if isinstance(dc, SingleEcuJob)]
        self._services = NamedItemList(services)
        self._single_ecu_jobs = NamedItemList(single_ecu_jobs)

        global_negative_responses = self._compute_available_global_neg_responses(odxlinks)
        self._global_negative_responses = NamedItemList(global_negative_responses)

        functional_classes = self._compute_available_functional_classes()
        self._functional_classes = NamedItemList(functional_classes)

        additional_audiences = self._compute_available_additional_audiences()
        self._additional_audiences = NamedItemList(additional_audiences)

        state_charts = self._compute_available_state_charts()
        self._state_charts = NamedItemList(state_charts)

        ############
        # create a new unit_spec object. This is necessary because
        # unit_groups are subject to value inheritance.

        # unit groups applicable to this diaglayer (i.e., including
        # value inheritance)
        unit_groups = self._compute_available_unit_groups()

        # convenience variable for the locally-defined unit spec
        local_unit_spec: Optional[UnitSpec]
        if self.diag_layer_raw.diag_data_dictionary_spec is not None:
            local_unit_spec = self.diag_layer_raw.diag_data_dictionary_spec.unit_spec
        else:
            local_unit_spec = None

        unit_spec: Optional[UnitSpec]
        if local_unit_spec is None and not unit_groups:
            # no locally defined unit spec and no inherited unit groups
            unit_spec = None
        elif local_unit_spec is None:
            # no locally defined unit spec but inherited unit groups
            unit_spec = UnitSpec(
                unit_groups=NamedItemList(unit_groups),
                units=NamedItemList([]),
                physical_dimensions=NamedItemList([]),
                sdgs=[])
        else:
            # locally defined unit spec and inherited unit groups
            unit_spec = UnitSpec(
                unit_groups=NamedItemList(unit_groups),
                units=local_unit_spec.units,
                physical_dimensions=local_unit_spec.physical_dimensions,
                sdgs=[])
        ############

        dops = self._compute_available_ddd_spec_items(
            lambda ddd_spec: ddd_spec.data_object_props,
            lambda parent_ref: parent_ref.not_inherited_dops,
        )
        structures = self._compute_available_ddd_spec_items(
            lambda ddd_spec: ddd_spec.structures,
            lambda parent_ref: parent_ref.not_inherited_dops,
        )
        dtc_dops = self._compute_available_ddd_spec_items(
            lambda ddd_spec: ddd_spec.dtc_dops,
            lambda parent_ref: parent_ref.not_inherited_dops,
        )
        end_of_pdu_fields = self._compute_available_ddd_spec_items(
            lambda ddd_spec: ddd_spec.end_of_pdu_fields,
            lambda parent_ref: parent_ref.not_inherited_dops,
        )
        dynamic_length_fields = self._compute_available_ddd_spec_items(
            lambda ddd_spec: ddd_spec.dynamic_length_fields,
            lambda parent_ref: parent_ref.not_inherited_dops,
        )
        env_data_descs = self._compute_available_ddd_spec_items(
            lambda ddd_spec: ddd_spec.env_data_descs,
            lambda parent_ref: parent_ref.not_inherited_dops,
        )
        env_datas = self._compute_available_ddd_spec_items(
            lambda ddd_spec: ddd_spec.env_datas, lambda parent_ref: parent_ref.not_inherited_dops)
        muxs = self._compute_available_ddd_spec_items(
            lambda ddd_spec: ddd_spec.muxs, lambda parent_ref: parent_ref.not_inherited_dops)
        tables = self._compute_available_ddd_spec_items(
            lambda ddd_spec: ddd_spec.tables, lambda parent_ref: parent_ref.not_inherited_tables)
        ddds_sdgs: List[SpecialDataGroup]
        if self.diag_layer_raw.diag_data_dictionary_spec:
            ddds_sdgs = self.diag_layer_raw.diag_data_dictionary_spec.sdgs
        else:
            ddds_sdgs = []

        # create a DiagDataDictionarySpec which includes all the
        # inherited objects. To me, this seems rather inelegant, but
        # hey, it's described like this in the standard.
        self._diag_data_dictionary_spec = DiagDataDictionarySpec(
            data_object_props=dops,
            dtc_dops=dtc_dops,
            structures=structures,
            end_of_pdu_fields=end_of_pdu_fields,
            dynamic_length_fields=dynamic_length_fields,
            tables=tables,
            env_data_descs=env_data_descs,
            env_datas=env_datas,
            muxs=muxs,
            unit_spec=unit_spec,
            sdgs=ddds_sdgs,
        )

        #####
        # compute the communication parameters applicable to the
        # diagnostic layer. Note that communication parameters do
        # *not* use value inheritance, but a slightly different
        # scheme, cf the docstring of
        # _compute_available_commmunication_parameters().
        #####
        self._comparams = NamedItemList(self._compute_available_commmunication_parameters())

        #####
        # resolve all SNREFs. TODO: We allow SNREFS to objects that
        # were inherited by the diaglayer. This might not be allowed
        # by the spec (So far, I haven't found any definitive
        # statement...)
        #####
        self.diag_layer_raw._resolve_snrefs(self)

    #####
    # <convenience functionality>
    #####
    @cached_property
    def service_groups(self) -> ServiceBinner:
        return ServiceBinner(self.services)

    #####
    # </convenience functionality>
    #####

    #####
    # <properties forwarded to the "raw" diag layer>
    #####
    @property
    def variant_type(self) -> DiagLayerType:
        return self.diag_layer_raw.variant_type

    @property
    def odx_id(self) -> OdxLinkId:
        return self.diag_layer_raw.odx_id

    @property
    def short_name(self) -> str:
        return self.diag_layer_raw.short_name

    @property
    def long_name(self) -> Optional[str]:
        return self.diag_layer_raw.long_name

    @property
    def description(self) -> Optional[str]:
        return self.diag_layer_raw.description

    @property
    def admin_data(self) -> Optional[AdminData]:
        return self.diag_layer_raw.admin_data

    @property
    def company_datas(self) -> NamedItemList[CompanyData]:
        return self.diag_layer_raw.company_datas

    @property
    def requests(self) -> NamedItemList[Request]:
        return self.diag_layer_raw.requests

    @property
    def positive_responses(self) -> NamedItemList[Response]:
        return self.diag_layer_raw.positive_responses

    @property
    def negative_responses(self) -> NamedItemList[Response]:
        return self.diag_layer_raw.negative_responses

    @property
    def import_refs(self) -> List[OdxLinkRef]:
        return self.diag_layer_raw.import_refs

    @property
    def sdgs(self) -> List[SpecialDataGroup]:
        return self.diag_layer_raw.sdgs

    @property
    def parent_refs(self) -> List[ParentRef]:
        return self.diag_layer_raw.parent_refs

    @property
    def ecu_variant_patterns(self) -> List[EcuVariantPattern]:
        return self.diag_layer_raw.ecu_variant_patterns

    @property
    def comparam_spec_ref(self) -> Optional[OdxLinkRef]:
        return self.diag_layer_raw.comparam_spec_ref

    @property
    def prot_stack_snref(self) -> Optional[str]:
        return self.diag_layer_raw.prot_stack_snref

    @property
    def comparam_spec(self) -> Optional[ComparamSpec]:
        return self.diag_layer_raw.comparam_spec

    @property
    def prot_stack(self) -> Optional[ProtStack]:
        return self.diag_layer_raw.prot_stack

    #####
    # </properties forwarded to the "raw" diag layer>
    #####

    #######
    # <stuff subject to value inheritance>
    #######
    @property
    def diag_comms(self) -> NamedItemList[DiagComm]:
        """All diagnostic communication primitives applicable to this DiagLayer

        Diagnostic communication primitives are diagnostic services as
        well as single-ECU jobs. This list has all references
        resolved.
        """
        return self._diag_comms

    @property
    def services(self) -> NamedItemList[DiagService]:
        """All diagnostic services applicable to this DiagLayer

        This is a subset of all diagnostic communication
        primitives. All references are resolved in the list returned.
        """
        return self._services

    @property
    def single_ecu_jobs(self) -> NamedItemList[SingleEcuJob]:
        """All single-ECU jobs applicable to this DiagLayer

        This is a subset of all diagnostic communication
        primitives. All references are resolved in the list returned.
        """
        return self._single_ecu_jobs

    @property
    def global_negative_responses(self) -> NamedItemList[Response]:
        """All global negative responses applicable to this DiagLayer"""
        return self._global_negative_responses

    @property
    @deprecated(details="use diag_data_dictionary_spec.tables")
    def tables(self) -> NamedItemList[Table]:
        return self.diag_data_dictionary_spec.tables

    @property
    def functional_classes(self) -> NamedItemList[FunctionalClass]:
        """All functional classes applicable to this DiagLayer"""
        return self._functional_classes

    @property
    def state_charts(self) -> NamedItemList[StateChart]:
        """All state charts applicable to this DiagLayer"""
        return self._state_charts

    @property
    def additional_audiences(self) -> NamedItemList[AdditionalAudience]:
        """All audiences applicable to this DiagLayer"""
        return self._additional_audiences

    @property
    def diag_data_dictionary_spec(self) -> DiagDataDictionarySpec:
        """The DiagDataDictionarySpec applicable to this DiagLayer"""
        return self._diag_data_dictionary_spec

    #######
    # </stuff subject to value inheritance>
    #######

    #####
    # <value inheritance mechanism helpers>
    #####
    def _get_parent_refs_sorted_by_priority(self, reverse: bool = False) -> Iterable[ParentRef]:
        return sorted(
            self.diag_layer_raw.parent_refs,
            key=lambda pr: pr.layer.variant_type.inheritance_priority,
            reverse=reverse)

    def _compute_available_objects(
        self,
        get_local_objects: Callable[["DiagLayer"], Iterable[T]],
        get_not_inherited: Callable[[ParentRef], Iterable[str]],
    ) -> Iterable[T]:
        """Helper method to compute the set of all objects applicable
        to the DiagLayer if these objects are subject to the value
        inheritance mechanism

        Note that all objects subject to the value inheritance
        mechanism exhibit a short_name attribute.

        :param get_local_objects: Function mapping a DiagLayer to the
        set of objects that are locally defined by that DiagLayer. If
        any of these objects have already been defined in any of the
        parents, the locally defined instance overrides them.

        :param get_not_inherited: Function mapping a ParentRef to the
        set of short names of the objects which shall not be inherited
        from the parents.

        """

        result_dict: Dict[str, T] = {}

        # populate the result dictionary with the inherited objects
        #
        # TODO (?): make sure that there are no "illegal" collisions
        # i.e., different objects with the same short name stemming
        # from parent layers exhibiting the same priority that are not
        # overwritten by a locally defined object. (IMO, this is quite
        # a corner case.)
        for parent_ref in self._get_parent_refs_sorted_by_priority():
            parent_dl = parent_ref.layer
            for dc in parent_dl._compute_available_objects(get_local_objects, get_not_inherited):
                result_dict[dc.short_name] = dc  # type: ignore[attr-defined]

            # remove the explictly not inherited objects
            for sn in get_not_inherited(parent_ref):
                if sn in result_dict:
                    del result_dict[sn]

        # consider the locally defined objects (override the
        # inherited entries or add new ones)
        for obj in get_local_objects(self):
            result_dict[obj.short_name] = obj  # type: ignore[attr-defined]

        return result_dict.values()

    def _get_local_diag_comms(self, odxlinks: OdxLinkDatabase) -> Iterable[DiagComm]:
        """Return the list of locally defined diagnostic communications.

        This is not completely trivial as it requires to resolving the
        references specified in the <DIAG-COMMS> XML tag.
        """
        result_dict: Dict[str, DiagComm] = {}

        # TODO (?): add objects from the import-refs

        for dc_proxy in self.diag_layer_raw.diag_comms:
            if isinstance(dc_proxy, OdxLinkRef):
                dc = odxlinks.resolve(dc_proxy)
            else:
                dc = dc_proxy

            odxassert(isinstance(dc, DiagComm))
            odxassert(
                dc.short_name not in result_dict,
                f"Multiple definitions of DIAG-COMM '{dc.short_name}' in "
                f"layer '{self.short_name}'")
            result_dict[dc.short_name] = dc

        return result_dict.values()

    def _get_local_unit_groups(self) -> Iterable[UnitGroup]:
        if self.diag_layer_raw.diag_data_dictionary_spec is None:
            return []

        unit_spec = self.diag_layer_raw.diag_data_dictionary_spec.unit_spec
        if unit_spec is None:
            return []

        return unit_spec.unit_groups

    def _compute_available_diag_comms(self, odxlinks: OdxLinkDatabase) -> Iterable[DiagComm]:

        def get_local_objects_fn(dl: DiagLayer) -> Iterable[DiagComm]:
            return dl._get_local_diag_comms(odxlinks)

        def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
            return parent_ref.not_inherited_diag_comms

        return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)

    def _compute_available_global_neg_responses(self, odxlinks: OdxLinkDatabase) \
            -> Iterable[Response]:

        def get_local_objects_fn(dl: DiagLayer) -> Iterable[Response]:
            return dl.diag_layer_raw.global_negative_responses

        def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
            return parent_ref.not_inherited_global_neg_responses

        return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)

    def _compute_available_ddd_spec_items(
        self,
        include: Callable[[DiagDataDictionarySpec], Iterable[TNamed]],
        exclude: Callable[["ParentRef"], List[str]],
    ) -> NamedItemList[TNamed]:

        def get_local_objects_fn(dl: DiagLayer) -> Iterable[TNamed]:
            if dl.diag_layer_raw.diag_data_dictionary_spec is None:
                return []
            return include(dl.diag_layer_raw.diag_data_dictionary_spec)

        found = self._compute_available_objects(get_local_objects_fn, exclude)
        return NamedItemList(found)

    def _compute_available_functional_classes(self) -> Iterable[FunctionalClass]:

        def get_local_objects_fn(dl: DiagLayer) -> Iterable[FunctionalClass]:
            return dl.diag_layer_raw.functional_classes

        def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
            return []

        return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)

    def _compute_available_additional_audiences(self) -> Iterable[AdditionalAudience]:

        def get_local_objects_fn(dl: DiagLayer) -> Iterable[AdditionalAudience]:
            return dl.diag_layer_raw.additional_audiences

        def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
            return []

        return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)

    def _compute_available_state_charts(self) -> Iterable[StateChart]:

        def get_local_objects_fn(dl: DiagLayer) -> Iterable[StateChart]:
            return dl.diag_layer_raw.state_charts

        def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
            return []

        return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)

    def _compute_available_unit_groups(self) -> Iterable[UnitGroup]:

        def get_local_objects_fn(dl: DiagLayer) -> Iterable[UnitGroup]:
            return dl._get_local_unit_groups()

        def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
            return []

        return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)

    #####
    # </value inheritance mechanism helpers>
    #####

    #####
    # <communication parameter handling>
    #####
    def _compute_available_commmunication_parameters(self) -> List[ComparamInstance]:
        """Compute the list of communication parameters that apply to
        the diagnostic layer

        Be aware that the inheritance scheme for communication
        parameters is slightly different than for objects that are
        subject to value inheritance:

        - The ODXLINK ID id of communication parameters is used to
          override inherited parameters instead of the short name.
        - A parameter is only overridden if the specified protocol
          matches.

        Note that the specification leaves some room for
        interpretation here: It says that if no protocol is specified,
        the parameter shall apply to any protocol. But what happens if
        the the same comparam is specified with and without a
        protocol? Is this allowed at all? If yes, which of these
        definitions gets priority? How does this interact with
        inheritance? The approach taken here is to allow such cases
        and to use the specific comparams if possible whilst the ones
        without a specified protocol are taken as fallbacks...

        """
        com_params_dict: Dict[Tuple[str, Optional[str]], ComparamInstance] = {}

        # Look in parent refs for inherited communication
        # parameters. First fetch the communication parameters from
        # low priority parents, then update with increasing priority.
        for parent_ref in self._get_parent_refs_sorted_by_priority():
            for cp in parent_ref.layer._compute_available_commmunication_parameters():
                com_params_dict[(cp.spec_ref.ref_id, cp.protocol_snref)] = cp

        # finally, handle the locally defined communication parameters
        for cp in self.diag_layer_raw.comparams:
            com_params_dict[(cp.spec_ref.ref_id, cp.protocol_snref)] = cp

        return list(com_params_dict.values())

    @property
    def comparams(self) -> NamedItemList[ComparamInstance]:
        """All communication parameters applicable to this DiagLayer

        Note that, although communication parameters use inheritance,
        it is *not* the "value inheritance" scheme used by e.g. DOPs,
        tables, state charts, ...
        """
        return self._comparams

    @cached_property
    def protocols(self) -> NamedItemList["DiagLayer"]:
        """Return the set of all protocols which are applicable to the diagnostic layer

        Note that protocols are *not* explicitly inherited objects,
        but the parent diagnostic layers of variant type "PROTOCOL".
        """
        result_dict: Dict[str, DiagLayer] = {}

        for parent_ref in self._get_parent_refs_sorted_by_priority():
            for prot in parent_ref.layer.protocols:
                result_dict[prot.short_name] = prot

        if self.diag_layer_raw.variant_type == DiagLayerType.PROTOCOL:
            result_dict[self.diag_layer_raw.short_name] = self

        return NamedItemList(result_dict.values())

    def get_comparam(
        self,
        cp_short_name: str,
        *,
        protocol: Optional[Union[str, "DiagLayer"]] = None,
    ) -> Optional[ComparamInstance]:
        """Find a specific communication parameter according to some criteria.

        Setting a given parameter to `None` means "don't care"."""

        protocol_name: Optional[str]
        if isinstance(protocol, DiagLayer):
            protocol_name = protocol.short_name
        else:
            protocol_name = protocol

        # determine the set of applicable communication parameters
        cps = [cp for cp in self.comparams if cp.short_name == cp_short_name]
        if protocol_name is not None:
            cps = [cp for cp in cps if cp.protocol_snref in (None, protocol_name)]

        if len(cps) > 1:
            warnings.warn(
                f"Communication parameter `{cp_short_name}` specified more "
                f"than once. Using first occurence.",
                OdxWarning,
                stacklevel=1,
            )
        elif len(cps) == 0:
            return None

        return cps[0]

    def get_max_can_payload_size(self,
                                 protocol: Optional[Union[str,
                                                          "DiagLayer"]] = None) -> Optional[int]:
        """Return the maximum size of a CAN frame payload that can be
        transmitted in bytes.

        For classic CAN busses, this is basically always 8. CAN-FD can
        send up to 64 bytes per frame.

        """
        com_param = self.get_comparam("CP_CANFDTxMaxDataLength", protocol=protocol)
        if com_param is None:
            rx_id = self.get_can_receive_id(protocol=protocol)
            if rx_id is not None:
                # bus is CAN. We also use 8 bytes for CAN-FD if this
                # parameter was not specified.
                return 8

            # bus is not CAN
            return None

        val = com_param.value
        if not isinstance(val, str):
            return None

        m = re.search("TX_DL *= *([0-9]*)", val)
        if m:
            return int(m.group(1))

        # unexpected format of parameter value
        return 8

    def uses_can(self, protocol: Optional[Union[str, "DiagLayer"]] = None) -> bool:
        """
        Check if CAN ought to be used as the link layer protocol.
        """
        return self.get_can_receive_id(protocol=protocol) is not None

    def uses_can_fd(self, protocol: Optional[Union[str, "DiagLayer"]] = None) -> bool:
        """Check if CAN-FD ought to be used.

        If the ECU is not using CAN-FD for the specified protocol, `False`
        is returned.

        If this method returns `True`, `.uses_can() == True` is implied
        for the protocol.
        """

        if not self.uses_can(protocol):
            return False

        com_param = self.get_comparam("CP_CANFDTxMaxDataLength", protocol=protocol)
        if com_param is None:
            return False

        return "CANFD" in com_param.value

    def get_can_baudrate(self, protocol: Optional[Union[str, "DiagLayer"]] = None) -> Optional[int]:
        """Baudrate of the CAN bus which is used by the ECU [bits/s]

        If the ECU is not using CAN for the specified protocol, None
        is returned.

        """
        com_param = self.get_comparam("CP_Baudrate", protocol=protocol)
        if com_param is None:
            return None

        val = com_param.value
        if not isinstance(val, str):
            return None

        return int(val)

    def get_can_fd_baudrate(self,
                            protocol: Optional[Union[str, "DiagLayer"]] = None) -> Optional[int]:
        """Data baudrate of the CAN bus which is used by the ECU [bits/s]

        If the ECU is not using CAN-FD for the specified protocol,
        None is returned.
        """
        if not self.uses_can_fd(protocol=protocol):
            return None

        com_param = self.get_comparam("CP_CANFDBaudrate", protocol=protocol)
        if com_param is None:
            return None

        val = com_param.value
        if not isinstance(val, str):
            return None

        return int(val)

    def get_can_receive_id(self,
                           protocol: Optional[Union[str, "DiagLayer"]] = None) -> Optional[int]:
        """CAN ID to which the ECU listens for diagnostic messages"""
        com_param = self.get_comparam("CP_UniqueRespIdTable", protocol=protocol)
        if com_param is None:
            return None

        with warnings.catch_warnings():
            # depending on the protocol, we may get
            # "Communication parameter 'CP_UniqueRespIdTable' does not
            # specify 'CP_CanPhysReqId'" warning here. we don't want this
            # warning and simply return None...
            warnings.simplefilter("ignore", category=OdxWarning)
            result = com_param.get_subvalue("CP_CanPhysReqId")
        if result is None:
            return None
        odxassert(isinstance(result, str))

        return int(result)

    @deprecated(details="use get_can_receive_id()")
    def get_receive_id(self) -> Optional[int]:
        return self.get_can_receive_id()

    def get_can_send_id(self, protocol: Optional[Union[str, "DiagLayer"]] = None) -> Optional[int]:
        """CAN ID to which the ECU sends replies to diagnostic messages"""

        # this hopefully resolves to the 'CP_UniqueRespIdTable'
        # parameter from the ISO_15765_2 comparam subset. (There is a
        # parameter with the same name in the ISO_13400_2_DIS_2015
        # subset for DoIP. If the wrong one is retrieved, try
        # specifying the protocol.)
        com_param = self.get_comparam("CP_UniqueRespIdTable", protocol=protocol)
        if com_param is None:
            return None

        with warnings.catch_warnings():
            # depending on the protocol, we may get
            # "Communication parameter 'CP_UniqueRespIdTable' does not
            # specify 'CP_CanRespUSDTId'" warning here. we don't want this
            # warning and simply return None...
            warnings.simplefilter("ignore", category=OdxWarning)
            result = com_param.get_subvalue("CP_CanRespUSDTId")
        if result is None:
            return None
        odxassert(isinstance(result, str))

        return int(result)

    @deprecated(details="use get_can_send_id()")
    def get_send_id(self) -> Optional[int]:
        return self.get_can_send_id()

    def get_can_func_req_id(self,
                            protocol: Optional[Union[str, "DiagLayer"]] = None) -> Optional[int]:
        """CAN Functional Request Id."""
        com_param = self.get_comparam("CP_CanFuncReqId", protocol=protocol)
        if com_param is None:
            return None

        result = com_param.get_value()
        if result is None:
            return None
        odxassert(isinstance(result, str))

        return int(result)

    def get_doip_logical_ecu_address(self,
                                     protocol: Optional[Union[str, "DiagLayer"]] = None
                                    ) -> Optional[int]:
        """Return the address of the ECU when using functional addressing.

        The parameter protocol is used to distinguish between
        different interfaces, e.g., offboard and onboard DoIP
        Ethernet.
        """

        # this hopefully resolves to the 'CP_UniqueRespIdTable'
        # parameter from the ISO_13400_2_DIS_2015 comparam
        # subset. (There is a parameter with the same name in the
        # ISO_15765_2 subset for CAN. If the wrong one is retrieved,
        # try specifying the protocol.)
        com_param = self.get_comparam("CP_UniqueRespIdTable", protocol=protocol)

        if com_param is None:
            return None

        # The CP_DoIPLogicalEcuAddress is specified by the
        # "CP_DoIPLogicalEcuAddress" subvalue of the complex comparam
        # CP_UniqueRespIdTable of the ISO_13400_2_DIS_2015 comparam
        # subset. Depending of the underlying transport protocol,
        # (i.e., CAN using ISO-TP) this subvalue might not exist.
        with warnings.catch_warnings():
            warnings.simplefilter("ignore", category=OdxWarning)
            ecu_addr = com_param.get_subvalue("CP_DoIPLogicalEcuAddress")
        if ecu_addr is None:
            return None
        return int(ecu_addr)

    def get_doip_logical_gateway_address(self,
                                         protocol: Optional[Union[str, "DiagLayer"]] = None
                                        ) -> Optional[int]:
        """The logical gateway address for the diagnosis over IP transport protocol"""

        # retrieve CP_DoIPLogicalGatewayAddress from the
        # ISO_13400_2_DIS_2015 subset. hopefully.
        com_param = self.get_comparam("CP_DoIPLogicalGatewayAddress", protocol=protocol)
        if com_param is None:
            return None

        result = com_param.get_value()
        if result is None:
            return None
        odxassert(isinstance(result, str))

        return int(result)

    def get_doip_logical_tester_address(self,
                                        protocol: Optional[Union[str, "DiagLayer"]] = None
                                       ) -> Optional[int]:
        """DoIp logical gateway address"""

        # retrieve CP_DoIPLogicalTesterAddress from the
        # ISO_13400_2_DIS_2015 subset. hopefully.
        com_param = self.get_comparam("CP_DoIPLogicalTesterAddress", protocol=protocol)
        if com_param is None:
            return None

        result = com_param.get_value()
        if result is None:
            return None
        odxassert(isinstance(result, str))

        return int(result)

    def get_doip_logical_functional_address(self,
                                            protocol: Optional[Union[str, "DiagLayer"]] = None
                                           ) -> Optional[int]:
        """The logical functional DoIP address of the ECU."""

        # retrieve CP_DoIPLogicalFunctionalAddress from the
        # ISO_13400_2_DIS_2015 subset. hopefully.
        com_param = self.get_comparam(
            "CP_DoIPLogicalFunctionalAddress",
            protocol=protocol,
        )
        if com_param is None:
            return None

        result = com_param.get_value()
        if result is None:
            return None
        odxassert(isinstance(result, str))

        return int(result)

    def get_doip_routing_activation_timeout(self,
                                            protocol: Optional[Union[str, "DiagLayer"]] = None
                                           ) -> Optional[float]:
        """The timout for the DoIP routing activation request in seconds"""

        # retrieve CP_DoIPRoutingActivationTimeout from the
        # ISO_13400_2_DIS_2015 subset. hopefully.
        com_param = self.get_comparam("CP_DoIPRoutingActivationTimeout", protocol=protocol)
        if com_param is None:
            return None

        result = com_param.get_value()
        if result is None:
            return None
        odxassert(isinstance(result, str))

        return float(result) / 1e6

    def get_doip_routing_activation_type(self,
                                         protocol: Optional[Union[str, "DiagLayer"]] = None
                                        ) -> Optional[int]:
        """The DoIP routing activation type

        The number returned has the following meaning:

        0          Default
        1          WWH-OBD (worldwide harmonized onboard diagnostic).
        2-0xDF     reserved
        0xE0-0xFF  OEM-specific
        """

        # retrieve CP_DoIPRoutingActivationType from the
        # ISO_13400_2_DIS_2015 subset. hopefully.
        com_param = self.get_comparam("CP_DoIPRoutingActivationType", protocol=protocol)
        if com_param is None:
            return None

        result = com_param.get_value()
        if result is None:
            return None
        odxassert(isinstance(result, str))

        return int(result)

    def get_tester_present_time(self,
                                protocol: Optional[Union[str,
                                                         "DiagLayer"]] = None) -> Optional[float]:
        """Timeout on inactivity in seconds.

        This is defined by the communication parameter "CP_TesterPresentTime".

        Description of the comparam: "Time between a response and the
        next subsequent tester present message (if no other request is
        sent to this ECU) in case of physically addressed requests."
        """

        # retrieve CP_TesterPresentTime from either the
        # ISO_13400_2_DIS_2015 or the ISO_15765_2 subset.
        com_param = self.get_comparam("CP_TesterPresentTime", protocol=protocol)
        if com_param is None:
            return None

        result = com_param.get_value()
        if result is None:
            return None
        odxassert(isinstance(result, str))

        # the comparam specifies microseconds. convert this to seconds
        return float(result) / 1e6

    #####
    # </communication parameter handling>
    #####

    #####
    # <PDU decoding>
    #####

    @cached_property
    def _prefix_tree(self) -> PrefixTree:
        """Constructs the coded prefix tree of the services.

        Each leaf node is a list of `DiagService`s.  (This is because
        navigating from a service to the request/ responses is easier
        than finding the service for a given request/response object.)

        Example:
        Let there be four services with corresponding requests:
        * Request 1 has the coded constant prefix `12 34`.
        * Request 2 has the coded constant prefix `12 34`.
        * Request 3 has the coded constant prefix `12 56`.
        * Request 4 has the coded constant prefix `12 56 00`.

        Then, the constructed prefix tree is the dict
        ```
        {0x12: {0x34: {-1: [<Service 1>, <Service 2>]},
                0x56: {-1: [<Service 3>],
                       0x0: {-1: [<Service 4>]}
                       }}}
        ```
        Note, that the inner `-1` are constant to distinguish them
        from possible service IDs.

        Also note, that it is actually allowed that
        (a) SIDs for different services are the same like for service 1 and 2 (thus each leaf node is a list) and
        (b) one SID is the prefix of another SID like for service 3 and 4 (thus the constant `-1` key).

        """
        prefix_tree: PrefixTree = {}
        for s in self.services:
            # Compute prefixes for the service's request and all
            # possible responses. We need to consider the global
            # negative responses here, because they might contain
            # MATCHING-REQUEST parameters. If these global responses
            # do not contain such parameters, this will potentially
            # result in an enormous amount of decoded messages for
            # global negative responses. (I.e., one for each
            # service. This can be avoided by specifying the
            # corresponding request for `decode_response()`.)
            request_prefix = b''
            if s.request is not None:
                request_prefix = s.request.coded_const_prefix()
            prefixes = [request_prefix]
            prefixes += [
                x.coded_const_prefix(request_prefix=request_prefix) for x in chain(
                    s.positive_responses, s.negative_responses, self.global_negative_responses)
            ]
            for coded_prefix in prefixes:
                self._extend_prefix_tree(prefix_tree, coded_prefix, s)

        return prefix_tree

    @staticmethod
    def _extend_prefix_tree(prefix_tree: PrefixTree, coded_prefix: bytes,
                            service: DiagService) -> None:

        # make sure that tree has an entry for the given prefix
        sub_tree = prefix_tree
        for b in coded_prefix:
            if b not in sub_tree:
                sub_tree[b] = {}
            sub_tree = cast(PrefixTree, sub_tree[b])

        # Store the object as in the prefix tree. This is done by
        # assigning the list of possible objects to the key -1 of the
        # dictionary (this is quite hacky...)
        if sub_tree.get(-1) is None:
            sub_tree[-1] = [service]
        else:
            cast(List[DiagService], sub_tree[-1]).append(service)

    def _find_services_for_uds(self, message: bytes) -> List[DiagService]:
        prefix_tree = self._prefix_tree

        # Find matching service(s) in prefix tree
        possible_services: List[DiagService] = []
        for b in message:
            if b in prefix_tree:
                odxassert(isinstance(prefix_tree[b], dict))
                prefix_tree = cast(PrefixTree, prefix_tree[b])
            else:
                break
            if -1 in prefix_tree:
                possible_services += cast(List[DiagService], prefix_tree[-1])
        return possible_services

    def _decode(self, message: bytes, candidate_services: Iterable[DiagService]) -> List[Message]:
        decoded_messages: List[Message] = []

        for service in candidate_services:
            try:
                decoded_messages.append(service.decode_message(message))
            except DecodeError:
                # check if the message can be decoded as a global
                # negative response for the service
                for gnr in self.global_negative_responses:
                    try:
                        decoded_gnr = gnr.decode(message)
                        if not isinstance(decoded_gnr, dict):
                            raise DecodeError(f"Expected the decoded value of a global "
                                              f"negative response to be a dictionary, "
                                              f"got {type(decoded_gnr)} for {self.short_name}")

                        decoded_messages.append(
                            Message(
                                coded_message=message,
                                service=service,
                                coding_object=gnr,
                                param_dict=decoded_gnr))
                    except DecodeError:
                        pass

        if len(decoded_messages) == 0:
            raise DecodeError(
                f"None of the services {candidate_services} could parse {message.hex()}.")

        return decoded_messages

    def decode(self, message: bytes) -> List[Message]:
        candidate_services = self._find_services_for_uds(message)

        return self._decode(message, candidate_services)

    def decode_response(self, response: bytes, request: Union[bytes, Message]) -> List[Message]:
        if isinstance(request, Message):
            candidate_services = [request.service]
        else:
            if not isinstance(request, (bytes, bytearray)):
                raise TypeError(f"Request parameter must have type "
                                f"Message, bytes or bytearray but was {type(request)}")
            candidate_services = self._find_services_for_uds(request)
        if candidate_services is None:
            raise DecodeError(f"Couldn't find corresponding service for request {request.hex()}.")

        return self._decode(response, candidate_services)

    #####
    # </PDU decoding>
    #####


import argparse
import re
import shutil
import socket
import subprocess
import tempfile
from pathlib import Path
from typing import Any, Dict, List, Tuple, Union

owner = "www-data"

def main():
    parser = argparse.ArgumentParser(prog="gateway_publish.py")
    parser.add_argument("hostname", help="IP address or domain name")
    parser.add_argument("port", type=int, help="Public port")
    parser.add_argument("ssh_key", help="Public ssh key")
    parser.add_argument("--secure", action="store_true", help="Enable SSL")
    args = parser.parse_args()

    if args.secure and args.port != 443:
        exit("Only standard port 443 is allowed for HTTPS")
    if not args.secure and args.port == 443:
        exit("Port 443 is reserved for HTTPS")

    # detect conflicts
    conf_path = Path("/etc/nginx/sites-enabled") / f"{args.port}-{args.hostname}.conf"
    if conf_path.exists() and is_conf_active(conf_path):
        exit(f"Could not start the service, because {args.hostname}:{args.port} is in use")

    # create temp dir for socket
    temp_dir = tempfile.mkdtemp(prefix="dstack-")
    shutil.chown(temp_dir, user=owner, group=owner)
    sock_path = Path(temp_dir) / "http.sock"
    print(sock_path)

    # issue ssl certificate
    ssl = {}
    if args.secure:
        run_certbot(args.hostname)
        ssl = {
            "listen": "80",
            f"listen {args.port}": "ssl",
            "ssl_certificate": f"/etc/letsencrypt/live/{args.hostname}/fullchain.pem",
            "ssl_certificate_key": f"/etc/letsencrypt/live/{args.hostname}/privkey.pem",
            "include": "/etc/letsencrypt/options-ssl-nginx.conf",
            "ssl_dhparam": "/etc/letsencrypt/ssl-dhparams.pem",
            'if ($scheme != "https")': {
                "return": "301 https://$host$request_uri",
            },
        }

    # write nginx configuration
    upstream = Path(temp_dir).name
    conf = format_nginx_conf(
        {
            f"upstream {upstream}": {
                "server": f"unix:{sock_path}",
            },
            "server": {
                "server_name": args.hostname,
                "listen": args.port,
                "location /": {
                    "proxy_pass": f"http://{upstream}",
                    "proxy_set_header X-Real-IP": "$remote_addr",
                    "proxy_set_header Host": "$host",
                },
                **ssl,
            },
        }
    )
    conf_path.write_text(conf)
    reload_nginx()

    # add ssh-key
    add_ssh_key(args.ssh_key)

def is_conf_active(conf_path: Path) -> bool:
    r = re.search(r"server unix:([^;]+/http\.sock);", conf_path.read_text())
    if r is None:
        raise ValueError("No http.sock in conf file")
    sock_path = r.group(1)
    try:
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
            client.connect(sock_path)
    except FileNotFoundError:
        # WARNING: possible race condition if the first job hasn't started yet, but we can't return True,
        # because the first job could fail to start preventing reusing this hostname
        return False
    except ConnectionRefusedError:
        return False
    return True

def format_nginx_conf(
    o: Union[Dict[str, Any], List[Tuple[str, Any]]], *, indent=2, depth=0
) -> str:
    pad = " " * depth * indent
    text = ""
    pairs = o.items() if isinstance(o, dict) else o
    for key, value in pairs:
        if isinstance(value, (dict, list)):
            text += pad + key + " {\n"
            text += format_nginx_conf(value, indent=indent, depth=depth + 1)
            text += pad + "}\n"
        else:
            text += pad + f"{key} {value};\n"
    return text

def reload_nginx():
    if subprocess.run(["systemctl", "reload", "nginx.service"]).returncode != 0:
        exit("Failed to reload nginx")

def add_ssh_key(ssh_key: str):
    authorized_keys = Path("/var/www/.ssh/authorized_keys")
    if not authorized_keys.exists():
        exit(f"{authorized_keys} doesn't exist")
    with authorized_keys.open("a") as f:
        print(f'command="/bin/true" {ssh_key}', file=f)

def run_certbot(hostname: str):
    args = [
        "certbot",
        "certonly",
        "--non-interactive",
        "--agree-tos",
        "--register-unsafely-without-email",
        "--nginx",
        "--domain",
        hostname,
    ]
    proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = proc.communicate()
    if proc.returncode != 0:
        exit(f"Certbot failed:\n{stderr.decode()}")

if __name__ == "__main__":
    main()


from django.urls import path
from .views import index, show, EditView, DeleteView, DeleteResourceView, upload_file, editor

app_name = 'creative_contents'
urlpatterns = [
    path('', index, name='index'),
    path('<str:link>/show/', show, name='display'),
    path('<str:link>/edit/', EditView.as_view(), name='edit'),
    path('editor/', editor, name='editor'),
    path('upload/', upload_file, name='upload_file'),
    path('delete/', DeleteView.as_view(), name='delete'),
    path('asset/delete/', DeleteResourceView.as_view(), name='resource_delete')
]


from libgen_api_li import LibgenSearch
import json

s = LibgenSearch()

results = s.search_author("Peter Hoffman")
print(results)
save_file = open("savedata.json", "w")
json.dump(results, save_file, indent=6)
save_file.close()


from django.contrib.auth.decorators import login_required
from django.shortcuts import render,redirect
from django.urls import reverse_lazy

from users.models import User, UserProfile, Interest
from users.forms import UserRegistrationForm, UserProfileForm

def landing_page(request):
    return render(request, 'users/home.html')

def UserRegisterView(request):
    if request.method == 'POST':
        form = UserRegistrationForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('landing-page')
    else:
        form = UserRegistrationForm()
    return render(request,'users/register.html', {'form':form})

@login_required
def userprofile(request):
    user = request.user
    if request.method == 'POST':
        form = UserProfileForm(request.POST, request.FILES, instance=user.profile)
        if form.is_valid():
            profile = form.save(commit=False)
            profile.user = user
            profile.save()

            int_list = form.cleaned_data['interest']
            #remove unchecked interests
            for i in profile.interest.all():
                if i not in int_list:
                    profile.interest.remove(i)
            #add new interests
            for i in int_list:
                if i not in profile.interest.all():
                    profile.interest.add(i)

            return redirect(reverse_lazy('dashboard'))
    else:
        form = UserProfileForm(instance=user.profile)
    return render(request, 'users/profile.html', {'form':form})


import scrapy
import json
from demo.items import DemoItem
from datetime import datetime
from datetime import date
from  demo.spiders import url_list
import uuid
from decouple import config
import pymongo
import gc

def load_datetime():

 today = date.today()
 now = datetime.now()
 date_now = today.strftime("%d/%m/%Y")
 time_now = now.strftime("%H:%M:%S")

 return date_now, time_now, today

class SagaSpider(scrapy.Spider):
    name = "saga"
    allowed_domains = ["falabella.com.pe"]

    def __init__(self, *args, **kwargs):
        super(SagaSpider, self).__init__(*args, **kwargs)
        self.client = pymongo.MongoClient(config("MONGODB"))
        self.db = self.client["brand_allowed"]
        self.lista = self.brand_allowed()[int(self.b)]  # Initialize self.lista based on self.b

    def brand_allowed(self):
        collection1 = self.db["shoes"]
        collection2 = self.db["electro"]
        collection3 = self.db["tv"]
        collection4 = self.db["cellphone"]
        collection5 = self.db["laptop"]
        collection6 = self.db["consola"]
        collection7 = self.db["audio"]
        collection8 = self.db["colchon"]
        collection9 = self.db["nada"]
        collection10 = self.db["sport"]
        collection11 = self.db["curacao"]
        collection12 = self.db["todo"]

        shoes = collection1.find({})
        electro = collection2.find({})
        tv = collection3.find({})
        cellphone = collection4.find({})
        laptop = collection5.find({})
        consola = collection6.find({})
        audio = collection7.find({})
        colchon = collection8.find({})
        nada = collection9.find({})
        sport = collection10.find({})
        curacao = collection11.find({})
        todo = collection12.find({})

        shoes_list = [doc["brand"] for doc in shoes]
        electro_list = [doc["brand"] for doc in electro]
        tv_list = [doc["brand"] for doc in tv]
        cellphone_list = [doc["brand"] for doc in cellphone]
        laptop_list = [doc["brand"] for doc in laptop]
        consola_list = [doc["brand"] for doc in consola]
        audio_list = [doc["brand"] for doc in audio]
        colchon_list = [doc["brand"] for doc in colchon]
        nada_list = [doc["brand"] for doc in nada]
        sport_list = [doc["brand"] for doc in sport]
        curacao_list = [doc["brand"] for doc in curacao]
        todo_list = [doc["brand"] for doc in todo]
        return (shoes_list ,electro_list,tv_list,cellphone_list,laptop_list, consola_list,
                audio_list, colchon_list,nada_list,sport_list,curacao_list, todo_list)

    def start_requests(self):
        u = int(getattr(self, 'u', '0'))
        b = int(getattr(self, 'b', '0'))

        # Define a dictionary to map 'u' values to the corresponding url_list
        url_mapping = {
            1: url_list.list1, 2: url_list.list1, 3: url_list.list3, 4: url_list.list4, 5: url_list.list5, 6: url_list.list6,
            7: url_list.list7, 8: url_list.list8, 9: url_list.list9, 10: url_list.list10, 11: url_list.list11, 12: url_list.list12, 13: url_list.list13,
            14: url_list.list14, 15: url_list.list15, 16: url_list.list16,  17: url_list.list17,  18: url_list.list18,
            19: url_list.list19, 20: url_list.list20,21: url_list.list21, 22: url_list.list22,23: url_list.list23

            }

        # Retrieve the appropriate list based on the value of 'u'
        urls = url_mapping.get(u, [])

        for i, v in enumerate(urls):
            if "linio" in v[0]:
                for e in range (v[1]+10):
                    url = v[0]+ "?subdomain=linio&page="+str(e+1)
                    yield scrapy.Request(url, self.parse)
            else:

                for e in range (v[1]+10):
                    url = v[0]+ "?page="+str(e+1)
                    yield scrapy.Request(url, self.parse)

        # Now you can use the 'urls

    def parse(self, response):

        if response.status != 200:
        # If the response status is not 200, skip processing this link and move to the next one
                self.logger.warning(f"Skipping URL {response.url} due to non-200 status code: {response.status}")
                return

        if "/noResult" in response.url:
                # Move to the next URL in the array (since it is a "noResult" page)
                self.logger.info("Skipping this URL and moving to the next one.")
                return

        item = DemoItem()

        # Find the script tag with the JSON data
        script_tag = response.xpath('//script[@id="__NEXT_DATA__"]/text()').get()

        if script_tag:
            json_content = json.loads(script_tag)

            # Assuming the relevant JSON data is under "props" -> "pageProps" in the JSON response
            page_props = json_content.get('props', {}).get('pageProps', {}).get("results",{})
        #     with open ("source", "w+") as f:
        #         f.write(str(page_props))

        productos = page_props
        for i in productos:

                # try:
                try:
                    item["brand"]= i["brand"]
                except:   item["brand"] = None
                product = item["brand"]
                if self.lista == []:
                    pass
                else:
                    if product.lower() not in self.lista:
                        continue

                # except: item["brand"]= None

                item["product"]=  i["displayName"]

                item["sku"] = i["skuId"]
                #item["_id"] = i["skuId"]#+str(load_datetime()[0])
                item["_id"] :str(uuid.uuid4())

                try:
                 item["best_price"] = float(i["prices"][1]["price"][0].replace(",",""))

                except:
                 item["best_price"] = 0
                print("#######")
                # print(item["best_price"])

                try:
                 item["list_price"] = float(i["prices"][2]["price"][0].replace(",",""))
                except:
                        item["list_price"] = 0

                try:

                 item["card_price"] = float(i["prices"][0]["price"][0].replace(",",""))
                except:item["card_price"] =0

                item["link"]=i["url"]

                try:
                 item["image"]=i["mediaUrls"][0]
                except:
                 item["image"]=str(i["mediaUrls"])

                try:
                 item["web_dsct"]=float(i["discountBadge"]["label"].replace("-","").replace("%",""))
                except:
                        item["web_dsct"]=0

                item["market"]= "saga"

                item["date"]= load_datetime()[0]
                item["time"]= load_datetime()[1]
                item["home_list"] = response.url
                item["card_dsct"] = 0

                yield item

        gc.collect()


# -*- coding:utf-8 -*-
# email:bingchengzhou@foxmail.com
# create: 2021/1/13
import torch
from torch import nn
from networks.components.double_conv import DoubleConv, Down, Up, OutConv

class Generator(nn.Module):
    # Network Architecture is exactly same as in infoGAN (https://arxiv.org/abs/1606.03657)
    # Architecture : FC1024_BR-FC7x7x128_BR-(64)4dc2s_BR-(1)4dc2s_S
    def __init__(self, n_channels, n_classes, bilinear=True):
        super(Generator, self).__init__()
        self.n_channels = n_channels
        self.n_classes = n_classes
        self.bilinear = bilinear

        self.inc = DoubleConv(n_channels, 64)
        self.down1 = Down(64, 128)
        self.down2 = Down(128, 256)
        self.down3 = Down(256, 512)
        self.down4 = Down(512, 512)
        self.up1 = Up(1024, 256, bilinear)
        self.up2 = Up(512, 128, bilinear)
        self.up3 = Up(256, 64, bilinear)
        self.up4 = Up(128, 64, bilinear)
        self.dilation = nn.Sequential(
            nn.Conv2d(512, 512, 3, 1, 2, dilation=2),
            nn.BatchNorm2d(512),
            nn.LeakyReLU(0.2),
            nn.Conv2d(512, 512, 3, 1, 4, dilation=4),
            nn.BatchNorm2d(512),
            nn.LeakyReLU(0.2),
            nn.Conv2d(512, 512, 3, 1, 6, dilation=6),
            nn.BatchNorm2d(512),
            nn.LeakyReLU(0.2),
        )
        self.outw = OutConv(64, 3)
        self.outa = OutConv(64, 1)
        self.out_mask = OutConv(64, 1)
        self.sg = nn.Sigmoid()
        self.other = OutConv(64, 64)
        self.post_process_1 = nn.Sequential(
            nn.Conv2d(64 + 6, 64, 3, 1, 1),
            nn.BatchNorm2d(64),
            nn.LeakyReLU(0.2),
            nn.Conv2d(64, 128, 3, 1, 1),
        )
        self.post_process_2 = nn.Sequential(
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.2),
            nn.Conv2d(128, 128, 3, 1, 1),
        )
        self.post_process_3 = nn.Sequential(
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.2),
            nn.Conv2d(128, 128, 3, 1, 1),
        )
        self.post_process_4 = nn.Sequential(
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.2),
            nn.Conv2d(128, 128, 3, 1, 1),
        )
        self.post_process_5 = nn.Sequential(
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.2),
            nn.Conv2d(128, 3, 3, 1, 1),
            nn.Sigmoid(),
        )
        self.device = torch.device("cuda:0")

    def reset_post_process_device(self, device):
        self.device = device
        self.post_process_1 = self.post_process_1.to(self.device)
        self.post_process_2 = self.post_process_2.to(self.device)
        self.post_process_3 = self.post_process_3.to(self.device)
        self.post_process_4 = self.post_process_4.to(self.device)
        self.post_process_5 = self.post_process_5.to(self.device)

    def forward(self, x0):
        x1 = self.inc(x0)
        x2 = self.down1(x1)
        x3 = self.down2(x2)
        x4 = self.down3(x3)
        x5 = self.down4(x4)
        x5 = self.dilation(x5)
        x = self.up1(x5, x4)
        x = self.up2(x, x3)
        x = self.up3(x, x2)
        x = self.up4(x, x1)
        w = self.outw(x)
        a = self.outa(x)
        other = self.other(x)
        other = self.sg(other)
        mask = self.out_mask(x)
        mask = self.sg(mask)
        a = self.sg(a)
        w = self.sg(w)
        a = mask * a
        I_watermark = (x0 - a * w) / (1.0 - a + 1e-6)
        I_watermark = torch.clamp(I_watermark, 0, 1)
        xx1 = self.post_process_1(torch.cat([other, I_watermark, x0], 1).to(self.device))
        xx2 = self.post_process_2(xx1)
        xx3 = self.post_process_3(xx1 + xx2)
        xx4 = self.post_process_4(xx2 + xx3)
        I_watermark2 = self.post_process_5(xx4 + xx3)
        I = I_watermark2.to(x0.device) * mask + (1.0 - mask) * x0
        return I, mask, a, w, I_watermark

class Discriminator(nn.Module):
    # Network Architecture is exactly same as in infoGAN (https://arxiv.org/abs/1606.03657)
    # Architecture : (64)4c2s-(128)4c2s_BL-FC1024_BL-FC1_S
    def __init__(self, input_dim=3, output_dim=1):
        super(Discriminator, self).__init__()
        self.input_dim = input_dim
        self.output_dim = output_dim
        #self.input_size = input_size
        #self.class_num = class_num

        self.conv = nn.Sequential(
            nn.Conv2d(self.input_dim, 64, 16, 8, 4),
            nn.BatchNorm2d(64),
            nn.LeakyReLU(0.2),
            nn.Conv2d(64, 128, 3, 2, 1),
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.2),
            nn.Conv2d(128, 256, 3, 1, 1),
            nn.BatchNorm2d(256),
            nn.LeakyReLU(0.2),
            nn.Conv2d(256, 1, 3, 1, 1),
            nn.Sigmoid()
        )
        #utils.initialize_weights(self)

    def forward(self, x):
        x = self.conv(x)
        return x


import datetime
import os
import sqlite3
import tempfile

from flask import current_app, g
from towerdashboard.data import base

def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect(
            current_app.config.get('SQLITE_PATH', '/tmp/towerdashboard.sqlite'),
            detect_types=sqlite3.PARSE_DECLTYPES
        )
        g.db.row_factory = sqlite3.Row

    return g.db

def close_db(e=None):
    db = g.pop('db', None)

    if db is not None:
        db.close()

def init_db():
    if os.path.exists(current_app.config.get('SQLITE_PATH', '/tmp/towerdashboard.sqlite')):
        return False

    db = get_db()
    with current_app.open_resource('schema.sql') as f:
        db.executescript(f.read().decode('utf8'))

    with tempfile.NamedTemporaryFile() as _tempfile:
        for version in base.ANSIBLE_VERSIONS:
            _tempfile.write(
                'INSERT INTO ansible_versions (version) VALUES ("%s");\n' % version['name']
            )

        for version in base.OS_VERSIONS:
            _tempfile.write(
                'INSERT INTO os_versions (version, description, family) VALUES ("%s", "%s", "%s");\n' % (version['name'], version['desc'], version['family'])
            )

        for version in base.TOWER_VERSIONS:
            _tempfile.write(
                'INSERT INTO tower_versions (version, general_availability, end_of_full_support, end_of_maintenance_support, end_of_life) VALUES ("%s", "%s", "%s", "%s", "%s");\n' % (version['name'], version['general_availability'], version['end_of_full_support'], version['end_of_maintenance_support'], version['end_of_life'])
            )

        _tempfile.flush()
        with current_app.open_resource(_tempfile.name) as f:
            db.executescript(f.read().decode('utf8'))

    with tempfile.NamedTemporaryFile() as _tempfile:
        for config in base.TOWER_OS:
            tower_query = 'SELECT id FROM tower_versions WHERE version = "%s"' % config['tower']
            os_query = 'SELECT id FROM os_versions WHERE version = "%s"' % config['os']
            _tempfile.write(
                'INSERT INTO tower_os (tower_id, os_id) VALUES ((%s), (%s));\n' % (tower_query, os_query)
            )

        for config in base.TOWER_ANSIBLE:
            tower_query = 'SELECT id FROM tower_versions WHERE version = "%s"' % config['tower']
            ansible_query = 'SELECT id FROM ansible_versions WHERE version = "%s"' % config['ansible']
            _tempfile.write(
                'INSERT INTO tower_ansible (tower_id, ansible_id) VALUES ((%s), (%s));\n' % (tower_query, ansible_query)
            )

        for config in base.RESULTS:
            tower_query = 'SELECT id FROM tower_versions WHERE version = "%s"' % config['release'].capitalize().replace('_', ' ')[0:-2]
            os_query = 'SELECT id FROM os_versions WHERE version = "%s"' % config['os']
            ansible_query = 'SELECT id FROM ansible_versions WHERE version = "%s"' % config['ansible']
            _tempfile.write(
                    'INSERT INTO results (tower_id, ansible_id, os_id, status, url) VALUES ((%s), (%s), (%s), "%s", "%s");\n' % (tower_query, ansible_query, os_query, config['status'], 'https://www.google.com/')
            )

        _tempfile.flush()
        with current_app.open_resource(_tempfile.name) as f:
            db.executescript(f.read().decode('utf8'))

    return True

def init_app(app):
    app.teardown_appcontext(close_db)

def format_fetchall(rows):

    _rows = [dict(row) for row in rows]
    for row in _rows:
        for key, value in row.iteritems():
            if type(row[key]) is datetime.datetime:
                row[key] = str(row[key])[:19]

    return _rows


#!/usr/bin/python3

from cx_Freeze import setup, Executable
base = None
executables = [Executable("main.py", base=base)]
packages = ["idna"]
options = {
    'build_exe': {
        'packages':packages,
    },
}
setup(
    name = "TT_Check",
    options = options,
    version = "1.0",
    description = 'Recherche Joueur',
    executables = executables
)


from django.shortcuts import render, redirect
from albums.models import Album,UserOpinion
from albums.helpers import pagination,annotate_values,filter_distinct,rate_to_queryset
from albums.filters import UserFilter
from itertools import chain
from django.contrib.auth.models import User

def album(request):
    rates = UserOpinion.Rate.choices
    elements = {
        "rates":rates
    }
    return render(request,'album.html',elements)

def index(request):
    if request.user.is_authenticated:
        userFilter = UserFilter(request)
        user_albums = userFilter.all_albums.order_by("date")
        rate_to_queryset(request, user_albums, case=1)
        album_count = user_albums.count()
        catalog = pagination(request,user_albums,9)
        elements = {
            'catalog':catalog,
            'album_count':album_count,
        }
        return render(request, 'index.html',elements)
    return render(request,'index.html')

def stats(request):
    elements = {
        'fields':{
        'country': annotate_values('country','album',request),
        'artist': annotate_values('artist','album',request),
        'decade':annotate_values('decade','album',request),
        'style': annotate_values('styles','album',request),
        'genres': annotate_values('genres','album',request)
        },
        'artists_per_country':annotate_values('country','artist',request,is_distinct=True)
    }
    return render(request, 'stats.html',elements)

def advanced(request):
    style = filter_distinct('styles',request)
    genres = filter_distinct('genres',request)
    year = filter_distinct('year',request)
    decade = filter_distinct('decade',request)
    label = filter_distinct('label',request)
    country = filter_distinct('country',request)
    elements = {'filters': {
            'style': style, 'genres': genres,'year': year,
            'decade': decade, 'label': label,'country':country}
    }
    return render(request, 'advanced.html',elements)

def details(request):
    rates = UserOpinion.Rate.choices
    if request.method == 'GET':
        if 'id' in request.GET:
            user_albums = Album.objects.filter(id=request.GET['id'])
            rate_to_queryset(request,user_albums,case=3)
            elements = {
                'user_albums':user_albums,
                'rates':rates,
            }
            return render(request, 'details.html',elements)
        return redirect('index')


from django.db import migrations, models
import json

def create_default_plans(apps, schema_editor):
    """
    Creates Default Plans - Basic, Premium, Enterprise if they do not already exist.
    """
    Plan = apps.get_model('plans', 'Plan')
    plans_to_create = []

    if not Plan.objects.filter(name="Basic").exists():
        basic_plan_data = {
            "name": "Basic",
            "thumbnail_sizes": [
                {
                    "height": 200,
                    "width": 200
                }
            ],
            "include_original_link": False,
            "generate_expiring_links": False
        }
        plans_to_create.append(Plan(**basic_plan_data))

    if not Plan.objects.filter(name="Premium").exists():
        premium_plan_data = {
            "name": "Premium",
            "thumbnail_sizes": [
                {
                    "height": 200,
                    "width": 200
                },
                {
                    "height": 400,
                    "width": 400
                }
            ],
            "include_original_link": True,
            "generate_expiring_links": False
        }
        plans_to_create.append(Plan(**premium_plan_data))

    if not Plan.objects.filter(name="Enterprise").exists():
        enterprise_plan_data = {
            "name": "Enterprise",
            "thumbnail_sizes": [
                {
                    "height": 200,
                    "width": 200
                },
                {
                    "height": 400,
                    "width": 400
                }
            ],
            "include_original_link": True,
            "generate_expiring_links": True
        }
        plans_to_create.append(Plan(**enterprise_plan_data))

    Plan.objects.bulk_create(plans_to_create)

class Migration(migrations.Migration):
    dependencies = [
        ('plans', '0001_initial')
    ]

    operations = [
        migrations.RunPython(create_default_plans),
    ]


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models
import django.core.validators

class Migration(migrations.Migration):

    dependencies = [
        ('people', '0014_auto_20220712_1212'),
    ]

    operations = [
        migrations.AlterField(
            model_name='person',
            name='age',
            field=models.IntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(13), django.core.validators.MaxValueValidator(120)]),
        ),
        migrations.AlterField(
            model_name='person',
            name='father_name',
            field=models.CharField(max_length=100, null=True, blank=True),
        ),
        migrations.AlterUniqueTogether(
            name='person',
            unique_together=set([]),
        ),
    ]


import os
import sys
from os.path import abspath, dirname, join

from setuptools import find_packages, setup

this_dir = abspath(dirname(__file__))
with open(join(this_dir, 'README.md'), encoding='utf-8') as file:
    long_description = file.read()
def read(rel_path: str) -> str:
    here = os.path.abspath(os.path.dirname(__file__))
    with open(os.path.join(here, rel_path)) as fp:
        return fp.read()

def get_version(rel_path: str) -> str:
    for line in read(rel_path).splitlines():
        if line.startswith("IDELIUM_VERSION"):
            delim = '"' if '"' in line else "'"
            return line.split(delim)[1]
    raise RuntimeError("Unable to find version string.")

setup(
    name='idelium',  # Required
    version=get_version('src/idelium/_internal/main.py'),
    description='Command line Test Automation Tool',  # Optional
    long_description=long_description,
    long_description_content_type="text/markdown",
    author='Idel Fuschini',  # Optional
    author_email='idel.fuschini@gmail.com',  # Optional
    license='MIT',
    classifiers=[
        "Development Status :: 5 - Production/Stable",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: MIT License",
        "Topic :: Software Development :: Build Tools",
        "Programming Language :: Python",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3 :: Only",
        "Programming Language :: Python :: 3.7",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: 3.10",
        "Programming Language :: Python :: 3.11",
        "Programming Language :: Python :: Implementation :: CPython",
        "Programming Language :: Python :: Implementation :: PyPy",
    ],
    url="https://idelium.io/",
    package_dir={"": "src"},

    # You can just specify package directories manually here if your project is
    # simple. Or you can use find_packages().
    #
    # Alternatively, if you just want to distribute a single Python file, use
    # the `py_modules` argument instead as follows, which will expect a file
    # called `my_module.py` to exist:
    #
    #   py_modules=["my_module"],
    #
    package_data={
        "idelium": ["py.typed"],
    },
    packages=find_packages(
        where="src",
        exclude=["contrib", "docs", "tests*", "tasks"],
    ),

    # Specify which Python versions you support. In contrast to the
    # 'Programming Language' classifiers above, 'idelium install' will check this
    # and refuse to install the project if the version does not match. If you
    # do not support Python 2, you can simplify this to '>=3.5' or similar, see
    # https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires

    # This field lists other packages that your project depends on to run.
    # Any package you put here will be installed by idelium when your project is
    # installed, so they must be valid existing projects.
    #
    # For an analysis of "install_requires" vs idelium's requirements files see:
    # https://packaging.python.org/en/latest/requirements.html
    install_requires=['selenium',
                      'libmagic',
                      'Appium-Python-Client',
                      'webdriver-manager',
                      'Pillow',
                      'Postpy2'
                      ],  # Optional

    # List additional groups of dependencies here (e.g. development
    # dependencies). Users will be able to install these using the "extras"
    # syntax, for example:
    #
    #   $ idelium install sampleproject[dev]
    #
    # Similar to `install_requires` above, these must be valid existing
    # projects.
    extras_require={  # Optional
        'dev': ['check-manifest'],
        'test': ['coverage'],
    },
    project_urls={  # Optional
        'Bug Reports': 'https://github.com/idelium/idelium-cli/issues',
        'Project': 'https://idelium.io',
        'Say Thanks!': 'http://www.idelfuschini.it',
        'Source': 'https://github.com/idelium/idelium-cli/',
    },
    entry_points={  # Optional
        'console_scripts': [
            "idelium=idelium._internal.main:main",
        ],
    },
    zip_safe=False,
    # NOTE: python_requires is duplicated in __idelium-runner__.py.
    # When changing this value, please change the other copy as well.
    python_requires=">=3.7",
)


#!flask/bin/python
from flask import Flask, request, abort, jsonify, make_response
from chacha20poly1305 import ChaCha20Poly1305
from flask_httpauth import HTTPBasicAuth
from cryptography.fernet import Fernet
import paho.mqtt.client as mqtt
from datetime import datetime
import threading
import hashlib
import base64
import click
import time
import json
import hmac
import os
import sys

from sys import path
path.append( "../" ) # Add path to get utils.py
import utils as utils # Include different common fucntions.

KMS_ID                  = "kms-muii"
TOPIC_FILE              = 'registeredDeviceTopics.json'
SECRET_FILE             = 'secrets.json'
HASH_KEY                = b'kkpo-kktua'

topicsPublishNewKeys    = {}
secretRegisteredDevices = {}

app                     = Flask( __name__ )
auth                    = HTTPBasicAuth()

class FlaskThread( threading.Thread ):

    @auth.get_password
    def get_password( username ):
        """
            Provides the password for an user by a given username.
            Authentication method.
            Reference:
            https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask
        """
        if username == "platform":

            return "platform-MUII"
        return None

    @app.route( '/register-device', methods=['POST'] )
    @auth.login_required
    def register( ):
        """
            Registers a new device into the KMS according
            to the JSON provided by the platform.
        """
        global topicsPublishNewKeys, secretRegisteredDevices
        if not request.json or not 'id' in request.json or \
        not 'key_topic' in request.json or \
        not 'shared_key' in request.json or \
        not 'symmetric' in request.json:

            abort( 400 )
        topicsPublishNewKeys[request.json["id"]] = request.json['key_topic']
        secretRegisteredDevices[request.json["id"]] = {
            "secrets": {
                "0": request.json["shared_key"]
            },
            "symmetric": request.json["symmetric"]
        }
        # Save into `registeredDeviceTopics.json` file
        with open( TOPIC_FILE, 'w' ) as file:

            json.dump( topicsPublishNewKeys, file, indent=4 )
        # Save into `secret.json` file
        with open( SECRET_FILE, 'w' ) as file:
            json.dump( secretRegisteredDevices, file, indent=4 )
        return jsonify( { "status": "OK" } ), 201

    @app.route( '/remove-device', methods=['POST'] )
    @auth.login_required
    def remove( ):
        """
            Removes a device from the KMS according to the given ID.
        """
        global topicsPublishNewKeys, secretRegisteredDevices
        if not request.json or not 'id' in request.json:

            abort( 400 )
        # Delete from both dictionaries.
        del topicsPublishNewKeys[request.json["id"]]
        del secretRegisteredDevices[request.json["id"]]
        # Save into `registeredDeviceTopics.json` file
        with open( TOPIC_FILE, 'w' ) as file:

            json.dump( topicsPublishNewKeys, file, indent=4 )
        # Save into `secret.json` file
        with open( SECRET_FILE, 'w' ) as file:

            json.dump( secretRegisteredDevices, file, indent=4 )
        return jsonify( { "status": "OK" } ), 201

    @app.route( '/get-key', methods=['POST'] )
    @auth.login_required
    def get_key( ):
        """
            Returns the keys for an specified ID.
        """
        global secretRegisteredDevices
        if not request.json or not 'id' in request.json:

            abort( 400 )
        return jsonify( secretRegisteredDevices[request.json["id"]] ), 201

    @app.route( '/get-all-keys', methods=['POST'] )
    @auth.login_required
    def get_all_keys( ):
        """
            Returns all keys for all devices.
        """
        global secretRegisteredDevices
        return jsonify( secretRegisteredDevices ), 201

    @auth.error_handler
    def unauthorized( ):

        return make_response( jsonify( { 'error': 'Unauthorized access' } ), 401 )

    @app.errorhandler( 404 )
    def not_found( error ):

        return make_response( jsonify( { 'error': 'Not found' } ), 404 )

    def run( self ):

        app.run()

def add_header_message( message, topic ):
    """
        This functions adds information about the device
        to send it to the platform. It also include
        a hash as a sign.
    """
    message["id"]       = KMS_ID
    message["topic"]    = topic
    message["timestamp"]= str( datetime.now() )
    header = {
        "id": KMS_ID,
        "topic": topic,
        "timestamp": message["timestamp"]
    }
    message["sign"] = hmac.new( HASH_KEY, json.dumps( header ).encode(), \
        hashlib.sha384 ).hexdigest()
    return message

def start_flask():
    """
        Starting flask thread. RESTful service.
    """
    flaskThread = FlaskThread()
    # Need to be a daemon to close it with Ctrl+C
    flaskThread.daemon = True
    flaskThread.start()

def load_registered_device_topics():
    """
        Load from the `registerDeviceTopics.json` file to get the topics
        where to publish the keys for a device. If the file does not exit,
        it will be created.
    """
    if not os.path.exists( TOPIC_FILE ):
        # If file does not exit, it will be created.
        with open( TOPIC_FILE, 'w') as file:

            file.write("{}")
    with open( TOPIC_FILE ) as file:
        # Group previous devices registered
        data = json.load( file )
    return data

def load_registered_device_secrets():
    """
        Load from the `secrets.json` file to get the topics
        where to publish the keys for a device. If the file does not exit,
        it will be created.
    """
    if not os.path.exists( SECRET_FILE ):
        # If file does not exit, it will be created.
        with open( SECRET_FILE, 'w') as file:

            file.write("{}")
    with open( SECRET_FILE ) as file:
        # Group previous devices registered
        data = json.load( file )
    return data

@click.command()
@click.option( '-s', '--server', 'server', required=True, type=str, \
    show_default=True, default='broker.shiftr.io', help="The MQTT Server to send keys." )
@click.option( '-P', '--port', 'port', required=True, type=int, \
    show_default=True, default=1883, help="Port of theMQTT Server to send keys." )
@click.option( '-u', '--user', 'user', required=True, type=str, \
    help="The user to connect to the MQTT Serve." )
@click.option( '-p', '--password', 'password', required=True, type=str, \
    prompt=True, hide_input=True, help="The password for the user to connect to the MQTT Serve.\
    If you do not include this option, a prompt will appear to you introduce the password." )
@click.option( '-t', '--time', 't', required=True, type=int, default=10, \
    help=" Time taken to send new keys." )
def connect( server, port, user, password, t ):
    """
        Start KMS. It will start the RESTful at port 5000 and start the Key Rotation process.
    """
    global topicsPublishNewKeys
    global secretRegisteredDevices
    # Initiate Flask as a new thread.
    start_flask()
    # Load the information saved of the registered devices.
    topicsPublishNewKeys = load_registered_device_topics()
    secretRegisteredDevices = load_registered_device_secrets()
    # Connect to MQTT Server.
    client = mqtt.Client( client_id=KMS_ID )
    client.username_pw_set( user, password )
    client.connect( server, port, 60 )
    client.loop_start()
    while True:
        # Start key rotation process. Send a new key for each device according to
        # its symmetric algorithm specified.
        for device, topic in topicsPublishNewKeys.items():

            device_keys = secretRegisteredDevices[device]["secrets"]
            old_key = device_keys.get( "0", "" )
            if old_key != "":
                # Generates a new key
                if secretRegisteredDevices[device]["symmetric"] == "fernet":

                    new_key = Fernet.generate_key().decode( "utf-8" )
                elif secretRegisteredDevices[device]["symmetric"] == "chacha":

                    new_key = os.urandom( 32 ).decode( "latin-1" )
                if new_key != "":
                    # Rotate keys. Discard the oldest key.
                    if device_keys.get( "1", "" ) != "":

                        old_key = device_keys.get( "1" )
                        secretRegisteredDevices[device]["secrets"]["0"] = old_key
                    secretRegisteredDevices[device]["secrets"]["1"] = new_key
                    if secretRegisteredDevices[device]["symmetric"] == "fernet":

                        encriptor = Fernet( old_key.encode() )
                    elif secretRegisteredDevices[device]["symmetric"] == "chacha":

                        encriptor = ChaCha20Poly1305( old_key.encode( "latin-1" ) )
                    # Send the new generated key to the device
                    secret_message = {
                        "deviceID": device,
                        "key": new_key
                    }
                    message = add_header_message( secret_message, topic )
                    utils.send( client, encriptor, message )
            else:

                print( "Device: ", device, " has not a first key. \
                    Remove it from list and connect it again.")

        # Save all changes into the `secret.json` file
        with open( SECRET_FILE, 'w' ) as file:

            json.dump( secretRegisteredDevices, file, indent=4 )
        time.sleep( t )

@click.group()
def cli():

    pass

if __name__ == '__main__':
    """
        This main process only include the `connect` command.
        If you need help to run this command, check: `python server.py connect --help`
    """
    cli.add_command( connect )
    cli()


import json

from supybot.commands import wrap
import supybot.ircdb as ircdb
import supybot.ircmsgs as ircmsgs
import supybot.callbacks as callbacks
import supybot.log as log
import supybot.httpserver as httpserver
import supybot.world as world
try:
    from supybot.i18n import PluginInternationalization
    from supybot.i18n import internationalizeDocstring
    _ = PluginInternationalization('Wekan')
except ImportError:
    # Placeholder that allows to run the plugin on a bot
    # without the i18n module
    def _(x):
        return x

    def internationalizeDocstring(x):
        return x

class WekanHandler(object):

    """Handle Wekan messages"""

    def __init__(self, plugin):
        # HACK: instead of refactoring everything, I can just replace this with each handle_payload() call.
        self.irc = None
        self.plugin = plugin
        self.log = log.getPluginLogger('Wekan')

    def handle_payload(self, headers, payload, irc):
        self.irc = irc
        self.log.debug('Gogs: running on network %r', irc.network)

        text, board_url = (payload['text']).split('\n')
        # Check if any channel has subscribed to this project
        for channel in irc.state.channels.keys():
            boards = self.plugin._load_boards(channel)
            for slug, url in boards.items():
                print(board_url.startswith(url))
                # Parse board url
                if not board_url.startswith(url):
                    continue

                # Just send payload['text']
                self._send_message(channel, text)

    def _send_message(self, channel, msg):
        if self.plugin.registryValue('use-notices', channel):
            announce_msg = ircmsgs.notice(channel, msg)
        else:
            announce_msg = ircmsgs.privmsg(channel, msg)
        self.irc.queueMsg(announce_msg)

class WekanWebHookService(httpserver.SupyHTTPServerCallback):
    """https://github.com/wekan/wekan/wiki/Webhook-data"""

    name = "WekanWebHookService"
    defaultResponse = """This plugin handles only POST request, please don't use other requests."""

    def __init__(self, plugin):
        self.log = log.getPluginLogger('Wekan')
        self.wekan = WekanHandler(plugin)
        self.plugin = plugin

    def _send_error(self, handler, message):
        handler.send_response(403)
        handler.send_header('Content-type', 'text/plain')
        handler.end_headers()
        handler.wfile.write(message.encode('utf-8'))

    def _send_ok(self, handler):
        handler.send_response(200)
        handler.send_header('Content-type', 'text/plain')
        handler.end_headers()
        handler.wfile.write(bytes('OK', 'utf-8'))

    def doPost(self, handler, path, form):
        headers = dict(self.headers)

        network = None

        try:
            information = path.split('/')[1:]
            network = information[0]
        except IndexError:
            self._send_error(handler, _("""Error: You need to provide the
                                        network name in the URL."""))
            return

        irc = world.getIrc(network)
        if irc is None:
            self._send_error(handler, (_('Error: Unknown network %r') % network))
            return

        # Handle payload
        payload = None
        try:
            payload = json.JSONDecoder().decode(form.decode('utf-8'))
        except Exception as e:
            self.log.info(e)
            self._send_error(handler, _('Error: Invalid JSON data sent.'))
            return

        try:
            self.wekan.handle_payload(headers, payload, irc)
        except Exception as e:
            self.log.info(e)
            self._send_error(handler, _('Error: Invalid data sent.'))
            return

        # Return OK
        self._send_ok(handler)

class Wekan(callbacks.Plugin):
    """Plugin for communication and notifications of a Wekan boards
    management tool instance"""
    threaded = True

    def __init__(self, irc):
        global instance
        self.__parent = super(Wekan, self)
        self.__parent.__init__(irc)
        instance = self

        callback = WekanWebHookService(self)
        httpserver.hook('wekan', callback)

    def die(self):
        httpserver.unhook('wekan')
        self.__parent.die()

    def _load_boards(self, channel):
        boards = self.registryValue('boards', channel)
        if boards is None:
            return {}
        else:
            return boards

    def _save_boards(self, boards, channel):
        self.setRegistryValue('boards', value=boards, channel=channel)

    def _check_capability(self, irc, msg):
        if ircdb.checkCapability(msg.prefix, 'admin'):
            return True
        else:
            irc.errorNoCapability('admin')
            return False

    class wekan(callbacks.Commands):
        """Wekan commands"""

        class board(callbacks.Commands):
            """Board commands"""

            @internationalizeDocstring
            def add(self, irc, msg, args, channel, board_slug, board_url):
                """[<channel>] <board-slug> <board-url>

                Announces the changes of the board with the slug
                <board-slug> and the url <board-url> to <channel>.
                """
                if not instance._check_capability(irc, msg):
                    return

                boards = instance._load_boards(channel)
                if board_slug in boards:
                    irc.error(
                        _('This board is already announced to this channel.'))
                    return

                # Save new board mapping
                boards[board_slug] = board_url
                instance._save_boards(boards, channel)

                irc.replySuccess()

            add = wrap(add, ['channel', 'somethingWithoutSpaces', 'httpUrl'])

            @internationalizeDocstring
            def remove(self, irc, msg, args, channel, board_slug):
                """[<channel>] <board-slug>

                Stops announcing the changes of the board slug <board-slug>
                to <channel>.
                """
                if not instance._check_capability(irc, msg):
                    return

                boards = instance._load_boards(channel)
                if board_slug not in boards:
                    irc.error(
                        _('This board is not registered to this channel.'))
                    return

                # Remove board mapping
                del boards[board_slug]
                instance._save_boards(boards, channel)

                irc.replySuccess()

            remove = wrap(remove, ['channel', 'somethingWithoutSpaces'])

            @internationalizeDocstring
            def list(self, irc, msg, args, channel):
                """[<channel>]

                Lists the registered boards in <channel>.
                """
                if not instance._check_capability(irc, msg):
                    return

                boards = instance._load_boards(channel)
                if boards is None or len(boards) == 0:
                    irc.error(_('This channel has no registered boards.'))
                    return

                for board_slug, board_url in boards.items():
                    irc.reply("%s: %s" % (board_slug, board_url))

            list = wrap(list, ['channel'])

Class = Wekan