from prompt import ValidationSettings
from ROOT import TFile, TH1F, TGraph, TCanvas, TLegend, gROOT, gStyle, PyConfig
from basf2 import B2ERROR
import sys

#: Tells the automated system some details of this script
settings = ValidationSettings(name='TOP post-tracking calibration',
                              description=__doc__,
                              download_files=[],
                              expert_config=None)

def run_validation(job_path, input_data_path, requested_iov, expert_config):
    '''
    Makes validation plots
    :job_path: path to top posttracking calibration output
    :input_data_path: required argument but not used
    :requested_iov: required argument but not used
    :expert_config: required argument but not used
    '''

    # input/output file names

    inputFileName = job_path + '/TOP_validation/0/algorithm_output/TOPCalValidation.root'
    outputFileName = 'TOPValidation.pdf'

    # configuration

    PyConfig.IgnoreCommandLineOptions = True
    gROOT.SetBatch(True)
    gROOT.ForceStyle()
    gStyle.SetOptStat(0)
    gStyle.SetOptFit(1)
    gStyle.SetHistMinimumZero(True)
    gStyle.SetLabelSize(0.05, "X")
    gStyle.SetLabelSize(0.05, "Y")
    gStyle.SetTitleSize(0.06, "X")
    gStyle.SetTitleSize(0.06, "Y")
    gStyle.SetTitleOffset(0.7, "X")
    gStyle.SetTitleOffset(0.8, "Y")
    gStyle.SetGridColor(11)

    # open input file and create the canvas

    file_in = TFile.Open(inputFileName)
    if (not file_in):
        B2ERROR(inputFileName + ": file not found")
        return
    canvas = TCanvas("c1", "c1", 1000, 750)

    # prepare and fit histograms for channel T0

    channelT0s = []
    residuals = []
    residuals_all = TH1F("residuals_all", "ChannelT0 residuals; residuals [ns]", 200, -1.0, 1.0)
    h_rms = TH1F("h_rms", "Width of residuals (rms); slot; r.m.s [ps]", 16, 0.5, 16.5)
    h_core = TH1F("h_core", "Core width of residuals; slot; sigma [ps]", 16, 0.5, 16.5)
    h_out = TH1F("h_out", "Outlayers (more than 3 sigma); slot; fraction [%]", 16, 0.5, 16.5)
    for slot in range(1, 17):
        name = "slot" + '{:0=2d}'.format(slot)
        h = file_in.Get("channelT0_" + name)
        channelT0s.append(h)
        r = TH1F("residuals_" + name, "ChannelT0, " + name + "; residuals [ns]", 200, -1.0, 1.0)
        residuals.append(r)
        if not h:
            continue
        for i in range(1, h.GetNbinsX() + 1):
            if h.GetBinError(i) > 0:
                r.Fill(h.GetBinContent(i))
                residuals_all.Fill(h.GetBinContent(i))
        r.Fit("gaus", "Q")
        residuals_all.Fit("gaus", "Q")
        h_rms.SetBinContent(slot, r.GetRMS() * 1000)
        fun = r.GetFunction("gaus")
        if not fun:
            continue
        fun.SetLineWidth(1)
        h_core.SetBinContent(slot, fun.GetParameter(2) * 1000)
        mean = fun.GetParameter(1)
        wid = fun.GetParameter(2)
        n_all = 0
        n_in = 0
        for i in range(1, h.GetNbinsX() + 1):
            if h.GetBinError(i) == 0:
                continue
            n_all += 1
            if abs(h.GetBinContent(i) - mean) < 3 * wid:
                n_in += 1
        if n_all > 0:
            h_out.SetBinContent(slot, (1 - n_in / n_all) * 100)
    residuals_all.Fit("gaus", "Q")

    # open pdf file

    canvas.Print(outputFileName + "[")

    # make plots of channelT0 residuals vs. channel

    canvas.Clear()
    canvas.Divide(4, 4)
    graphs = []
    for i, h in enumerate(channelT0s):
        canvas.cd(i + 1)
        if h:
            h.SetMinimum(-1.0)
            h.SetMaximum(1.0)
            h.Draw()
            g_over = TGraph()
            g_under = TGraph()
            for ibin in range(1, h.GetNbinsX() + 1):
                if h.GetBinContent(ibin) > 1.0:
                    g_over.SetPoint(g_over.GetN(), h.GetBinCenter(ibin), 0.95)
                elif h.GetBinContent(ibin) < -1.0:
                    g_under.SetPoint(g_under.GetN(), h.GetBinCenter(ibin), -0.95)
            if g_over.GetN() > 0:
                g_over.SetMarkerStyle(22)
                g_over.SetMarkerColor(2)
                g_over.Draw("P")
                graphs.append(g_over)
            if g_under.GetN() > 0:
                g_under.SetMarkerStyle(23)
                g_under.SetMarkerColor(2)
                g_under.Draw("P")
                graphs.append(g_under)
    canvas.Print(outputFileName)

    # make plots of channelT0 residual distirbutions

    canvas.Clear()
    canvas.Divide(4, 4)
    gStyle.SetOptFit(0)
    gStyle.SetOptStat("oue")
    W = gStyle.GetStatW()
    H = gStyle.GetStatH()
    gStyle.SetStatW(0.4)
    gStyle.SetStatH(0.3)
    for i, h in enumerate(residuals):
        canvas.cd(i + 1)
        h.Draw()
    canvas.Print(outputFileName)
    gStyle.SetStatW(W)
    gStyle.SetStatH(H)

    # make summary plots for channelT0

    canvas.Clear()
    canvas.Divide(2, 2)
    gStyle.SetOptFit(111)
    gStyle.SetOptStat(0)
    histos = [residuals_all, h_rms, h_core, h_out]
    for i, h in enumerate(histos):
        canvas.cd(i + 1)
        h.Draw()
    canvas.Print(outputFileName)

    # prepare commonT0 histogram

    commonT0 = file_in.Get("commonT0")
    if commonT0:
        commonT0.Scale(1000)
        commonT0.SetYTitle(commonT0.GetYaxis().GetTitle().replace('[ns]', '[ps]'))
        commonT0.SetMinimum(-100)
        commonT0.SetMaximum(100)
        commonT0.Fit("pol0", "Q")

    # make plots for commonT0

    canvas.Clear()
    canvas.Divide(1, 2)
    canvas.GetPad(2).SetGrid()
    gStyle.SetHistMinimumZero(False)
    histos = [commonT0, file_in.Get("runIndex")]
    for i, h in enumerate(histos):
        canvas.cd(i + 1)
        if h:
            h.SetMarkerStyle(20)
            h.SetMarkerSize(0.7)
            h.Draw()
    canvas.Print(outputFileName)
    gStyle.SetHistMinimumZero(True)

    # make plots for moduleT0

    canvas.Clear()
    canvas.Divide(4, 4)
    histos = []
    for slot in range(1, 17):
        canvas.cd(slot)
        h = file_in.Get("moduleT0_slot" + '{:0=2d}'.format(slot))
        if h:
            h.Scale(1000)
            h.SetYTitle(h.GetYaxis().GetTitle().replace('[ns]', '[ps]'))
            h.SetMinimum(-100)
            h.SetMaximum(100)
            h.Fit("pol0", "Q")
            histos.append(h)
    canvas.Print(outputFileName)

    # prepare moduleT0 summary histograms

    h_offset = TH1F("h_offset", "ModuleT0: fitted offset; slot; p0 [ps]", 16, 0.5, 16.5)
    h_chi = TH1F("h_chi", "ModuleT0: chi2/ndf; slot; #chi^{2}/ndf", 16, 0.5, 16.5)
    h_res = TH1F("h_res", "ModuleT0: residuals; residual - p0 [ps]", 200, -100, 100)
    h_pul = TH1F("h_pul", "ModuleT0: pulls; (residual - p0) / error", 200, -10, 10)
    for i, h in enumerate(histos):
        if not h:
            continue
        fun = h.GetFunction("pol0")
        if not fun:
            continue
        p0 = fun.GetParameter(0)
        err = fun.GetParError(0)
        h_offset.SetBinContent(i + 1, p0)
        h_offset.SetBinError(i + 1, err)
        h_chi.SetBinContent(i + 1, fun.GetChisquare() / max(fun.GetNDF(), 1))
        for ibin in range(1, h.GetNbinsX() + 1):
            err = h.GetBinError(ibin)
            if err > 0:
                h_res.Fill(h.GetBinContent(ibin) - p0)
                h_pul.Fill((h.GetBinContent(ibin) - p0) / err)

    # plot moduleT0 summary histograms

    canvas.Clear()
    canvas.Divide(2, 2)
    histos = [h_offset, h_chi, h_res, h_pul]
    h_offset.SetStats(False)
    h_chi.SetStats(False)
    gStyle.SetOptStat("rme")
    for i, h in enumerate(histos):
        canvas.cd(i + 1)
        h.Draw()
    canvas.Print(outputFileName)
    gStyle.SetOptStat(0)

    # plot fractions of active and active-and-calibrated channels

    canvas.Clear()
    canvas.Divide(4, 4)
    legends = []
    for slot in range(1, 17):
        canvas.cd(slot)
        name = "slot" + '{:0=2d}'.format(slot)
        histos = [file_in.Get("numActive_" + name), file_in.Get("numActiveCalibrated_" + name)]
        legend = TLegend(0.1, 0.1, 0.7, 0.3)
        legends.append(legend)
        opt = ''
        for i, h in enumerate(histos):
            if h:
                h.SetMinimum(0.0)
                h.SetMaximum(1.05)
                text = h.GetTitle().split(',')[0]
                h.SetTitle(name)
                h.SetLineColor(i+1)
                h.Draw(opt)
                opt = 'same'
                legend.AddEntry(h, text)
        if legend.GetNRows() > 0:
            legend.Draw("same")
    canvas.Print(outputFileName)

    # plot fractions of time base and channelT0 calibrated channels

    canvas.Clear()
    canvas.Divide(4, 4)
    legends = []
    for slot in range(1, 17):
        canvas.cd(slot)
        name = "slot" + '{:0=2d}'.format(slot)
        histos = [file_in.Get("numTBCalibrated_" + name), file_in.Get("numT0Calibrated_" + name)]
        legend = TLegend(0.1, 0.1, 0.7, 0.3)
        legends.append(legend)
        opt = ''
        for i, h in enumerate(histos):
            if h:
                h.SetMinimum(0.0)
                h.SetMaximum(1.05)
                text = h.GetTitle().split(',')[0]
                h.SetTitle(name)
                h.SetLineColor(i+1)
                h.Draw(opt)
                opt = 'same'
                legend.AddEntry(h, text)
        if legend.GetNRows() > 0:
            legend.Draw("same")
    canvas.Print(outputFileName)

    # plot threshold efficiencies

    canvas.Clear()
    canvas.Divide(4, 4)
    legends = []
    for slot in range(1, 17):
        canvas.cd(slot)
        name = "slot" + '{:0=2d}'.format(slot)
        h = file_in.Get("thrEffi_slot" + '{:0=2d}'.format(slot))
        legend = TLegend(0.1, 0.1, 0.7, 0.3)
        legends.append(legend)
        if h:
            h.SetMinimum(0.0)
            h.SetMaximum(1.0)
            text = h.GetTitle().split(',')[0]
            h.SetTitle(name)
            h.Draw()
            legend.AddEntry(h, text)
            legend.Draw("same")
    canvas.Print(outputFileName)

    # plot BS13d asic shifts

    canvas.Clear()
    canvas.Divide(2, 2)
    for cb in range(4):
        canvas.cd(cb + 1)
        canvas.GetPad(cb + 1).SetGrid()
        h = file_in.Get("asicShifts_" + str(cb))
        if h:
            h.SetMinimum(-50.0)
            h.SetMaximum(50.0)
            h.Draw()
    canvas.Print(outputFileName)

    # plot offsets

    canvas.Clear()
    canvas.Divide(2, 2)
    offsets = [file_in.Get("svdOffset"), file_in.Get("cdcOffset")]
    sigmas = [file_in.Get("svdSigma"), file_in.Get("cdcSigma")]
    components = ['SVD', 'CDC']
    colors = [4, 2]

    canvas.cd(1)
    hmin = 0
    hmax = 0
    for h in offsets:
        if h:
            hmin = min(hmin, h.GetMinimum())
            hmax = max(hmax, h.GetMaximum())
    hmin = round(hmin, 0) - 1
    hmax = round(hmax, 0) + 1
    for i, h in enumerate(offsets):
        if h:
            h.SetMinimum(hmin)
            h.SetMaximum(hmax)
            h.SetMarkerStyle(24)
            h.SetMarkerColor(colors[i])
            h.SetLineColor(colors[i])
    first = True
    legend1 = TLegend()
    for i, h in enumerate(offsets):
        if not h or not sigmas[i] or sigmas[i].Integral() == 0:
            continue
        if first:
            h.Draw()
            first = False
        else:
            h.Draw("same")
        legend1.AddEntry(h, components[i])
    legend1.Draw("same")

    canvas.cd(2)
    hmax = 0
    for h in sigmas:
        if h:
            hmax = max(hmax, h.GetMaximum())
    for i, h in enumerate(sigmas):
        if h:
            h.SetMinimum(0)
            h.SetMaximum(hmax * 1.1)
            h.SetMarkerStyle(24)
            h.SetMarkerColor(colors[i])
            h.SetLineColor(colors[i])
    first = True
    legend2 = TLegend()
    for i, h in enumerate(sigmas):
        if not h or h.Integral() == 0:
            continue
        if first:
            h.Draw()
            first = False
        else:
            h.Draw("same")
        legend2.AddEntry(h, components[i])
    legend2.Draw("same")

    fillPatt = [file_in.Get("fillPatternOffset"), file_in.Get("fillPatternFract")]
    for i, h in enumerate(fillPatt):
        canvas.cd(3 + i)
        if h:
            if i == 1:
                for k in range(h.GetNbinsX()):
                    h.SetBinError(k + 1, h.GetBinContent(k + 1) / 1000)
            h.Draw()

    canvas.Print(outputFileName)

    # close pdf file

    canvas.Print(outputFileName + "]")

    # plot photon hits, use png format to shorten file size

    canvas.Clear()
    canvas.Divide(4, 4)
    for slot in range(1, 17):
        canvas.cd(slot)
        h = file_in.Get("hits_slot" + '{:0=2d}'.format(slot))
        if h:
            h.Draw("colz")
    canvas.SaveAs(outputFileName.replace('.pdf', 'Hits.png'))

if __name__ == "__main__":
    run_validation(*sys.argv[1:])


"""
Base module and utils for the mean curvature based-equation learning problem
"""

import torch
from torch.utils.data import DataLoader

from nnpf.utils import get_default_args
from nnpf.problems import Problem
from nnpf.domain import Domain
from nnpf.functional import norm
import nnpf.shapes as shapes
from .datasets import MCSphereDataset, MCSphereLazyDataset
from .utils import check_sphere_mass

__all__ = [
    "MeanCurvatureProblem",
]

class MeanCurvatureProblem(Problem):
    """
    Base class for the mean curvature problem

    Features the train and validation data (MCSphereDataset), and the metric.

    Parameters
    ----------
    bounds: iterable of pairs of float
        Bounds of the domain
    N: int or iterable of int
        Number of discretization points
    epsilon: float
        Interface sharpness in phase field model
    dt: float
        Time step. If None, set to the maximum time step that guarantee the scheme stability.
    batch_size: int
        Size of the batch during training and validation steps. Full data if None.
    batch_shuffle: bool
        Shuffle batch content.
    lr: float
        Learning rate of the optimizer
    loss_norms: list of pair (p, weight)
        Compose loss as sum of weight * (output - target).norm(p).pow(e).
        Default to l2 norm.
        Exponent e is defined with loss_power parameter.
    loss_power: float
        Power applied to each loss term (for regularization purpose).
    radius: list of 2 floats
        Bounds on sphere radius (ratio of domain bounds) used for training and validation datasets.
    lp: int or float
        Power of the lp-norm used to defined the spheres in training and validation datasets.
    train_N, val_N: int
        Size of the training and validation datasets
    train_steps, val_steps: int
        Number of evolution steps applied to each input
    train_reverse, val_reverse: float
        Probability of having a sample with reversed inside and outside
    kwargs: dict
        Parameters passed to Problem (see doc)
    """

    def __init__(self, bounds=[[0., 1.], [0., 1.]], N=256, epsilon=2/256, dt=None,
                 batch_size=10, batch_shuffle=True, lr=1e-4,
                 loss_norms=None, loss_power=2.,
                 radius=[0.05, 0.45], lp=2,
                 train_N=100, train_steps=1, train_reverse=0.,
                 val_N=200, val_steps=5, val_reverse=0.,
                 use_lazy_datasets=False, num_workers=0,
                 **kwargs):

        super().__init__(**kwargs)

        # Default values
        dt = dt or self.stability_dt(epsilon)
        loss_norms = loss_norms or [[2, 1.]]

        # Hyper-parameters (used for saving/loading the module)
        self.save_hyperparameters(
            'bounds', 'N', 'dt', 'epsilon',
            'batch_size', 'batch_shuffle', 'lr', 'loss_norms', 'loss_power',
            'radius', 'lp', 'train_N', 'val_N', 'train_steps', 'val_steps',
            'train_reverse', 'val_reverse', 'use_lazy_datasets',
            'num_workers',
        )

    @staticmethod
    def stability_dt(epsilon):
        """ Maximum allowed time step that guarantee stability of the scheme """
        ...

    @staticmethod
    def profil(dist, epsilon):
        """ Solution profil from distance field """
        ...

    @staticmethod
    def iprofil(dist, epsilon):
        """ Distance field from solution profil """
        ...

    @staticmethod
    def sphere_radius(r0, t):
        """ Time evolution of the radius of the sphere """
        ...

    def shape(self, radius, center, p=2):
        """ Reference shape (sphere by design or any shape with same signature) """
        return shapes.sphere(radius, center, p)

    @property
    def domain(self):
        return Domain(self.hparams.bounds, self.hparams.N, device=self.device)

    @property
    def example_input_array(self):
        """ Example of input (for graph generation) """
        return torch.rand(self.hparams.batch_size or self.hparams.train_N, 1, *self.domain.N)

    def loss(self, output, target):
        """ Default loss function """
        dim = tuple(range(2, 2 + self.domain.dim))
        error = target - output
        return sum(
            w * norm(error, p, dim).pow(self.hparams.loss_power)
            for p, w in self.hparams.loss_norms).mean() / torch.tensor(self.domain.N).prod()

        # Below, loss with rescale depending on the solution norm (i.e. the circle size)
        # The idea was to compensate the less importance of small circle in the final loss
        # Need a lit more work on the scaling to work good
        """
        return sum(
            w * (norm(error, p, dim) / norm(target, p, dim)).pow(self.hparams.loss_power)
            for p, w in self.hparams.loss_norms).mean()
        """

    def check_sphere_mass(self, radius=0.45, num_steps=100, center=None, progress_bar=False):
        """
        Check a mean curvature model by measuring the decreasing of the mass of the profil associated to a sphere.

        Parameters
        ----------
        radius: float or list of float
            List of sphere radius (ratio of domain diameter) used as initial condition.
        num_steps: int or list of int
            Number of time steps (possibly per radius)
        center: list of float
            Sphere center (domain center if None)
        progress_bar: bool
            True to display a progress bar

        Returns
        -------
        model_mass: torch.tensor
            Sphere mass evolution for the given model
        exact_mass: torch.tensor
            Sphere mass evolution of the solution
        """

        def as_list(v):
            try: iter(v)
            except TypeError: return [v]
            else: return list(v)

        radius = as_list(radius)
        num_steps = as_list(num_steps)
        num_steps += [num_steps[-1]] * (len(num_steps) - len(radius))

        domain_diameter = min(b[1] - b[0] for b in self.domain.bounds)
        model_mass, exact_mass = [], []

        for r, n in zip(radius, num_steps):
            r = r * domain_diameter

            with torch.no_grad():
                mm, em = check_sphere_mass(
                    self.sphere_radius,
                    self.profil,
                    self, self.domain, r, self.hparams.epsilon, self.hparams.dt,
                    n, center, p=self.hparams.lp, progress_bar=progress_bar,
                    shape=self.shape)
                model_mass.append(mm)
                exact_mass.append(em)

        return torch.cat(model_mass), torch.cat(exact_mass)

    def training_step(self, batch, batch_idx):
        """ Default training step with custom loss function """
        data, *targets = batch
        loss = data.new_zeros([])
        for target in targets:
            data = self.forward(data)
            loss += self.loss(data, target)
        loss /= len(targets)

        return self.dispatch_metrics({'loss': loss})

    def validation_step(self, batch, batch_idx):
        """ Called at each batch of the validation data """
        data, *targets = batch
        loss = data.new_zeros([])
        for target in targets:
            data = self(data)
            loss += self.loss(data, target)
        loss /= len(targets)

        return {'val_loss': loss}

    def validation_epoch_end(self, outputs):
        """ Called at epoch end of the validation step (after all batches) """
        avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean()

        # Metric calculation
        model_mass, exact_mass = self.check_sphere_mass()
        mass_error = ((model_mass - exact_mass) / exact_mass).norm() / model_mass.numel()

        self.dispatch_metrics({'val_loss': avg_loss, 'metric': mass_error})

    def configure_optimizers(self):
        """ Default optimizer """
        return torch.optim.Adam(self.parameters(), lr=self.hparams.lr)

    def prepare_data(self):
        """ Prepare training and validation data """

        # Additionnal dimensions for appropriate broadcasting
        sup_dims = (1,) + self.domain.dim * (1,)

        # Shape properties
        domain_diameter = min(b[1] - b[0] for b in self.domain.bounds)
        radius = [domain_diameter * r for r in self.hparams.radius]
        center = [0.5 * sum(b) for b in self.domain.bounds]

        # Datasets
        Dataset = MCSphereLazyDataset if self.hparams.use_lazy_datasets else MCSphereDataset
        def generate_data(num_samples, steps, reverse):
            return Dataset(
                self.sphere_radius,
                self.profil,
                self.domain.X,
                radius=torch.linspace(radius[0], radius[1], num_samples),
                center=center,
                epsilon=self.hparams.epsilon,
                dt=self.hparams.dt,
                lp=self.hparams.lp,
                steps=steps,
                reverse=torch.rand(num_samples) < reverse,
                shape=self.shape,
            )

        self.train_dataset = generate_data(self.hparams.train_N, self.hparams.train_steps, self.hparams.train_reverse)
        self.val_dataset = generate_data(self.hparams.val_N, self.hparams.val_steps, self.hparams.val_reverse)

    def train_dataloader(self):
        """ Returns the training data loader """
        return DataLoader(
            self.train_dataset,
            batch_size=self.hparams.batch_size or len(self.train_dataset),
            shuffle=self.hparams.batch_shuffle,
            num_workers=self.hparams.num_workers
        )

    def val_dataloader(self):
        """ Returns the validation data loader """
        return DataLoader(
            self.val_dataset,
            batch_size=self.hparams.batch_size or len(self.val_dataset),
            num_workers=self.hparams.num_workers
        )

    @staticmethod
    def add_model_specific_args(parent_parser, defaults={}):

        # Parser for the domain bounds
        import re
        def bounds_parser(s):
            bounds = []
            per_dim = s.split('x')
            for dim_spec in per_dim:
                match = re.fullmatch(r'\s*\[([^\]]*)\]\s*', dim_spec)
                if not match:
                    raise ValueError(f"Invalid bound specification {dim_spec}")
                bounds.append([float(b) for b in match.group(1).split(',')])
            return bounds

        # Parser for loss definition
        def float_or_str(v):
            try:
                return float(v)
            except ValueError:
                return v

        # Parser for lp norm
        def int_or_float(v):
            try:
                return int(v)
            except ValueError:
                return float(v)

        from distutils.util import strtobool

        parser = Problem.add_model_specific_args(parent_parser, defaults)
        group = parser.add_argument_group("Mean curvature problem", "Options common to all models of mean curvature based equation.")
        group.add_argument('--bounds', type=bounds_parser, help="Domain bounds in format like '[0, 1]x[1, 2.5]'")
        group.add_argument('--N', type=int, nargs='+', help="Domain discretization")
        group.add_argument('--epsilon', type=float, help="Interface sharpness")
        group.add_argument('--dt', type=float, help="Time step (epsilon**2 if None)")
        group.add_argument('--train_N', type=int, help="Number of initial conditions in the training dataset")
        group.add_argument('--val_N', type=int, help="Number of initial conditions in the validation dataset")
        group.add_argument('--train_steps', type=int, help="Number of evolution steps in the training dataset")
        group.add_argument('--val_steps', type=int, help="Number of evolution steps in the validation dataset")
        group.add_argument('--train_reverse', type=float, help="Probability of having a sample with reversed inside and outside in training dataset")
        group.add_argument('--val_reverse', type=float, help="Probability of having a sample with reversed inside and outside in validation dataset")
        group.add_argument('--radius', type=float, nargs=2, help="Bounds on sphere radius (ratio of domain bounds) used for training and validation dataset.")
        group.add_argument('--lp', type=int_or_float, help="Power of the lp-norm used to define the spheres for training and validation dataset.")
        group.add_argument('--batch_size', type=int, help="Size of batch")
        group.add_argument('--batch_shuffle', type=lambda v: bool(strtobool(v)), nargs='?', const=True, help="Shuffle batch")
        group.add_argument('--lr', type=float, help="Learning rate")
        group.add_argument('--loss_norms', type=float_or_str, nargs=2, action='append', help="List of (p, weight). Compose loss as sum of weight * (output - target).norm(p).pow(e). Default to l2 norm. Exponent e is defined with loss_power parameter.")
        group.add_argument('--loss_power', type=float, help="Power applied to each loss term (for regularization purpose)")
        group.add_argument('--use_lazy_datasets', type=lambda v: bool(strtobool(v)), nargs='?', const=True, help="Use lazy-evaluation datasets")
        group.add_argument('--num_workers', type=int, help="Number of subprocesses used for data loading")
        group.set_defaults(**{**get_default_args(MeanCurvatureProblem), **defaults})

        return parser


import requests
from bs4 import BeautifulSoup

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'}
url ='https://www.ptt.cc/ask/over18?from=%2Fbbs%2FGossiping%2Findex.html'       #是否滿18歲頁面

ss = requests.session()

# Session1
#res = requests.get(url,headers=headers)
res = ss.get(url,headers=headers)            #目的在讓request在同一個session內
soup = BeautifulSoup(res.text,'html.parser')
button = soup.select('button[class="btn-big"]')[0]
print(button)
print(button['name'])               #取button(postdata)的key(name)
print(button['value'])              #取button(postdata)的value
print(ss.cookies)

hidden = soup.select('input')           #尋找hidden
print(hidden)

data = {}
data[button['name']] = button['value']
for k in hidden:                    #將值帶入
    data[k['name']] = k['value']
print(data)

# Session2
target_url = 'https://www.ptt.cc/ask/over18'
#res_target = requests.post(target_url,data=data,headers=headers)
res_target = ss.post(target_url,data=data,headers=headers)      #目的在讓request在同一個session內

# Session3
final_url = 'https://www.ptt.cc/bbs/Gossiping/index.html'
#final_res = requests.get(final_url,headers=headers)
final_res = ss.get(final_url,headers=headers)           #目的在讓request在同一個session內
print(final_res.text)

print(ss.cookies)           #可看到cookie內的東西


import pytest
from hueb.apps.hueb20.models.utils import (
    close_range,
    open_range,
    timerange_serialization,
)
from psycopg2.extras import NumericRange

@pytest.mark.parametrize(
    "test_input,expected",
    [
        # Convert infinite ranges into bounded ranges
        (NumericRange(1, None), NumericRange(1, 2)),
        (NumericRange(2, None), NumericRange(2, 3)),
        (NumericRange(None, 2), NumericRange(2, 3)),
        (NumericRange(None, 1), NumericRange(1, 2)),
        # Convert non-ranges (shouldn't occur because postgres creates empty ranges out of them)
        (NumericRange(1, 1), NumericRange(1, 2)),
        (NumericRange(2, 2), NumericRange(2, 3)),
        # Empty
        (NumericRange(empty=True), NumericRange(empty=True)),
        # None
        (None, None),
    ],
)
def test_close_range(test_input, expected):
    output = close_range(test_input)
    assert output == expected

@pytest.mark.parametrize(
    "test_input,expected",
    [
        # Convert infinite ranges into bounded ranges
        (NumericRange(1, 2), NumericRange(1, None)),
        (NumericRange(2, 3), NumericRange(2, None)),
        # Convert closed ranges with a greater delta than one from a range with excluded upper bound to one with an included upper bound
        (NumericRange(2, 4), NumericRange(2, 3)),
        (NumericRange(2, 5), NumericRange(2, 4)),
    ],
)
def test_open_range(test_input, expected):
    output = open_range(test_input)
    assert output == expected

@pytest.mark.parametrize(
    "timerange,expected",
    [
        # delta 1 range, return only single value
        (NumericRange(2, 3), "2"),
        (NumericRange(3, 4), "3"),
        (NumericRange(4, 5), "4"),
        # default range, reduce upper end by one
        (NumericRange(1, 3), "1 - 2"),
        (NumericRange(1, 4), "1 - 3"),
        (NumericRange(1, 5), "1 - 4"),
        # upper end unknown
        (NumericRange(1, None), "1 - ?"),
        (NumericRange(1, None), "1 - ?"),
        (NumericRange(1, None), "1 - ?"),
        # lower end unknown
        (NumericRange(None, 3), "? - 2"),
        (NumericRange(None, 4), "? - 3"),
        (NumericRange(None, 5), "? - 4"),
        # unbound range
        (NumericRange(None, None), "? - ?"),
        # empty range
        (NumericRange(empty=True), "-"),
        # None
        (None, "-"),
    ],
)
def test_timerange_serialization(timerange, expected):
    output = timerange_serialization(timerange)
    assert output == expected


import os, glob, sys
import numpy as np
from PyQt5 import QtGui, QtCore
import pyqtgraph as pg
from . import guiparts
from facemap import roi, utils
from natsort import natsorted
import pickle
from PyQt5.QtWidgets import (QFileDialog, QMessageBox)

def open_file(parent, file_name=None):
    if file_name is None:
        file_name = QFileDialog.getOpenFileName(parent,
                            "Open movie file", "", "Movie files (*.h5 *.mj2 *.mp4 *.mkv *.avi *.mpeg *.mpg *.asf *m4v)")
    # load ops in same folder
    if file_name:
        parent.filelist = [[file_name[0]]]
        load_movies(parent)

def open_folder(parent, folder_name=None):
    if folder_name is None:
        folder_name = QFileDialog.getExistingDirectory(parent,
                            "Choose folder with movies")
    # load ops in same folder
    if folder_name:
        extensions = ['*.mj2','*.mp4','*.mkv','*.avi','*.mpeg','*.mpg','*.asf']
        file_name = []
        for extension in extensions:
            files = glob.glob(folder_name+"/"+extension)
            files = [folder_name+"/"+os.path.split(f)[-1] for f in files]
            file_name.extend(files)
        for folder in glob.glob(folder_name+'/*/'):
            for extension in extensions:
                files = glob.glob(os.path.join(folder_name,folder,extension))
                files = [folder_name+"/"+folder+"/"+os.path.split(f)[-1] for f in files]
                file_name.extend(files)
        if len(file_name) > 1:
            choose_files(parent, file_name)
            load_movies(parent)

def choose_files(parent, file_name):
    parent.filelist = file_name
    LC=guiparts.ListChooser('Choose movies', parent)
    result = LC.exec_()
    if len(parent.filelist)==0:
        parent.filelist=file_name
    parent.filelist = natsorted(parent.filelist)
    if len(parent.filelist)>1:
        dm = QMessageBox.question(
            parent,
            "multiple videos found",
            "are you processing multiple videos taken simultaneously?",
            QMessageBox.Yes | QMessageBox.No,
        )
        if dm == QMessageBox.Yes:
            print('multi camera view')
            # expects first 4 letters to be different e.g. cam0, cam1, ...
            files = []
            iview = [os.path.basename(parent.filelist[0])[:4]]
            for f in parent.filelist[1:]:
                fbeg = os.path.basename(f)[:4]
                inview = np.array([iv==fbeg for iv in iview])
                if inview.sum()==0:
                    iview.append(fbeg)
            print(iview)
            for k in range(len(iview)):
                ij = 0
                for f in parent.filelist:
                    if iview[k] == os.path.basename(f)[:4]:
                        if k==0:
                            files.append([])
                        files[ij].append(f)
                        ij +=1
            parent.filelist = files
        else:
            print('single camera')
            files = parent.filelist.copy()
            parent.filelist = []
            for f in files:
                parent.filelist.append([f])

    else:
        parent.filelist = [parent.filelist]
    parent.filelist = natsorted(parent.filelist)

def open_proc(parent, file_name=None):
    if file_name is None:
        file_name = QFileDialog.getOpenFileName(parent,
                        "Open processed file", filter="*.npy")
        file_name = file_name[0]
    try:
        proc = np.load(file_name, allow_pickle=True).item()
        parent.filenames = proc['filenames']
        good=True
    except:
        good=False
        print("ERROR: not a processed movie file")
    if good:
        v = []
        good = load_movies(parent, filelist=parent.filenames)
        if good:
            if 'fullSVD' in proc:
                parent.fullSVD = proc['fullSVD']
            else:
                parent.fullSVD = True
            k=0 # number of processed things
            parent.proctype = [0,0,0,0,0,0,0,0]
            parent.wroi = [0,0,0,0,0,0,0,0]

            if 'motSVD' in proc:
                parent.processed = True
            else:
                parent.processed = False

            iROI=0
            parent.typestr = ['pupil', 'motSVD', 'blink', 'run']
            if parent.processed:
                parent.col = []
                if parent.fullSVD:
                    parent.cbs1[k].setText('fullSVD')
                    parent.cbs2[k].setText('fullSVD')
                    parent.lbls[k].setText('fullSVD')
                    parent.lbls[k].setStyleSheet("color: white;")
                    parent.proctype[0] = 0
                    parent.col.append((255,255,255))
                    k+=1
                parent.motSVDs = proc['motSVD']
                parent.running = proc['running']
                parent.pupil = proc['pupil']
                parent.blink = proc['blink']
            else:
                k=0

            kt = [0,0,0,0]
            # whether or not you can move the ROIs
            moveable = not parent.processed
            if proc['rois'] is not None:
                for r in proc['rois']:
                    dy = r['yrange'][-1] - r['yrange'][0]
                    dx = r['xrange'][-1] - r['xrange'][0]
                    pos = [r['yrange'][0]+parent.sy[r['ivid']], r['xrange'][0]+parent.sx[r['ivid']], dy, dx]
                    parent.saturation.append(r['saturation'])
                    parent.rROI.append([])
                    parent.reflectors.append([])
                    if 'pupil_sigma' in r:
                        psig = r['pupil_sigma']
                        parent.pupil_sigma = psig
                        parent.sigmaBox.setText(str(r['pupil_sigma']))
                    else:
                        psig = None
                    parent.ROIs.append(roi.sROI(rind=r['rind'], rtype=r['rtype'], iROI=r['iROI'], color=r['color'],
                                        moveable=moveable, parent=parent, saturation=r['saturation'], pupil_sigma=psig,
                                        yrange=r['yrange'], xrange=r['xrange'], pos=pos, ivid=r['ivid']))
                    if 'reflector' in r:
                        for i,rr in enumerate(r['reflector']):
                            pos = [rr['yrange'][0], rr['xrange'][0], rr['yrange'][-1]-rr['yrange'][0], rr['xrange'][-1]-rr['xrange'][0]]
                            parent.rROI[-1].append(roi.reflectROI(iROI=r['iROI'], wROI=i, pos=pos, parent=parent,
                                                yrange=rr['yrange'], xrange=rr['xrange'], ellipse=rr['ellipse']))
                    if parent.fullSVD:
                        parent.iROI = k-1
                    else:
                        parent.iROI = k
                    parent.ROIs[-1].ellipse = r['ellipse']
                    parent.sl[1].setValue(int(parent.saturation[parent.iROI] * 100 / 255))
                    parent.ROIs[parent.iROI].plot(parent)
                    if parent.processed:
                        if k < 5:
                            parent.cbs1[k].setText('%s%d'%(parent.typestr[r['rind']], kt[r['rind']]))
                            parent.cbs2[k].setText('%s%d'%(parent.typestr[r['rind']], kt[r['rind']]))
                            parent.lbls[k].setText('%s%d'%(parent.typestr[r['rind']], kt[r['rind']]))
                            parent.lbls[k].setStyleSheet("color: rgb(%s,%s,%s);"%
                                                        (str(int(r['color'][0])), str(int(r['color'][1])), str(int(r['color'][2]))))
                            parent.wroi[k] = kt[r['rind']]
                            kt[r['rind']]+=1
                            parent.proctype[k] = r['rind'] + 1
                            parent.col.append(r['color'])
                    k+=1
            parent.kroi = k

            # initialize plot
            parent.cframe = 1
            if parent.processed:
                for k in range(parent.kroi):
                    parent.cbs1[k].setEnabled(True)
                    parent.cbs2[k].setEnabled(True)
                if parent.fullSVD:
                    parent.cbs1[0].setEnabled(True)
                    parent.cbs1[0].setChecked(True)
                    parent.cbs2[0].setEnabled(True)
                parent.plot_processed()
            parent.next_frame()

def load_movies(parent, filelist=None):
    if filelist is not None:
        parent.filelist = filelist
    try:
        cumframes, Ly, Lx, containers = utils.get_frame_details(parent.filelist)
        nframes = cumframes[-1]
        good = True
    except Exception as e:
        print("ERROR: not a supported movie file")
        print(e)
        good = False
    if good:
        parent.reset()
        parent.video = containers
        parent.filenames = parent.filelist
        parent.nframes = nframes
        parent.cumframes = np.array(cumframes).astype(int)
        parent.Ly = Ly
        parent.Lx = Lx
        parent.p1.clear()
        parent.p2.clear()
        if len(parent.Ly)<2:
            parent.LY = parent.Ly[0]
            parent.LX = parent.Lx[0]
            parent.sx = np.array([int(0)])
            parent.sy = np.array([int(0)])
            parent.vmap = np.zeros((parent.LY,parent.LX), np.int32)
        else:
            # make placement of movies
            Ly = np.array(parent.Ly.copy())
            Lx = np.array(parent.Lx.copy())

            LY, LX, sy, sx = utils.video_placement(Ly, Lx)
            parent.vmap = -1 * np.ones((LY,LX), np.int32)
            for i in range(Ly.size):
                parent.vmap[sy[i]:sy[i]+Ly[i], sx[i]:sx[i]+Lx[i]] = i
            parent.sy = sy
            parent.sx = sx
            parent.LY = LY
            parent.LX = LX

        parent.fullimg = np.zeros((parent.LY, parent.LX, 3))
        parent.imgs = []
        parent.img = []
        for i in range(len(parent.Ly)):
            parent.imgs.append(np.zeros((parent.Ly[i], parent.Lx[i], 3, 3)))
            parent.img.append(np.zeros((parent.Ly[i], parent.Lx[i], 3)))
        #parent.movieLabel.setText(os.path.dirname(parent.filenames[0][0]))
        parent.save_path = os.path.dirname(parent.filenames[0][0])
        parent.frameDelta = int(np.maximum(5,parent.nframes/200))
        parent.frameSlider.setSingleStep(parent.frameDelta)
        if parent.nframes > 0:
            parent.update_frame_slider()
            parent.update_buttons()
        parent.cframe = 1
        parent.loaded = True
        parent.processed = False
        parent.jump_to_frame()
    return good

def save_folder(parent):
    folderName = QFileDialog.getExistingDirectory(parent,
                        "Choose save folder")
    # load ops in same folder
    if folderName:
        parent.save_path = folderName
        if len(folderName) > 30:
            parent.savelabel.setText("..."+folderName[-30:])
        else:
            parent.savelabel.setText(folderName)

def get_pose_file(parent):
    # Open a folder and allow selection of multiple files with extension *.h5 only
    # Returns a list of files
    filelist = []
    filelist = QFileDialog.getOpenFileNames(parent, 'Open Pose File', parent.save_path, '*.h5')
    if filelist[0] == '':
        return
    else:
        parent.poseFilepath = natsorted(filelist[0])
        parent.poseFileLoaded = True
        parent.load_labels()
        parent.Labels_checkBox.setChecked(True)
        parent.update_status_bar("Pose file(s) loaded")

def load_cluster_labels(parent):
    try:
        file_name = QFileDialog.getOpenFileName(parent,
                        "Select cluster labels file", "", "Cluster label files (*.npy *.pkl)")[0]
        extension = file_name.split(".")[-1]
        if extension == "npy":
            parent.loaded_cluster_labels = np.load(file_name, allow_pickle=True)
            parent.is_cluster_labels_loaded = True
        elif extension == "pkl":
            with open(file_name, 'rb') as f:
                parent.loaded_cluster_labels = pickle.load(f)
                parent.is_cluster_labels_loaded = True
        else:
            return
    except Exception as e:
        msg = QMessageBox(parent)
        msg.setIcon(QMessageBox.Warning)
        msg.setText("Error: not a supported filetype")
        msg.setStandardButtons(QMessageBox.Ok)
        msg.exec_()
        print(e)

def load_umap(parent):
    try:
        file_name = QFileDialog.getOpenFileName(parent,
                        "Select UMAP data file", "", "UMAP label files (*.npy *.pkl)")[0]
        extension = file_name.split(".")[-1]
        if extension == "npy":
            embedded_data = np.load(file_name, allow_pickle=True)
        elif extension == "pkl":
            with open(file_name, 'rb') as f:
                embedded_data = pickle.load(f)
        else:
            return
        return embedded_data
    except Exception as e:
        msg = QMessageBox(parent)
        msg.setIcon(QMessageBox.Warning)
        msg.setText("Error: not a supported filetype")
        msg.setStandardButtons(QMessageBox.Ok)
        msg.exec_()
        print(e)

def load_trace_data(parent):
    try:
        file_name = QFileDialog.getOpenFileName(parent,
                        "Select data file", "", "(*.npy *.pkl)")[0]
        extension = file_name.split(".")[-1]
        if extension == "npy":
            dat = np.load(file_name, allow_pickle=True)
        elif extension == "pkl":
            with open(file_name, 'rb') as f:
                dat = pickle.load(f)
        else:
            return
        return dat
    except Exception as e:
        msg = QMessageBox(parent)
        msg.setIcon(QMessageBox.Warning)
        msg.setText("Error: not a supported filetype")
        msg.setStandardButtons(QMessageBox.Ok)
        msg.exec_()
        print(e)

def save_clustering_output(output, parent):
    filename, ext = parent.filenames[0][0].split(".")
    filename = filename.split("/")[-1]    # Use video filename
    savename = os.path.join(parent.save_path, ("%s_facemap_clusters.npy"%filename))
    np.save(savename, output)
    parent.update_status_bar("Clustering output saved: "+savename)


import dash
import plotly.express as px
import plotly.graph_objects as go

from dash import dcc, html, Input, Output
from data import *

import os
from dotenv import load_dotenv

load_dotenv()

MAPBOX_ACCESS_TOKEN = os.getenv('MAPBOX_ACCESS_TOKEN')

# -----------------------------------------------------------------------------------------------------------------------
#MAPBOX_ACCESS_TOKEN: str = open('mapbox_token').read()
MAPBOX_STYLE = 'mapbox://styles/plotlymapbox/cjyivwt3i014a1dpejm5r7dwr'

countries = ['Austria', 'Belgium', 'Bulgaria', 'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland',
             'France', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland', 'Italy', 'Latvia', 'Lithuania',
             'Luxembourg', 'Malta', 'Netherlands', 'Norway', 'Poland', 'Portugal', 'Romania', 'Slovakia', 'Slovenia',
             'Spain', 'Sweden', 'Switzerland', 'United Kingdom']

sectors = ['All', 'National total for the entire territory', 'Energy production and distribution',
           'Energy use in industry', 'Road transport', 'Non-road transport', 'Commercial, institutional and households',
           'Industrial processes and product use', 'Agriculture', 'Waste', 'Other']

pollutants = ['All', 'CO', 'NH3', 'NMVOC', 'NOx', 'PM10', 'PM2.5', 'SOx', 'TSP']
years = [2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020]

models = ['Random Forest Regressor', 'Decision Tree Regressor', 'Linear Regression']
# -----------------------------------------------------------------------------------------------------------------------

app = dash.Dash(__name__, meta_tags=[{'name': 'viewport', 'content': 'width=device-width, initial-scale=1.0'}])

# This is for gunicorn
server = app.server
suppress_callback_exceptions = True

# Side panel

side_panel_layout = html.Div(
    id='panel-side',
    children=[
        html.P(id='country-dropdown-text', children=['Air Pollution', html.Br(), ' Dashboard']),
        html.Div(id='country-dropdown',
                 children=dcc.Dropdown(id='dropdown-component', options=countries, clearable=False, value='Germany')),
        html.Br(),
        html.Div(id='city-dropdown',
                 children=dcc.Dropdown(
                     id='city-dropdown-component',
                     options=[],
                     clearable=False,
                     value='Hamburg')),
        html.Br(),
        html.Div(id='sector-dropdown',
                 children=dcc.Dropdown(
                     id='sector-dropdown-component',
                     options=[],
                     clearable=False,
                     value='All')),
        html.Br(),
        html.Div(id='panel-side-text', children=[
            html.H1(id='country-name', children=''),
            html.P(className='country-description', id='country-description', children=[''])
        ]),
    ],
)

# Histogram

histogram = html.Div(
    id='histogram-container',
    children=[
        html.Div(
            id='histogram-header',
            children=[
                html.H1(id='histogram-title', children=['Gothenburg Protocol, LRTAP Convention']),
                html.H1(
                    id='histogram-dropdown-sector',
                    children=[dcc.Dropdown(
                        id='dropdown-component-sector',
                        options=sectors,
                        clearable=False,
                        value='All'
                    )]
                ),
                html.H1(
                    id='histogram-dropdown',
                    children=[dcc.Dropdown(
                        id='dropdown-component-second',
                        options=pollutants,
                        clearable=False,
                        value='All'
                    )]
                )
            ],
        ),
        dcc.Graph(id='histogram-graph', config={'displayModeBar': False}, animate=True),
    ],
)

# Map

map_graph = html.Div(
    id='world-map-wrapper',
    children=[dcc.Graph(id='world-map', config={'displayModeBar': False, 'scrollZoom': True})]
)
slider = html.H1(dcc.Slider(
    2007,
    2020,
    value=2007,
    step=None,
    marks={str(year): str(year) for year in years},
    id='year-slider'
))

@app.callback(Output('world-map', 'figure'),
              Output('city-dropdown-component', 'options'),
              Output('sector-dropdown-component', 'options'),
              Input('dropdown-component', 'value'),
              Input('city-dropdown-component', 'value'),
              Input('sector-dropdown-component', 'value'),
              Input('year-slider', 'value')
              )
def show_initial_elements(country, city, sector, year):
    df = country_df_map(country, city, sector)
    filtered_df = df[df.Year == year]

    figure = px.scatter_mapbox(filtered_df,
                               lat='Latitude', lon='Longitude', hover_name='Country',
                               hover_data=['eprtrSectorName', 'Emissions', 'EPRTRAnnexIMainActivityCode', 'City'],
                               color_discrete_sequence=['fuchsia'], zoom=5, height=655)

    figure.update_layout(mapbox_accesstoken=MAPBOX_ACCESS_TOKEN,
                         mapbox_style=MAPBOX_STYLE,
                         margin={'r': 0, 't': 0, 'l': 0, 'b': 0},
                         autosize=True,
                         paper_bgcolor='#1e1e1e',
                         plot_bgcolor='#1e1e1e')

    # figure = go.Figure(go.Scattermapbox(
    #     lat=df['Latitude'],
    #     lon=df['Longitude'],
    #     mode='markers',
    #     marker=go.scattermapbox.Marker(
    #         autocolorscale=False,
    #         showscale=True,
    #         size=filtered_df['Emissions'],
    #         opacity=0.8,
    #         color=filtered_df['Emissions'],
    #         colorscale='blues',
    #         colorbar=dict(
    #             title='',
    #             thickness=20,
    #             titleside='top',
    #             outlinecolor='rgba(68,68,68,0)',
    #             ticks='outside',
    #             ticklen=3)
    #     ),
    # ))
    #
    # figure.update_layout(
    #     autosize=True,
    #      height=655,
    #      plot_bgcolor='#1e1e1e',
    #     paper_bgcolor='#1e1e1e',
    #
    #      mapbox=dict(
    #         accesstoken=MAPBOX_ACCESS_TOKEN,
    #         style=MAPBOX_STYLE,
    #           zoom=5
    #     ),
    # )
    city_options = np.append('All', filtered_df['City'].unique())
    sector_options = np.append('All', filtered_df['eprtrSectorName'].unique())

    return figure, city_options, sector_options

#--------- predictions--------------------------------------------------------------------------------------------------

predictions = html.Div(
    id='predictions-container',
    children=[
        html.Div(
            id='predictions-header',
            children=[
                html.H1(id='predictions-title', children=['Predictions for Emissions']),
                html.H1(
                    id='predictions-dropdown-sector',
                    children=[dcc.Dropdown(
                        id='predictions-component-sector',
                        options=models,
                        clearable=False,
                        value='Decision Tree Regressor'
                    )]
                ),
                html.H1(
                    id='predictions-dropdown',
                    children=[dcc.Dropdown(
                        id='predictions-component-second',
                        options=pollutants,
                        clearable=False,
                        value='All'
                    )]
                )
            ],
        ),
        dcc.Graph(id='predictions-graph', config={'displayModeBar': False}, animate=True),
    ],
)

#--------- predictions--------------------------------------------------------------------------------------------------

predictions2 = html.Div(
    id='predictions-container2',
    children=[
        html.Div(
            id='predictions-header2',
            children=[
                html.H1(id='predictions-title2', children=['Predictions for Emissions']),
                html.H1(
                    id='predictions-dropdown-sector2',
                    children=[dcc.Dropdown(
                        id='predictions-component-sector2',
                        options=models,
                        clearable=False,
                        value='Decision Tree Regressor'
                    )]
                ),
                html.H1(
                    id='predictions-dropdown2',
                    children=[dcc.Dropdown(
                        id='predictions-component-second2',
                        options=years,
                        clearable=False,
                        value=2014
                    )]
                )
            ],
        ),
        dcc.Graph(id='predictions-graph2', config={'displayModeBar': False}, animate=True),
    ],
)

# Show histogram with predictions --------------------------------------------------------------------------------------

@app.callback(Output('predictions-graph', 'figure'),
              Input('city-dropdown-component', 'value'),
              Input('predictions-component-sector', 'value'))
def show_initial_elements(city, model):

    predictions_, expectations_ = get_predictions_with_model(city, model)

    fig1 = px.line(predictions_)
    fig2 = px.line(expectations_)

    fig1.update_traces(line_color='#fec036', line_width=2)
    fig2.update_traces(line_color='#FF00FF', line_width=2)

    fig3 = go.Figure(data=fig1.data + fig2.data,
                     layout=go.Layout(
                         margin={'t': 30, 'r': 35, 'b': 40, 'l': 50},
                         legend=dict(title=None, orientation='v', y=0.7, yanchor='bottom', x=1, xanchor='right'),
                         font=dict(color='gray'),
                         paper_bgcolor='#2b2b2b',
                         plot_bgcolor='#2b2b2b',
                         autosize=True,
                         bargap=0.1,
                         xaxis={'gridcolor': '#636363', 'showline': False},
                         yaxis={'showgrid': False, 'showline': False}
                     )
                     )
    return fig3

@app.callback(Output('predictions-graph2', 'figure'),
              Input('city-dropdown-component', 'value'),
              Input('predictions-component-second2', 'value'),
              Input('predictions-component-sector2', 'value'))
def show_initial_elements(city, year, model):

    y_pred, y_test = get_scatter(city, year, model)

    #fig1 = px.scatter(df, x="sepal_width", y="sepal_length", color='petal_length')
    fig = go.Figure()

    fig.add_trace(go.Scatter(x=np.arange(1, len(y_pred) + 1), y=y_pred,

                             name='ŷ'))
    fig.add_trace(go.Scatter(x=np.arange(1, len(y_test) + 1), y=y_test,

                             name='y'))

    fig.update_layout(margin={'t': 30, 'r': 35, 'b': 40, 'l': 50},
                       legend=dict(title=None, orientation='v', y=0.7, yanchor='bottom', x=1, xanchor='right'),
                      font=dict(color='gray'),
                      paper_bgcolor='#2b2b2b',
                      plot_bgcolor='#2b2b2b',
                      autosize=True,
                      bargap=0.1,
                      xaxis={'dtick': 5, 'gridcolor': '#636363', 'showline': False},
                      yaxis={'showgrid': False})
    return fig

# ----------------------------------------------------------------------------------------------------------------------

# Show histogram related to the CLRTAP Dataset -------------------------------------------------------------------------

@app.callback(Output('histogram-graph', 'figure'),
              Input('dropdown-component', 'value'),
              Input('dropdown-component-second', 'value'),
              Input('dropdown-component-sector', 'value'))
def show_initial_elements(country, pollutant, sector):
    hist = px.bar(sector_emissions_per_country(country, pollutant, sector),
                  x='Year', y='Emissions', color='Sector_label_EEA', barmode='overlay',
                  hover_data=['Sector_name'],
                  height=750, color_discrete_sequence=px.colors.qualitative.Vivid)
    hist.update_layout(margin={'t': 30, 'r': 35, 'b': 40, 'l': 50},
                       legend=dict(title=None, orientation='v', y=0.7, yanchor='bottom', x=1, xanchor='right'),
                       font=dict(color='gray'),
                       paper_bgcolor='#2b2b2b',
                       plot_bgcolor='#2b2b2b',
                       autosize=True,
                       bargap=0.1,
                       xaxis={'dtick': 5, 'gridcolor': '#636363', 'showline': False},
                       yaxis={'showgrid': False},
                       height=650)
    return hist

# ----------------------------------------------------------------------------------------------------------------------

# Control panel + map
main_panel_layout = html.Div(
    id='panel-upper-lower',
    children=[
        map_graph,
        slider,
        html.Div(
            id='panel',
            children=[
                histogram,
                html.Div(),
            ],
        ),
        html.H1(),
        html.Div(
            children=[
                predictions,
                html.Div(),
            ],
        ),
        html.H1(),
        html.Div(
            children=[
                predictions2,
                html.Div(id='panel-lower' ),
            ],
        ),
    ],
)

# Root
root_layout = html.Div(
    id='root',
    children=[
        dcc.Store(id='store-placeholder'),
        # For the case no components were clicked, we need to know what type of graph to preserve
        dcc.Store(id='store-data-config', data={'info_type': '', 'satellite_type': 0}),
        side_panel_layout,
        main_panel_layout,
    ],
)

app.layout = root_layout

@app.callback(
     Output('country-name', 'children'),
     [Input('dropdown-component', 'value')],
)
def update_name(val):
     return 'Predictions are in yellow'
#
#
# @app.callback(
#     Output('country-description', 'children'),
#     [Input('dropdown-component', 'value')],
# )
# def update_description(val):
#     text = ' '
#
#     if val == "France":
#         text = ( ' ' )
#
#     elif val == "Germany":
#         text = (  ' '  )
#     return text
#
if __name__ == "__main__":
    app.run_server(debug=True)


#
# @lc app=leetcode id=2 lang=python3
#
# [2] Add Two Numbers
#
# https://leetcode.com/problems/add-two-numbers/description/
#
# algorithms
# Medium (30.61%)
# Total Accepted:    774.6K
# Total Submissions: 2.5M
# Testcase Example:  '[2,4,3]\n[5,6,4]'
#
# You are given two non-empty linked lists representing two non-negative
# integers. The digits are stored in reverse order and each of their nodes
# contain a single digit. Add the two numbers and return it as a linked list.
#
# You may assume the two numbers do not contain any leading zero, except the
# number 0 itself.
#
# Example:
#
#
# Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
# Output: 7 -> 0 -> 8
# Explanation: 342 + 465 = 807.
#
#
#
# Definition for singly-linked list.
#class ListNode(object):
#    def __init__(self, x):
#        self.val = x
#        self.next = None

class Solution:
    def addTwoNumbers(self, l1, l2):
        ex = 0
        ret = None
        last = None
        while (l1 and l2):
            num = l1.val + l2.val + ex
            ex, num = num // 10, num % 10
            node = ListNode(num)
            if not ret:
                ret = node
                last = ret
            else:
                last.next = node
                last = node
            l1 = l1.next
            l2 = l2.next
        if l1:
            while l1:
                num = l1.val + ex
                ex, num = num // 10, num % 10
                node = ListNode(num)
                if not ret:
                    ret = node
                    last = node
                else:
                    last.next = node
                    last = node
                l1 = l1.next
        else:
            while l2:
                num = l2.val + ex
                ex, num = num // 10, num % 10
                node = ListNode(num)
                if not ret:
                    ret = node
                    last = node
                else:
                    last.next = node
                    last = node
                l2 = l2.next
        if ex:
            node = ListNode(ex)
            last.next = node
            last = node
        return ret


import os
from models.gcp_info import GCP_SPARK_MASTER_ADDRESS

HOME_PATH = os.path.expanduser('~')
PROJECT_NAME = 'SparkTuning'

SPARK_CONF_PATH = os.path.join(HOME_PATH, PROJECT_NAME, 'data/spark.conf')
SPARK_CONF_TEMPLATE_PATH = os.path.join(HOME_PATH, PROJECT_NAME, 'data/spark.conf.template')

MASTER_ADDRESS = GCP_SPARK_MASTER_ADDRESS
MASTER_CONF_PATH = os.path.join(HOME_PATH, 'HiBench/conf')
MASTER_BENCH_BASH = os.path.join(HOME_PATH, 'scripts/run_terasort.sh')

HIBENCH_REPORT_PATH = os.path.join(HOME_PATH, PROJECT_NAME, 'data/hibench.report')

INCUMBENTS_RESULTS_PATH = os.path.join(HOME_PATH, PROJECT_NAME, 'results')

baxus_params = {'embedding_type': 'baxus',
                'acquisition_function': 'ts',
                'mle_optimization': 'sample-and-choose-best',
                'target_dim': 10, # 10
                'n_init': 20, # 10
                'max_evals': 300, # 100
                'noise_std': 0,
                'new_bins_on_split': 3,
                'multistart_samples': 100,
                'mle_training_steps': 50,
                'multistart_after_samples': 10,
                'l_init': 0.8,
                'l_min': 0.5**7,
                'l_max': 1.6,
                'adjust_initial_target_dim': True,
                'budget_until_input_dim': 0
                }


from itertools import permutations

from edge import Edge
from node import Node

import networkx as nx
from matplotlib import pyplot as plt

class Graph():

    def __init__(self):#construtor da classe Grafo
        self.edges: list[Edge] = [] #lista de arestas do grafo
        self.nmNodes : int = None #numero de vertices do grafo
        self.G = nx.DiGraph() #formação do grafo direcionado pela lib NetworkX
        self.nodes: list[Node] = [] #lista de vertices do grafo
        self.visitedEdges: list[Edge] = [] #lista de arestas já visitadas no grafo
        self.visitedNodes: list[Node] = [] #lista de vertices já visitados no grafo
        self.workload: int = 0 #carga horária
        self.gain: int = 0 #ganho total

    def addEdge(self, edge : Edge): #adiciona uma aresta no grafo
        self.edges.append(edge)
        self.G.add_edge(edge.begin.id, edge.end.id, length = edge.gas_cost, weight = edge.distance)

    def addNode(self, node : Node): #adiciona um vertice no grafo
        replica = False
        for i in self.nodes:
            if (node.id == i.id):
                replica = True
                break

        if (not replica):
            self.nodes.append(node)

    def printGraph(self, nodes): #printa e plota o grafo, utilizando as libs: networkX e matplot
        color_map = []
        pos = { #posições dos nós no plano cartesiano
            '0':([0.0, 0.0]),
            '1':([-0.2, 4.0]),
            '2':([5.5, 8.0]),
            '3':([8.0, 3.0]),
            '4':([10.0, -4.0]),
            '5':([1.0, -4.0]),
            '6':([0.3, -7.0]),
            '7':([-4.0, -3.8]),
            '8':([-4.5, 2.5]),
            '9':([-2.0, 6.0])
        }

        for i in self.nodes:#loop que colorirá o grafo, verde os vértices já visitados e azul os não visitados
            if (self.existNode(i, nodes)):
                color_map.append('green')
            else:
                color_map.append('blue')

        nx.draw_networkx_edges(self.G, pos, edge_color= '#000000')
        nx.draw_networkx_nodes(self.G, pos, node_color=color_map)
        nx.draw_networkx_labels(self.G, pos)
        plt.show()

    def setNmNodes(self, nmNodes : int): #define o número de vértices do grafo
        self.nmNodes = nmNodes

    def verifyNode(self, node_id): #recebe um id de um vértice e verifica se este existe no grafo
        for i in self.nodes:
            if (i.id == node_id):
                return i

    def existNode(self, node, visitedNodes): #recebe um id de um vértice e verifica se este existe já foi visitado no grafo
        for i in visitedNodes:
            if (i == node):
                return True

        return False

    def return_VisitedNodeList(self, str): #retorna uma lista de vértices visitados no grafo
        split = str.split(' ')
        list = []
        for i in split:
            if (i.isalnum() and i != "home"):
                list.append(self.verifyNode(i))
        return list

    def verifyEdge(self, edge): #recebe uma aresta e verifica se esta ja foi percorrida no grafo
        for i in self.visitedEdges:
            if (i == edge):
                return True
        return False

    def find_BestStep(self, node): #função de caminhamento guloso
        max_bounty = 0
        canContinue = False

        for i in node.edges: #recebe um vértice 'node' e faz-se a seguinte verificação: o ganho do vértice da outra ponta da aresta possui um ganho maior que o já armazenado em 'max_bounty'? Com a carga-horária atual, é possível caminhar para este vértice em observação e voltar para divinópolis? A aresta a ser caminhada ja foi utilizada?

            if (i.end.bounty > max_bounty and i.end.time_spent < (self.workload - self.timeToReturn(i.end) - i.distance) and (not self.verifyEdge(i))):
                max_bounty = i.end.bounty
                best_node = i.end
                edge_passed = i
                canContinue = True

        if(canContinue): #se foi possível dar um passo, adiciona o vértice na lista de visitados e atualiza o ganho e a carga-horária do grafo
            self.visitedNodes.append(best_node)
            node = best_node
            if (edge_passed.begin == self.nodes[0]):
                self.addEdge_NodeEdgeList(node)
            self.workload = self.workload - (best_node.time_spent + edge_passed.distance)
            self.gain = self.gain + (best_node.bounty - edge_passed.gas_cost)

        else: #se não foi possível dar o passo, volta para Divinópolis e atualiza o ganho e a carga-horária
            self.visitedNodes.append(self.nodes[0])
            self.workload = self.workload - self.timeToReturn(node)
            self.gain = self.gain - self.costToReturn(node)

        return node

    def gulosoMaxValue(self, workload): #recebe a carga horária semanal e roda o algoritmo guloso olhando todas as possibildades de caminho, fazendo um intermédio entre programação-dinâmica e guloso
        results_dict = {} #dicionário que armazenará o resultado final do algoritmo
        results_dict["Nodes"] = ""
        results_dict["Gain"] = self.gain

        for i in range (len(self.nodes[0].edges)): #enquando houver arestas saindo de divinópolis, rodará a função find_BestStep()
            self.cleanVariables(workload)
            no = self.nodes[0]
            self.visitedNodes.append(no)
            if (self.can_ChooseEdge(no)): #observa se há carga-horária suficiente para andar no grafo, pois há casos que há algumas arestas que não são possíveis de percorrer
                while (no != self.find_BestStep(no)):
                    no = self.visitedNodes[-1]
            else:
                break

            if (self.gain > results_dict["Gain"]):
                results_dict["Nodes"] = self.returnVisitedNodes()
                results_dict["Gain"] = self.gain

        return results_dict

    def timeToReturn(self, node): #recebe um vértice e retorna o tempo necessário para voltar desse vertice para divinopolis
        for i in node.edges:
            if (i.end == self.nodes[0]):
                return i.distance

    def costToReturn(self, node): #recebe um vértice e retorna o custo com gasolina necessário para voltar desse vertice para divinopolis
        for i in node.edges:
            if (i.end == self.nodes[0]):
                return i.gas_cost

    def returnVisitedNodes(self): #retorna uma string apontando os vértices que foram caminhados
        str = ""
        for i in self.visitedNodes:
            str = str + i.id + " -> "

        str = str + "home"
        return str

    def printVisitedEdges(self): #printa as arestas visitadas
        for i in self.visitedEdges:
            print(i.begin.id," -> ", i.end.id)

    def addEdge_NodeEdgeList(self, node): #adiciona uma aresta na lista de arestas visitadas de um vértice específico
        for i in self.edges:
            if (node.id == i.end.id and i.begin == self.nodes[0]):
                self.visitedEdges.append(i)

    def cleanVariables(self, workload): #limpa todas as variáveis utilizadas no loop guloso-dinâmico
        self.workload = workload
        self.visitedNodes.clear()
        self.gain = 0

    def can_ChooseEdge(self, node): #retorna se é possível percorrer por alguma aresta no grafo
        for i in node.edges:
            if (i.end.time_spent < (self.workload - self.timeToReturn(i.end) - i.distance) and (not self.verifyEdge(i))):
                return True

        return False


"""
Fixtures for floating IPs.

@author: schipiga@mirantis.com
"""

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import pytest

from horizon_autotests.steps import FloatingIPsSteps

from ._utils import AttrDict

__all__ = [
    'allocate_floating_ip',
    'floating_ip',
    'floating_ips_steps'
]

@pytest.fixture
def floating_ips_steps(login, horizon):
    """Fixture to get floating IPs steps."""
    return FloatingIPsSteps(horizon)

@pytest.yield_fixture
def allocate_floating_ip(floating_ips_steps):
    """Fixture to create floating IP with options.

    Can be called several times during test.
    """
    floating_ips = []

    def _allocate_floating_ip():
        ip = floating_ips_steps.allocate_floating_ip()
        floating_ip = AttrDict(ip=ip)
        floating_ips.append(floating_ip)
        return floating_ip

    yield _allocate_floating_ip

    for floating_ip in floating_ips:
        floating_ips_steps.release_floating_ip(floating_ip.ip)

@pytest.fixture
def floating_ip(allocate_floating_ip):
    """Fixture to create floating IP with default options before test."""
    return allocate_floating_ip()


from collections.abc import Iterable
from itertools import chain

takes_synonyms = ['takes', 'take', 'consume', 'consumes']
gives_synonyms = ['gives', 'give', 'produce', 'produces']
constraints_synonyms = ['constraints', "dependency", "dependencies"]
reciprocal_synonyms = ['reciprocal']
name_synonyms = ['name']
type_synonyms = ['type']
module_synonyms = ['module']
link_synonyms = ['link', 'links']
index_synonyms = ['index']

def first_intersection(a, b):
    if isinstance(b, Iterable):
        for e in a:
            if e in b:
                return e
    return None

class Element:
    @classmethod
    def get_values_from_parameter(cls, parameter):
        """
        Parses the parameter values
        There are several types of parameter values:
            Single value:       1           gpio
            List of values:     1 2 3
            Diapason of values: -1..3
            List of diapasons:  1..3 5..7
            Mixed type:         1 3..5 7
        There are several types of parameter constraints:
            Single constraint: =1   ~1
            List of single constraints: =1 =-3
            Diapason constraint: =1..3  ~5..7
        Values/constraints in lists have a logical relationship of "and"
        :param: parameter: string or list to parse
        :return: dictionary with a values and constraints
        """
        values = []
        constraints = []
        parameters = []
        if isinstance(parameter, str):
            if ' ' in parameter:
                # Complex parameter
                parameters = parameter.split(' ')
            else:
                # Simple parameter
                parameters.append(parameter)
        for param in parameters:
            if '=' == param[0]:
                # Value must be equal to param, or must be in diapason
                if '..' in param:
                    boundaries = param.split('..')
                    constraints.append((boundaries[0][1:], boundaries[-1]))
                else:
                    constraints.append((param[1:]))
            elif '~' == param[0]:
                # Value must be not equal to param, or must be out of diapason
                if '..' in param:
                    boundaries = param.split('..')
                    constraints.append([boundaries[0][1:], boundaries[-1]])
                else:
                    constraints.append([param[1:]])
            else:
                values.append(param)

        return {'values': values, 'constraints': constraints}

class Pin(Element):
    @classmethod
    def parse(cls, name, parent_module, descriptor):
        parameters = {}
        reciprocal = {}
        links = []
        scope = ''
        if descriptor:
            if type(descriptor) == str:
                links.append(descriptor)
            else:
                for pin_attribute_name, pin_attribute_value in descriptor.items():
                    if pin_attribute_name in name_synonyms:
                        name = pin_attribute_value
                    elif pin_attribute_name in reciprocal_synonyms:
                        # todo: parse
                        reciprocal = pin_attribute_value
                    elif pin_attribute_name in link_synonyms:
                        links.append(pin_attribute_value)
                    elif pin_attribute_name == 'scope':
                        assert isinstance(pin_attribute_value, str)
                        scope = {'values': pin_attribute_value}
                    else:
                        parameters[pin_attribute_name] = cls.get_values_from_parameter(pin_attribute_value)

        # If type is unknown, get type and index from name
        type_synonym = first_intersection(type_synonyms, parameters)
        if not type_synonym:
            type_synonym = type_synonyms[0]
            listed_name = name.split('_')
            if listed_name[-1].isdigit():
                parameters[type_synonym] = {'values': ['_'.join(listed_name[:-1])]}
                index_synonym = first_intersection(index_synonyms, parameters)
                if not index_synonym:
                    index_synonym = index_synonyms[0]
                    parameters[index_synonym] = {'values': [int(listed_name[-1])]}
            else:
                parameters[type_synonym] = {'values': [name]}

        # Add type constraints (types must matches for connecting)
        for type_synonym in type_synonyms:
            if type_synonym in parameters:
                for pin_type_name in parameters[type_synonym]['values']:
                    if 'constraints' not in parameters[type_synonym]:
                        parameters[type_synonym]['constraints'] = []
                    if not parameters[type_synonym]['constraints']:
                        parameters[type_synonym]['constraints'].append(pin_type_name)

        # Add module constraints to the pin
        for type_synonym in type_synonyms:
            if type_synonym in parameters:
                for pin_type_name in parameters[type_synonym]['values']:
                    if pin_type_name in parent_module.constraints:
                        for parameter_name in parent_module.constraints[pin_type_name]:
                            if parameter_name not in parameters[type_synonym]:
                                parameters[parameter_name] = {'values': [], 'constraints': []}
                            for pin_type_value in parent_module.constraints[pin_type_name][parameter_name]['values']:
                                parameters[parameter_name]['values'].append(pin_type_value)
                            for pin_type_constraint in parent_module.constraints[pin_type_name][parameter_name]['constraints']:
                                parameters[parameter_name]['constraints'].append(pin_type_constraint)

        pin = cls(parent_module, parameters)
        pin.name = name
        pin.reciprocal = reciprocal
        pin.links = links
        return pin, scope

    def __init__(self, module, parameters={}):
        self.name = None
        self.module = module
        self.connected_to = []
        self.parameters = parameters
        self.reciprocal = {}
        self.links = []

    def __str__(self):
        repr = 'Pin {}: parameters: {}'.format(self.name, self.parameters)
        for connected_pin in self.connected_to:
            repr += '\n        Connected to: {}:{}'.format(connected_pin.module.name, connected_pin.name)
        return repr

    def __repr__(self):
        return self.__str__()

    def connect(self, pin):
        if self.is_pins_connectable(pin):
            self.connected_to.append(pin)
            pin.connected_to.append(self)

    def is_pins_connectable(self, pin):
        """
        Checks parameters for matching.
        :param: pin2: pin to check a connection possibility
        :return: True/False - connection is possible / connection is not possible
        """
        match = False
        pin1 = self
        pin2 = pin
        for _ in range(2):
            for parameter in pin1.parameters:
                if 'constraints' in pin1.parameters[parameter] and pin1.parameters[parameter]['constraints']:
                    pin1_constraints = pin1.parameters[parameter]['constraints']
                    if parameter in pin2.parameters:
                        pin2_values = pin2.parameters[parameter]['values']
                        # Compare values and constraints
                        values_is_satisfied = [False] * len(pin2_values)
                        for cnstr in pin1_constraints:
                            constraint_is_satisfied = False
                            constraint_index = 0
                            for vl in pin2_values:
                                if isinstance(cnstr, tuple):
                                    # Value must be in diapason
                                    # todo
                                    pass
                                elif isinstance(cnstr, list):
                                    # Value must NOT be in diapason
                                    # Diapason
                                    # todo
                                    pass
                                else:
                                    # Other type
                                    # The value must equal to the constraint
                                    if cnstr in vl:
                                        values_is_satisfied[constraint_index] = True if values_is_satisfied[constraint_index] is False else False
                                        constraint_is_satisfied = True if constraint_is_satisfied is False else False
                                constraint_index += 1
                            if not constraint_is_satisfied:
                                # Constraint is not satisfied
                                return False
                        if False in values_is_satisfied:
                            # At list one value out of constraints
                            return False
                    else:
                        # There are not values to compare with constraints
                        # Pins is not connectable
                        return False
                    # All constraints and values is satisfied
                    match = True
            pin2 = self
            pin1 = pin
        return match

class Module(Element):
    parse_recurse_deep_counter = 0

    @classmethod
    def parse(cls, parent, path, descriptor):
        cls.parse_recurse_deep_counter += 1

        links = {}
        items = descriptor.items()
        module = cls(parent, None, path)
        for attribute_name, attribute_value in items:
            type_synonym = first_intersection(type_synonyms, attribute_value)
            if attribute_name in name_synonyms:
                module.name = attribute_value
            elif type_synonym and attribute_value[type_synonym] in module_synonyms:
                # Module in root
                module.children.append(cls.parse(module, path + ':' + attribute_name, attribute_value))
            elif attribute_name in takes_synonyms:
                for take_name, take_value in attribute_value.items():
                    type_synonym = first_intersection(type_synonyms, take_value)
                    if type_synonym and take_value[type_synonym] in module_synonyms:
                        # Element is module, parse it recursively
                        module.children.append(cls.parse(module, path + ':' + take_name, take_value))
                    else:
                        # Element is pin, parse it
                        pin, scope = Pin.parse(take_name, module, take_value)
                        module.add_pin_to_module_by_scope(pin, scope, False)
            elif attribute_name in gives_synonyms:
                for give_name, give_value in attribute_value.items():
                    type_synonym = first_intersection(type_synonyms, give_value)
                    if type_synonym and give_value[type_synonym] in module_synonyms:
                        # Element is module, parse it recursively
                        module.children.append(cls.parse(module, path + ':' + give_name, give_value))
                    else:
                        # Element is pin, parse it
                        pin, scope = Pin.parse(give_name, module, give_value)
                        module.add_pin_to_module_by_scope(pin, scope, True)
            elif attribute_name in constraints_synonyms:
                for pin_type in attribute_value:
                    module.constraints[pin_type] = {}
                    if attribute_value[pin_type]:
                        for parameter in attribute_value[pin_type]:
                            module.constraints[pin_type][parameter] =\
                                cls.get_values_from_parameter(attribute_value[pin_type][parameter])
            elif attribute_name in link_synonyms:
                links.update(attribute_value)
            else:
                type_synonym = first_intersection(type_synonyms, attribute_value)
                if type_synonym in module_synonyms:
                    # Element is module, parse it recursively
                    module.children.append(cls.parse(module, path + ':' + attribute_name, attribute_value))
                else:
                    module.attributes[attribute_name] = attribute_value

        # Resolve links
        for give_pin_name in module.give_pins:
            for link_pin_name in module.give_pins[give_pin_name].links:
                if link_pin_name in module.take_pins:
                    module.links[module.give_pins[give_pin_name]] = module.take_pins[link_pin_name]
                    module.links[module.take_pins[link_pin_name]] = module.give_pins[give_pin_name]
                else:
                    # Link error, no such pin
                    print('\nLink error, no such pin:', link_pin_name, '\n')
        for take_pin_name in module.take_pins:
            for link_pin_name in module.take_pins[take_pin_name].links:
                if link_pin_name in module.give_pins:
                    module.links[module.take_pins[take_pin_name]] = module.give_pins[link_pin_name]
                    module.links[module.give_pins[link_pin_name]] = module.take_pins[take_pin_name]
                else:
                    # Link error, no such pin
                    print('\nLink error, no such pin:', link_pin_name, '\n')
        for take_pin_name in links:
            if take_pin_name in module.take_pins:
                if type(links[take_pin_name]) and links[take_pin_name] in module.give_pins:
                    module.links[module.take_pins[take_pin_name]] = module.give_pins[links[take_pin_name]]
                    module.links[module.give_pins[links[take_pin_name]]] = module.take_pins[take_pin_name]
                else:
                    # Link error, no such pin
                    print('\nLink error, no such pin:', module.links[take_pin_name], '\n')
            else:
                # Link error, no such pin
                print('\nLink error, no such pin:', take_pin_name, '\n')

        cls.parse_recurse_deep_counter -= 1

        # Root module post parse actions
        if cls.parse_recurse_deep_counter == 0:
            # Resolve all connections with children
            for module_a in chain(module.children, [module]):
                for module_b in chain(module.children, [module]):
                    if module_a != module_b:
                        module_a.connect(module_b)
        return module

    def __init__(self, parent, name, path):
        self.parent = parent
        self.name = name
        self.path = path
        self.take_pins = {}
        self.give_pins = {}
        self.attributes = {}
        self.children = []
        self.constraints = {}
        self.links = {}

    def __str__(self):
        repr = '\nModule {}: name: {}, attributes: {}'.format(self.path, self.name, self.attributes)
        for pin in self.give_pins:
            repr += '\n    ->' + str(self.give_pins[pin])
        for pin in self.take_pins:
            repr += '\n    <-' + str(self.take_pins[pin])
        for link in self.links:
            repr += '\n        Link: ' + link.name + ' - ' + self.links[link].name
        for child in self.children:
            repr += '\n    ' + str(child)
        return repr

    def __repr__(self):
        return self.__str__()

    def connect(self, module):
        print('Trying to connect module {} to {}'.format(self.name, module.name))
        for pin in self.take_pins:
            for pin_candidate in module.give_pins:
                # Check all constraints
                for pin_parameter in self.take_pins[pin].parameters:
                    for pin_candidat_parameter in module.give_pins[pin_candidate].parameters:
                        if pin_parameter == pin_candidat_parameter:
                            # print('    ', pin_parameter, self.take_pins[pin].parameters[pin_parameter])
                            self.take_pins[pin].connect(module.give_pins[pin_candidate])

    def add_pin_to_module_by_scope(self, pin, scope, is_pin_give=False):
        # Add pin to modules according scope
        module = self
        if is_pin_give:
            module.give_pins[pin.name] = pin
        else:
            module.take_pins[pin.name] = pin
        if scope:
            while module:
                if module.parent:
                    if module.name in scope['values']:
                        if is_pin_give:
                            module.give_pins[pin.name] = pin
                        else:
                            module.take_pins[pin.name] = pin
                        break
                else:
                    # It's a root module
                    if 'root' in scope['values']:
                        if is_pin_give:
                            module.give_pins[pin.name] = pin
                        else:
                            module.take_pins[pin.name] = pin
                module = module.parent


from rest_framework import serializers

from ids.models import Id

class IdListSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Id
        fields = [
            "id",
            "url",
            "type",
            "verifiable_id",
            "verified_at",
            "created_at",
        ]
        extra_kwargs  = {
            "url": {
                "view_name": "ids:api:id-detail"
            }
        }


import turtle
s=turtle.Screen()
t=turtle.Turtle()

color=input ('Please pick a colour: ')
shape=input ('Please choose an object: ')
if color in('black','red','green','blue','cyan','yellow','magenta','brown'):
    t.fillcolor(color)
    if shape=='truck':
        t.begin_fill()
        t.dot (50)
        t.forward (300)
        t.dot (50)
        t.left(90)
        t.circle(150,180)
        t.end_fill()
        t.hideturtle()

    elif shape=='bird':

        t.begin_fill()
        t.circle(150,180)
        t.left(180)
        t.forward(25)
        t.left(130)
        t.forward (30)
        t.circle(25)
        t.penup()
        t.goto(0,0)
        t.pendown()
        t.left(45)
        t.circle(150,90)
        t.right(90)
        t.forward(30)
        t.goto(0,300)
        t.end_fill()
        t.hideturtle()
    elif shape=='flower':

        t.pensize(5)
        t.pencolor('green')
        t.right(90)
        t.forward(150)
        t.goto(0,0)
        t.pensize(15)
        t.pencolor('purple')
        t.begin_fill()
        t.circle(50,90)
        t.left(90)
        t.circle(50,90)
#makes one pettle
        t.circle(50,90)
        t.left(90)
        t.circle(50,90)
        t.circle(50,90)
        t.left(90)
        t.circle(50,90)
        t.circle(50,90)
        t.left(90)
        t.circle(50,90)
        t.end_fill()
    #insert another step here for center
        t.dot(15,'yellow')
        t.penup()
        t.forward(150)
    #ask about writing name for base
        t.pencolor('black')
        t.fillcolor('brown')
        t.begin_fill()
        t.right(90)
        t.forward(30)
        t.pendown()
        t.left (135)
        t.forward (50)
        t.left(90)
        t.forward (50)
        t.left (135)
        t.end_fill()
        t.hideturtle()
    else :
        print('sorry try another shape')

else :
    print('sorry try another colour')


import pygame
import sys
import settings
import spacecraft
import game_functions as gf

def run_game():
  pygame.init()
  rocket_settings = settings.Settings()
  screen = pygame.display.set_mode((rocket_settings.height, rocket_settings.width))
  pygame.display.set_caption('Rocket Game')
  rocket = spacecraft.Spacecraft(screen)

  while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.quit
        gf.deal_with_up_events(event, rocket)
        gf.deal_with_down_events(event, rocket)
    rocket.moving_rocket()
    gf.update_screen(rocket_settings, screen, rocket)

run_game()


import torch.nn as nn
import torch

class ResBlock(nn.Module):
    def __init__(self, in_channel = 32, out_channel_1 = 128, out_channel_2 = 32, dilation = 1):
        super(ResBlock, self).__init__()
        # conv
        self.add_module("resblock_conv_1", nn.Conv2d(in_channel, out_channel_1, (3,3), dilation = dilation, padding='same'))
        self.add_module('resblock_act', nn.ReLU(inplace=True))
        self.add_module("resblock_conv_2", nn.Conv2d(out_channel_1, out_channel_2, (3,3), padding='same'))

    def forward(self, x):
        out = x
        for layer in self.children():
            out = layer(out)
        out += x
        return out

class DWAN_network(nn.Module):
    def __init__(self, img_channel=1):
        super(DWAN_network, self).__init__()
        self.in_channels = img_channel
        self.add_module("conv0", nn.Conv2d(self.in_channels, 32, (3,3), padding='same'))
        self.local_pathway = self.make_local_pathway()
        self.global_pathway = self.make_global_pathway()
        self.add_module("conv_final", nn.Conv2d(64, 1, (3,3), padding='same'))
        self.add_module("direct_conv",nn.Conv2d(self.in_channels,1,(3,3), padding = 'same'))

    def make_local_pathway(self):

        layers = []
        # 4 layers of resblock in local pathway
        for i in range(4):
            layers.append(ResBlock(in_channel = 32, out_channel_1 = 128, out_channel_2=32,dilation=1))
        return nn.Sequential(*layers)

    def make_global_pathway(self):
        layers = []
        for i in range(4):
            layers.append(ResBlock(in_channel = 32, out_channel_1 = 128, out_channel_2=32,dilation=2**i))
        return nn.Sequential(*layers)

    def forward(self, x):
        # for test purpose only
        out0 = self.direct_conv(x)
        x1 = self.conv0(x)
        # local_pathway

        local_out = self.local_pathway(x1)
        # global pathway
        global_out = self.global_pathway(x1)
        # concatenation
        x2 = torch.cat((local_out, global_out),1) # need to check the concatenation dimension
        # conv final
        out1 = self.conv_final(x2)
        out = out0 + out1

        return out

# test
'''
my_DWAN = DWAN_network(img_channel=3)
print(sum(p.numel() for p in my_DWAN.parameters()))
'''


import base64
import csv
import json
import typing
from datetime import datetime

from Crypto.Cipher import AES
from Crypto.Util import Padding
from starlette.responses import Response

class PrettyJSONResponse(Response):
    media_type = "application/json"

    def render(self, content: typing.Any) -> bytes:
        return json.dumps(
            content,
            ensure_ascii=False,
            allow_nan=False,
            indent=4,
            separators=(", ", ": "),
        ).encode("utf-8")

def int_to_datestr(value):
    s = str(value)
    fecnac = datetime(year=int(s[0:4]), month=int(s[4:6]), day=int(s[6:8]))
    return fecnac.strftime("%d/%m/%Y")

def str_to_datestr(value):
    return datetime.strptime(value, "%Y-%m-%d").strftime("%d/%m/%Y")

def encrypt_param(param, variant="ruc"):
    encryption_key = base64.b64decode("aCIbjMuVGtwF8nlSKoPydE==")
    text = json.dumps({variant: param}).encode()
    text_padded = Padding.pad(text, AES.block_size)
    iv = base64.b64decode("JAwlt7SNbYLycmPRqeDFou==")
    cipher = AES.new(encryption_key, AES.MODE_CBC, iv)
    cipher_enc = cipher.encrypt(text_padded)
    return base64.b64encode(cipher_enc).decode()

def get_delimitter(buffer):
    try:
        dialect = csv.Sniffer().sniff(buffer.readline(), delimiters=";,")
        return dialect.delimiter
    except csv.Error:
        return "\t"


import numpy as np

lookup = {0: " ", 1: ".", 2: "+", -1: "o"}

def print_grid(grid, size):
    grid[size // 2, size // 2] = -1
    for i in range(size):
        for j in range(size):
            print(lookup[grid[i][j]], end="")
        print()

def distance(a, b):
    return np.abs(a - b).sum()

def follow_wire_path(central_port, wire):
    y, x = central_port
    coords = set([])

    for instructions in wire:
        direction, magnitude = instructions[0], int(instructions[1:])
        if direction == "U":
            for y1 in range(y - magnitude, y):
                coords.add((y1, x))
            y -= magnitude
        elif direction == "D":
            for y1 in range(y, y + magnitude):
                coords.add((y1, x))
            y += magnitude
        elif direction == "R":
            for x1 in range(x, x + magnitude):
                coords.add((y, x1))
            x += magnitude
        else:
            for x1 in range(x - magnitude, x):
                coords.add((y, x1))
            x -= magnitude

    return coords

def main():
    central_port = np.array((0, 0))

    with open("input.txt", "r") as fr:
        lines = fr.readlines()
        wires = [line.strip().split(",") for line in lines]
        wire1, wire2 = wires

        wire1_coords = follow_wire_path(central_port, wire1)
        wire2_coords = follow_wire_path(central_port, wire2)
        common = wire1_coords.intersection(wire2_coords)

        distances = []
        for coord in common:
            dist = distance(np.array(coord), central_port)
            distances.append(dist)

        print(
            f"The Manhattan distance from the central port to the closest intersection is {min(distances)}"
        )

if __name__ == "__main__":
    main()


import time

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager

chrome_opt = webdriver.ChromeOptions()
prefs = {"profile.default_content_Setting_values.notification":2}
chrome_opt.add_experimental_option("prefs",prefs)
chrome_opt.headless = False

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()),options=chrome_opt)
driver.get("https://www.espn.in/soccer/table/_/league/eng.1")
driver.implicitly_wait(2)
driver.maximize_window()
print(driver.title)
time.sleep(5)
r = driver.find_elements(By.XPATH,'//tbody/tr')
c= driver.find_elements(By.XPATH,'//tbody/tr[1]/td')
rows = len(r)
cols = len(c)
print(rows, ' & ', cols)

for row in range(1, rows):
    for col in range(1,cols):
        print(driver.find_element(By.XPATH,f'//tbody/tr[{row}]/td[{col}]').text, end=' ')
    print()

S = lambda X: driver.execute_script('return document.body.parentNode.scroll' +X)
driver.set_window_size(S('Width'),S('Height'))
driver.find_element(By.TAG_NAME,'body').screenshot('./screenshots/fullpage.png')
driver.close()


import time

MAX = 750
MIN = 0

def gen_motor_values(step):
    nums = [MIN] * 4
    while 1:
        while nums[0] + step < MAX:
            for i, num in enumerate(nums):
                nums[i] += step
            yield nums

        while nums[0] - step > MIN:
            for i, num in enumerate(nums):
                nums[i] -= step
            yield nums

# while 1:
#     for nums in gen_motor_values(4):
#         line = ','.join([str(i) for i in nums])
#         with open('dat.txt', 'w') as f:
#             f.write(line)
#         time.sleep(0.05)
def gen_data():
    with open('data_time.txt') as f:
        lines = f.readlines()
        i = 0
        while 1:
            while i < len(lines):
                yield lines[i]
                i += 1
            while i > 0:
                i -= 1
                yield lines[i]

while 1:
    for d in gen_data():
        with open('dat.txt', 'w') as f:
            f.write(d)
        time.sleep(0.1)