from collections import OrderedDict

import numpy as np
import pytorch_lightning as pl
import torch
import torchvision
from nowcasting_utils.models.loss import get_loss
from pl_bolts.optimizers.lr_scheduler import LinearWarmupCosineAnnealingLR
from torch.optim import lr_scheduler

from satflow.models import ConvLSTM, R2U_Net
from satflow.models.gan import GANLoss, define_discriminator, define_generator
from satflow.models.layers import ConditionTime

class CloudGAN(pl.LightningModule):
    def __init__(
        self,
        forecast_steps: int = 48,
        input_channels: int = 12,
        lr: float = 0.0002,
        beta1: float = 0.5,
        beta2: float = 0.999,
        num_filters: int = 64,
        generator_model: str = "runet",
        norm: str = "batch",
        use_dropout: bool = False,
        discriminator_model: str = "enhanced",
        discriminator_layers: int = 0,
        loss: str = "vanilla",
        scheduler: str = "plateau",
        lr_epochs: int = 10,
        lambda_l1: float = 100.0,
        l1_loss: str = "l1",
        channels_per_timestep: int = 12,
        condition_time: bool = False,
        pretrained: bool = False,
    ):
        """
        Creates CloudGAN, based off of https://www.climatechange.ai/papers/icml2021/54
        Changes include allowing outputs for all timesteps, optionally conditioning on time
        for single timestep output

        Args:
            forecast_steps: Number of timesteps to forecast
            input_channels: Number of input channels
            lr: Learning Rate
            beta1: optimizer beta1
            beta2: optimizer beta2 value
            num_filters: Number of filters in generator
            generator_model: Generator name
            norm: Norm type
            use_dropout: Whether to use dropout
            discriminator_model: model for discriminator, one of options in define_discriminator
            discriminator_layers: Number of layers in discriminator, only for NLayerDiscriminator
            loss: Loss function, described in GANLoss
            scheduler: LR scheduler name
            lr_epochs: Epochs for LR scheduler
            lambda_l1: Lambda for L1 loss, from slides recommended between 5-200
            l1_loss: Loss to use for the L1 in the slides, default is L1, also SSIM is available
            channels_per_timestep: Channels per input timestep
            condition_time: Whether to condition on a future timestep, similar to MetNet
        """
        super().__init__()
        self.lr = lr
        self.b1 = beta1
        self.b2 = beta2
        self.loss = loss
        self.lambda_l1 = lambda_l1
        self.lr_epochs = lr_epochs
        self.lr_method = scheduler
        self.forecast_steps = forecast_steps
        self.input_channels = input_channels
        self.output_channels = forecast_steps * channels_per_timestep
        self.channels_per_timestep = channels_per_timestep
        self.condition_time = condition_time
        if condition_time:
            self.ct = ConditionTime(forecast_steps)
        # define networks (both generator and discriminator)
        gen_input_channels = (
            input_channels  # + forecast_steps if condition_time else input_channels
        )
        self.recurrent = (
            False  # Does the generator generate all timesteps at once, or a single one at a time?
        )
        if generator_model == "runet":
            generator_model = R2U_Net(gen_input_channels, self.output_channels, t=3)
        elif generator_model == "convlstm":
            self.recurrent = True  # ConvLSTM makes a list of output timesteps
            generator_model = ConvLSTM(
                gen_input_channels, hidden_dim=num_filters, out_channels=self.channels_per_timestep
            )
        self.generator = define_generator(
            gen_input_channels,
            self.output_channels,
            num_filters,
            generator_model,
            norm,
            use_dropout,
        )
        if generator_model == "convlstm":
            # Timestep x C x H x W inputs/outputs, need to flatten for discriminator
            # TODO Add Discriminator that can use timesteps
            self.flatten_generator = True
        else:
            self.flatten_generator = False

        self.discriminator = define_discriminator(
            self.channels_per_timestep if condition_time else self.output_channels,
            num_filters,
            discriminator_model,
            discriminator_layers,
            norm,
        )

        # define loss functions
        self.criterionGAN = GANLoss(loss)
        self.criterionL1 = get_loss(l1_loss, channels=self.channels_per_timestep)
        self.save_hyperparameters()

    def train_per_timestep(
        self, images: torch.Tensor, future_images: torch.Tensor, optimizer_idx: int, batch_idx: int
    ):
        """
        For training with conditioning on time, so when the model is giving a single output

        This goes through every timestep in forecast_steps and runs the training
        Args:
            images: (Batch, Timestep, Channels, Width, Height)
            future_images: (Batch, Timestep, Channels, Width, Height)
            optimizer_idx: int, the optiimizer to use

        Returns:

        """

        if optimizer_idx == 0:
            # generate images
            total_loss = 0
            vis_step = True if np.random.random() < 0.01 else False
            generated_images = self(
                images, forecast_steps=self.forecast_steps
            )  # (Batch, Channel, Width, Height)
            for i in range(self.forecast_steps):
                # x = self.ct.forward(images, i)  # Condition on future timestep
                # fake = self(x, forecast_steps=i + 1)  # (Batch, Channel, Width, Height)
                fake = generated_images[:, :, i, :, :]  # Only take the one at the end
                if vis_step:
                    self.visualize_step(
                        images, future_images[:, i, :, :], fake, batch_idx, step=f"train_frame_{i}"
                    )
                # adversarial loss is binary cross-entropy
                gan_loss = self.criterionGAN(self.discriminator(fake), True)
                # Only L1 loss on the given timestep
                l1_loss = self.criterionL1(fake, future_images[:, i, :, :]) * self.lambda_l1
                self.log(f"train/frame_{i}_l1_loss", l1_loss)
                g_loss = gan_loss + l1_loss
                total_loss += g_loss
            g_loss = total_loss / self.forecast_steps  # Get the mean loss over all timesteps
            tqdm_dict = {"g_loss": g_loss}
            output = OrderedDict({"loss": g_loss, "progress_bar": tqdm_dict, "log": tqdm_dict})
            self.log_dict({"train/g_loss": g_loss})
            return output

        # train discriminator
        if optimizer_idx == 1:
            # Measure discriminator's ability to classify real from generated samples
            # generate images
            total_loss = 0
            generated_images = self(
                images, forecast_steps=self.forecast_steps
            )  # (Batch, Channel, Width, Height)
            for i in range(self.forecast_steps):
                # x = self.ct.forward(images, i)  # Condition on future timestep
                # fake = self(x, forecast_steps=i + 1)  # (Batch, Channel, Width, Height)
                fake = generated_images[:, :, i, :, :]  # Only take the one at the end
                real_loss = self.criterionGAN(self.discriminator(future_images[:, i, :, :]), True)
                # adversarial loss is binary cross-entropy
                fake_loss = self.criterionGAN(self.discriminator(fake), False)
                # Only L1 loss on the given timestep
                # discriminator loss is the average of these
                d_loss = (real_loss + fake_loss) / 2
                self.log(f"train/frame_{i}_d_loss", d_loss)
                total_loss += d_loss
            d_loss = total_loss / self.forecast_steps  # Average of the per-timestep loss
            tqdm_dict = {"d_loss": d_loss}
            output = OrderedDict({"loss": d_loss, "progress_bar": tqdm_dict, "log": tqdm_dict})
            self.log_dict({"train/d_loss": d_loss})
            return output

    def train_all_timestep(
        self, images: torch.Tensor, future_images: torch.Tensor, optimizer_idx: int, batch_idx: int
    ):
        """
        Train on all timesteps, instead of single timestep at a time. No conditioning on future timestep
        Args:
            images:
            future_images:
            optimizer_idx:
            batch_idx:

        Returns:

        """
        if optimizer_idx == 0:
            # generate images
            generated_images = self(images)
            fake = torch.cat((images, generated_images), 1)
            # log sampled images
            if np.random.random() < 0.01:
                self.visualize_step(
                    images, future_images, generated_images, batch_idx, step="train"
                )

            # adversarial loss is binary cross-entropy
            gan_loss = self.criterionGAN(self.discriminator(fake), True)
            l1_loss = self.criterionL1(generated_images, future_images) * self.lambda_l1
            g_loss = gan_loss + l1_loss
            tqdm_dict = {"g_loss": g_loss}
            output = OrderedDict({"loss": g_loss, "progress_bar": tqdm_dict, "log": tqdm_dict})
            self.log_dict({"train/g_loss": g_loss})
            return output

        # train discriminator
        if optimizer_idx == 1:
            # Measure discriminator's ability to classify real from generated samples

            # how well can it label as real?
            real = torch.cat((images, future_images), 1)
            real_loss = self.criterionGAN(self.discriminator(real), True)

            # how well can it label as fake?
            gen_output = self(images)
            fake = torch.cat((images, gen_output), 1)
            fake_loss = self.criterionGAN(self.discriminator(fake), False)

            # discriminator loss is the average of these
            d_loss = (real_loss + fake_loss) / 2
            tqdm_dict = {"d_loss": d_loss}
            output = OrderedDict({"loss": d_loss, "progress_bar": tqdm_dict, "log": tqdm_dict})
            self.log_dict({"train/d_loss": d_loss})
            return output

    def training_step(self, batch, batch_idx, optimizer_idx):
        images, future_images = batch
        if self.condition_time:
            return self.train_per_timestep(images, future_images, optimizer_idx, batch_idx)
        return self.train_all_timestep(images, future_images, optimizer_idx, batch_idx)

    def val_all_timestep(self, images, future_images, batch_idx):
        # generate images
        generated_images = self(images)
        fake = torch.cat((images, generated_images), 1)
        # log sampled images
        if np.random.random() < 0.01:
            self.visualize_step(images, future_images, generated_images, batch_idx, step="val")
        # adversarial loss is binary cross-entropy
        gan_loss = self.criterionGAN(self.discriminator(fake), True)
        l1_loss = self.criterionL1(generated_images, future_images) * self.lambda_l1
        g_loss = gan_loss + l1_loss
        # how well can it label as real?
        real = torch.cat((images, future_images), 1)
        real_loss = self.criterionGAN(self.discriminator(real), True)

        # how well can it label as fake?
        fake_loss = self.criterionGAN(self.discriminator(fake), True)

        # discriminator loss is the average of these
        d_loss = (real_loss + fake_loss) / 2
        tqdm_dict = {"d_loss": d_loss}
        output = OrderedDict(
            {
                "val/discriminator_loss": d_loss,
                "val/generator_loss": g_loss,
                "progress_bar": tqdm_dict,
                "log": tqdm_dict,
            }
        )
        self.log_dict({"val/d_loss": d_loss, "val/g_loss": g_loss, "val/loss": d_loss + g_loss})
        return output

    def val_per_timestep(self, images, future_images, batch_idx):
        total_g_loss = 0
        total_d_loss = 0
        vis_step = True if np.random.random() < 0.01 else False
        generated_images = self(
            images, forecast_steps=self.forecast_steps
        )  # (Batch, Channel, Width, Height)
        for i in range(self.forecast_steps):
            # x = self.ct.forward(images, i)  # Condition on future timestep
            fake = generated_images[:, :, i, :, :]  # Only take the one at the end
            if vis_step:
                self.visualize_step(
                    images, future_images[:, i, :, :], fake, batch_idx, step=f"val_frame_{i}"
                )
            # adversarial loss is binary cross-entropy
            gan_loss = self.criterionGAN(self.discriminator(fake), True)
            # Only L1 loss on the given timestep
            l1_loss = self.criterionL1(fake, future_images[:, i, :, :]) * self.lambda_l1
            real_loss = self.criterionGAN(self.discriminator(future_images[:, i, :, :]), True)
            # adversarial loss is binary cross-entropy
            fake_loss = self.criterionGAN(self.discriminator(fake), False)
            # Only L1 loss on the given timestep
            # discriminator loss is the average of these
            d_loss = (real_loss + fake_loss) / 2
            self.log(f"val/frame_{i}_d_loss", d_loss)
            total_d_loss += d_loss
            self.log(f"val/frame_{i}_l1_loss", l1_loss)
            g_loss = gan_loss + l1_loss
            total_g_loss += g_loss
        g_loss = total_g_loss / self.forecast_steps
        d_loss = total_d_loss / self.forecast_steps
        loss = g_loss + d_loss
        tqdm_dict = {"loss": loss}
        output = OrderedDict(
            {
                "val/discriminator_loss": d_loss,
                "val/generator_loss": g_loss,
                "progress_bar": tqdm_dict,
                "log": tqdm_dict,
            }
        )
        self.log_dict({"val/d_loss": d_loss, "val/g_loss": g_loss, "val/loss": d_loss + g_loss})
        return output

    def validation_step(self, batch, batch_idx):
        images, future_images = batch
        if self.condition_time:
            return self.val_per_timestep(images, future_images, batch_idx)
        return self.val_all_timestep(images, future_images, batch_idx)

    def forward(self, x, **kwargs):
        return self.generator.forward(x, **kwargs)

    def configure_optimizers(self):
        lr = self.lr
        b1 = self.b1
        b2 = self.b2

        opt_g = torch.optim.Adam(self.generator.parameters(), lr=lr, betas=(b1, b2))
        opt_d = torch.optim.Adam(self.discriminator.parameters(), lr=lr, betas=(b1, b2))
        if self.lr_method == "plateau":
            g_scheduler = lr_scheduler.ReduceLROnPlateau(
                opt_g, mode="min", factor=0.2, threshold=0.01, patience=10
            )
            d_scheduler = lr_scheduler.ReduceLROnPlateau(
                opt_d, mode="min", factor=0.2, threshold=0.01, patience=10
            )
        elif self.lr_method == "cosine":
            g_scheduler = lr_scheduler.CosineAnnealingLR(opt_g, T_max=self.lr_epochs, eta_min=0)
            d_scheduler = lr_scheduler.CosineAnnealingLR(opt_d, T_max=self.lr_epochs, eta_min=0)
        elif self.lr_method == "warmup":
            g_scheduler = LinearWarmupCosineAnnealingLR(
                opt_g, warmup_epochs=self.lr_epochs, max_epochs=100
            )
            d_scheduler = LinearWarmupCosineAnnealingLR(
                opt_d, warmup_epochs=self.lr_epochs, max_epochs=100
            )
        else:
            return NotImplementedError("learning rate policy is not implemented")

        return [opt_g, opt_d], [g_scheduler, d_scheduler]

    def visualize_step(
        self, x: torch.Tensor, y: torch.Tensor, y_hat: torch.Tensor, batch_idx: int, step: str
    ):
        # the logger you used (in this case tensorboard)
        tensorboard = self.logger.experiment[0]
        # Image input is either (B, C, H, W) or (B, T, C, H, W)
        if len(x.shape) == 5:
            # Timesteps per channel
            images = x[0].cpu().detach()
            for i, t in enumerate(images):  # Now would be (C, H, W)
                t = [torch.unsqueeze(img, dim=0) for img in t]
                image_grid = torchvision.utils.make_grid(t, nrow=self.channels_per_timestep)
                tensorboard.add_image(
                    f"{step}/Input_Image_Stack_Frame_{i}", image_grid, global_step=batch_idx
                )
        else:
            images = x[0].cpu().detach()
            images = [torch.unsqueeze(img, dim=0) for img in images]
            image_grid = torchvision.utils.make_grid(images, nrow=self.channels_per_timestep)
            tensorboard.add_image(f"{step}/Input_Image_Stack", image_grid, global_step=batch_idx)
        # In all cases, the output target and image are in (B, C, H, W) format
        images = y[0].cpu().detach()
        images = [torch.unsqueeze(img, dim=0) for img in images]
        image_grid = torchvision.utils.make_grid(images, nrow=12)
        tensorboard.add_image(f"{step}/Target_Image_Stack", image_grid, global_step=batch_idx)
        images = y_hat[0].cpu().detach()
        images = [torch.unsqueeze(img, dim=0) for img in images]
        image_grid = torchvision.utils.make_grid(images, nrow=12)
        tensorboard.add_image(f"{step}/Generated_Image_Stack", image_grid, global_step=batch_idx)


import modules.images as images
import modules.scripts as scripts
import modules.sd_samplers as sd_samplers
import gradio as gr
import os
import glob
import random
import operator

from modules.processing import process_images, StableDiffusionProcessingImg2Img, Processed
import cv2
import copy
import numpy as np
from PIL import Image
import time
import json

def resize_img(img, w, h):
    if img.shape[0] + img.shape[1] < h + w:
        interpolation = interpolation=cv2.INTER_CUBIC
    else:
        interpolation = interpolation=cv2.INTER_AREA

    return cv2.resize(img, (w, h), interpolation=interpolation)

def draw_grid(rows, image_list, x_labels, y_labels):
    ver_texts = [[images.GridAnnotation(y)] for y in y_labels]
    hor_texts = [[images.GridAnnotation(x)] for x in x_labels]

    w = image_list[0].width
    h = image_list[0].height

    grid = images.image_grid(image_list, rows=rows)
    grid = images.draw_grid_annotations(grid, w, h, hor_texts, ver_texts)

    return grid

class Script(scripts.Script):

# The title of the script. This is what will be displayed in the dropdown menu.
    def title(self):
        return "img2img for all method"

# Determines when the script should be shown in the dropdown menu via the
# returned value. As an example:
# is_img2img is True if the current tab is img2img, and False if it is txt2img.
# Thus, return is_img2img to only show the script on the img2img tab.

    def show(self, is_img2img):
        return is_img2img

# How the script's is displayed in the UI. See https://gradio.app/docs/#components
# for the different UI components you can use and how to create them.
# Most UI components can return a value, such as a boolean for a checkbox.
# The returned values are passed to the run method as parameters.

    def ui(self, is_img2img):
        input_dir = gr.Textbox(label='Input directory', lines=1)
        mask_dir = gr.Textbox(label='Mask directory(optional)', lines=1)
        output_dir = gr.Textbox(label='Output directory', lines=1)

        chs = ["All"]
        chs.extend([x.name for x in sd_samplers.samplers_for_img2img])
        sampler = gr.Dropdown(label='Sampling method', choices=chs, value=chs[1], type="value")
        step_and_cfg_list = gr.Textbox(label='[Sampling Steps, CFG Scale] List', lines=1, value="[20,7],[50,20]")
#        dstr_list = gr.Textbox(label='Denoising Strength List', lines=1, value="0.1,0.15,0.2,0.25,0.3,0.35")
        dstr_list = gr.Textbox(label='Denoising Strength List', lines=1, value="0.1,0.35")
        repeat_count = gr.Slider(minimum=1, maximum=10, step=1, value=3, label="Img2Img Repeat Count")
        inc_seed = gr.Slider(minimum=0, maximum=9999999, step=1, value=0, label="Add N to seed when repeating")

        return [input_dir, mask_dir, output_dir, sampler, step_and_cfg_list, dstr_list, repeat_count, inc_seed]

# This is where the additional processing is implemented. The parameters include
# self, the model object "p" (a StableDiffusionProcessing class, see
# processing.py), and the parameters returned by the ui method.
# Custom functions can be defined here, and additional libraries can be imported
# to be used in processing. The return value should be a Processed object, which is
# what is returned by the process_images method.

    def run(self, p:StableDiffusionProcessingImg2Img, input_dir, mask_dir, output_dir, sampler, step_and_cfg_list_str, dstr_list_str, img2img_repeat_count, inc_seed):
        args = locals()
        result = []

        if p.seed == -1:
            p.seed = int(random.randrange(4294967294))

        print(step_and_cfg_list_str)
        print(dstr_list_str)

        step_and_cfg_list = json.loads("[" + step_and_cfg_list_str + "]")
        dstr_list = json.loads("[" + dstr_list_str + "]")

        print(step_and_cfg_list)
        print(dstr_list)

        def get_img_filename(p:StableDiffusionProcessingImg2Img, basename, i):
            def get_step_str(steps):
                return "Step"+str(steps)
            def get_cfg_str(cfg):
                return "Cfg"+str(cfg)
            def get_dstr_str(dstr):
                return "Dstr"+str(dstr)
            def get_repeat_str(repeat):
                return "Repeat"+str(repeat+1)

            return basename + "_" + p.sampler_name +\
                    "_" + get_step_str(p.steps) +\
                    "_" + get_cfg_str(p.cfg_scale) +\
                    "_" + get_dstr_str(p.denoising_strength) +\
                    "_" + get_repeat_str(i) + ".png"

        def append_result(p:StableDiffusionProcessingImg2Img, i, imgname, saved_path, seed):
            item = {
                "base_img_name" : imgname,
                "sampler_name" : p.sampler_name,
                "steps" : p.steps,
                "cfg_scale" : p.cfg_scale,
                "denoising_strength" : p.denoising_strength,
                "repeat_count" : i + 1,
                "saved_path" : saved_path,
                "seed" : seed
            }
            result.append(item)

        def img2img_process(p:StableDiffusionProcessingImg2Img, i, img_path):
            _p = copy.copy(p)

            proc = process_images( _p )
            print(proc.seed)

            base_name = os.path.splitext(os.path.basename(img_path))[0]

            filename = get_img_filename( p, base_name, i )
            save_path = os.path.join( output_dir , filename )
            proc.images[0].save( save_path )

            append_result(p, i, base_name, save_path, proc.seed)

            return proc

        def img2img_for_denoising_strength(p:StableDiffusionProcessingImg2Img, img_path):
            mask = p.image_mask
            resized_mask = None

            for i in range(img2img_repeat_count):
                proc = img2img_process(p, i, img_path)
                p.init_images=[proc.images[0]]
                if mask is not None and resized_mask is None:
                    resized_mask = resize_img(np.array(mask) , proc.images[0].width, proc.images[0].height)
                    resized_mask = Image.fromarray(resized_mask)
                p.image_mask = resized_mask
                p.seed = (proc.seed + inc_seed)

            return proc

        def img2img_for_cfg_and_steps(p:StableDiffusionProcessingImg2Img, img_path):
            for ds in dstr_list:
                _p = copy.copy(p)
                _p.denoising_strength = ds
                proc = img2img_for_denoising_strength(_p, img_path)
            return proc

        def img2img_for_method(p:StableDiffusionProcessingImg2Img, img_path):
            for steps,cfg in step_and_cfg_list:
                _p = copy.copy(p)
                _p.steps = steps
                _p.cfg_scale = cfg
                proc = img2img_for_cfg_and_steps(_p, img_path)
            return proc

        ########### start

        if not os.path.isdir(input_dir):
            print("input_dir not found")
            return Processed()

        if not os.path.isdir(output_dir):
            print("output_dir not found")
            return Processed()

        if step_and_cfg_list == None or len(step_and_cfg_list) == 0:
            print("(Sampling Steps, CFG Scale) List cannot be parsed.")
            return Processed()

        if dstr_list == None or len(dstr_list) == 0:
            print("Denoising Strength List cannot be parsed.")
            return Processed()

        for s,c in step_and_cfg_list:
            if s == None or c == None:
                print("(Sampling Steps, CFG Scale) List cannot be parsed.2")
                return Processed()

        root_output_dir = output_dir = os.path.join(output_dir, time.strftime("%Y%m%d-%H%M%S"))
        os.makedirs(output_dir, exist_ok=False)
        output_dir = os.path.join(output_dir, "imgs")
        os.makedirs(output_dir, exist_ok=False)

        p.do_not_save_samples = True
        p.do_not_save_grid = True

        imgs = glob.glob( os.path.join(input_dir ,"*.png") )
        for img in imgs:

            image = Image.open(img)
            mask = None

            img_basename = os.path.basename(img)
            mask_path = os.path.join( mask_dir , img_basename )

            if os.path.isfile( mask_path ):
                mask = Image.open(mask_path)

            _p = copy.copy(p)
            _p.init_images=[image]
            _p.image_mask = mask

            if sampler == "All":
                for method in sd_samplers.samplers_for_img2img:
                    _p.sampler_name = method.name
                    proc = img2img_for_method(_p, img)
            else:
                _p.sampler_name = sampler
                proc = img2img_for_method(_p, img)

        ### param txt
        with open( os.path.join( root_output_dir ,"param.txt" ), "w") as f:
            f.write(proc.info)
        with open( os.path.join( root_output_dir ,"args.txt" ), "w") as f:
            f.write(str(args))
        with open( os.path.join( root_output_dir ,"output.txt" ), "w") as f:
            f.write(json.dumps(result, indent=4))

        ### create grid
        def create_grid_per_img(method_name, img_path):
            print("create grid per image")
            print(method_name)

            image_list = []
            image_name = os.path.splitext(os.path.basename(img_path))[0]

            items = list(filter(lambda d: d["base_img_name"] == image_name and d["sampler_name"] == method_name, result))
            items = sorted(items, key=operator.itemgetter("steps","cfg_scale","denoising_strength","repeat_count"))

            for item in items:
                image_list.append( Image.open(item["saved_path"]) )

            label_x = [ "repeat " + str(x+1) for x in range(img2img_repeat_count) ]

            items = list(filter(lambda d: d["repeat_count"] == 1, items))

            label_y = [ "steps :" + str(y["steps"]) +", cfg_scale :"+ str(y["cfg_scale"]) +", denoising_strength :"+ str(y["denoising_strength"]) for y in items]

            grid = draw_grid( len(label_y), image_list, label_x, label_y)

            save_path = os.path.join(root_output_dir, image_name + "_" + method_name + ".png")

            grid.save( save_path )

            return grid

        def create_grid(method_name, img_path_list):
            print("create grid")
            print(method_name)
            image_list = []

            tmp = Image.open(result[0]["saved_path"])
            width = tmp.width
            height = tmp.height

            items = list(filter(lambda d: d["sampler_name"] == method_name, result))
            items = sorted(items, key=operator.itemgetter("steps","cfg_scale","denoising_strength","repeat_count"))

            for img in img_path_list:
                image_list.append( Image.open(img).resize((width, height), Image.LANCZOS) )

            for item in items:
                image_list.append( Image.open(item["saved_path"]) )

            label_x = [ os.path.basename(img) for img in img_path_list ]

            image_name = os.path.splitext(os.path.basename(img_path_list[0]))[0]
            items = list(filter(lambda d: d["base_img_name"] == image_name, items))

            label_y = ["original"]
            label_y.extend([ "steps :" + str(y["steps"]) +", cfg_scale :"+ str(y["cfg_scale"]) +", denoising_strength :"+ str(y["denoising_strength"]) +", repeat :" + str(y["repeat_count"]) for y in items])

            grid = draw_grid( len(label_y), image_list, label_x, label_y)

            save_path = os.path.join(root_output_dir, method_name + ".png")

            grid.save( save_path )

            return grid

        def create_grid_all(img_path):
            print("create grid all")
            image_list = []

            tmp = Image.open(result[0]["saved_path"])
            width = tmp.width
            height = tmp.height

            image_name = os.path.splitext(os.path.basename(img_path))[0]

            items = list(filter(lambda d: d["base_img_name"] == image_name, result))
            items = sorted(items, key=operator.itemgetter("steps","cfg_scale","denoising_strength","repeat_count"))

            label_x = ["original"]
            label_x.extend([ method.name for method in sd_samplers.samplers_for_img2img ])

            label_items = list(filter(lambda d: d["sampler_name"] == "Euler a", items))
            label_y = ([ "steps :" + str(y["steps"]) +", cfg_scale :"+ str(y["cfg_scale"]) +", denoising_strength :"+ str(y["denoising_strength"]) +", repeat :" + str(y["repeat_count"]) for y in label_items])

            org_image = Image.open(img_path).resize((width, height), Image.LANCZOS)

            for index_item in label_items:
                for method in label_x:
                    if method == "original":
                        image_list.append( org_image )
                    else:
                        item = list(filter(lambda d: d["steps"] == index_item["steps"] and\
                                                d["cfg_scale"] == index_item["cfg_scale"] and\
                                                d["denoising_strength"] == index_item["denoising_strength"] and\
                                                d["repeat_count"] == index_item["repeat_count"] and\
                                                d["sampler_name"] == method \
                                                , items))[0]
                        image_list.append( Image.open(item["saved_path"]) )

            grid = draw_grid( len(label_y), image_list, label_x, label_y)

            save_path = os.path.join(root_output_dir, image_name + "_ALL.png")

            grid.save( save_path )

            grid = grid.resize((grid.width//3, grid.height//3), Image.LANCZOS)
            save_path = os.path.join(root_output_dir, image_name + "_small_ALL.png")
            grid.save( save_path )

            return grid

        # x ... repeat
        # y ... (step + cfg)*str

        for img in imgs:
            if sampler == "All":
                for method in sd_samplers.samplers_for_img2img:
                    grid = create_grid_per_img( method.name, img )
            else:
                grid = create_grid_per_img( sampler, img )

        if len(imgs) > 1:
            if sampler == "All":
                for method in sd_samplers.samplers_for_img2img:
                    grid = create_grid( method.name, imgs )
            else:
                grid = create_grid( sampler, imgs )

        if sampler == "All":
            grid = create_grid_all( imgs[0] )

#        proc.images[0] = grid

        return proc


import abc
import asyncio
import collections
import contextlib
import inspect
import logging.handlers
import signal
import sys
from typing import Awaitable, Callable, Dict, Generic, GenericMeta, Optional, Type, TypeVar, Union
import weakref

from .config import Config

LOG = logging.getLogger(__name__)

Self = TypeVar('Self')

class Runnable(collections.Awaitable):
    """
    Runnable is a convenience wrapper around asyncio.Task with distinct flow:
      - `initialize` is called only once, and is the best place to allocate task-related resources or abort execution
        entirely due to some precondition
      - `main` is where all the work happens
      - `cleanup` is called only once after main returns or raises

    Each runnable has an associated `name` (ivar) and `LOG` (cvar):
      - `name` defaults to classname and can be customized via `__init__`
      - `LOG` defaults to __module__.__qualname__ (e.g. my.app.Service) and can be customized as any other cvar

    As asyncio's Task, Runnable can either complete by return, due to exception or by being cancelled.
    Completion due to exception (including cancellation) is considered an abnormal abort.

    A concept of abortion is just a convenience to distinguish non-clean exits and otherwise identical to stop.

    >>> class Service(Runnable):
    >>>     async def initialize(self):
    >>>         # It's safe to allocate resources that require event loop here
    >>>         if not await allocate_resources():
    >>>             self.abort()
    >>>             return
    >>>
    >>>         super().initialize()
    >>>
    >>>     async def main(self):
    >>>         await do_some_work()
    >>>
    >>>     async def cleanup(self, exc_type=None, exc_val=None, exc_tb=None):
    >>>         await dealloc_resources()
    >>>
    >>> s = Service(name='MyService')
    >>> await s.start()

    @cvar LOG: For each subclass new LOG variable is automatically created unless explicitly set.
    """
    LOG: logging.Logger = LOG.getChild('Runnable')

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)

        if 'LOG' not in cls.__dict__:
            cls.LOG = logging.getLogger('{}.{}'.format(cls.__module__, cls.__qualname__))

    def __init__(self, *, name: str = None) -> None:
        self._name = name or type(self).__name__

        self._run_f = None

        self._initialize_f = None
        self._main_f = None
        self._cleanup_f = None

        self._should_stop = False
        self._is_initialized = False
        self._is_aborted = False

    @property
    def name(self) -> str:
        """
        Name of the runnable.
        """
        return self._name

    @property
    def should_stop(self) -> bool:
        """
        Whether runnable should stop.
        """
        return self._should_stop

    @property
    def is_initialized(self) -> bool:
        """
        Whether runnable is initialized.

        Can be used to detect errors during initialization.
        """
        return self._is_initialized

    @property
    def is_aborted(self) -> bool:
        """
        Whether runnable is aborted, e.g. due to exception.
        """
        return self._is_aborted

    @property
    def is_started(self) -> bool:
        """
        Whether task is started.
        """
        return self._run_f is not None

    @property
    def is_alive(self) -> bool:
        """
        Whether task was started and still running.
        """
        return self._run_f and not self._run_f.done()

    @property
    def is_done(self) -> bool:
        """
        Whether task was started and completed its execution.
        """
        return self._run_f and self._run_f.done()

    async def initialize(self) -> None:
        """
        Convenience method that's called only once before `main`.

        Subclasses must call super(), usually at the end of the custom implementation.

        If abort or stop is called, neither main nor cleanup will be called and underlying task will finish
        as soon as possible.

        @see: abort
        """
        self.LOG.debug("\"%s\" initialized.", self.name)
        self._is_initialized = True

    @abc.abstractmethod
    async def main(self):
        """
        Subclasses must implement this method.

        @see: initialize
        @see: cleanup
        """
        pass

    async def cleanup(self, exc_type=None, exc_val=None, exc_tb=None) -> None:
        """
        Convenience method that's called only once after `main` exits.
        """
        pass

    def start(self: Self, *, loop: asyncio.AbstractEventLoop = None) -> Self:
        """
        Schedule runnable execution.

        Create and configure underlying asyncio.Task.

        @raise RuntimeError: If started more than once.
        """
        if self._run_f:
            raise RuntimeError(f"\"{self.name}\" can only be started once")

        self.LOG.debug("\"%s\" started.", self.name)
        self._run_f = asyncio.ensure_future(self.run(), loop=loop)
        self._run_f.add_done_callback(self.on_run_done)

        if self.should_stop:
            self._run_f.cancel()

        return self

    async def run(self):
        self._initialize_f = asyncio.ensure_future(self.initialize())
        self._initialize_f.add_done_callback(self.on_initialize_done)
        await self._initialize_f

        if not self.is_initialized and not self.should_stop:
            raise NotImplementedError("either super or abort()/stop() must be called in overridden initialize()")
        elif self.should_stop:
            return

        try:
            self._main_f = asyncio.ensure_future(self.main())
            self._main_f.add_done_callback(self.on_main_done)
            result = await self._main_f
        except:
            self._cleanup_f = asyncio.ensure_future(self.cleanup(*sys.exc_info()))
            self._cleanup_f.add_done_callback(self.on_cleanup_done)
            await self._cleanup_f
            raise
        else:
            self._cleanup_f = asyncio.ensure_future(self.cleanup())
            self._cleanup_f.add_done_callback(self.on_cleanup_done)
            await self._cleanup_f

        return result

    def stop(self) -> None:
        """
        Stop runnable by cancelling wrapped task.
        """
        self.LOG.debug("\"%s\" stopped.", self.name)

        if not self._should_stop:
            self._should_stop = True

            if self._run_f:
                self._run_f.cancel()

    def abort(self) -> None:
        """
        Same as stop, but sets the abort flag.

        @see: stop
        """
        self.LOG.debug("\"%s\" aborted.", self.name)
        self._is_aborted = True
        self.stop()

    def on_run_done(self, f: asyncio.Task) -> None:
        """
        Called when the run task is done.
        """
        assert f == self._run_f

        if self._run_f.cancelled():
            if not self._should_stop:
                self.LOG.info("\"%s\" task was manually cancelled.", self.name)
        elif self._run_f.exception() is not None:
            self.LOG.exception("\"%s\" task failed with exception:", self.name, exc_info=self._run_f.exception())
            self._is_aborted = True
        else:
            self.LOG.debug("\"%s\" task finished.", self.name)

        self._should_stop = True

    def on_initialize_done(self, f: asyncio.Task) -> None:
        """
        Called when the initialize task is done.
        """
        assert f == self._initialize_f

    def on_main_done(self, f: asyncio.Task) -> None:
        """
        Called when the main task is done.
        """
        assert f == self._main_f

    def on_cleanup_done(self, f: asyncio.Task) -> None:
        """
        Called when the cleanup task is done.
        """
        assert f == self._cleanup_f

    async def __aenter__(self):
        return self.start()

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        self.stop()

        with contextlib.suppress(asyncio.CancelledError):
            await self

    def __await__(self):
        if self._run_f:
            return self._run_f.__await__()
        else:
            raise RuntimeError(f"\"{self.name}\" is not running")

    def __repr__(self):
        return '<{}(name={})>'.format(type(self).__name__, self.name)

    def __del__(self):
        if getattr(self, '_run_f', None) and not self._run_f.done():
            self.LOG.error("\"%s\" is destroyed with pending task.", self.name)

AppType = TypeVar('AppType', bound='App')
ConfigType = TypeVar('ConfigType', bound=Config)
ServiceType = TypeVar('ServiceType', bound='Service')

class App(Runnable, Generic[ConfigType]):
    """
    App is the root runnable for the application.

    It's semantics is alike Thread: user can either subclass it and override main
    or provide a callable that returns a coroutine.

    App handles the SIGINT and SIGTERM signals by stopping itself.

    >>> class MyApp(App):
    >>>     async def main(self):
    >>>         a_service = ...
    >>>         b_service = ...
    >>>         await asyncio.gather(a_service, b_service)
    >>>
    >>> MyApp().exec()

    Alternatively:

    >>> async with App():
    >>>     a_service = ...
    >>>     b_service = ...
    >>>     await asyncio.gather(a_service, b_service)
    """
    _current_apps: Dict[weakref.ReferenceType, 'App'] = weakref.WeakValueDictionary()

    @classmethod
    def current_app(cls: Type[AppType], loop: asyncio.AbstractEventLoop = None) -> Optional[AppType]:
        """
        Return App for the current event loop.
        """
        try:
            loop = loop or asyncio.get_event_loop()
            loop_ref = weakref.ref(loop)
        except RuntimeError:
            cls.LOG.warning("There is no current asyncio event loop.")
            return None

        app = cls._current_apps.get(loop_ref)

        if app is None:
            cls.LOG.debug("There is no active App in event loop %s.", loop)

        return app

    def __init_subclass__(cls, **kwargs):
        super(GenericMeta, cls).__setattr__('_gorg', cls)
        super().__init_subclass__(**kwargs)

    def __init__(self, target: Union[Callable[[], Awaitable], Runnable] = None, *, config: ConfigType = None, name: str = None) -> None:
        """
        @param target: Either coroutine or Runnable that will be awaited. If None, main must be overridden.
        """
        super().__init__(name=name)
        self._target = target
        self._config = config
        self._is_context = False
        self._loop_ref = None

    @property
    def config(self) -> Optional[ConfigType]:
        return self._config

    def exec(self, *, loop: asyncio.AbstractEventLoop = None):
        """
        Convenience method to start and await completion of the application.

        CancelledError is ignored as an expected way to complete execution.
        """
        loop = loop or asyncio.get_event_loop()

        try:
            t = self.start(loop=loop)
            return loop.run_until_complete(t)
        except asyncio.CancelledError:
            self.LOG.info("\"%s\" is cancelled.", self.name)

    #{ Runnable

    def start(self, *, loop=None):
        loop = loop or asyncio.get_event_loop()
        self._loop_ref = weakref.ref(loop, lambda ref: self.__class__._current_apps.pop(ref, None))

        if self._loop_ref in self.__class__._current_apps:
            raise RuntimeError("only one app can be active in an event loop")
        else:
            self.__class__._current_apps[self._loop_ref] = self

        return super().start(loop=loop)

    def on_run_done(self, f):
        super().on_run_done(f)

        if not self._is_context:
            del self.__class__._current_apps[self._loop_ref]

    async def initialize(self):
        await super().initialize()

        loop = asyncio.get_event_loop()

        try:
            loop.add_signal_handler(signal.SIGINT, self.stop)
            loop.add_signal_handler(signal.SIGTERM, self.stop)
        except NotImplementedError:
            self.LOG.debug("%s does not implement add_signal_handler", loop)

    async def main(self):
        if self._target:
            if isinstance(self._target, Runnable) and not self._target.is_started:
                return await self._target.start()
            elif inspect.isawaitable(self._target):
                return await self._target
            else:
                return await asyncio.ensure_future(self._target())

    async def __aenter__(self):
        r = await super().__aenter__()
        self._is_context = True
        return r

    async def __aexit__(self, *args, **kwargs):
        try:
            return await super().__aexit__(*args, **kwargs)
        finally:
            del self.__class__._current_apps[self._loop_ref]
            self._is_context = False
    #}

class Service(Runnable, Generic[AppType, ConfigType]):
    """
    Service is an asynchronous task for the app.
    """
    def __init_subclass__(cls, **kwargs):
        super(GenericMeta, cls).__setattr__('_gorg', cls)
        super().__init_subclass__(**kwargs)

    def __init__(self, *, app: AppType = None, config: ConfigType = None, name: str = None) -> None:
        """
        @param app: App that owns the service. If None, will be resolved at the beginning of the service's execution.
        @param config: Custom config.
        """
        super().__init__(name=name)
        self._app = app
        self._config = config

    @property
    def app(self) -> Optional[AppType]:
        return self._app

    @property
    def config(self) -> Optional[ConfigType]:
        return self._config or self.app.config

    #{ Runnable

    async def run(self, *args, **kwargs):
        current_app = App.current_app()

        if current_app is None:
            raise RuntimeError(f"{self.name} must run inside an app")
        elif self._app is None:
            self._app = current_app
        elif self._app != current_app:
            raise RuntimeError(f"{self.name} should run in {self._app.name} but runs in {current_app.name} instead")

        return await super().run(*args, **kwargs)

    #}


#!/usr/bin/env python
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.api import xmpp
from google.appengine.api import mail
from google.appengine.ext.webapp.mail_handlers import InboundMailHandler
from google.appengine.ext import db
from google.appengine.ext.webapp import template
from uuid import uuid4
from email.utils import *
from datetime import *
import re
import logging
import os

class Ping(db.Model):
    uuid = db.StringProperty()
    name = db.StringProperty()
    date_sent = db.DateTimeProperty()
    date_recv = db.DateTimeProperty()

def stats_for(service, count = 1000):
    q = Ping.all()
    q.filter("date_sent >", datetime.now() - timedelta(days=1))
    q.filter("name =", service)
    q.order("-date_sent")

    values = []
    for test in q.fetch(count):
        if test.date_recv != None:
            values.append((test.date_recv - test.date_sent).seconds)
        else:
            values.append((datetime.now() - test.date_sent).seconds)
    values.reverse()
    return values

def handle_response(body):
    uuid_search = re.search('\((.*)\)', body)
    if uuid_search == None: return
    uuid = uuid_search.group(1)
    ping = db.Query(Ping).filter('uuid = ', uuid).get()
    if ping == None: return
    ping.date_recv = datetime.now()
    ping.put()

class XMPPHandler(webapp.RequestHandler):
    def post(self):
        message = xmpp.Message(self.request.POST)
        sender = message.sender.split('/')[0]
        if sender == 'geochat@instedd.org':
            handle_response(message.body)

class MailHandler(InboundMailHandler):
    def receive(self, mail_message):
        sender = parseaddr(mail_message.sender)[1]
        if sender == 'geochat@instedd.org':
            handle_response(mail_message.body.decode())

class PingGeoChatXMPP(webapp.RequestHandler):
    def get(self):
        id = str(uuid4())
        xmpp.send_message('geochat@instedd.org', '.ping %s' % id)
        ping = Ping()
        ping.uuid = id
        ping.name = 'GeoChat (XMPP)'
        ping.date_sent = datetime.now()
        ping.put()

class PingGeoChatMail(webapp.RequestHandler):
    def get(self):
        id = str(uuid4())
        mail.send_mail(sender='test@insteddstatus.appspotmail.com',
                       to='geochat@instedd.org',
                       subject='Test Ping',
                       body='.ping %s' % id)
        ping = Ping()
        ping.uuid = id
        ping.name = 'GeoChat (Mail)'
        ping.date_sent = datetime.now()
        ping.put()

class Alert(webapp.RequestHandler):
    def get(self):
        # for stat in ['GeoChat (XMPP)', 'GeoChat (Mail)']:
        for stat in ['GeoChat (XMPP)']:
            stats = stats_for(stat, 2)
            if stats[0] > 60 and stats[1] > 60:
                mail.send_mail(sender='test@insteddstatus.appspotmail.com',
                               to='jwajnerman@manas.com.ar',
                               subject='Found huge delay in service: %s' % stat,
                               body='Found huge delay in service: %s' % stat)

class MainHandler(webapp.RequestHandler):
    def get(self):
        template_values = {
            'xmpp': ','.join([str(x) for x in stats_for('GeoChat (XMPP)')]),
            'mail': ','.join([str(x) for x in stats_for('GeoChat (Mail)')])
        }
        path = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(template.render(path, template_values))

def main():
    application = webapp.WSGIApplication([('/', MainHandler),
                                          ('/tasks/ping_geochat_xmpp', PingGeoChatXMPP),
                                          ('/tasks/ping_geochat_mail', PingGeoChatMail),
                                          ('/tasks/alert', Alert),
                                          ('/_ah/xmpp/message/chat/', XMPPHandler),
                                          ('/_ah/mail/.+', MailHandler)],
                                         debug=True)
    util.run_wsgi_app(application)

if __name__ == '__main__':
    main()


import itertools

# from hypothesis import assume
import hypothesis.strategies as st

from reef.rules import (
    MAX_HAND_SIZE,
    BOARD_SIZE,
    MAX_STACK_HEIGHT,
    # _is_gamestate_valid,
)
from reef import _types as types

@st.composite
def graphs(draw):
    r = draw(st.randoms(use_true_random=False))
    n_nodes = draw(st.integers(min_value=2, max_value=30))
    nodes = list(range(n_nodes))
    # for a graph with n nodes the maximum number of edges is n*(n-1)/2 if
    # every possible edge is chosen
    n_edges = draw(
        st.integers(min_value=0, max_value=int(n_nodes * (n_nodes - 1) / 2))
    )
    all_possible_edges = list(itertools.combinations(nodes, 2))
    edges = r.choices(all_possible_edges, k=n_edges)

    return nodes, edges

def action_strategy():

    placement_strat = st.tuples(
        st.integers(min_value=0, max_value=BOARD_SIZE - 1),
        st.integers(min_value=0, max_value=BOARD_SIZE - 1),
    )
    play_card_strategy = st.builds(
        types.PlayCardAction,
        hand_index=st.integers(min_value=0, max_value=MAX_HAND_SIZE),
        placement1=placement_strat,
        placement2=placement_strat,
    )
    return (
        st.builds(types.DrawCenterCardAction, st.integers(min_value=0))
        | st.builds(types.DrawDeckAction)
        | play_card_strategy
    )

@st.composite
def player_strategy(draw):
    board = []
    for _ in range(4):
        row = []
        for _ in range(4):
            height = draw(st.integers(min_value=0, max_value=MAX_STACK_HEIGHT))
            color = None if height == 0 else draw(st.sampled_from(types.Color))
            row.append(types.BoardStack(height, color))
        board.append(row)

    hand = draw(st.lists(st.builds(types.Card), max_size=MAX_HAND_SIZE))

    return types.PlayerState(
        hand=hand, board=board, score=draw(st.integers(min_value=0))
    )

@st.composite
def gamestate_strategy(draw):
    non_negative_int = st.integers(min_value=0)
    color_piles = st.fixed_dictionaries(
        {k: non_negative_int for k in list(types.Color)}
    )

    gamestate = draw(
        st.builds(
            types.GameState,
            color_piles=color_piles,
            # players hardcoded to 2
            players=st.lists(player_strategy(), min_size=2, max_size=2),
            player=st.integers(min_value=0, max_value=1),
        )
    )

    # assume(_is_gamestate_valid(gamestate))
    return gamestate


import tweepy

# 발급 완료된 key 입력
CONSUMER_KEY = '{}'
CONSUMER_SECRET = '{}'
ACCESS_TOKEN_KEY = '{}'
ACCESS_TOKEN_SECRET = '{}'

# 개인정보 인증 요청하는 Handler
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)

# 인증 요청
auth.set_access_token(ACCESS_TOKEN_KEY, ACCESS_TOKEN_SECRET)

# twitter API 사용하기 위한 준비
api = tweepy.API(auth)

# twitter API 를 사용하여 'son heung-min' 포함된 트윗 크롤링
# entities에서 'user_mentions', 'hashtags'추출
keyword = 'son heung-min'
tweets = api.search(keyword)
for tweet in tweets:
    print(tweet.entities['user_mentions'])
    print(tweet.entities['hashtags'])
    print(tweet.text)


import requests
from bs4 import BeautifulSoup
import smtplib
import time

URL = 'https://www.amazon.com/Apple-MacBook-1-8GHz-dual-core-Intel/dp/B07211W6X2/ref=sxin_0_ac_d_rm?ac_md=0-0-bWFj-ac_d_rm&keywords=mac&pd_rd_i=B07211W6X2&pd_rd_r=1148d494-32e6-45c5-8329-293cad65a21d&pd_rd_w=vyXCu&pd_rd_wg=vczrW&pf_rd_p=6d29ef56-fc35-411a-8a8e-7114f01518f7&pf_rd_r=A00BA435Q117CAM7KXJM&psc=1&qid=1576378749&smid=ATVPDKIKX0DER'

headers = {'User-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36' }

def check_price():
    page = requests.get(URL, headers = headers)

    soup = BeautifulSoup(page.content, 'html.parser')

    title = soup.find(id="productTitle").get_text()
    price = soup.find(id="priceblock_dealprice").get_text()
    converted_price = float(price[1:5])

    print(converted_price)
    print(title.strip())

    if(converted_price < 700):
        send_mail()

def send_mail():
    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.ehlo()
    server.starttls()
    server.ehlo()

    server.login()

    subject = 'price fell down!'
    body = 'Ceck the amazon link ' + URL
    msg = 'Subject:' + subject + body
    # msg = f'Subject: {subject}{body}'

    server.sendmail(
        'email',
        'email',
        msg
    )

    print('EMAIL HAS BEEN SENT')

    server.quit()

while(True):
    check_price()
    time.sleep(60 * 60 * 24)


import numpy
import torch
from torch import nn

device = 'cuda' if torch.cuda.is_available() else 'cpu'

def retrieve_model(path):
    model = torch.load(path, map_location=device)
    model.eval()
    return model

class SwarmNet(nn.Module):
    def __init__(self, agent_state_vector_length, predict_state_length):
        super(SwarmNet, self).__init__()
        self.conv_layer1 = nn.Conv1d(agent_state_vector_length, 32, kernel_size=3, groups=1)
        self.conv_layer2 = nn.Conv1d(32, 64, kernel_size=3, groups=1)
        self.conv_layer3 = nn.Conv1d(64, 32, kernel_size=3, groups=1)
        self.edge_state_mlp = nn.Linear(32 * 2, 32)
        self.edge_agg_mlp = nn.Linear(32, 32)
        self.node_updater_mlp = nn.Linear(32 * 2, 32)
        self.node_decoder_mlp = nn.Linear(32, predict_state_length)
        # self.node_decoder_mlp.requires_grad_(False)
        self.relu = nn.ReLU()
        self.scaler = None
        self.predictions_trained_to = 1
        self.predict_state_length = predict_state_length
        self.lowest_mse_this_horizon = 999999999999

    def conv_1d(self, x, predict_step):
        if predict_step == 0:
            agent_transpose = torch.transpose(x, 1, 2)
            condense_1 = self.conv_layer1(agent_transpose)
            condense_1 = self.relu(condense_1)
            condense_2 = self.conv_layer2(condense_1)
            condense_2 = self.relu(condense_2)
            condense_3 = self.conv_layer3(condense_2)
            condense_3 = self.relu(condense_3)
            # Reshape into [agent, condensed time-step, condensed state vector]
            condense_3 = torch.transpose(condense_3, 1, 2)
            return condense_3
        else:
            # Convolve over each window from previous prediction steps
            agent_transpose = torch.transpose(x, 2, 3)
            num_agents = x.shape[0]
            num_steps = x.shape[1]
            agent_transpose = torch.flatten(agent_transpose, start_dim=0, end_dim=1).float()
            condense_1 = self.conv_layer1(agent_transpose)
            condense_1 = self.relu(condense_1)
            condense_2 = self.conv_layer2(condense_1)
            condense_2 = self.relu(condense_2)
            condense_3 = self.conv_layer3(condense_2)
            condense_3 = self.relu(condense_3)
            # Reshape into [agent, condensed time-step, condensed state vector]
            condense_3 = torch.reshape(condense_3, [num_agents, num_steps, condense_3.shape[1]])
            return condense_3

    def graph_conv(self, condensed_steps):
        num_nodes = condensed_steps.shape[1]
        outgoing_messages = torch.repeat_interleave(torch.unsqueeze(
            condensed_steps, 2), num_nodes, dim=2)
        incoming_messages = torch.repeat_interleave(torch.unsqueeze(
            condensed_steps, 1), num_nodes, dim=1)
        # Shape [Condensed steps, agents, incoming agents, concatenated state vectors]
        node_msgs = torch.concat([incoming_messages, outgoing_messages], dim=-1)
        edges = self.edge_state_mlp(node_msgs)
        edges = self.relu(edges)
        agg_edges = torch.sum(edges, dim=2)
        agg_edges = self.edge_agg_mlp(agg_edges)
        agg_edges = self.relu(agg_edges)
        updated_nodes = torch.concat([condensed_steps, agg_edges], dim=-1)
        updated_nodes = self.node_updater_mlp(updated_nodes)
        updated_nodes = self.relu(updated_nodes)
        return updated_nodes

    def decode(self, predict, x, predict_step):
        original = x
        original = torch.swapaxes(original, 0, 1)
        decoded_states = self.node_decoder_mlp(predict)
        if predict_step == 0:
            original_states = original[6:, :, :]
        else:
            original_states = original[:, :, 6, :]
        # Add predicted change to original state
        decoded_steps = torch.add(original_states[:, :, :self.predict_state_length], decoded_states)
        # Concatenate additional information not being predicted for next steps of predictions
        decoded_steps = torch.concat([decoded_steps, original_states[:, :, self.predict_state_length:]], axis=2)
        # TODO temporarily not predicting obstacles (nodes 50+ for this dataset) Hard coding this is not good
        decoded_steps[:, 50:, :] = original_states[:, 50:, :]
        # decoded_steps_arr = numpy.array(decoded_steps.tolist())
        # original_steps_arr = numpy.array(original.tolist())
        return decoded_steps

    def forward(self, x, predict_steps):
        x = numpy.swapaxes(x, 0, 1)
        condensed_steps = x
        all_predict = []
        for i in range(0, predict_steps):
            condensed_agents = self.conv_1d(condensed_steps, i)
            # Reshaped to [time-steps, agents, condensed_state_length]
            time_steps = torch.transpose(condensed_agents, 0, 1)
            predict = self.graph_conv(time_steps)
            # Shape [condensed time-steps, agents, predicted state vector]
            predict = self.decode(predict, condensed_steps, i)
            # Shape [agents, condensed time-steps, predicted state vector]
            predict = torch.swapaxes(predict, 0, 1)
            extend_pred_list = numpy.zeros([predict.size(0), predict.size(1), 7, predict.size(2)])
            test_list = numpy.array(condensed_steps.tolist())
            extend_pred = torch.tensor(extend_pred_list)
            extend_pred = extend_pred.to(device)
            if predict_steps > 1:
                # On first prediction, reshape to keep track of original window that was condensed
                if i == 0:
                    for j in range(0, predict.size(0)):
                        for z in range(0, predict.size(1)):
                            extend_pred_list[j, z, :, :] = test_list[j, z:z + 7, :]
                            extend_pred[j, z, :, :] = condensed_steps[j, z:z + 7, :]
                else:
                    # extend_pred_list = test_list
                    extend_pred = condensed_steps

                # pre_roll = extend_pred.tolist()
                # Roll window to left
                extend_pred = torch.roll(extend_pred, -1, 2)
                # Replace last step in window (first step before roll) with previous prediction
                extend_pred[:, :, 6, :] = predict[:, :, :]
                # test2 = extend_pred.tolist()
                condensed_steps = extend_pred
                # test3 = predict.tolist()
            all_predict.append(predict)

        # Shape = [agent, condensed time-step, prediction step, state-vector]
        predict = torch.stack(all_predict, 2)
        # Shape = [condensed time-step, agent, prediction step, state-vector]
        predict = torch.swapaxes(predict, 0, 1)
        # test = predict.tolist()

        return predict


from aiogram import Bot, Dispatcher, executor, types
from classes import *
from lists import *

TOKEN = '5599993276:AAFmRTWMwwqa-MW5gwsxIMf85r0bTD4Kfm4'

bot = Bot(token=TOKEN, parse_mode='HTML')
dp = Dispatcher(bot)

# frenchpress = 'frenchpress.jpg'

@dp.message_handler(commands=['start'])
async def command_start(message: types.Message):
    await bot.send_message(message.from_user.id, 'Hello')

# пользователю придлагается список упражнеий на группы мышц для ознакомления
@dp.message_handler(commands=['exercises'])
async def command_exercises(message: types.Message):
    await bot.send_message(message.from_user.id,
                           'Список упражнений по группам мышц, чтобы наглядно увидеть выполнение упражнения, '
                           'щелкните по названию упражнения:\n\n'
                           'Руки: \n'
                           '/biceps - упражнения на бицепс\n'
                           '/triceps - упражнения на трицепс\n'
                           '/wrists - упражнения для развития запястий\n\n'
                           'Грудь:\n'
                           '/chest - упражнения на грудные мышцы\n\n'
                           'Спина:\n'
                           '/back - упражнения на спину\n\n'
                           'Плечи:\n'
                           '/shoulders - упражнения на плечи\n\n'
                           'Ноги:\n'
                           '/legs - упражнения на ноги\n\n'
                           'Пресс\n'
                           '/press - упражнения на пресс \n\n'
                           'Упражнения с собственным весом:\n'
                           '/ownweight - упражнения для тренировки с собственным весом\n\n'
                           'Кардио и жиросжигание\n'
                           '/cardio - кардио тренировка')

# упражнения на бицепс
@dp.message_handler(commands=[Commands.biceps.value])
async def command_biceps(message: types.Message):
    await bot.send_message(message.from_user.id,
                           'Упражнения для развития бицепса. Возможно выполнение 3-4 упражнений с большими весами по'
                           '4-5 подходов на 10-12 повторений каждый, либо выполнение 2-3 упражнений подряд'
                           'по 4-5 подходов на 6-8 повторений каждый с весами поменьше')
    await ExercisesGenerator.list_exercires(biceps, message.from_user.id)

# упражнения на трицепс
@dp.message_handler(commands=[Commands.triceps.value])
async def command_triceps(message: types.Message):
    await bot.send_message(message.from_user.id, 'Упражнения для развития трицепса. Оптимально выполнять 3-4 упражнения'
                                                 ' по 4-5 подходов на 10-12 повторений в каждом подходе.'
                                                 'Возможно комбинировать 2 упражнения выполняя подряд')
    await ExercisesGenerator.list_exercires(triceps, message.from_user.id)

# упражнения на запястья
@dp.message_handler(commands=[Commands.wrists.value])
async def command_wrists(message: types.Message):
    await bot.send_message(message.from_user.id, 'Упражнения для развития запястий: \n')
    await ExercisesGenerator.list_exercires(wrists, message.from_user.id)

# упражнения на грудь
@dp.message_handler(commands=[Commands.chest.value])
async def command_chest(message: types.Message):
    await bot.send_message(message.from_user.id, 'Упражнения для развития грудных мышц: \n')
    await ExercisesGenerator.list_exercires(chest, message.from_user.id)

# упражнения на спину
@dp.message_handler(commands=[Commands.back.value])
async def command_back(message: types.Message):
    await bot.send_message(message.from_user.id, 'Упражнения для развития мышц спины: \n')
    await ExercisesGenerator.list_exercires(back, message.from_user.id)

# упражнения на плечи
@dp.message_handler(commands=[Commands.shoulders.value])
async def command_shoulders(message: types.Message):
    await bot.send_message(message.from_user.id, 'Упражнения для развития плечевых мышц: \n')
    await ExercisesGenerator.list_exercires(shoulders, message.from_user.id)

# упражнения на ноги
@dp.message_handler(commands=[Commands.legs.value])
async def command_legs(message: types.Message):
    await bot.send_message(message.from_user.id, 'Упражнения для развития мышц ног: \n')
    await ExercisesGenerator.list_exercires(legs, message.from_user.id)

# упражнения для прокачки пресса
@dp.message_handler(commands=[Commands.press.value])
async def command_press(message: types.Message):
    await bot.send_message(message.from_user.id, 'Упражнения для прокачки пресса: \n')
    await ExercisesGenerator.list_exercires(press, message.from_user.id)

# упражнения для прокачки тела с собственным весом
@dp.message_handler(commands=[Commands.ownweight.value])
async def command_press(message: types.Message):
    await bot.send_message(message.from_user.id, 'Упражнения для прокачки тела с собственным весом: \n')
    await ExercisesGenerator.list_exercires(ownweight, message.from_user.id)

# упражнения для кардио тренировки
@dp.message_handler(commands=[Commands.cardio.value])
async def command_press(message: types.Message):
    await bot.send_message(message.from_user.id, 'Упражнения для кардио тренировки: \n')
    await ExercisesGenerator.list_exercires(cardio, message.from_user.id)

# запуск бота
if __name__ == '__main__':
    executor.start_polling(dp, skip_updates=True)


import logging
from permhash.helpers import (
    check_type,
    calc_permhash,
    create_crx_permlist,
    create_crx_manifest_permlist,
    create_apk_manifest_permlist,
    create_apk_permlist,
)

APK_MIMETYPES = ["application/zip", "application/java-archive"]
CRX_MANIFEST_MIMETYPES = ["text/plain", "application/json"]
CRX_MIMETYPES = ["application/x-chrome-extension", "application/zip"]
APK_MANIFEST_MIMETYPES = ["application/octet-stream"]

def permhash_crx(path):
    """
    Returns the permhash of a file at the designated path.
    The file at the designated path must be a CRX file.
    http://www.dre.vanderbilt.edu/~schmidt/android/android-4.0/external/chromium/chrome/common/extensions/docs/crx.html

    :param path: The targeted file
    :type path: string
    """
    if check_type(path, CRX_MIMETYPES):
        return calc_permhash(create_crx_permlist(path), path)
    logging.warning(
        "This file is not a type that is currently handled \
(CRX, APK, CRX Manifest, or APK Manifest): (%s)",
        path,
    )
    return False

def permhash_crx_manifest(path):
    """
    Returns the permhash of a file at the designated path.
    The file at the designated path must be a chromium-based extension manifest.
    https://developer.chrome.com/docs/extensions/mv3/manifest/

    :param path: The targeted file
    :type path: string
    """
    if check_type(path, CRX_MANIFEST_MIMETYPES):
        return calc_permhash(create_crx_manifest_permlist(path), path)
    logging.warning(
        "This file is not a type that is currently handled \
(CRX, APK, CRX Manifest, or APK Manifest): (%s)",
        path,
    )
    return False

def permhash_apk_manifest(path):
    """
    Returns the permhash of a file at the designated path

    :param path: The targeted file
    :type path: string
    """
    if check_type(path, APK_MANIFEST_MIMETYPES):
        return calc_permhash(create_apk_manifest_permlist(path), path)
    logging.warning(
        "This file is not a type that is currently handled \
(CRX, APK, CRX Manifest, or APK Manifest): (%s)",
        path,
    )
    return False

def permhash_apk(path):
    """
    Returns the permhash of a file at the designated path

    :param path: The targeted file
    :type path: string
    """
    if check_type(path, APK_MIMETYPES):
        return calc_permhash(create_apk_permlist(path), path)
    logging.warning(
        "This file is not a type that is currently handled \
(CRX, APK, CRX Manifest, or APK Manifest): (%s)",
        path,
    )
    return False


import argparse

def build_parser():
    """Create a parser to parse command-line arguments."""
    parser = argparse.ArgumentParser(description="Meme Generator tool.")
    parser.add_argument('--body', help="a string quote body", default="I met a blind dog, who taught me how to smell.")
    parser.add_argument('--author', help="a string quote author", default="Snoopy")
    parser.add_argument('--path', help="an image path", default='./_data/photos/dog/perro_tronco_5.jpg')
    return parser


from sklearn.model_selection import StratifiedKFold,train_test_split
import pandas as pd
import keras
from keras import *
from keras.layers import *
from sklearn.decomposition import PCA

vect_file = "data\\vectGene2.csv"
train_file = "data\\train_data.csv"
bench_file = "data\\bench_data.csv"
model_file = r'D:\dis_ml\data_tmp\nn_model_last_train.h5'
chunk_size = 1000  # , chunksize=chunk_size

def get_vect_index(id, list):
    for i in range(len(list)):
        if list[i] == id:
            return i

def generate_vect_by_two_id(id1, id2, ids, vects):
    i = get_vect_index(id1, ids)
    j = get_vect_index(id2, ids)
    if i == None:
        return None
    if j == None:
        return None
    dis1 = vects.loc[[i, j]]
    dis1 = dis1.values.tolist()
    dis1 = dis1[0] + dis1[1]
    return dis1

def r_square(y_true, y_pred):
    SSR = keras.backend.mean(keras.backend.square(y_pred - keras.backend.mean(y_true)), axis=-1)
    SST = keras.backend.mean(keras.backend.square(y_true - keras.backend.mean(y_true)), axis=-1)
    return SSR / SST

# 产生训练数据
# 获取基准集数据
train_data = pd.read_csv(bench_file, engine='python')
# 区别特征X和目标Y
x_data = []
y_data = []

# 读取疾病id以及对应向量
vects = pd.read_csv(vect_file, engine='python')
# print(vects)
ids = vects.pop('disease_id')

for index, pair in train_data.iterrows():
    # print(pair)
    dis = generate_vect_by_two_id(pair['id1'], pair['id2'], ids, vects)
    if dis == None:
        continue
    x_data.append(dis)
    y_data.append(pair['y'])

#降维pca
di = 128
pca = PCA(n_components=di)
# pca = PCA(n_components=0.99)
x_data = pca.fit_transform(x_data)
print(pca.n_components_)

# cross validation
# n-fold=5
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=47)
cvscores = []  # 交叉验证结果
for cnt,(train,test) in enumerate(skf.split(x_data,y_data)):
    k = len(x_data[0])
    model = Sequential()
    model.add(Dense(48, activation='relu', input_shape=(k,), ))
    model.add(Dropout(0.5))
    model.add(Dense(12, activation='relu'))
    model.add((Dropout(0.5)))
    # model.add(Dense(12, activation='relu'))
    # model.add(Dropout(0.5))
    model.add(Dense(1, activation='sigmoid'))
    # model.summary()
    print('完成构建网络')
    # model.compile(loss='mean_squared_error', optimizer='adam', metrics=['acc'])
    model.compile(loss='mse', optimizer='adam', metrics=[r_square])
    print('编译网络')
    #注意,如何取数据!当然若是df型,df.iloc[train]取值
    x_train = x_data[train]
    x_test = x_data[test]
    y_train = np.array(y_data)[train]
    y_test = np.array(y_data)[test]
    # y_train = y_data[train]
    # y_test = y_data[test]
    history = model.fit(x_train, y_train,epochs=800,batch_size=60,verbose=0,)

    # scores的第一维是loss，第二维是acc
    scores = model.evaluate(x_test, y_test)
    print('[INFO] %s: %.4f' % (model.metrics_names[1], scores[1]))
    cvscores.append(scores[1])
cvscores = np.asarray(cvscores)
print('[INFO] %.4f (+/- %.4f)' % (np.mean(cvscores), np.std(cvscores)))


# -*- coding: UTF-8 -*-
import requests
from lxml import etree

def get_elements(url, xpath_expr, parser=etree.HTMLParser(recover=True)):
	r = requests.get(
		url,
		headers={
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1',
			'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
		}
	)
	xml = etree.fromstring(r.text.encode('utf-8'), parser=parser)
	return xml.xpath(xpath_expr)


from abc import ABC, abstractmethod
from typing import Optional, Tuple

import numpy as np

from .ues import UEs

class Association(ABC):
    """
    Associations abstract class to implement dynamic basestations, slices and UEs associations.

    ...

    Attributes
    ----------
    max_number_ues : int
        Maximum number of UEs in the simulation
    max_number_basestations : int
        Maximum number of basestations in the simulation
    max_number_slices: int
        Maximum number of supported slices

    Methods
    -------
    step(self, step_number: int, episode_number: int)
        Generate 2D positions for each UE in the simulation
    """

    def __init__(
        self,
        ues: UEs,
        max_number_ues: int,
        max_number_basestations: int,
        max_number_slices: int,
        rng: np.random.Generator = np.random.default_rng(),
        root_path: str = "",
    ) -> None:
        """
        Parameters
        ----------
        ues: UEs
            UE class containing all UEs
        max_number_ues : int
            Maximum number of UEs in the simulation
        max_number_basestations : int
            Maximum number of basestations in the simulation
        max_number_slices: int
            Maximum number of supported slices
        """
        self.ues = ues
        self.max_number_ues = max_number_ues
        self.max_number_basestations = max_number_basestations
        self.max_number_slices = max_number_slices
        self.rng = rng
        self.root_path = root_path

    @abstractmethod
    def step(
        self,
        basestation_ue_assoc: np.ndarray,
        basestation_slice_assoc: np.ndarray,
        slice_ue_assoc: np.ndarray,
        slice_req: dict,
        step_number: int,
        episode_number: int,
    ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, dict]:
        """Update Basestations, Slices and UEs associations

        Parameters
        ----------
        basestation_ue_assoc: np.ndarray
            Numpy array associating UEs to basestations with a form BxU,
            where represents the maximum number of UEs
        basestation_slice_assoc: np.ndarray
            Numpy array associating basestations to slices with a form BxS,
            where B is the maximum number of basestations and S is the
            maximum number of slices
        slice_ue_assoc: Optional[np.ndarray]
            UE association to slices
        slice_req: dict
            Dictionary contaning the slice requirements defined for each slice
        step_number: int
            Step number in the simulation
        episode_number: int
            Episode number in the simulation

        Returns
        -------
        basestation_ue_assoc: np.ndarray
            New Numpy array associating UEs to basestations with a form BxU,
            where represents the maximum number of UEs
        basestation_slice_assoc: np.ndarray
            New numpy array associating basestations to slices with a form BxS,
            where B is the maximum number of basestations and S is the
            maximum number of slices
        slice_ue_assoc: Optional[np.ndarray]
            New UE association to slices to update the existent one
        slice_req: dict
            New dictionary contaning the slice requirements defined for each slice
        """
        return (
            basestation_ue_assoc,
            basestation_slice_assoc,
            slice_ue_assoc,
            slice_req,
        )


from collections import UserDict

class Tag():
    def __init__(self, value):
        self.__value = None
        self.value = value

    def __str__(self):
        return self.__value

    def __repr__(self):
        return self.__value

    @property
    def value(self):
        return self.__value

    @value.setter
    def value(self, tag: str):
        if isinstance(tag, str):
            result = tag.lower().replace(" ","")
            self.__value = result

class Note():
    def __init__(self, title: str, text: str, tags):
        self.title = title
        self.text = text
        self.__tags = set()
        self.tags = tags

    def __str__(self):
        return self.title

    def __repr__(self):
        title_str = f'{self.title}'
        text_str = f'{self.text}'
        tags_str = f"{','.join([str(tag) for tag in self.__tags])}"
        return title_str + text_str + tags_str

    def add_tag(self, tags):
        new_tag = [tag for tag in tags]
        for i in new_tag:
            self.__tags.add(i)

    @property
    def tags(self):
        return self.__tags

    @tags.setter
    def tags(self, tags):
        if isinstance(tags, str):
            self.__tags.add(tags)
        elif isinstance(tags, list):
            for tag in tags:
                self.__tags.add(tag)

    def change_title(self, new_title):
        old_title = self.title
        self.title = new_title
        return f"Title: '{old_title}' change to '{new_title}'"

    def change_text(self, new_text):
        old_text = self.text
        self.text = new_text
        return f"Title: '{old_text}' change to '{new_text}'"

    def change_tags(self, new_tag):
        try:
            old_tag = ','.join(self.__tags)
        except TypeError:
            old_tag = 'tags are missing'
        self.__tags = new_tag
        return f"Tags: '{old_tag}' change to '{new_tag}'"

class NoteBook(UserDict):

    def add_notes(self, note: Note):
        if self.get(note.title):
            return f'{note.title} is already exists'

        self.data[note.title] = note
        return f'{note.title.title()} is successfully added in notes'

    def remove_note(self, word: str):
        for note in self.data.values():
            if word.lower() == note.title.lower():
                remove_note = note.title
        self.data.pop(remove_note)
        return word, self

    def to_dict(self):
        data = {}
        for note in self.data.values():
            data.update({str(note.title): {"title": note.title,
                                    "text": note.text,
                                    "tags": [str(i) for i in note.tags]}})
        return data

    def from_dict(self, data):
        for note in data:
            nt = data[note]
            self.add_notes(Note(nt['title'],
                                nt['text'],
                                [Tag(i) for i in nt['tags']]))

    def show_all(self):
        return self.data

    def find(self, word: str):
        data = []

        for note in self.data.values():

            if word.lower() in note.__repr__():
                data.append(note)

            return data
        else:
            "Nothing found"

    def paginator(self, notes_num):
        start = 0
        while True:
            # превращаем в список ключи словаря и слайсим
            result_keys = list(self.data)[start: start + notes_num]
            # превращаем список ключей словаря в список строк с форматом "ключ : [значение]"
            result_list = [f"{key}: {self.data.get(key).title},{self.data.get(key).text},{self.data.get(key).tags}" for key in result_keys]
            if not result_keys:
                break
            yield '\n'.join(result_list)
            start += notes_num

if __name__=='__main__':
    ...


from flask import Flask, jsonify
app = Flask(__name__)

@app.route('/')
def hello():
    return "Welcome to test-api!"

@app.route('/all/')
def get_all():
    results = [
        {
            "rec_create_date": "12 Jun 2016",
            "rec_dietary_info": "nothing",
            "rec_dob": "01 Apr 1988",
            "rec_first_name": "New",
            "rec_last_name": "Guy",
        },
        {
            "rec_create_date": "1 Apr 2016",
            "rec_dietary_info": "Nut allergy",
            "rec_dob": "01 Feb 1988",
            "rec_first_name": "Old",
            "rec_last_name": "Guy",
        },
    ]
    return jsonify({'employee':results})

@app.route('/info/<name>')
def hello_name(name):
    return jsonify({'info':format(name)})

if __name__ == '__main__':
    app.run(debug=True,host='0.0.0.0', port=8091)


import utils
import json

config_path = utils.get_file_path("config.json")

def init():
    saveHistory = True
    showList = False
    setup = False
    settings = utils.read_file(config_path)
    if settings == None:
        print("config.json not found. Creating a new one...")
        utils.write_file(config_path, dict_to_json(
            {"history": True, "list": False, "setup": False}))
    else:
        settings = json_to_dict(settings)
        if "history" in settings:
            saveHistory = settings["history"]
        if "list" in settings:
            showList = settings["list"]
        if "setup" in settings:
            setup = settings["setup"]
    return saveHistory, showList, setup

def json_to_dict(str):
    return json.loads(str)

def dict_to_json(dict):
    return json.dumps(dict)

def save_history(history):
    settings = utils.read_file(config_path)
    settings = json_to_dict(settings)
    settings["history"] = history
    utils.write_file(config_path, dict_to_json(settings))

def save_list(list):
    settings = utils.read_file(config_path)
    settings = json_to_dict(settings)
    settings["list"] = list
    utils.write_file(config_path, dict_to_json(settings))

def get_setting(setting):
    settings = utils.read_file(config_path)
    settings = json_to_dict(settings)
    return settings[setting]

def save_setup(setup):
    settings = utils.read_file(config_path)
    settings = json_to_dict(settings)
    settings["setup"] = setup
    utils.write_file(config_path, dict_to_json(settings))


from typing import List
from collections import defaultdict
from itertools import combinations, permutations
import copy
class Solution:
    def shortestSequence(self, rolls: List[int], k: int) -> int:
        num_set = set(i for i in range(1, k + 1))
        tmp_for_remove = copy.deepcopy(num_set)
        ans = 1
        for n in rolls:
            if n in tmp_for_remove:
                tmp_for_remove.remove(n)
                if not tmp_for_remove:
                    tmp_for_remove = copy.deepcopy(num_set)
                    ans += 1
        return ans

s = Solution()
print(s.shortestSequence(rolls = [4,2,1,2,3,3,2,4,1], k = 4))
print('6',s.shortestSequence([3,2,1,3,3,3,3,3,1,2,2,3,1,3,3,1,1,3,1,1,1,3,1,3,3,1,2,3,2,1,1,2],3))


import json
import numpy as np
import pandas as pd

import dash
import dash_bootstrap_components as dbc
#import dash_core_components as dcc
from dash import dcc
#import dash_html_components as html
from dash import html
from dash.dependencies import Input, Output, State

import app_config as cfg
import helpers

from app import app

app.title = 'Chart Results'

#
### GLOBALS
#

HOURS_IN_YEAR = 8760
# Note: names need to match 'Name' values in the JSON
# solar_names = ('System power generated','Receiver mass flow rate',
#                 'Receiver thermal losses','Resource Beam normal irradiance')
# desal_names = ('Water production','Fossil fuel usage','Storage status')

# sumCols = {'System power generated','Irradiance GHI from weather file','Water production'}
# determine how weekly and monthly data is aggregated
# SUM if in sumCols set, else MEAN

solar_names = {'linear_fresnel_dsg_iph':('System power generated','Receiver mass flow rate','Receiver thermal losses','Resource Beam normal irradiance'),
               'SC_FPC':('Thermal power generation','Field outlet temperature'),
               'SC_ETC':('Thermal power generation','Field outlet temperature'),
               'tcslinear_fresnel':('Condenser steam temperature','Steam mass flow rate', 'Waste heat generation'),
               'tcsdirect_steam':('Condenser steam temperature','Steam mass flow rate', 'Waste heat generation'),
               'trough_physical_process_heat':('Heat sink thermal power','Field total mass flow delivered', 'Receiver thermal losses','Resource Beam normal irradiance'),
               'pvsamv1':('System power generated','Irradiance GHI from weather file'),
               'pvwattsv7':('System power generated','Irradiance GHI from weather file'),
               'tcstrough_physical':('Steam mass flow rate', 'Waste heat generation'),
               'tcsMSLF':('Steam mass flow rate', 'Waste heat generation'),
               'tcsmolten_salt':('Steam mass flow rate', 'Waste heat generation')}
desal_names = {'FO': ('Water production','Storage status','Fossil fuel usage'),
               'RO': ('Water production','Storage status','Fossil fuel usage'),
               'VAGMD':('Water production','Fossil fuel usage', 'Storage status'),
               'LTMED':('Water production','Fossil fuel usage', 'Storage status'),
               'ABS':('Water production','Fossil fuel usage', 'Storage status'),
               'MEDTVC':('Water production','Fossil fuel usage', 'Storage status'),
               'MDB':('Water production','Fossil fuel usage', 'Storage status'),
               'OARO':('Water production','Grid electricity usage', 'Storage status'),
               'LSRRO':('Water production','Grid electricity usage', 'Storage status'),
               'COMRO':('Water production','Fossil fuel usage', 'Storage status'),
               'RO_FO':('Water production','Grid electricity usage', 'External thermal energy usage'),
               'RO_MDB':('Water production','Grid electricity usage', 'External thermal energy usage'),
               'Generic':('Water production','Fossil fuel usage', 'Storage status')}
sumCols = {'Field total mass flow delivered','Heat sink thermal power','Thermal power generation','System power generated','Irradiance GHI from weather file','Water production','Receiver thermal losses','Fossil fuel usage','Steam mass flow rate','Waste heat generation','Grid electricity usage', 'External thermal energy usage'}

# #
### METHODS
#

def aggregate_data(dataframe, variable, time):
    '''returns dataframe aggregated by selected time value'''
    if time == 'Hourly':
        return dataframe
    elif time == 'Daily':
        if variable in sumCols:
            return dataframe.groupby(dataframe.index // 24).sum().round(2).reset_index()
        else:
            return dataframe.groupby(dataframe.index // 24).mean().round(2).reset_index()
    elif time == 'Weekly':
        if variable in sumCols:
            return dataframe.groupby(dataframe.index // 168).sum().round(2).reset_index()
        else:
            return dataframe.groupby(dataframe.index // 168).mean().round(2).reset_index()

def gen_title(variable, time):
    d = {'Hourly':'Hour', 'Daily':'Day', 'Weekly':'Week'}
    return f"{variable.title()} per {d[time]}"

#
### LAYOUT
#

time_series = [{'label':' Hourly', 'value':'Hourly'},
               {'label':' Daily', 'value':'Daily'},
               {'label':' Weekly','value':'Weekly'}]

chart_navbar = dbc.NavbarSimple(
    children=[dbc.NavItem(dbc.NavLink("Home", href='/home')),
              dbc.NavItem(dbc.NavLink("Charts"), active=True),
              #dbc.NavItem(dbc.NavLink("Charts")),
              dbc.NavItem(dbc.NavLink("Report", href='/analysis-report')),
              dbc.NavItem(dbc.NavLink("Results Map", href='/results-map'))],
    brand="Model Results",
    color="primary",
    dark=True,
    sticky='top',
    style={'margin-bottom':60}
)

def real_time_layout():
    solar_radios = html.Div([
        dbc.Row([
            dbc.Col(
                dcc.RadioItems(
                    id='select-solar-chart',
                    options=[{'label': f" {name}", 'value': name}
                            for name in solar_names[helpers.json_load(cfg.app_json)['solar']]],
                    value=solar_names[helpers.json_load(cfg.app_json)['solar']][0],
                    labelStyle = {'display': 'block'}),
                width=4),
            dbc.Col(
                dcc.RadioItems(
                    id='select-solar-time',
                    options=time_series,
                    value='Hourly',
                    labelStyle={'display': 'block'}),
            width=2)
        ], justify="center")
    ])

    desal_radios = html.Div([
        dbc.Row([
            dbc.Col(
                dcc.RadioItems(
                    id='select-desal-chart',
                    options=[{'label': f" {name}", 'value': name}
                            for name in desal_names[helpers.json_load(cfg.app_json)['desal']]],
                    value=desal_names[helpers.json_load(cfg.app_json)['desal']][0],
                    labelStyle = {'display': 'block'}),
                width=2),
            dbc.Col(
                dcc.RadioItems(
                    id='select-desal-time',
                    options=time_series,
                    value='Hourly',
                    labelStyle={'display': 'block'}),
            width=2)
        ], justify="center")
    ])

    chart_results_layout = html.Div([
        html.Div(id='initialize'),
        chart_navbar,
        dcc.Store(id='solar-storage'),
        dcc.Store(id='desal-storage'),
        dbc.Container([
            html.H3(cfg.Solar[helpers.json_load(cfg.app_json)['solar']], className='text-success', style={'margin-bottom':0, 'text-align':'center'}),
            dcc.Graph(id='solar-graph'),
            solar_radios,
            html.H3(cfg.Desal[helpers.json_load(cfg.app_json)['desal']], className='text-success',style={'margin-top':45, 'margin-bottom':0, 'text-align':'center'}),
            dcc.Graph(id='desal-graph'),
            desal_radios,
        ],style={'margin-bottom':150})
    ])
    return chart_results_layout

#
### CALLBACKS
#

@app.callback(
    Output('solar-storage', 'data'),
    [Input('initialize', 'children')])
def store_solar_data(x):
    # load simulation results from JSONs
    sol_outfile = 'Solar_output' + helpers.json_load(cfg.app_json)['timestamp'] + '.json'
    print(sol_outfile)
    sol = helpers.json_load(cfg.sam_results_dir / sol_outfile)

    solar_indexes = [helpers.index_in_list_of_dicts(sol,'Name', x)
                    for x in solar_names[helpers.json_load(cfg.app_json)['solar']]]
    solar_units = {sol[x]['Name']:sol[x]['Unit'] for x in solar_indexes}
    # the arrays all need to be the same size
    # creates dummy variables (zeros) if they are not
    # on failed runs of SamBaseClass the file can have empty values
    solar_arrays = []
    for x in solar_indexes:
        if len(sol[x]['Value']) == HOURS_IN_YEAR:
            solar_arrays.append(sol[x]['Value'])
        else:
            solar_arrays.append(np.zeros(HOURS_IN_YEAR))

    # combine results into list of series; note the .T for transpose,
    # otherwise the lists are read as rows, not columns. '''
    solar_array_data = np.array([pd.Series(x) for x in solar_arrays]).T

    # data frame of arrays, fortunately all the same shape!
    df_solar = pd.DataFrame(
            solar_array_data,
            columns = solar_names[helpers.json_load(cfg.app_json)['solar']]
    ).round(2)
    df = df_solar.to_dict()
    solarDict = {'units': solar_units, 'df_dict': df}
    return solarDict

@app.callback(
    Output('desal-storage', 'data'),
    [Input('initialize', 'children')])
def store_desal_data(x):
    alkup = helpers.json_load(cfg.app_json)
    flkup = cfg.build_file_lookup(alkup['solar'], alkup['desal'],
                                  alkup['finance'],alkup['timestamp'])
    des = helpers.json_load(flkup['sam_desal_simulation_outfile'])

    desal_indexes = [helpers.index_in_list_of_dicts(des,'Name', x)
                    for x in desal_names[helpers.json_load(cfg.app_json)['desal']]]
    desal_arrays = [des[x]['Value'] for x in desal_indexes]
    desal_units = {des[x]['Name']:des[x]['Unit'] for x in desal_indexes}

    desal_array_data = np.array([pd.Series(x) for x in desal_arrays]).T

    # data frame of arrays, fortunately all the same shape!
    df_desal = pd.DataFrame(
            desal_array_data,
            columns = desal_names[helpers.json_load(cfg.app_json)['desal']]
    ).round(2)

    dfd = df_desal.to_dict()
    desalDict = {'units': desal_units, 'df_dict': dfd}
    return desalDict

@app.callback(
    Output('solar-graph','figure'),
    [Input('select-solar-chart', 'value'),
    Input('select-solar-time', 'value'),
    Input('solar-storage', 'data')])
def update_solar_graph(solarValue, timeAggValue, solarData):
    ''' update the solar figure object '''

    solar_units = solarData['units']
    df_solar = pd.DataFrame.from_dict(solarData['df_dict'])
    # change index from dict conversion from string back to int
    df_solar.index = df_solar.index.astype(int)
    df_tmp = aggregate_data(dataframe=df_solar,
                            variable=solarValue,
                            time=timeAggValue)
    figure={
        'data': [
            {
                'x': df_tmp.index,
                'y': df_tmp[solarValue],
                'name': solarValue,
                'mode': 'bar',
                'marker': {'size': 10, 'color':'#2C3E50'},
            }],
        'layout': {
            'title': {'text':gen_title(solarValue,timeAggValue),
                      'font':{'color':'#2C3E50'}},
            'clickmode': 'event+select',
            'xaxis':{'title': '{} values'.format(timeAggValue)},
            'yaxis':{'title': solar_units[solarValue]},
        }
    }
    # print(solarValue)
    return figure

@app.callback(
    Output('desal-graph','figure'),
    [Input('select-desal-chart', 'value'),
    Input('select-desal-time', 'value'),
    Input('desal-storage', 'data')])
def update_desal_graph(desalValue, timeAggValue, desalData):
    ''' update the desal figure object '''

    desal_units = desalData['units']
    df_desal = pd.DataFrame.from_dict(desalData['df_dict'])
    # change index from dict conversion from string back to int
    df_desal.index = df_desal.index.astype(int)
    df_tmp = aggregate_data(dataframe=df_desal,
                            variable=desalValue,
                            time=timeAggValue)
    figure={
        'data': [
            {
                'x': df_tmp.index,
                'y': df_tmp[desalValue],
                'name': desalValue,
                'mode': 'bar',
                'marker': {'size': 12, 'color':'#2C3E50'},
            }
        ],
        'layout': {
            'title': {'text':gen_title(desalValue,timeAggValue),
                      'font':{'color':'#2C3E50'}},
            'clickmode': 'event+select',
            'xaxis':{'title': '{} values'.format(timeAggValue)},
            'yaxis':{'title': desal_units[desalValue]},
        }
    }
    return figure


import pygame
import time
import random

# initializing pygame
x = pygame.init()

screen_width = 900
screen_height = 600

# setting the size of game window
gameWindow = pygame.display.set_mode((screen_width,screen_height))

#setting the title of the window
pygame.display.set_caption('Snake Game - Nirmal')
icon = pygame.image.load('snake.png')
pygame.display.set_icon(icon)

#colors
white = (255,255,255)
red = (255,0,0)
black = (0,0,0)
lightgreen = (153, 255, 51)

# game specific variables
exit_game = False
game_over = False
velocity_x = 0
velocity_y = 0
snake_x = 45
snake_y = 55
snake_size = 10
fps =  30
food_x = random.randint(20,screen_width/2)
food_y = random.randint(20,screen_height/2)
score = 0
clock = pygame.time.Clock()

#Gameover function
def gameover():
    gameWindow.fill(white)
    image1 = pygame.image.load('game-over.png')
    pygame.Surface.blit(gameWindow, image1,(10,10))
    pygame.display.update()
    time.sleep(2)
    # exit_game = False
    # pygame.quit()
    # quit()

# creating a game loop
while not exit_game:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit_game = True

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                exit_game = True
            if event.key == pygame.K_RIGHT:
                velocity_x = 10
                velocity_y = 0

            if event.key == pygame.K_DOWN:
                velocity_y = 10
                velocity_x = 0

            if event.key == pygame.K_LEFT:
                velocity_x = -10
                velocity_y = 0

            if event.key == pygame.K_UP:
                velocity_y = -10
                velocity_x = 0

    snake_x += velocity_x
    snake_y += velocity_y

    if snake_x<=15 or snake_x>=880:
        velocity_x = 0
        velocity_y = 0
        gameover()
        break
    elif snake_y<=15 or snake_y>=580:
        velocity_y = 0
        velocity_x = 0
        gameover()
        break

    if abs(food_y-snake_y)<9 and abs(food_x-snake_x)<9:
        score+=1
        print('score: ',score)
        food_x = random.randint(20,screen_width/2)
        food_y = random.randint(20,screen_height/2)

    gameWindow.fill(lightgreen)
    pygame.draw.circle(gameWindow, red, (food_x,food_y), 7)
    pygame.draw.rect(gameWindow, black, [snake_x,snake_y,snake_size,snake_size])
    pygame.display.update()
    clock.tick(fps)

pygame.quit()
quit()