from typing import Dict, List, Optional, Tuple

import tensorflow as tf

import nn_utils.math_utils as math_utils
from losses import multi_gpu_wrapper
from models.nerd_net.models import NerdCoarseModel, NerdFineModel
from models.nerd_net.sgs_store import SgsStore
from nn_utils.nerf_layers import add_base_args
from nn_utils.sg_rendering import SgRenderer
from utils.training_setup_utils import (
    StateRestoration,
    StateRestorationItem,
    get_num_gpus,
)

class NerdModel(tf.keras.Model):
    def __init__(self, num_images, args, **kwargs):
        super(NerdModel, self).__init__(**kwargs)

        # Setup the models
        self.coarse_model = NerdCoarseModel(args, **kwargs)
        self.fine_model = NerdFineModel(args, **kwargs)

        # Randomize if training
        self.randomized = args.perturb == 1.0
        print("Running with pertubation:", self.randomized)

        # Setup the place where the SGs are stored
        num_illuminations = 1 if args.single_env else num_images
        self.sgs_store = SgsStore(num_illuminations, args.num_sgs, args.mean_sgs_path)

        self.rotating_object = args.rotating_object

        # Add the renderer
        self.renderer = SgRenderer()
        self.num_gpu = max(1, get_num_gpus())

        # Setup the state restoration
        states = [
            StateRestorationItem("coarse", self.coarse_model),
            StateRestorationItem("fine", self.fine_model),
            StateRestorationItem("sgs", self.sgs_store),
        ]
        self.state_restoration = StateRestoration(args, states)

    def save(self, step):
        # Save weights for step
        self.state_restoration.save(step)

    def restore(self, step: Optional[int] = None) -> int:
        # Restore weights from step or if None the latest one
        return self.state_restoration.restore(step)

    @tf.function
    def call(
        self,
        ray_origins: tf.Tensor,
        ray_directions: tf.Tensor,
        camera_pose: tf.Tensor,
        near_bound: float,
        far_bound: float,
        sg_illumination_idx: tf.Tensor,
        ev100: tf.Tensor,
        training=False,
        skip_rendering: bool = False,
        high_quality=False,
    ) -> Tuple[Dict[str, tf.Tensor], Dict[str, tf.Tensor]]:
        """Evaluate the network for given ray origins and directions and camera pose

        Args:
            ray_origins (tf.Tensor(float32), [batch, 3]): the ray origin.
            ray_directions (tf.(float32), [batch, 3]): the ray direction.
            camera_pose (tf.Tensor(float32), [1, 3, 3]): the camera matrix.
            near_bound (float): the near clipping point.
            far_bound (float): the far clipping point.
            sg_illumination_idx (tf.Tensor(int32), [1]): the illumination index.
            ev100 (tf.Tensor(float32), [1]): the ev100 value of the image.
            training (bool, optional): Whether this is a training step or not.
                Activates noise and pertub ray features if requested. Defaults to True.

        Returns:
            coarse_payload (Dict[str, tf.Tensor]): dict with the payload for the coarse
                network.
            fine_payload (Dict[str, tf.Tensor]): dict with the payload for the fine
                network.
        """

        # Get the current illumination
        sg_illumination = self.sgs_store(
            sg_illumination_idx, camera_pose[0] if self.rotating_object else None
        )

        sg_illumination = self.sgs_store.validate_sgs(sg_illumination)

        # Coarse step
        (
            coarse_payload,
            coarse_z_samples,
            coarse_weights,
        ) = self.coarse_model.render_rays(
            ray_origins,
            ray_directions,
            near_bound,
            far_bound,
            tf.stop_gradient(sg_illumination),
            randomized=training and self.randomized,
            overwrite_num_samples=(self.coarse_model.num_samples * 2)
            if high_quality
            else None,
        )

        fine_payload, _, _ = self.fine_model.render_rays(
            ray_origins,
            ray_directions,
            coarse_z_samples,
            coarse_weights,
            sg_illumination,
            ev100,
            randomized=training and self.randomized,
            skip_rendering=skip_rendering,
            overwrite_num_samples=(self.fine_model.num_samples * 2)
            if high_quality
            else None,
        )

        return coarse_payload, fine_payload

    def distributed_call(
        self,
        strategy,
        chunk_size: int,
        ray_origins: tf.Tensor,
        ray_directions: tf.Tensor,
        camera_pose: tf.Tensor,
        near_bound: float,
        far_bound: float,
        sg_illumination_idx: tf.Tensor,
        ev100: tf.Tensor,
        training=False,
        illumination_context_override=None,
        high_quality=False,
    ):
        if illumination_context_override is not None:
            sg_illumination_idx = tf.cast(
                tf.ones_like(sg_illumination_idx) * illumination_context_override,
                tf.int32,
            )

        options = tf.data.Options()
        options.experimental_distribute.auto_shard_policy = (
            tf.data.experimental.AutoShardPolicy.DATA
        )

        dp_df = (
            tf.data.Dataset.from_tensor_slices(
                (ray_origins.numpy(), ray_directions.numpy())
            )
            .batch(chunk_size // (2 if high_quality else 1) * get_num_gpus())
            .with_options(options)
        )

        dp_dist_df = strategy.experimental_distribute_dataset(dp_df)
        coarse_payloads: Dict[str, List[tf.Tensor]] = {}
        fine_payloads: Dict[str, List[tf.Tensor]] = {}

        def add_to_dict(to_add, main_dict):
            for k, v in to_add.items():
                arr = main_dict.get(
                    k,
                    [],
                )
                arr.extend(v)
                main_dict[k] = arr

            return main_dict

        for dp in dp_dist_df:
            rays_o, rays_d = dp
            # Render image.
            coarse_result_per_replica, fine_result_per_replica = strategy.run(
                self.call,
                (
                    rays_o,
                    rays_d,
                    camera_pose,
                    near_bound,
                    far_bound,
                    sg_illumination_idx,
                    ev100,
                    training,
                    illumination_context_override,
                    high_quality,
                ),
            )

            coarse_result = {
                k: strategy.experimental_local_results(v)
                for k, v in coarse_result_per_replica.items()
            }
            fine_result = {
                k: strategy.experimental_local_results(v)
                for k, v in fine_result_per_replica.items()
            }
            coarse_payloads = add_to_dict(coarse_result, coarse_payloads)
            fine_payloads = add_to_dict(fine_result, fine_payloads)

        coarse_payloads = {k: tf.concat(v, 0) for k, v in coarse_payloads.items()}
        fine_payloads = {k: tf.concat(v, 0) for k, v in fine_payloads.items()}

        return coarse_payloads, fine_payloads

    @tf.function
    def train_step(
        self,
        ray_origins: tf.Tensor,
        ray_directions: tf.Tensor,
        camera_pose: tf.Tensor,
        near_bound: float,
        far_bound: float,
        sg_illumination_idx: tf.Tensor,
        ev100: tf.Tensor,
        is_wb_ref_image: tf.Tensor,
        wb_input_value: tf.Tensor,
        optimizer: tf.keras.optimizers.Optimizer,
        target: tf.Tensor,
        target_mask: tf.Tensor,
        lambda_advanced_loss: tf.Tensor,
        lambda_color_loss: tf.Tensor,
        stop_sgs: bool,
    ) -> Tuple[
        Dict[str, tf.Tensor], Dict[str, tf.Tensor], tf.Tensor, tf.Tensor, tf.Tensor
    ]:
        """Perform a single training step.

        Args:
            ray_origins (tf.Tensor(float32), [batch, 3]): the ray origin.
            ray_directions (tf.Tensor(float32), [batch, 3]): the ray direction.
            camera_pose (tf.Tensor(float32), [1, 3, 3]): the camera matrix.
            near_bound (tf.Tensor(float32), [1]): the near clipping point.
            far_bound (tf.Tensor(float32), [1]): the far clipping point.
            sg_illumination_idx (tf.Tensor(int32), [1]): the illumination index.
            ev100 (tf.Tensor(float32), [1]): the ev100 value of the image.
            is_wb_ref_image (tf.Tensor(bool) [1]): whether the current image is
                a reference whitebalance image.
            wb_input_value (tf.Tensor(float32) [1, 3]): if `is_wb_ref_image` then
                this is defines the whitebalance value.
            optimizer (tf.keras.optimizers.Optimizer): the optimizer to use in the
                train step.
            target (tf.Tensor(float32), [batch, 3]): the rgb target from the image.
            target_mask (tf.Tensor(float32), [batch, 1]): the segmentation mask
                target from the image.
            lambda_advanced_loss (tf.Tensor(float32), [1]): current advanced loss
                interpolation value.
            lambda_color_loss (tf.Tensor(float32), [1]): current color loss
                interpolation value.

        Returns:
            coarse_payload (Dict[str, tf.Tensor]): dict with the payload for the
                coarse network.
            fine_payload (Dict[str, tf.Tensor]): dict with the payload for the fine
                network.
            loss (tf.Tensor(float32), [1]): the joint loss.
            coarse_losses (Dict[str, tf.Tensor]): a dict of loss names with the
                evaluated losses. "loss" stores the final loss of the layer.
            fine_losses (Dict[str, tf.Tensor]): a dict of loss names with the evaluated
                losses. "loss" stores the final loss of the layer.
        """
        # fine_result, coarse_result, loss, coarse_losses, fine_losses
        if is_wb_ref_image[0]:
            self.sgs_store.apply_whitebalance_to_idx(
                sg_illumination_idx,
                wb_input_value,
                ray_origins,
                ev100,
                clip_range=None if stop_sgs else (0.99, 1.01),
            )
        else:
            if stop_sgs:
                self.sgs_store.apply_whitebalance_to_idx(
                    sg_illumination_idx,
                    tf.constant([[0.8, 0.8, 0.8]], dtype=tf.float32),
                    ray_origins,
                    ev100,
                    clip_range=None if stop_sgs else (0.99, 1.01),
                    grayscale=True,
                )

        with tf.GradientTape() as tape:
            coarse_result, fine_result = self.call(
                ray_origins,
                ray_directions,
                camera_pose,
                near_bound,
                far_bound,
                sg_illumination_idx,
                ev100,
                training=True,
            )

            coarse_losses = self.coarse_model.calculate_losses(
                coarse_result, target, target_mask, lambda_advanced_loss
            )

            fine_losses = self.fine_model.calculate_losses(
                fine_result,
                target,
                target_mask,
                lambda_advanced_loss,
                lambda_color_loss,
            )

            loss = coarse_losses["loss"] + fine_losses["loss"]

        grad_vars = (
            self.coarse_model.trainable_variables
            + self.fine_model.trainable_variables
            + self.sgs_store.trainable_variables
        )
        gradients = tape.gradient(loss, grad_vars)

        gradients, global_norm = tf.clip_by_global_norm(gradients, 1.0)

        optimizer.apply_gradients(zip(gradients, grad_vars))

        return fine_result, coarse_result, loss, coarse_losses, fine_losses

    @tf.function
    def illumination_single_step(
        self,
        ray_directions,
        basecolor,
        metallic,
        roughness,
        normal,
        alpha,
        camera_pose,
        sg_illumination_idx,
        target,
        mse,
        optimizer,
    ):
        view_direction = math_utils.normalize(-1 * ray_directions)

        with tf.GradientTape(watch_accessed_variables=False) as tape:
            tape.watch(self.sgs_store.trainable_variables)

            sg = self.sgs_store(
                sg_illumination_idx,
                camera_pose if self.rotating_object else None,
            )  # Receive latest sgs

            render = self.renderer(
                sg_illuminations=sg,
                basecolor=basecolor,
                metallic=metallic,
                roughness=roughness,
                normal=normal,
                alpha=alpha,
                view_dir=view_direction,
            )

            # Auto exposure + srgb to model camera setup
            render = math_utils.white_background_compose(
                self.fine_model.camera_post_processing(render, None), alpha
            )

            sgs_loss = mse(
                math_utils.white_background_compose(tf.reshape(target, (-1, 3)), alpha),
                render,
            )
            tf.debugging.check_numerics(
                sgs_loss, "loss sgs: {}".format(tf.math.is_nan(sgs_loss))
            )

        grad_vars = self.sgs_store.trainable_variables
        gradients = tape.gradient(sgs_loss, grad_vars)

        optimizer.apply_gradients(zip(gradients, grad_vars))

        # self.sgs_store.ensure_sgs_correct(sg_illumination_idx)

        return sgs_loss

    def illumination_steps(
        self,
        ray_origins: tf.Tensor,
        ray_directions: tf.Tensor,
        camera_pose: tf.Tensor,
        near_bound: float,
        far_bound: float,
        sg_illumination_idx: tf.Tensor,
        optimizer: tf.keras.optimizers.Optimizer,
        target: tf.Tensor,
        steps: int,
        chunk_size: int = 1024,
        strategy=tf.distribute.get_strategy(),
    ) -> tf.Tensor:
        """Perform a illumination optimization step. This only performs the illumination
        with a fixed network.

        Args:
            ray_origins (tf.Tensor(float32), [batch, 3]): the ray origin.
            ray_directions (tf.Tensor(float32), [batch, 3]): the ray direction.
            camera_pose (tf.Tensor(float32), [batch, 3, 4]): the camera matrix.
            near_bound (tf.Tensor(float32), [1]): the near clipping point.
            far_bound (tf.Tensor(float32), [1]): the far clipping point.
            sg_illumination_idx (tf.Tensor(int32), [1]): the illumination index.
            optimizer (tf.keras.optimizers.Optimizer): the optimizer to use in the
                train step.
            target (tf.Tensor(float32), [batch, 3]): the rgb target from the image.
            steps (int): the number of optimization steps to perform.
            chunk_size (int): If specified runs the sampling in
                batches. Runs everything jointly if 0.

        Returns:
            tf.Tensor(float32), [1]: the loss after the optimization
        """
        mse = multi_gpu_wrapper(tf.keras.losses.MeanSquaredError, target.shape[0])

        _, fine_result = self.distributed_call(
            strategy,
            chunk_size,
            ray_origins,
            ray_directions,
            camera_pose,
            near_bound,
            far_bound,
            sg_illumination_idx,
            None,
            False,
        )

        dp_df = (
            tf.data.Dataset.from_tensor_slices(
                (
                    ray_directions,
                    target,
                    fine_result["basecolor"],
                    fine_result["metallic"],
                    fine_result["roughness"],
                    fine_result["normal"],
                    fine_result["acc_alpha"][..., None],
                )
            )
            .shuffle(chunk_size * 2, reshuffle_each_iteration=True)
            .batch(chunk_size * get_num_gpus())
        )
        dp_dist_df = strategy.experimental_distribute_dataset(dp_df)

        for i in tf.range(steps):
            total_loss = 0

            for dp in dp_dist_df:
                ray_d, trgt, bcol, mtl, rgh, nrm, alp = dp
                illum_loss_per_replica = strategy.run(
                    self.illumination_single_step,
                    (
                        ray_d,
                        bcol,
                        mtl,
                        rgh,
                        nrm,
                        alp,
                        camera_pose[:1],
                        sg_illumination_idx[:1],
                        trgt,
                        mse,
                        optimizer,
                    ),
                )
                illum_loss = strategy.reduce(
                    tf.distribute.ReduceOp.SUM, illum_loss_per_replica, axis=None
                )
                total_loss = total_loss + illum_loss

        return total_loss

    @classmethod
    def add_args(cls, parser):
        """Add the base nerf arguments to the parser with addition
        to the specific NeRD ones.

        Args:
            parser (ArgumentParser): the current ArgumentParser.

        Returns:
            ArgumentParser: the modified ArgumentParser for call chaining
        """
        add_base_args(parser)
        parser.add_argument(
            "--coarse_samples",
            type=int,
            default=64,
            help="number of coarse samples per ray in a fixed grid",
        )
        parser.add_argument(
            "--fine_samples",
            type=int,
            default=128,
            help="number of additional samples per ray based on the coarse samples",
        )
        parser.add_argument(
            "--fourier_frequency",
            type=int,
            default=10,
            help="log2 of max freq for positional encoding",
        )
        parser.add_argument(
            "--net_width", type=int, default=256, help="channels per layer"
        )
        parser.add_argument(
            "--net_depth", type=int, default=8, help="layers in network"
        )

        parser.add_argument(
            "--num_sgs",
            type=int,
            default=24,
            help=(
                "Number of spherical Gaussians per illumination. "
                "Default is 24 and mean"
            ),
        )
        parser.add_argument(
            "--mean_sgs_path",
            type=str,
            default="data/nerd/mean_sgs.npy",
            help="Path to the initial mean spherical Gaussians initialization file.",
        )

        parser.add_argument(
            "--rotating_object",
            action="store_true",
            help=(
                "The object is rotating instead of the camera. The illumination then "
                "needs to stay static"
            ),
        )
        parser.add_argument(
            "--single_env",
            action="store_true",
            help="All input images are captured under a single environment",
        )

        # Coarse configs
        parser.add_argument(
            "-lindisp",
            "--linear_disparity_sampling",
            action="store_true",
            help="Coarse sampling linearly in disparity rather than depth",
        )

        parser.add_argument(
            "--sgs_condense_features",
            type=int,
            default=16,
            help=(
                "The number features the sgs condense network reduces the spherical "
                "gaussians to."
            ),
        )

        # Fine configs

        parser.add_argument(
            "--brdf_interpolation_z",
            type=int,
            default=2,
            choices=[0, 1, 2, 3],
            help=(
                "The BRDF is not predicted directly but a BRDF embedding space is "
                "created which is then used decoded to a BRDF. This defines the size "
                "of the latent vector. 0 disables the interpolation",
            ),
        )

        parser.add_argument(
            "--direct_rgb",
            action="store_true",
            help=(
                "Also performs a direct RGB color prediction. This is useful in the "
                "beginning of the training."
            ),
        )

        parser.add_argument(
            "--mlp_normal",
            action="store_true",
            help="The MLP generates the normal instead of deriving it from gradients.",
        )

        return parser


import random

from core.configuration import Configuration
from model.person import State

class Neighborhood:

    # Constructor
    def __init__(self, neighborhood):
        self.people = neighborhood

    # Chequea si todas las células de la vecindad son susceptibles
    def isSusceptible(self):
        if list(filter(lambda p: p.getState() == State.SUSCEPTIBLE, self.people)).__len__() == list(
                self.people).__len__():
            return True
        return False

    def getCountForState(self, state):
        choices = {
            State.SUSCEPTIBLE: (list(filter(lambda p: p.getState() == State.SUSCEPTIBLE, self.people))).__len__(),
            State.EXPUESTO: (list(filter(lambda p: p.getState() == State.EXPUESTO, self.people))).__len__(),
            State.INFECTADO: (list(filter(lambda p: p.getState() == State.INFECTADO, self.people))).__len__(),
            State.RECUPERADO: (list(filter(lambda p: p.getState() == State.RECUPERADO, self.people))).__len__(),
        }
        return choices.get(state, 0)

    def getBoolProbability(self, state):
        choices = {
            State.SUSCEPTIBLE: self.probability(1),
            # probabilidad de ser expuesto al virus
            State.EXPUESTO: self.probability(
                self.getCountForState(State.INFECTADO) * Configuration().getTransmissibilityDisease()),
            State.INFECTADO: self.probability(1),
            State.RECUPERADO: self.probability(1),
        }
        return choices.get(state, False)

    def probability(self, probability):
        return random.random() <= probability


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

import numpy as np

class Pitch:
    def __init__(self, cents:float):
        """
        Musical pitch.

        :cents: Number of cents away from A4 (midi 69, 440Hz).
        """
        self._cents = cents

    @staticmethod
    def from_frequency(frequency:float, detune=0) -> 'Pitch':
        """
        Create new Pitch object.

        :frequency: Frequency in hertz.
        :detune: Number of cents away from given frequency.
        """
        return Pitch(1200*np.log2(frequency/440) + detune)

    @staticmethod
    def from_midi(midi_pitch_id:int, detune=0) -> 'Pitch':
        """
        Create new Pitch object.

        :midi_pitch_id: Midi note pitch value.
        :detune: Number of cents away from normal frequency.
        """
        return Pitch((midi_pitch_id - 69)*100 + detune)

    @staticmethod
    def from_name(pitch_name:str, detune=0) -> 'Pitch':
        """
        Create new Pitch object.

        :pitch_name: Note name in midi notation.
        :detune: Number of cents away from normal frequency.
        """
        octave = int(pitch_name[-1])
        possible_names = {
           'C': 0,
           'C#': 1,
           'ces': -1,
           'c': 0,
           'cis': 1,
           'D': 2,
           'D#': 3,
           'des': 1,
           'd': 2,
           'dis': 3,
           'E': 4,
           'E#': 5,
           'es': 3,
           'e': 4,
           'eis': 5,
           'F': 5,
           'F#': 6,
           'fes': 4,
           'f': 5,
           'fis': 6,
           'G': 7,
           'G#': 8,
           'ges': 6,
           'g': 7,
           'gis': 8,
           'A': 9,
           'A#': 10,
           'as': 8,
           'a': 9,
           'ais': 10,
           'B': 11,
           'H': 11,
           'B#': 12,
           'H#': 12,
           'b': 10,
           'h': 11,
           'his': 12
        }
        midi_id = 12*(1 + octave) + possible_names[pitch_name[:-1]]
        return Pitch.from_midi(midi_id, detune)

    @staticmethod
    def from_cents_from_a(cents:float) -> 'Pitch':
        """
        Create new Pitch object.

        :cents: Number of cents away from A4 (midi 69, 440Hz).
        """
        return Pitch(cents)

    @staticmethod
    def from_pitch(pitch:'Pitch', detune=0) -> 'Pitch':
        """
        Create new Pitch object.

        :pitch: Pitch object.
        :detune: Number of cents away from normal frequency.
        """
        return Pitch(pitch.get_cents_from_a() + detune)

    def get_cents_from_a(self) -> float:
        """
        Get distance from A4 (midi 69, 440Hz) in cents.

        :returns: Distance from A4 in cents.
        """
        return self._cents

    def get_midi_pitch(self) -> (int, float):
        """
        Get mini value + detune.

        :returns: Midi value, Detune in cents in range <-50, 50).
        """
        pitch_id = 69 + int(self._cents/100)
        detune = self._cents - 100*(pitch_id - 69)
        while detune >= 50:
            pitch_id += 1
            detune -= 100
        while detune < -50:
            pitch_id -= 1
            detune += 100
        return (pitch_id, detune)

    def get_frequency(self, detune=0) -> float:
        """
        Get frequency in hertz from the pitch.

        :detune: Number of cents away from original frequency.

        :returns: Frequency in hertz.
        """
        return np.power(2, (self._cents + detune)/1200) * 440

    def copy(self, detune=0) -> 'Pitch':
        return Pitch(self._cents + detune)

    def add_interval(self, interval:'Interval', detune=0) -> 'Pitch':
        return Pitch(self._cents + interval.get_cents() + detune)

    @staticmethod
    def lower(pitch1:'Pitch', pitch2:'Pitch'):
        if pitch1._cents <= pitch2._cents:
            return pitch1
        else:
            return pitch2

    @staticmethod
    def higher(pitch1:'Pitch', pitch2:'Pitch'):
        if pitch1._cents >= pitch2._cents:
            return pitch1
        else:
            return pitch2


import jax.numpy as jnp
from tensorflow_probability.substrates import jax as tfp

class AffineTransform:
    """Y = transform(X) = normalization_std @ X + normalization_mean"""

    def __init__(self, shift: jnp.array, scale: jnp.array):
        self.shift = shift
        self.scale = scale

        shift = tfp.bijectors.Shift(self.shift)
        if jnp.size(self.scale) == 1:
            scale = tfp.bijectors.Scale(self.scale)
        else:
            scale = tfp.bijectors.ScaleMatvecDiag(self.scale)
        self.transform = tfp.bijectors.Chain([shift, scale])

    def __call__(self, base_dist: tfp.distributions.Distribution) -> tfp.distributions.TransformedDistribution:
        # Transform distribution to access `log_prob` and `sample` methods
        transformed_dist = self.transform(base_dist)

        # Fill in mean, stddev and variance methods
        if callable(base_dist.mean):
            mean, stddev, var = base_dist.mean(), base_dist.stddev(), base_dist.variance()
        else:
            mean, stddev, var = base_dist.mean, base_dist.stddev, base_dist.variance
        transformed_dist.mean = self.transform(mean)
        transformed_dist.stddev = jnp.exp(jnp.log(stddev) + jnp.log(self.scale))
        transformed_dist.variance = jnp.exp(jnp.log(var) + 2. * jnp.log(self.scale))
        return transformed_dist


import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QHBoxLayout, QWidget, QTabWidget
import GeneralUI as gui
import ProtocolUI as pui
import ScriptUI as sui

class hETH(QMainWindow):
    def __init__(self):
        super().__init__()

        self.InitUI()

    def InitUI(self):
        tabs = QTabWidget(self)
        tabs.addTab(gui.GeneralUI(), 'General')
        tabs.addTab(pui.ProtocolUI(), 'Protocol')
        tabs.addTab(sui.ScriptUI(), 'Script')

        self.setCentralWidget(tabs)
        self.setWindowTitle('hEmbedded_TestHarness')
        self.setGeometry(300, 300, 500, 500)
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = hETH()
    sys.exit(app.exec_())


from data_augmentation.xml_helper import *
from data_augmentation.DataAugmentForObjectDetection import *
import cv2
import os

def data_aug_sinlge(augmenter, xml_path, out_image_path, out_xml_path, rotate, hori_flip, add_noise):
    # 读取原图片路径和box
    image_path, bbox = parse_xml(xml_path)
    image_name = os.path.split(image_path)[1]
    # 读取源图片
    image = cv2.imread(image_path)
    if rotate:
        # augment 1: rotate
        aug_class = "rotate"
        auged_image, auged_bbox = augmenter._rotate_img_bbox(image, bbox)
        # auged_image, auged_bbox = augmenter.dataAugment(image, bbox)
        #输出图片的路径
        auged_image_path = out_image_path + "/" + image_name[:-4] + aug_class + image_name[-4:]
        cv2.imwrite(auged_image_path, auged_image)
        img_size = (120, 120, 3)
        for i, box in enumerate(bbox):
            class_name = box[-1]
            auged_bbox[i].append(class_name)

        generate_xml(auged_image_path, auged_bbox, img_size, out_xml_path)
        # show_pic(auged_image, auged_bbox)

    if hori_flip:
        # augment 2: flip
        aug_class = "horizontal_flip"
        auged_image, auged_bbox = augmenter._filp_pic_bboxes(image, bbox)
        # auged_image, auged_bbox = augmenter.dataAugment(image, bbox)
        # 输出图片的路径
        auged_image_path = out_image_path + "/" + image_name[:-4] + aug_class + image_name[-4:]
        cv2.imwrite(auged_image_path, auged_image)
        img_size = (120, 120, 3)
        for i, box in enumerate(bbox):
            class_name = box[-1]
            auged_bbox[i].append(class_name)
        generate_xml(auged_image_path, auged_bbox, img_size, out_xml_path)
        # show_pic(auged_image, auged_bbox)

    if add_noise:
        # augment 2: flip
        aug_class = "add_noise"
        auged_image = augmenter._addNoise(image)
        auged_bbox = bbox
        # 输出图片的路径
        auged_image_path = out_image_path + "/" + image_name[:-4] + aug_class + image_name[-4:]
        cv2.imwrite(auged_image_path, auged_image)
        img_size = (120, 120, 3)
        generate_xml(auged_image_path, auged_bbox, img_size, out_xml_path)
        # show_pic(auged_image, auged_bbox)

def data_aug(augmenter, xml_path, out_image_path, out_xml_path, rotate, hori_flip, add_noise):
    # out_xml_path = xml_path
    out_image_path = os.path.abspath(out_image_path)
    all_xml_path = [xml_path + xml_name for xml_name in os.listdir(xml_path) if xml_name.endswith(".xml")]
    for i, single_xml_path in enumerate(all_xml_path):
        data_aug_sinlge(augmenter, single_xml_path, out_image_path, out_xml_path, rotate, hori_flip, add_noise)
    print("data augmentation successfully !")

if __name__ == "main":
    xml_path = os.path.dirname(__file__) + "/data_xml/"
    out_image_path = os.path.dirname(__file__) + "/augmented_data/image"
    out_xml_path = os.path.dirname(__file__) + "/augmented_data/annotations"

    augmenter = DataAugmentForObjectDetection()

    data_aug(augmenter, xml_path, out_image_path, out_xml_path, rotate=True, hori_flip=True, add_noise=True)


# -*- coding: utf-8 -*-
"""
Created on Fri Mar 19 17:18:02 2021

@author: phygbu
"""
from collections.abc import Iterable

from uncertainties.core import Variable,AffineScalarFunc

from .funcs import format_error
from . import exceptions as excp

def mk_answer(ans, key, units=None, fmt="text"):
    if key not in ans:
        return None
    val = ans.pop(key, None)
    err = ans.pop(key + "_error", None)
    if val is None:
        return None
    if err is None:
        err = 0.0
    try:
        val = float(val)
        err = float(err)
    except (ValueError, TypeError):
        raise excp.BadAnswer(
            "Bad format of answer for {} {}+/- {}".format(key, type(val), type(err))
        )
    return Result(val, err, units=units, fmt=fmt)

def _fix_val(v,s):
    """Try to ensure that v and s are both floats ad s is positive."""

    if isinstance(v,Iterable) and len (v)==2:
        v,s=v[0],v[1]
    while isinstance(s,Iterable) and not isinstance(s,str):
        s=s[0] # Un wrap 2 to get the first value

    while isinstance(v,Iterable) and not isinstance(v,str):
        v=v[0] # and unwrap the value

    s=abs(float(s))
    v=float(v)

    return v,s

class Result(Variable):
    """A subclass of the uncertainities Variable type for holding answers with uncertainities."""

    def __init__(self,v,s=None,**kargs):
        """Initialise the Result.

        Args:
            v (float): Value
            s (float): uncertainity

        Keywords:
            units (str): Units of result default''
            mode (str): 'float','eng','sci' how to format result (defaiult float)
            fmt (str): 'text','latex', 'html' output format (default html)
        """
        if s is None:
            s=0.0
        if isinstance(v,AffineScalarFunc):
            s=v.s
            v=v.n

        v,s = _fix_val(v,s)

        self.units=kargs.pop("units","")
        self.mode=kargs.pop("mode","float")
        self.fmt=kargs.pop("fmt","html")
        self.margin=kargs.pop("margin",5.0)
        super(Result,self).__init__(v,s,**kargs)

    def __or__(self,other):
        if not isinstance(other,Variable):
            try:
                other=float(other)
            except ValueError:
                return NotImplemented
            other=Variable(other,0.0)
        d=self-other
        return abs(d.n)-self.margin*d.s<=0.0 # Fixed if we're 3 std to being equal, we're equal

    def __repr__(self):
        if self.units!="":
            mode="eng"
        else:
            mode="eng"
        return format_error(self.n,self.s,units=self.units,mode=mode,fmt=self.fmt)

    def __format__(self,formatstr):
        return self.__repr__()

    def format(self,latex=False, mode=None, units=None, prefix=""):
        """This handles the printing out of the answer with the uncertaintly to 1sf and the
        value to no more sf's than the uncertainty.

        Args:
            self (Result): The value to be formated
            latex (bool): If true, then latex formula codes will be used for +/- symbol for matplotlib annotations
            mode (string): If "float" (default) the number is formatted as is, if "eng" the value and error is converted
                to the next samllest power of 1000 and the appropriate SI index appended. If mode is "sci" then a scientifc,
                i.e. mantissa and exponent format is used.
            units (string): A suffix providing the units of the value. If si mode is used, then appropriate si prefixes are
                prepended to the units string. In LaTeX mode, the units string is embedded in \\mathrm
            prefix (string): A prefix string that should be included before the value and error string. in LaTeX mode this is
                inside the math-mode markers, but not embedded in \\mathrm.

        Returns:
            String containing the formated number with the eorr to one s.f. and value to no more d.p. than the error.
        """
        if units is None:
            units=self.units
        if mode is None:
            mode="float" if units=="" else "eng"
        latex = "latex" if latex else "html"
        return format_error(self.n,self.s,fmt=latex,mode=mode,units=units,prefix=prefix)


# https://leetcode.com/problems/reverse-linked-list/description/

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head or not head.next: return head
        left, right = None, head

        while right:
            tmp, right.next = right.next, left
            left, right = right, tmp

        return left


# raspberry pi imports
import busio
import board

# environment variables setup
import os

# raspberry pi setup
I2C_BUS = busio.I2C(board.SCL, board.SDA)

# magnetometer import
import adafruit_mlx90393

# magnetometer setup
SENSOR = adafruit_mlx90393.MLX90393(I2C_BUS, gain=adafruit_mlx90393.GAIN_1X)

# time imports
import time
from datetime import datetime
import pytz

# time setup
zoneName = os.environ['TIMEZONE']
timezone = pytz.timezone(zoneName)
currentTime = datetime.now(tz=timezone)

# twilio imports
from twilio.rest import Client

# twilio setup
accountSid = os.environ['TWILIO_ACCOUNT_SID']
authToken = os.environ['TWILIO_AUTH_TOKEN']
twilioPhone = os.environ['TWILIO_PHONE_NUMBER']
recipient01 = os.environ['RECIPIENT_01']
recipient02 = os.environ['RECIPIENT_02']

# laps setup
magnet = False
laps = 0
distance = 0

# when running this script
while True:
    # set up x, y, z axes for the sensor
    MX, MY, MZ = SENSOR.magnetic

    # check for significant changes in axis z
    if (MZ < -500 or MZ > 500):
        print("MAGNET SENSED", MX, "  Y", MY,"  Z", MZ)
        # the magnet is near
        magnet = True
    else:
        print(MX," ",MY,"  ",MZ)
        # the magnet is far
        if magnet == True:
            magnet = False
            laps = laps + 1

   # update recipients every morning at 9am local time
    if currentTime.isoformat()[11:19] == "09:00:00":
        # hedgehog wheel circumference is 37 inches
        distance = (laps*37)/63360
        print (distance, " miles have been run")

        # text recipients the miles ran
        if (distance > 3):
            message = client.messages.create(
                body='Good Morning! Hedgie ran ' + str(distance) + ' miles last night. What a good boy.',
                from_=twilioPhone,
                to=recipient01
            )
            message = client.messages.create(
                body='Good Morning! Heggie ran ' + str(distance) + ' miles last night. What a good boy.',
                from_=twilioPhone,
                to=recipient02
            )

        else:
            message = client.messages.create(
                body='Hedgie ran ' + str(distance) + ' miles last night. We should check on him.',
                from_=twilioPhone,
                to=recipient01
            )
            message = client.messages.create(
                body='Alert! Alert! Heggie ran ' + str(distance) + ' miles last night. Alert!',
                from_=twilioPhone,
                to=recipient02
            )
        # reset laps and distance each morning after sending the text
        laps = 0
        distance = 0

    # sensor status displayed when script is stopped
    if SENSOR.last_status > adafruit_mlx90393.STATUS_OK:
        SENSOR.display_status()

    # check for magnet every quarter second
    time.sleep(0.25)


import csv
import sys
import json
import pickle

class Plik:
    def __init__(self, nazwa):
        self.nazwa = nazwa
        self.rozszerzenie = nazwa.split(".")[-1]  # wyodrębnij rozszerzenie pliku

    def odczytaj(self):
        if self.rozszerzenie == "csv":
            plik = PlikCSV(self.nazwa)
            return plik.odczytaj()
        elif self.rozszerzenie == "json":
            plik = PlikJSON(self.nazwa)
            return plik.odczytaj()
        elif self.rozszerzenie == "txt":
            plik = PlikTXT(self.nazwa)
            return plik.odczytaj()
        elif self.rozszerzenie == "pickle":
            plik = PlikPICKLE(self.nazwa)
            return plik.odczytaj()

        else:
            raise ValueError("Nieobsługiwane rozszerzenie pliku")

    def zapisz(self, dane):
        if self.rozszerzenie == "csv":
            plik = PlikCSV(self.nazwa)
            plik.zapisz(dane)
        elif self.rozszerzenie == "json":
            plik = PlikJSON(self.nazwa)
            plik.zapisz(dane)
        elif self.rozszerzenie == "txt":
            plik = PlikTXT(self.nazwa)
            plik.zapisz(dane)
        elif self.rozszerzenie == "pickle":
            plik = PlikPICKLE(self.nazwa)
            plik.zapisz(dane)
        else:
            raise ValueError("Nieobsługiwane rozszerzenie pliku")

class Zmieniacz(Plik):
    def zmien_wartosci(self, dane, zmiany):
        for zmiana in zmiany:
            z = zmiana.split(",")
            if len(z) != 3:
                print("Blad!!!!!!")
                quit()
            x, y, wartosc = z
            x = int(x) - 1
            y = int(y) - 1
            if x < 0 or y < 0:
                print("Blad, x i y musza byc wieksze od 0.")
                quit()
            dane[x][y] = wartosc
        return dane

class PlikCSV(Zmieniacz):
    def odczytaj(self):
        dane = []
        with open(self.nazwa, "r") as PlikCSV:
            czytnik = csv.reader(PlikCSV)
            for wiersz in czytnik:
                dane.append(wiersz)
        return dane

    def zapisz(self, dane):
        with open(self.nazwa, "w", newline="") as PlikCSV:
            zapis = csv.writer(PlikCSV)
            for wiersz in dane:
                zapis.writerow(wiersz)

class PlikJSON(Zmieniacz):
    def odczytaj(self):
        print(self.nazwa)
        with open(self.nazwa, "r") as plikjson:
            dane = json.load(plikjson)
        return dane

    def zapisz(self, dane):
        with open(self.nazwa, "w") as plikjson:
            json.dump(dane, plikjson)

class PlikTXT(Zmieniacz):
    def odczytaj(self):
        with open(self.nazwa, "r") as pliktxt:
            dane = pliktxt.read()
        return dane

    def zapisz(self, dane):
        with open(self.nazwa, "w") as pliktxt:
            dane_str = "\n".join(str(element) for element in dane)
            pliktxt.write(dane_str)

# Funkcje do odczytu i zapisu pliku PICKLE
class PlikPICKLE(Zmieniacz):
    def odczytaj(self):
        with open(self.nazwa, "rb") as plikpickle:
            dane = pickle.load(plikpickle)
        return dane

    def zapisz(self, dane):
        with open(self.nazwa, "wb") as plikpickle:
            pickle.dump(dane, plikpickle)

def stworz_klase_obslugi_pliku(plik_wejsciowy):
    if plik_wejsciowy.endswith(".csv"):
        plik = PlikCSV(plik_wejsciowy)
    elif plik_wejsciowy.endswith(".json"):
        plik = PlikJSON(plik_wejsciowy)
    elif plik_wejsciowy.endswith(".txt"):
        plik = PlikTXT(plik_wejsciowy)
    elif plik_wejsciowy.endswith(".pickle"):
        plik = PlikPICKLE(plik_wejsciowy)
    else:
        print("Nieobsługiwany format pliku!")
        sys.exit(1)
    return plik

# Główna funkcja programu
if __name__ == "__main__":
    # Sprawdzenie poprawności argumentów wywołania programu
    if len(sys.argv) < 3:
        print(
            "Sposób użycia: python reader.py <plik_wejsciowy> <plik_wyjsciowy> <zmiana_1> <zmiana_2> ... <zmiana_n>"
        )
        print(
            '<zmiana_x> - Zmiana w postaci "x,y,wartosc" - x (kolumna) oraz y (wiersz) są współrzędnymi liczonymi od 0, natomiast "wartosc" to zmiana, która ma trafić na podane miejsce.'
        )
        sys.exit(1)

    # Odczytanie nazw plików oraz zmian z argumentów wywołania programu
    plik_wejsciowy = sys.argv[1]
    plik_wyjsciowy = sys.argv[2]
    zmiany = sys.argv[3:]

    # # Odczytanie danych z pliku wejściowego
    # dane = odczytaj_csv(plik_wejsciowy)
    # Odczytanie danych z pliku wejściowego

    plik = stworz_klase_obslugi_pliku(plik_wejsciowy)
    plik_zapis = stworz_klase_obslugi_pliku(plik_wyjsciowy)

    dane = plik.odczytaj()

    # Przetworzenie zmian
    dane = plik.zmien_wartosci(dane, zmiany)

    # Wyświetlenie danych w terminalu
    for wiersz in dane:
        print(",".join(wiersz))

    # # Zapisanie danych do pliku wyjściowego
    # zapisz_csv(plik_wyjsciowy, dane)
    # Zapisanie danych do pliku wyjściowego
    plik_zapis.zapisz(dane)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" tool to help keeping updated with diffusion date of TV Shows """
# pyuic4 -o ui_KEpShow.py KEpShow.ui
# File : KEpShow.py
import datetime, locale, os, re, csv, signal, sys, html, time, urllib.request, urllib.error, urllib.parse, warnings
from PyQt5 import QtCore, QtGui, QtWidgets

from ui_KEpShow import Ui_MainQWidget

# display deprecation warnings
warnings.simplefilter('default')

XML_FILENAME = "KEpShow_dirs.xml"
DIRECTORIES_TO_PARSE = []

TODAY = datetime.datetime.now().strftime("%Y%m%d")

SHOW_SEEN_TO = {}
RUNNING_SHOWS = []

SKIPPED_EXTENSIONS = [".zip", ".srt", ".ass", ".nfo"]

################################################################################
################################################################################
def clean_name(filename):
    cleaned_name = filename.replace("720p", "", 1).lower()
    cleaned_name = cleaned_name.replace("1080p", "", 1)
    cleaned_name = cleaned_name.replace("1080", "", 1)
    cleaned_name = cleaned_name.replace("x264", "", 1)
    cleaned_name = cleaned_name.replace("h264", "", 1)
    cleaned_name = cleaned_name.replace("h.264", "", 1)
    cleaned_name = cleaned_name.replace("hdtv", "", 1)
    cleaned_name = cleaned_name.replace("dd5.1", "", 1)
    cleaned_name = cleaned_name.replace("french", "", 1)
    cleaned_name = re.sub("[\._\(]20\d{2}[\._\)]", "", cleaned_name, 1)

    return cleaned_name

################################################################################
################################################################################
def parse_epguides_page(view, page, dirpath):
    """ Parse epguides webpage """
    locale.setlocale(locale.LC_ALL, 'C')
    url  = "http://epguides.com/"
    url += str(page)
    request = urllib.request.Request(url)
    request.add_header('Cookie', 'ListDisplay=tvmaze.com')
    webpage = urllib.request.urlopen(request).readlines()
    found_first = 0
    model = QtGui.QStandardItemModel(0, 3)

    found = 0
    dir_content = []
    originalcase_dir_content = []
    for files in os.listdir(dirpath):
        if os.path.isfile(dirpath + "/" + files):
            clean = clean_name(files)
            if (not clean.endswith("zip")) & (not clean.endswith("srt")) & (not clean.endswith("ass")) & (not clean.endswith("nfo")):
                dir_content.append(clean)
                originalcase_dir_content.append(files)
        else:
            for child_files in os.listdir(dirpath + "/" + files):
                if os.path.isfile(
                    os.path.join(str(dirpath + "/" + files), child_files)
                ):
                    clean = clean_name(child_files)
                    if (not clean.endswith("zip")) & (not clean.endswith("srt")) & (not clean.endswith("ass")) & (not clean.endswith("nfo")):
                        dir_content.append(clean)
                        originalcase_dir_content.append(child_files)

    for line in webpage:
        line = line.decode("ISO-8859-1") # 'UTF-8'
        if found_first == 0:
            if line.lstrip().startswith('<div id="eplist"'):
                found_first += 1
        else:
            split_line = re.search(
                "^\d*\.\s*(\d*)[-](\d*).{15}\s*(\d*)[/ ]([a-zA-Z]*)[/ ](\d*)",line)
            if split_line:
                try:
                    diffusion_date = time.strptime(
                                            "%02d" % int(split_line.group(3))
                                            + "/" +
                                            split_line.group(4)
                                            + "/" +
                                            split_line.group(5),
                                        "%d/%b/%y")

                    season_nb  = int(split_line.group(1))
                    episode_nb = int(split_line.group(2))
                    str_cat = "s" + "%02d" % season_nb + "e" + "%02d" % episode_nb
                    found = 0
                    filename = ""

                    lowpage = str(page).lower()

                    if lowpage in SHOW_SEEN_TO:
                        split_ep = re.search("(\d*)[e](\d*)",
                                    SHOW_SEEN_TO[str(lowpage)])
                        if split_ep:
                            if season_nb < int(split_ep.group(1)):
                                found = 2
                            elif season_nb == int(split_ep.group(1)):
                                if episode_nb <= int(split_ep.group(2)):
                                    found = 2

                    # replace 720p by a space to avoid
                    # detecting it as an episode number
                    for index,mkv in enumerate(dir_content):
                        if mkv.find(str_cat) != -1:
                            filename = originalcase_dir_content[index]
                            found = 1
                        elif mkv.find(
                                str(season_nb) + str("%02d" % episode_nb)) != -1:
                            filename = originalcase_dir_content[index]
                            found = 1
                        elif mkv.find(
                                str(season_nb) + "x" + str("%02d" % episode_nb)
                                ) != -1:
                            filename = originalcase_dir_content[index]
                            found = 1

                    diffusion_date = datetime.datetime(*diffusion_date[0:7]).strftime("%d/%m/%Y")

                    check = datetime.datetime.strptime(diffusion_date,"%d/%m/%Y") - datetime.datetime.strptime(TODAY,"%Y%m%d")

                    color = "#86FF68"
                    # not aired yet
                    if check.days > 0:
                        color = "#8BB2FF"
                    # today date
                    elif check.days == 0:
                        color = "#FFF55C"
                    add_root_node(model, "", found)
                    add_child_node(model, 0, str_cat)
                    add_child_node(model, 1, diffusion_date, color)
                    if len(filename)>0:
                        rar_extension = re.search("r(\d*)$", filename)
                        if rar_extension:
                            add_child_node(model, 2, "RAR FILES " + filename)
                        else:
                            add_child_node(model, 2, filename)#color)
                except:
                    print("Something went wrong in " + line);
    model.sort(0)
    view.setModel(model)

################################################################################
################################################################################
def parse_tvmaze_page(view, page, dirpath):
    """ Parse epguides webpage """
    locale.setlocale(locale.LC_ALL, 'C')

    found_first = 0
    model = QtGui.QStandardItemModel(0, 3)

    found = 0
    dir_content = []
    originalcase_dir_content = []
    valid_extensions = [".mkv"]
    if len(dirpath) > 0 and os.path.exists(dirpath):
        for files in os.listdir(dirpath):
            if os.path.isfile(dirpath + "/" + files):
                clean = clean_name(files)
                filename, extension = os.path.splitext(clean)
                if (extension in valid_extensions or not extension in SKIPPED_EXTENSIONS):
                    dir_content.append(clean)
                    originalcase_dir_content.append(files)
            else:
                for child_files in os.listdir(dirpath + "/" + files):
                    if os.path.isfile(os.path.join(str(dirpath + "/" + files), child_files)):
                        clean = clean_name(child_files)
                        filename, extension = os.path.splitext(clean)
                        if (extension in valid_extensions or not extension in SKIPPED_EXTENSIONS):
                            dir_content.append(clean)
                            originalcase_dir_content.append(child_files)

    lowpage = str(page).lower()

    last_seen_season = 0
    last_seen_episode = 0
    if lowpage in SHOW_SEEN_TO:
        split_ep = re.search("(\d*)[e](\d*)", SHOW_SEEN_TO[str(lowpage)])
        if split_ep:
            last_seen_season = int(split_ep.group(1))
            last_seen_episode = int(split_ep.group(2))

    season_nb = 0
    episode_nb = 0

    if page in TVMAZE_ID:
        url  = "http://epguides.com/common/exportToCSVmaze.asp?maze="
        url += str(TVMAZE_ID[page])
        request = urllib.request.Request(url)
        webpage = urllib.request.urlopen(request).readlines()

        for line in webpage:
            line = line.decode("UTF-8")
            line = html.unescape(line)
            if found_first == 0:
                if line.lstrip().startswith('number,season,episode,airdate,title,tvmaze link'):
                    found_first += 1
            elif line.lstrip().startswith('
'):
                break
            else:
                try:
                    # page downloaded from epguides
                    # current format:
                    # number,season,episode,airdate,title,tvmaze link
                    expected_nb_of_fields = 6
                    csv_reader = csv.reader([line], delimiter=',')
                    for csv_line in csv_reader:
                        nb_fields = len(csv_line)
                        if nb_fields != expected_nb_of_fields:
                            if nb_fields == 0:
                                continue
                            if nb_fields > 0:
                                print("Warning! line {} we just received has {} fields instead of expected {}".format(csv_line, nb_fields, expected_nb_of_fields))

                        season_nb = int(csv_line[1])
                        episode_nb = int(csv_line[2])
                        diffusion_date_string = csv_line[3]
                        episode_title = csv_line[4]

                        str_cat = "s" + "%02d" % season_nb + "e" + "%02d" % episode_nb

                        found = 0
                        filename = ""

                        if season_nb < int(last_seen_season):
                            found = 2
                        elif season_nb == int(last_seen_season):
                            if episode_nb <= int(last_seen_episode):
                                found = 2

                        # replace 720p by a space to avoid
                        # detecting it as an episode number
                        for index,mkv in enumerate(dir_content):
                            if mkv.find(str_cat) != -1:
                                filename = originalcase_dir_content[index]
                                found = 1
                            elif mkv.find(
                                    str(season_nb) + str("%02d" % episode_nb)) != -1:
                                filename = originalcase_dir_content[index]
                                found = 1
                            elif mkv.find(
                                    str(season_nb) + "x" + str("%02d" % episode_nb)
                                    ) != -1:
                                filename = originalcase_dir_content[index]
                                found = 1

                        color = "#86FF68"

                        if len(diffusion_date_string.strip()) > 0:
                            diffusion_date = time.strptime(diffusion_date_string, "%d %b %y")
                            diffusion_date = datetime.datetime(*diffusion_date[0:7]).strftime("%d/%m/%Y")

                            check = datetime.datetime.strptime(diffusion_date,"%d/%m/%Y") - datetime.datetime.strptime(TODAY,"%Y%m%d")

                            # not aired yet
                            if check.days > 0:
                                color = "#8BB2FF"
                            # today date
                            elif check.days == 0:
                                color = "#FFF55C"
                        else:
                            diffusion_date = "Unknown"

                        add_root_node(model, "", found)
                        add_child_node(model, 0, str_cat)
                        add_child_node(model, 1, diffusion_date, color)

                        if len(filename)>0:
                            rar_extension = re.search("r(\d*)$", filename)
                            if rar_extension:
                                add_child_node(model, 2, "RAR FILES " + filename)
                            else:
                                add_child_node(model, 2, filename)#color)
                        else:
                            add_child_node(model, 2, episode_title)
                except:
                    print("Something went wrong when parsing line '" + line + "'");
    else:
        # replace 720p by a space to avoid
        # detecting it as an episode number
        for index,clean_filename in enumerate(dir_content):
            season_episode_regex = re.search("[sS]?(\d{1,2})[eE]?(\d{1,2})", clean_filename);
            if season_episode_regex:
                season_nb = int(season_episode_regex.group(1))
                episode_nb = int(season_episode_regex.group(2))

            color = "#86FF68"

            found = 1
            if season_nb < int(last_seen_season):
                found = 2
            elif season_nb == int(last_seen_season):
                if episode_nb <= int(last_seen_episode):
                    found = 2

            filename = originalcase_dir_content[index]

            str_cat = "s" + "%02d" % season_nb + "e" + "%02d" % episode_nb

            add_root_node(model, "", found)
            add_child_node(model, 0, str_cat)
            add_child_node(model, 1, "", color)
            add_child_node(model, 2, filename)

    model.sort(0)
    view.setModel(model)

################################################################################
################################################################################
def get_squared_pics_from_tvshow( in_show_name ):
    """ Get squared thumbnails for tv show name """
    if QtCore.QFile.exists(".thumbs/" + in_show_name):
        image = QtWidgets.QImage()
        image.load(in_show_name)
        return image
    else:
        url  = "http://mediaicons.org/Services/Find.ashx?term="
        url += str(in_show_name) + "&format=1"
        request = urllib.request.Request(url)
        webpage = urllib.request.urlopen(request).read()

        xml_reader = QtCore.QXmlStreamReader(webpage)

        while not xml_reader.atEnd():
            xml_reader.readNext()
            if xml_reader.isStartElement():
                if xml_reader.name() == "icon":
                    icon_url = xml_reader.attributes().value("url").toString()
                    icon_url.replace("mediaicons.org/GetIcon",
                                    "mediaicons.org/Services/GetIcon", 1)
                    print(icon_url)
                    pic = urllib.request.urlopen(str(icon_url)).read()
                    imag = QtWidgets.QImage()
                    imag.loadFromData(pic)
                    print("success ?")
                    print(imag.save(".thumbs/" + in_show_name, "PNG"))
                    return imag
        if xml_reader.atEnd():
            return ""

################################################################################
################################################################################
def get_squared_pics_list( in_show_name ):
    """ Get the squared thumbnails list for tv show name """
    url  = "http://mediaicons.org/Services/Find.ashx?term="
    url += str(in_show_name) + "&format=1"
    request = urllib.request.Request(url)
    webpage = urllib.request.urlopen(request).read()

    xml_reader = QtCore.QXmlStreamReader(webpage)
    icons_urls = []
    while not xml_reader.atEnd():
        xml_reader.readNext()
        if xml_reader.isStartElement():
            if xml_reader.name() == "icon":
                icon_url = xml_reader.attributes().value("url").toString()
                icon_url.replace("mediaicons.org/GetIcon",
                                "mediaicons.org/Services/GetIcon", 1)
                #print icon_url
                icons_urls.append(icon_url)
                #pic = urllib2.urlopen(str(icon_url)).read()
                #imag = QtWidgets.QImage()
                #imag.loadFromData(pic)
                #print "success ?"
                #print imag.save(".thumbs/" + in_show_name, "PNG")
                #return imag

    return icons_urls

################################################################################
################################################################################
# XML TOOLS
def read_dirs_from_xml():
    """ Read xml file for directories """
    xml_file = QtCore.QFile(XML_FILENAME)
    xml_file.open(QtCore.QIODevice.ReadOnly)
    xml_reader = QtCore.QXmlStreamReader(xml_file)
    while not xml_reader.atEnd():
        xml_reader.readNext()
        if xml_reader.isStartElement():
            if xml_reader.name() == "dir":
                DIRECTORIES_TO_PARSE.append(xml_reader.readElementText())

def write_dirs_to_xml():
    """ Write a selected directory to xml file """
    xml_file = QtCore.QFile(XML_FILENAME)
    xml_file.open(QtCore.QIODevice.WriteOnly)
    xml_writer = QtCore.QXmlStreamWriter(xml_file)
    xml_writer.setAutoFormatting(True)
    xml_writer.writeStartDocument()
    ################################################
    xml_writer.writeStartElement("directories")
    for directory_path in DIRECTORIES_TO_PARSE:
        xml_writer.writeTextElement("dir", directory_path)
    xml_writer.writeEndElement()
    ################################################
    xml_writer.writeEndDocument()
################################################################################
################################################################################

################################################################################
################################################################################
class PicturesSelector(QtWidgets.QDialog):
    """ Thumbnails selection popup """
    def __init__(self, parent, in_show_name):
        QtWidgets.QDialog.__init__(self, parent)
        self.setWindowTitle(in_show_name + " thumb Selector")
        #self.setLayout(QGridLayout(self))
        self.setLayout(QtWidgets.QVBoxLayout(self))
        self.resize(250, 50)

        icons_urls = get_squared_pics_list(in_show_name)

        if len(icons_urls) > 0:
            for url in icons_urls :
                print(url)
                pic = urllib.request.urlopen(str(url)).read()
                imag = QtWidgets.QImage()
                imag.loadFromData(pic)
                pixm = QtWidgets.QPixmap.fromImage(
                            imag.scaled(128, 128, QtCore.Qt.KeepAspectRatio)
                        )
                lbl = QtWidgets.QLabel()
                lbl.setPixmap(pixm)
                self.layout().addWidget(lbl)
        else :
            self.layout().addWidget(QtWidgets.QLabel("No pictures found"))

        self.show()
    #def leaveEvent(self, event):
        #""" When the mouse leave this widget, destroy it. """
        #self.destroy()

################################################################################
################################################################################
class KEpShow(QtWidgets.QWidget):
    """ Main class """
    def on_show_activated(self, in_index):
        """ Load found tv show episodes infos """
        index_showname = self.ui.found_tv_shows.model().index(in_index.row(), 1)
        current_disp   = self.ui.found_tv_shows.model().data(index_showname)
        ix_dir         = self.ui.found_tv_shows.model().index(in_index.row(), 2)
        current_dir    = self.ui.found_tv_shows.model().data(ix_dir)
        parse_tvmaze_page(self.ui.tableView, current_disp, current_dir)
        #image = get_squared_pics_from_tvshow(current_disp)
        #if image:
            #self.ui.found_tv_shows.model().setData(self.ui.found_tv_shows.model().index(in_index.row(), 3), QtWidgets.QPixmap.fromImage(image.scaled(64, 64, QtCore.Qt.KeepAspectRatio)), QtCore.Qt.DecorationRole)

        #print(self.ui.found_tv_shows.model().data(index_showname))

    def icon_selection(self, in_index):
        """ Handler for the thumbnails selection popup """
        if in_index.column() == 3 :
            index_showname = self.ui.found_tv_shows.model().index(in_index.row(), 1)
            #current_disp = self.ui.found_tv_shows.model().data(index_showname).toString()
            print("try to open popup" + index_showname)
            #select = PicturesSelector(self, current_disp)

    def update_global(self, rank):
        """ Load all tv show episodes infos """
        index_showname = self.ui.all_tv_shows.model().index(rank, 1)
        current_disp = self.ui.all_tv_shows.model().data(index_showname).toString()
        parse_epguides_page(self.ui.tableView, current_disp, "")
        #print self.ui.found_tv_shows.model().data(index_showname).toString()

    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)

        # Set up the user interface from Designer.
        self.ui = Ui_MainQWidget()
        self.ui.setupUi(self)

        self.ui.tableView.setAlternatingRowColors(True)

        self.ui.found_tv_shows.activated[QtCore.QModelIndex].connect(self.on_show_activated)

        #self.connect(self.ui.found_tv_shows, QtCore.SIGNAL("entered(QModelIndex)"), self.icon_selection)
        #self.connect(self.ui.all_tv_shows, QtCore.SIGNAL("currentIndexChanged(int)"), self.update_global)

    def directory_selector(self):
        """ Opens a popup to choose a directory containing tv shows """
        dir_full_path = QtWidgets.QFileDialog.getExistingDirectory(self,
                                    self.tr("Choose a directory"),
                                    QtCore.QDir.currentPath(),
                                    QtWidgets.QFileDialog.DontResolveSymlinks |
                                    QtWidgets.QFileDialog.ShowDirsOnly
                            )
        if not dir_full_path.isEmpty():
            DIRECTORIES_TO_PARSE.append(dir_full_path)
            self.write_dirs_to_xml()

################################################################################
################################################################################
def add_root_node(model, name, color):
    """ Add a parent element """
    model.insertRow(0)
    if color == 0:
        color_code = "#FF6767" #red
    elif color == 1:
        color_code = "#86FF68" #green
    elif color == 2:
        color_code = "#EEB95C"
    elif color == 3:
        color_code = "#8BB2FF" #blue
    else:
        color_code = "#FFFFFF"
    model.setData(model.index(0, 0), QtGui.QColor("#000000"), QtCore.Qt.ForegroundRole)
    model.setData(model.index(0, 1), QtGui.QColor("#000000"), QtCore.Qt.ForegroundRole)
    model.setData(model.index(0, 2), QtGui.QColor("#000000"), QtCore.Qt.ForegroundRole)
    model.setData(model.index(0, 3), QtGui.QColor("#000000"), QtCore.Qt.ForegroundRole)
    model.setData(model.index(0, 0), QtGui.QColor(color_code), QtCore.Qt.BackgroundColorRole)
    model.setData(model.index(0, 1), QtGui.QColor(color_code), QtCore.Qt.BackgroundColorRole)
    model.setData(model.index(0, 2), QtGui.QColor(color_code), QtCore.Qt.BackgroundColorRole)
    model.setData(model.index(0, 3), QtGui.QColor(color_code), QtCore.Qt.BackgroundColorRole)
    #model.setData(model.index(0, 0), QtCore.QVariant(name))

################################################################################
################################################################################
def add_child_node(model, in_index, name, in_color_code = ""):
    """ Add a child element at given level with color """
    if len(in_color_code) > 0:
        model.setData(model.index(0, in_index), QtGui.QColor("#000000"),     QtCore.Qt.ForegroundRole)
        model.setData(model.index(0, in_index), QtGui.QColor(in_color_code), QtCore.Qt.BackgroundColorRole)
    model.setData(model.index(0, in_index), name)

################################################################################
################################################################################
def parse_lastshows_file():
    """ Read last shows file for already seen episodes """
    path = "/media/sda4/home/belkiss/download/CloudSyncDocs/Series"
    full_path = os.path.join(path, "lastShows.txt")
    file_data = open(full_path, 'r')
    stop = False
    for line in file_data:
        tmp_show_name  = line[0:line.find(' ')]
        if tmp_show_name.find('###############') != -1:
            stop = True
        if stop == False:
            split_line = re.search(".*s(\d+)e(\d+).*\d+/\d+/\d+\s*;?\s*(.*)$",
                                   line)
            if split_line:
                SHOW_SEEN_TO[tmp_show_name.lower()] = split_line.group(1) + "e" + split_line.group(2)
                if split_line.group(3) not in ["CANCELLED", "FINISHED", "DROPPED"]:
                    RUNNING_SHOWS.append(tmp_show_name)

    file_data.close()

################################################################################
################################################################################
def parse_current_shtml():
    """ Parse the current.shtml file that contains an (old) offline html file of shows """
    path = "current.shtml"
    with open(path, 'r', -1, 'ISO-8859-1') as ALL_SHOW_FILE_DATA:
        FOUND_DATA_BEGINNING = 0
        for element in ALL_SHOW_FILE_DATA:
            if FOUND_DATA_BEGINNING == 0:
                if element[0:18] == '<a name="A':
                    FOUND_DATA_BEGINNING += 1
            else:
                if element[0:16] == '
', 16)
                    full_url   = element[16:found_rank]
                    dir_name   = full_url[full_url.find('.com/')+5:-1]
                    show_name  = element[found_rank+2:element.find('',
                                        found_rank+2)]
                    DIR_NAMES[dir_name] = show_name
                    SHOWNAME_LOWER_TO_UPPER[dir_name.lower()] = dir_name
                    add_root_node(ALL_SHOWS_MODEL, show_name, 4)
                    add_child_node(ALL_SHOWS_MODEL, 0, show_name)
                    add_child_node(ALL_SHOWS_MODEL, 1, dir_name)
                    #KEPSHOW.ui.all_tv_shows.addItem(show_name, dir_name)
                    #print show_name+ ":"+ dir_name

################################################################################
################################################################################
def parse_all_shows(filename):
    """ Parse the file that contains an offline csv file of shows """
    # downloaded from epguides
    # current format:
    # title,directory,tvrage,TVmaze,start date,end date,number of episodes,run time,network,country,onhiatus,onhiatusdesc
    expected_nb_of_fields = 12
    with open(filename, encoding='ISO-8859-1') as csv_file:
        csv_reader = csv.reader(csv_file, delimiter=',')
        for line in csv_reader:
            nb_fields = len(line)
            if nb_fields != expected_nb_of_fields:
                if nb_fields > 0:
                    print("Error! line {} in file {} has {} fields instead of expected {}".format(csv_reader.line_num, filename, nb_fields, expected_nb_of_fields))
                continue
            show_name = html.unescape(line[0])
            dir_name = line[1]
            tvmaze_id = line[3]
            TVMAZE_ID[dir_name] = tvmaze_id
            DIR_NAMES[dir_name] = show_name
            SHOWNAME_LOWER_TO_UPPER[dir_name.lower()] = dir_name
            #add_root_node(ALL_SHOWS_MODEL, show_name, 4)
            #add_child_node(ALL_SHOWS_MODEL, 0, show_name)
            #add_child_node(ALL_SHOWS_MODEL, 1, dir_name)

################################################################################
################################################################################
if __name__ == "__main__":
    APP = QtWidgets.QApplication(sys.argv)
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    parse_lastshows_file()
    KEPSHOW = KEpShow()
    read_dirs_from_xml()
    #KEPSHOW.directory_selector()

    allshows_filename = "allshows_" + TODAY + ".txt"
    if not os.path.exists(allshows_filename):
        url  = "http://epguides.com/common/allshows.txt"
        print("Downloading new version of allshows.txt from epguides")
        urllib.request.urlretrieve(url, allshows_filename)
        if not os.path.exists(allshows_filename):
            print("Warning! Download failed, use the offline version")
            allshows_filename = "allshows.txt"

    DIR_NAMES = {}
    TVMAZE_ID = {}
    SHOWNAME_LOWER_TO_UPPER = {}

    ALL_SHOWS_MODEL = QtGui.QStandardItemModel(0, 2)
    #parse_current_shtml()
    parse_all_shows(allshows_filename)
    #ALL_SHOWS_MODEL.sort(0)
    #KEPSHOW.ui.all_tv_shows.setModel(ALL_SHOWS_MODEL)

    TMP_DIRS = []
    FOUND_SHOWS_MODEL = QtGui.QStandardItemModel(0, 4)
    for directory in DIRECTORIES_TO_PARSE:
        directory = str(directory)
        print("parsing " + directory)
        for element in os.listdir(directory):
            element_full_path = os.path.join(directory, element)
            if os.path.isdir(element_full_path):
                # AIROUT ONAIR TOWATCH
                for child_element in os.listdir(element_full_path):
                    child_full_path = os.path.join(element_full_path, child_element)
                    if os.path.isdir(child_full_path):
                        element_color = 3
                        if element == "OnAir":
                            element_color = 3
                        elif element == "ToWatch":
                            element_color = 1
                        else:
                            element_color = 0
                        name_lowered = child_element.lower()
                        if name_lowered in SHOWNAME_LOWER_TO_UPPER:
                            if SHOWNAME_LOWER_TO_UPPER[name_lowered] != child_element :
                                print("    rename this " + child_full_path + " to this : " + os.path.join(element_full_path, SHOWNAME_LOWER_TO_UPPER[name_lowered]))
                            #print("name_lowered " + name_lowered)
                            #print("SHOWNAME_LOWER_TO_UPPER[name_lowered] " + SHOWNAME_LOWER_TO_UPPER[name_lowered])
                            #print("DIR_NAMES[SHOWNAME_LOWER_TO_UPPER[name_lowered]] " + DIR_NAMES[SHOWNAME_LOWER_TO_UPPER[name_lowered]])
                            add_root_node(FOUND_SHOWS_MODEL, DIR_NAMES[SHOWNAME_LOWER_TO_UPPER[name_lowered]], element_color)
                            add_child_node(FOUND_SHOWS_MODEL, 0, DIR_NAMES[SHOWNAME_LOWER_TO_UPPER[name_lowered]])
                            add_child_node(FOUND_SHOWS_MODEL, 1, SHOWNAME_LOWER_TO_UPPER[name_lowered])
                            if name_lowered in TMP_DIRS:
                                print("    " + child_element + " already inside")
                                # CHANGE THE BEHAVIOUR : when already inside, complete the data
                            else:
                                TMP_DIRS.append(name_lowered)
                            add_child_node(FOUND_SHOWS_MODEL, 2, child_full_path)
                        else:
                            print("    ####### not found : " + child_full_path)
                            add_root_node(FOUND_SHOWS_MODEL, child_element, element_color)
                            add_child_node(FOUND_SHOWS_MODEL, 0, child_element)
                            add_child_node(FOUND_SHOWS_MODEL, 1, child_element)
                            if name_lowered in TMP_DIRS:
                                print("    " + child_element + " already inside")
                                # CHANGE THE BEHAVIOUR : when already inside, complete the data
                            else:
                                TMP_DIRS.append(name_lowered)
                            add_child_node(FOUND_SHOWS_MODEL, 2, child_full_path)

    # add shows we don't have on disk
    for previous_show in RUNNING_SHOWS:
        name_lowered = previous_show.lower()

        # skip if it was found
        if name_lowered in TMP_DIRS:
            continue

        if name_lowered in SHOWNAME_LOWER_TO_UPPER:
            add_root_node(FOUND_SHOWS_MODEL, previous_show, 2)
            add_child_node(FOUND_SHOWS_MODEL, 0, DIR_NAMES[SHOWNAME_LOWER_TO_UPPER[name_lowered]])
            add_child_node(FOUND_SHOWS_MODEL, 1, SHOWNAME_LOWER_TO_UPPER[name_lowered])
            add_child_node(FOUND_SHOWS_MODEL, 2, SHOWNAME_LOWER_TO_UPPER[name_lowered])
        else:
            add_root_node(FOUND_SHOWS_MODEL, previous_show, 0)
            add_child_node(FOUND_SHOWS_MODEL, 0, previous_show)
            add_child_node(FOUND_SHOWS_MODEL, 1, previous_show)
            add_child_node(FOUND_SHOWS_MODEL, 2, "Not on epguides")

    FOUND_SHOWS_MODEL.sort(0)

    KEPSHOW.ui.found_tv_shows.setModel(FOUND_SHOWS_MODEL)

    ROWS_NB = FOUND_SHOWS_MODEL.rowCount()
    #print ROWS_NB
    #for row in xrange(ROWS_NB):
        #KEPSHOW.ui.found_tv_shows.setRowHeight(row, 64)
    KEPSHOW.ui.found_tv_shows.resizeColumnsToContents()
    KEPSHOW.ui.found_tv_shows.hideColumn(1)
    #KEPSHOW.ui.found_tv_shows.hideColumn(2)
    KEPSHOW.show()
    sys.exit(APP.exec_())


from rest_framework import exceptions
from rest_framework import serializers
from rest_framework.fields import ReadOnlyField

from ..models.certificates import Nonce
from ..models.core import Buffer
from ..models.core import Certificate
from ..models.core import InstanceIDModel
from ..models.core import RecordMaxCounterBuffer
from ..models.core import SyncSession
from ..models.core import TransferSession
from ..models.fields.crypto import SharedKey
from ..utils import SETTINGS
from .fields import PublicKeyField

class CertificateSerializer(serializers.ModelSerializer):

    public_key = PublicKeyField()

    def validate_parent(self, parent):
        if not parent:
            raise exceptions.ValidationError(
                "Parent certificate (to sign the requested certificate) must be specified!"
            )
        if not parent.has_private_key():
            raise exceptions.ValidationError(
                "Server does not have private key for requested parent certificate!"
            )
        return parent

    class Meta:
        model = Certificate
        fields = (
            "id",
            "parent",
            "profile",
            "scope_definition",
            "scope_version",
            "scope_params",
            "public_key",
            "serialized",
            "signature",
            "salt",
        )
        read_only_fields = ("serialized", "id", "signature", "salt")

class SharedKeySerializer(serializers.ModelSerializer):
    class Meta:
        model = SharedKey
        fields = ("public_key",)

class NonceSerializer(serializers.ModelSerializer):
    class Meta:
        model = Nonce
        fields = ("id", "timestamp", "ip")
        read_only_fields = fields

class SyncSessionSerializer(serializers.ModelSerializer):
    client_instance = serializers.CharField(source='client_instance_json')
    server_instance = serializers.CharField(source='server_instance_json')

    class Meta:
        model = SyncSession
        fields = (
            "id",
            "start_timestamp",
            "last_activity_timestamp",
            "active",
            "client_certificate",
            "server_certificate",
            "profile",
            "connection_kind",
            "connection_path",
            "client_ip",
            "server_ip",
            "client_instance",
            "server_instance",
        )
        # serializer is not used for creates, so these fields are not allowed updates
        read_only_fields = fields

class TransferSessionSerializer(serializers.ModelSerializer):
    class Meta:
        model = TransferSession
        fields = (
            "id",
            "start_timestamp",
            "last_activity_timestamp",
            "active",
            "filter",
            "push",
            "records_transferred",
            "records_total",
            "bytes_received",
            "bytes_sent",
            "sync_session",
            "server_fsic",
            "client_fsic",
            "transfer_stage",
            "transfer_stage_status",
        )
        # serializer is not used for creates, so these fields are not allowed updates
        read_only_fields = (
            "id",
            "start_timestamp",
            "last_activity_timestamp",
            "active",
            "filter",
            "push",
            "sync_session",
            "server_fsic",
            "client_fsic",
            "transfer_stage",
            "transfer_stage_status",
        )

class InstanceIDSerializer(serializers.ModelSerializer):
    def get_fields(self):
        """
        Override method that prepares what fields will be serialized, to include dynamic fields from
        custom instance info configured through settings
        """
        fields = super(InstanceIDSerializer, self).get_fields()
        # add read-only fields for custom instance info from settings
        for field_name, value in SETTINGS.MORANGO_INSTANCE_INFO.items():
            # we do not allow overwriting Morango instance info
            if field_name not in fields:
                custom_field = ReadOnlyField(source="instance_info.{}".format(field_name))
                custom_field.bind(field_name, self)
                fields[field_name] = custom_field
        return fields

    class Meta:
        model = InstanceIDModel
        fields = (
            "id",
            "platform",
            "hostname",
            "sysversion",
            "node_id",
            "database",
            "db_path",
            "system_id",
        )
        read_only_fields = fields

class RecordMaxCounterBufferSerializer(serializers.ModelSerializer):
    class Meta:
        model = RecordMaxCounterBuffer
        fields = ("transfer_session", "model_uuid", "instance_id", "counter")
        read_only_fields = fields

class BufferSerializer(serializers.ModelSerializer):
    rmcb_list = RecordMaxCounterBufferSerializer(many=True)

    class Meta:
        model = Buffer
        fields = (
            "serialized",
            "deleted",
            "last_saved_instance",
            "last_saved_counter",
            "hard_deleted",
            "partition",
            "source_id",
            "model_name",
            "conflicting_serialized_data",
            "model_uuid",
            "transfer_session",
            "profile",
            "rmcb_list",
            "_self_ref_fk",
        )
        read_only_fields = fields


from django.urls import path
from .views import HomeView, ArticleDetailView, AddpostView, BlogEditView,BlogDeleteView
#from . import views

urlpatterns = [
    #path('',views.home, name='home'),
    path('', HomeView.as_view(), name='home'),
    path('article/<int:pk>', ArticleDetailView.as_view(), name='article-view'),
    path('Add_post/', AddpostView.as_view(), name='Add_post'),
    path('article/edit/<int:pk>/', BlogEditView.as_view(), name='update-view'),
    path('article/delete/<int:pk>/', BlogDeleteView.as_view(), name='delete-view'),
]


""" ÐŸÑ€Ð°ÐºÑ‚Ð¸ÐºÐ° â„–2 """

class BasePokemon:
    """
    Pokemon class
    """
    def __init__(self, name: str, poketype: str):
        self.name = name
        self.poketype = poketype

    def __repr__(self):
        return f'{self.name}/{self.poketype}'

class EmojiMixin:
    """
    Mixin for changing pokemon type to emoji
    """
    pokemon_to_empji = {
        'grass': 'ðŸŒ¿',
        'fire': 'ðŸ”¥',
        'water': 'ðŸŒŠ',
        'electric': 'âš¡'
    }

    def __repr__(self):
        name_and_type = super().__repr__()
        name, pokemon_type = name_and_type.split('/')

        if pokemon_type in self.pokemon_to_empji:
            return name + '/' + self.pokemon_to_empji[pokemon_type]

        return name_and_type

class Pokemon(EmojiMixin, BasePokemon):
    """
    Pokemon class with emoji
    """

if __name__ == '__main__':
    bulbasaur = Pokemon(name='Bulbasaur', poketype='grass')
    print(bulbasaur)


# -*- coding: utf-8 -*-
# @Author  : ydf
# @Time    : 2019/10/8 0008 17:39
"""
桥接，是像一座桥连接两岸，而Python程序设计中的桥接指的是抽象部分和实体部分的连接，简单来说是类和类实例化过称中的连接。

桥接模式通过在类和类实例化中间作用，使其抽象和实现可以独立变化而不互相干扰，这就是桥接模式最大的作用。

核心的思想是通过封装，将一个抽象类的相关参数和方法分别作为桥接类的属性，这样在实例化桥接类后通过修改桥接类的属性，便可以实现抽象和实现之间的独立变化。

"""
from monkey_print2 import print

class A:
    def run(self, name):
        print("my name is :{}".format(name))

class B:
    def run(self, name):
        print("我的名字是：{}".format(name))

class Bridge:
    def __init__(self, ager, classname):
        self.ager = ager
        self.classname = classname

    def bridge_run(self):
        self.classname.run(self.ager)

if __name__ == '__main__':
    test = Bridge('李华', A())
    test.bridge_run()
    test.ager = 'Tome'
    test.bridge_run()
    test.classname = B()
    test.bridge_run()
    test.ager = '李华'
    test.bridge_run()
    """
    "D:/coding2/python36patterns/结构型模式-桥接模式.py:7"  17:56:50  my name is :李华
"D:/coding2/python36patterns/结构型模式-桥接模式.py:7"  17:56:50  my name is :Tome
"D:/coding2/python36patterns/结构型模式-桥接模式.py:12"  17:56:50  我的名字是：Tome
"D:/coding2/python36patterns/结构型模式-桥接模式.py:12"  17:56:50  我的名字是：李华

    """

结构型模式-桥接模式.py

结构型模式-桥接模式.py


def main():
    print("AdventOfCode - 08 - Challenge 01")
    f = open("input.txt", "r")
    image_width = 25
    image_height = 6
    layer_length = image_width * image_height
    layers = []
    if f.mode == 'r':
        f_content = f.read()
        chunks = [f_content[i:i+layer_length] for i in range(0, len(f_content), layer_length)]
        for chunk in chunks:
            if len(chunk) == image_width * image_height:
                layers.append(Layer(image_height, image_width, chunk))

        print("Found {} different layers".format(len(layers)))

        layer_fewest_zeros = layers[0]
        for layer in layers:
            if layer_fewest_zeros.count_str('0') > layer.count_str('0'):
                layer_fewest_zeros = layer

        print("The layer with the fewest zeros include {} '1' and {} '2' -> {}".format(
          layer_fewest_zeros.count_str('1'),
          layer_fewest_zeros.count_str('2'),
          layer_fewest_zeros.count_str('1') * layer_fewest_zeros.count_str('2')))

class Layer:
    width = 0
    height = 0
    pixels = ''

    def __init__(self, height, width, pixels):
        self.height = height
        self.width = width
        self.pixels = pixels

    def count_str(self, search_str):
        return self.pixels.count(search_str)

if __name__ == "__main__":
    main()


import json
import requests
import pytest
import sys
import pprint
from vvcli.api import call_api, get_audio_query, get_wav_data

expected_error_msg = "422 Client Error: Unprocessable Entity for url"

class TestAudioQuery:
    def test_01(self, audio_query_sample):
        response = call_api(
            "audio_query", {"speaker": 1, "text": "test text"}, http_method="POST"
        )
        assert response.status_code == 200
        expected_json = audio_query_sample
        assert response.json() == expected_json

    def test_02(self):
        # リクエストボディに不正なデータが含まれている場合、422エラーが発生することを確認する
        with pytest.raises(requests.exceptions.HTTPError, match=expected_error_msg):
            response = call_api(
                "audio_query",
                data={"speaker": 1, "text": "test text"},
                http_method="POST",
            )

    def test_03(self, audio_query_sample):
        assert get_audio_query(1, "test text") == audio_query_sample

class TestSynthesis:
    def test_01(self, audio_query_sample):
        # speakerはurlに含める必要があり、audio_queryはリクエストボディに含める必要がある
        response = call_api(
            "synthesis",
            {"speaker": 1},
            data=audio_query_sample,
            http_method="POST",
        )
        assert response.status_code == 200

    def test_02(self, audio_query_sample):
        # リクエストボディにspeakerが含まれている場合、422エラーが発生することを確認する
        data = audio_query_sample
        data["speaker"] = 1
        with pytest.raises(requests.exceptions.HTTPError, match=expected_error_msg):
            response = call_api("synthesis", data=data, http_method="POST")


import pandas as pd
from sklearn.decomposition import NMF, LatentDirichletAllocation
from src.scripts.preprocessing import preprocess

def nmf(X, k, init='random', random_state=0):
    model = NMF(n_components=k,
                init=init,
                random_state=random_state)
    w = model.fit_transform(X)
    h = model.components_
    return [model, w, h]

def lda(X, k, init='random', random_state=0):
    model = LatentDirichletAllocation(n_components=k,
                                      random_state=random_state)
    return [model, model.fit_transform(X)]

# Source : https://scikit-learn.org/stable/auto_examples/applications/plot_topics_extraction_with_nmf_lda.html
def get_n_top_words(n, model, feature_names):
    top_words = []
    top_weights = []
    for topic_idx, topic in enumerate(model.components_):
        top_features_ind = topic.argsort()[: -(n+1):-1]
        top_features = [feature_names[i] for i in top_features_ind]
        weights = topic[top_features_ind]

        top_words.append(top_features)
        top_weights.append(weights)
    return [top_words, top_weights]

if __name__ == "__main__":
    path_to_test_file = "C:/Users/salim/Documents/Cours/PPD/AnalyTweets/data/benchmark.csv"
    texts = pd.read_csv(path_to_test_file)['text'].values

    k = 10
    X, feature_names = preprocess(texts)

    lda_model, resultat_lda = lda(X, k)

    nmf_model, w, h = nmf(X, k)

    print("\n\n ----------------- \n\n")

    print(get_n_top_words(5, nmf_model, feature_names))
    print(get_n_top_words(5, lda_model, feature_names))


#!/usr/bin/env python3
"""Cyberjunky's 3Commas bot helpers."""
import argparse
import configparser
import json
import os
import sys
import time
from pathlib import Path

from telethon import TelegramClient, events

from helpers.logging import Logger, NotificationHandler
from helpers.threecommas import (
    init_threecommas_api,
    load_blacklist,
    prefetch_marketcodes
)
from watchlist import process_botlist

def load_config():
    """Create default or load existing config file."""

    cfg = configparser.ConfigParser()
    if cfg.read(f"{datadir}/{program}.ini"):
        return cfg

    cfg["settings"] = {
        "timezone": "Europe/Amsterdam",
        "debug": False,
        "logrotate": 7,
        "3c-apikey": "Your 3Commas API Key",
        "3c-apisecret": "Your 3Commas API Secret",
        "tgram-phone-number": "Your Telegram Phone number",
        "tgram-api-id": "Your Telegram API ID",
        "tgram-api-hash": "Your Telegram API Hash",
        "notifications": False,
        "notify-urls": ["notify-url1"],
        "exchange": "Bittrex / Binance / Kucoin",
        "mode": "Telegram"
    }

    cfg["hodloo_5"] = {
        "bnb-botids": [12345, 67890],
        "btc-botids": [12345, 67890],
        "busd-botids": [12345, 67890],
        "eth-botids": [12345, 67890],
        "eur-botids": [12345, 67890],
        "usdt-botids": [12345, 67890],
    }

    cfg["hodloo_10"] = {
        "bnb-botids": [12345, 67890],
        "btc-botids": [12345, 67890],
        "busd-botids": [12345, 67890],
        "eth-botids": [12345, 67890],
        "eur-botids": [12345, 67890],
        "usdt-botids": [12345, 67890],
    }

    with open(f"{datadir}/{program}.ini", "w") as cfgfile:
        cfg.write(cfgfile)

    return None

async def handle_hodloo_event(category, event):
    """Handle the received Telegram event"""

    logger.debug(
        "Received telegram message on %s: '%s'"
        % (category, event.message.text.replace("\n", " - "))
    )

    # Parse the event and do some error checking
    trigger = event.raw_text.splitlines()

    pair = trigger[0].replace("\n", "").replace("**", "")
    base = pair.split("/")[1]
    coin = pair.split("/")[0]

    logger.info(
        f"Received message on {category}% for {base}_{coin}"
    )

    if base.lower() not in ("bnb", "btc", "busd", "eth", "eur", "usdt"):
        logger.debug(
            f"{base}_{coin}: base '{base}' is not yet supported."
        )
        return

    botids = get_botids(category, base)

    if len(botids) == 0:
        logger.debug(
            f"{base}_{coin}: no valid botids configured for base '{base}'."
        )
        return

    await client.loop.run_in_executor(
        None, process_botlist, logger, api, blacklistfile, blacklist, marketcodes, botids, coin, "LONG"
    )

def get_botids(category, base):
    """Get list of botids from configuration based on category and base"""

    return json.loads(config.get(f"hodloo_{category}", f"{base.lower()}-botids"))

# Start application
program = Path(__file__).stem

# Parse and interpret options.
parser = argparse.ArgumentParser(description="Cyberjunky's 3Commas bot helper.")
parser.add_argument("-d", "--datadir", help="data directory to use", type=str)
parser.add_argument("-b", "--blacklist", help="blacklist to use", type=str)

args = parser.parse_args()
if args.datadir:
    datadir = args.datadir
else:
    datadir = os.getcwd()

# pylint: disable-msg=C0103
if args.blacklist:
    blacklistfile = f"{datadir}/{args.blacklist}"
else:
    blacklistfile = ""

# Create or load configuration file
config = load_config()
if not config:
    logger = Logger(datadir, program, None, 7, False, False)
    logger.info(
        f"Created example config file '{program}.ini', edit it and restart the program"
    )
    sys.exit(0)
else:
    # Handle timezone
    if hasattr(time, "tzset"):
        os.environ["TZ"] = config.get(
            "settings", "timezone", fallback="Europe/Amsterdam"
        )
        time.tzset()

    # Init notification handler
    notification = NotificationHandler(
        program,
        config.getboolean("settings", "notifications"),
        config.get("settings", "notify-urls"),
    )

    # Initialise logging
    logger = Logger(
        datadir,
        program,
        notification,
        int(config.get("settings", "logrotate", fallback=7)),
        config.getboolean("settings", "debug"),
        config.getboolean("settings", "notifications"),
    )

# Which Exchange to use?
exchange = config.get("settings", "exchange")
if exchange not in ("Bittrex", "Binance", "Kucoin"):
    logger.error(
        f"Exchange {exchange} not supported. Must be 'Bittrex', 'Binance' or 'Kucoin'!"
    )
    sys.exit(0)

# Which Mode to use?
mode = config.get("settings", "mode")
if mode not in ("Telegram", "Websocket"):
    logger.error(
        f"Mode {mode} not supported. Must be 'Telegram' or 'Websocket'!"
    )
    sys.exit(0)

# Initialize 3Commas API
api = init_threecommas_api(config)

# Prefetch marketcodes for all bots
botids = list()
for category in ("5", "10"):
    for base in ("bnb", "btc", "busd", "eth", "eur", "usdt"):
        botids += get_botids(category, base)
marketcodes = prefetch_marketcodes(logger, api, botids)

# Prefetch blacklists
blacklist = load_blacklist(logger, api, blacklistfile)

if mode == "Telegram":
    # Watchlist telegram trigger
    client = TelegramClient(
        f"{datadir}/{program}",
        config.get("settings", "tgram-api-id"),
        config.get("settings", "tgram-api-hash"),
    ).start(config.get("settings", "tgram-phone-number"))

    @client.on(events.NewMessage(chats=f"Hodloo {exchange} 5%"))
    async def callback_5(event):
        """Receive Telegram message."""

        await handle_hodloo_event("5", event)

        notification.send_notification()

    @client.on(events.NewMessage(chats=f"Hodloo {exchange} 10%"))
    async def callback_10(event):
        """Receive Telegram message."""

        await handle_hodloo_event("10", event)

        notification.send_notification()

    # Start telegram client
    client.start()
    logger.info(
        f"Listening to telegram chat 'Hodloo {exchange} 5%' and "
        f"'Hodloo {exchange} 10%' for triggers",
        True,
    )
    notification.send_notification()

    client.run_until_disconnected()
# No else case, mode is already checked before this statement


import asyncio

import httpx
from fake_useragent import UserAgent

settings = {
    "timeout": 5,
}

headers = {
    "user-agent": UserAgent().random,
    "accept": "text/html"
}

urls = []

result = {
    "success": [],
    "warning": [],
    "error": []
}

def load_urls():
    with open('url.txt', 'r', encoding='utf-8') as file:
        for line in file:
            line = line.strip()
            urls.append(line)

async def check_urls():
    for url in urls:
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(url, headers=headers, timeout=settings['timeout'])

                if response.status_code == 200 or response.status_code == 301:
                    print('{0:<30} {1:>30}'.format(url, '\033[92mSUCCESS\033[0m'))
                    result['success'].append(url)
                else:
                    print('{0:<30} {1:>30}'.format(url, '\033[93mWARNING\033[0m'))
                    result['warning'].append(url)
            except Exception as e:
                print('{0:<30} {1:>30}'.format(url, '\033[91mERROR\033[0m'))
                result['error'].append(url)

def save_data():
    success = "\n".join(result['success'])
    warning = "\n".join(result['warning'])
    error = "\n".join(result['error'])

    open('data/success.txt', 'w').write(success)
    open('data/warning.txt', 'w').write(warning)
    open('data/error.txt', 'w').write(error)

if __name__ == '__main__':
    load_urls()
    print('{0:<30} {1:>20}'.format('Url', 'Status'))
    asyncio.run(check_urls())
    save_data()


class Solution:
    def countGoodRectangles(self, rectangles: List[List[int]]) -> int:
        maxLen = 0
        ct = 0
        for l,w in rectangles:
            sq_side = min(l, w)
            if sq_side == maxLen:
                ct += 1
            elif sq_side > maxLen:
                maxLen = sq_side
                ct = 1
        return ct