# -*- coding: utf-8 -*-
import re
import traceback
from django.views.generic import View, ListView, TemplateView
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Q, F
from django.http import QueryDict, JsonResponse
from common.mixins import JSONResponseMixin

from cmdb.models.base import IDC
from cmdb.models.accessory import JumpWire, Accessory, UseRecord, InventoryRecord

#
# class JumpWireView(PermissionRequiredMixin, TemplateView):
#     permission_required = 'auth.perm_cmdb_accessory_view'
#     template_name = "cmdb/jump_wire.html"
#
#     def get_context_data(self, **kwargs):
#         ot_list = JumpWire.objects.get_queryset()
#         context = {
#             'path1': '配件',
#             'path2': '光纤模块',
#             'idc_list': [{"id": i.id, "name": i.idc_name} for i in IDC.objects.get_queryset()],
#             'mode_list': dict_list_duplicate_delete([
#                 {'key': obj.mode, 'value': obj.get_mode_display()} for obj in ot_list]),
#             'reach_list': [obj['reach'] for obj in ot_list.values('reach').distinct()],
#             'rate_list': dict_list_duplicate_delete([
#                 {'key': obj.rate, 'value': obj.get_rate_display()} for obj in ot_list]),
#             'version_list': [{
#                 'id': obj.id,
#                 'version': '%s|%s|%dKM|%s' % (
#                     obj.information, obj.get_mode_display(), obj.reach, obj.get_rate_display())
#             } for obj in ot_list]
#         }
#         kwargs.update(context)
#         return super().get_context_data(**kwargs)

class JumpWireListView(PermissionRequiredMixin, JSONResponseMixin, TemplateView):
    permission_required = 'auth.perm_cmdb_accessory_view'

    def get(self, request, **kwargs):
        limit = request.GET.get('limit')
        offset = request.GET.get('offset')
        is_active = request.GET.get("is_active", 1)
        sort_order = request.GET.get("sortOrder", '')
        sort_name = request.GET.get("sortName", 'id')
        sort_name = sort_name if sort_order == 'asc' else '-' + sort_name

        mode_id_list = list()
        mode_id = request.GET.get("mode_id", '')
        if mode_id:
            mode_id_list.append(int(mode_id))
            obj = JumpWire.objects.get(id=mode_id)
            acc_detail = {
                obj.id: {
                    'version': '%s|%s|%s|%s米' % (
                        obj.information, obj.get_mode_display(), obj.get_interface_display(), obj.length),
                    'information': obj.information, 'mode': obj.get_mode_display(),
                    'interface': obj.get_interface_display(), 'length': obj.length, 'image': str(obj.image)
                }
            }
        else:
            if re.match(r'-?mode|-?interface|-?length', sort_name, re.I):
                obj_list = JumpWire.objects.get_queryset().order_by(sort_name)
            else:
                obj_list = JumpWire.objects.get_queryset()
            mode = request.GET.get("mode", '')
            interface = request.GET.get("interface", '')
            length = request.GET.get("length", '')
            if mode:
                obj_list = obj_list.filter(mode=mode)
            if interface:
                obj_list = obj_list.filter(interface=interface)
            if length:
                obj_list = obj_list.filter(length=length)
            acc_detail = dict()
            for obj in obj_list:
                mode_id_list.append(obj.id)
                acc_detail[obj.id] = {
                    'version': '%s|%s|%s|%s米' % (
                        obj.information, obj.get_mode_display(), obj.get_interface_display(), obj.length),
                    'information': obj.information, 'mode': obj.get_mode_display(),
                    'interface': obj.get_interface_display(), 'length': obj.length, 'image': str(obj.image)
                }

        idc_id = request.GET.get("idc_id", '')
        if idc_id:
            idc = IDC.objects.get(id=idc_id)
            if not re.match(r'-?mode|-?interface|-?length', sort_name, re.I):
                accessory_list = Accessory.objects.filter(storehouse=idc, mode='jump_wire', mode_id__in=mode_id_list,
                                                          is_active=is_active).order_by(sort_name)
            else:
                accessory_list = Accessory.objects.filter(storehouse=idc, mode='jump_wire', mode_id__in=mode_id_list,
                                                          is_active=is_active)
        else:
            if not re.match(r'-?mode|-?interface|-?length', sort_name, re.I):
                accessory_list = Accessory.objects.filter(mode='jump_wire', mode_id__in=mode_id_list,
                                                          is_active=is_active).order_by(sort_name)
            else:
                accessory_list = Accessory.objects.filter(mode='jump_wire', mode_id__in=mode_id_list,
                                                          is_active=is_active)
        search = request.GET.get("search", '')
        if search:
            accessory_list = accessory_list.filter(Q(manufacturer=search) | Q(sn=search) |
                                                   Q(vendor=search) | Q(comment__contains=search)).distinct()

        result = list()
        for acc in accessory_list[int(offset):int(offset + limit)]:
            receive = list()
            for rev in UseRecord.objects.filter(accessory=acc).order_by('-id'):
                receive.append('%s %s %s' % (rev.created_date, rev.net_device.login_address, rev.get_operate_display()))
            result.append({
                'id': acc.id,
                'storehouse': acc.storehouse.idc_name,
                'version': acc_detail[acc.mode_id]['version'],
                'information': acc_detail[acc.mode_id]['information'],
                'mode': acc_detail[acc.mode_id]['mode'],
                'interface': acc_detail[acc.mode_id]['interface'],
                'length': acc_detail[acc.mode_id]['length'],
                'image': acc_detail[acc.mode_id]['image'],
                'receive': receive,
                'manufacturer': acc.manufacturer,
                'sn': acc.sn,
                'vendor': acc.vendor,
                'trade_date': acc.trade_date,
                'expired_date': acc.expired_date,
                'comment': acc.comment,
                'is_active': acc.is_active,
                'created_date': acc.created_date
            })
        res = {"total": accessory_list.count(), "rows": result}
        return self.render_json_response(res)

class JumpWireDetailView(PermissionRequiredMixin, JSONResponseMixin, TemplateView):
    permission_required = ('auth.perm_cmdb_accessory_view', 'auth.perm_cmdb_accessory_edit')

    def get(self, request, **kwargs):
        pk = kwargs.get('pk')
        try:
            obj = JumpWire.objects.get(id=pk)
            result = {
                'accessory': 'jump_wire',
                'version': '%s|%s|%s|%s米' % (
                    obj.information, obj.get_mode_display(), obj.get_interface_display(), obj.length),
            }
            res = {'code': 0, 'result': result}
        except JumpWire.DoesNotExist:
            res = {'code': 1, 'errmsg': '未找到！'}
        return self.render_json_response(res)

    def put(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        try:
            jw = JumpWire.objects.get(id=pk)
            put_data = QueryDict(request.body, mutable=True)
            idc_id = put_data.pop('idc_id')[0]
            count = put_data.pop('count')[0]
            acc_list = Accessory.objects.filter(storehouse__id=idc_id, mode='jump_wire', mode_id=jw.id, is_active=True)
            if acc_list.count() < int(count):
                res = {"code": 1, "errmsg": "可用数量不足！"}
                return JsonResponse(res, safe=True)
            for a in acc_list[:int(count)]:
                a.is_active = False
                a.save(update_fields=['is_active'])
            InventoryRecord.objects.create(accessory='jump_wire', operate='receive',
                                           content='%s %s %d米 跳线 %s 根！' % (
                                               jw.get_mode_display(), jw.get_interface_display(), jw.length, count),
                                           user=request.user)

            res = {"code": 0, "result": "领用成功"}
        except Exception as e:
            res = {"code": 1, "errmsg": "领用错误：%s" % str(e)}
        return JsonResponse(res, safe=True)

    def delete(self, *args, **kwargs):
        pk = kwargs.get('pk')
        try:
            obj = JumpWire.objects.filter(pk=pk).delete()
            if obj:
                res = {"code": 0, "result": "删除成功"}
            else:
                res = {"code": 1, "errmsg": "未找到！"}
        except Exception as e:
            res = {"code": 1, "errmsg": "删除错误！%s" % str(e)}
        return JsonResponse(res, safe=True)


"""Upper Confidence Bound 1
This module contains a class that implements UCB1 algorithm, a famous
multi-armed bandit algorithm without context.
"""
from __future__ import division
import numpy as np
import six

from striatum.bandit.bandit import BaseBandit

class UCB1(BaseBandit):
    r"""Upper Confidence Bound 1

    Parameters
    ----------
    history_storage : HistoryStorage object
        The HistoryStorage object to store history context, actions and rewards.

    model_storage : ModelStorage object
        The ModelStorage object to store model parameters.

    action_storage : ActionStorage object
        The ActionStorage object to store actions.

    recommendation_cls : class (default: None)
        The class used to initiate the recommendations. If None, then use
        default Recommendation class.

    References
    ----------
    .. [1]  Peter Auer, et al. "Finite-time Analysis of the Multiarmed Bandit
            Problem." Machine Learning, 47. 2002.
    """

    def __init__(self, history_storage, model_storage, action_storage,
                 recommendation_cls=None):
        super(UCB1, self).__init__(history_storage, model_storage,
                                   action_storage, recommendation_cls)
        total_action_reward = {}
        action_times = {}
        for action_id in self._action_storage.iterids():
            total_action_reward[action_id] = 1.0
            action_times[action_id] = 1
        n_rounds = self._action_storage.count()
        self._model_storage.save_model({
            'total_action_reward': total_action_reward,
            'action_times': action_times,
            'n_rounds': n_rounds,
        })

    def _ucb1_score(self):
        model = self._model_storage.get_model()
        total_action_reward = model['total_action_reward']
        action_times = model['action_times']
        n_rounds = model['n_rounds']

        estimated_reward_dict = {}
        uncertainty_dict = {}
        score_dict = {}
        for action_id in self._action_storage.iterids():
            estimated_reward = (total_action_reward[action_id]
                                / action_times[action_id])
            uncertainty = np.sqrt(2 * np.log(n_rounds)
                                  / action_times[action_id])
            estimated_reward_dict[action_id] = estimated_reward
            uncertainty_dict[action_id] = uncertainty
            score_dict[action_id] = estimated_reward + uncertainty
        return estimated_reward_dict, uncertainty_dict, score_dict

    def get_action(self, context=None, n_actions=None):
        """Return the action to perform

        Parameters
        ----------
        context : {array-like, None}
            The context of current state, None if no context available.

        n_actions: int (default: None)
            Number of actions wanted to recommend users. If None, only return
            one action. If -1, get all actions.

        Returns
        -------
        history_id : int
            The history id of the action.

        recommendations : list of dict
            Each dict contains
            {Action object, estimated_reward, uncertainty}.
        """
        if self._action_storage.count() == 0:
            return self._get_action_with_empty_action_storage(context,
                                                              n_actions)

        estimated_reward, uncertainty, score = self._ucb1_score()
        if n_actions == -1:
            n_actions = self._action_storage.count()

        if n_actions is None:
            recommendation_id = max(score, key=score.get)
            recommendations = self._recommendation_cls(
                action=self._action_storage.get(recommendation_id),
                estimated_reward=estimated_reward[recommendation_id],
                uncertainty=uncertainty[recommendation_id],
                score=score[recommendation_id],
            )
        else:
            recommendation_ids = sorted(score, key=score.get,
                                        reverse=True)[:n_actions]
            recommendations = []  # pylint: disable=redefined-variable-type
            for action_id in recommendation_ids:
                recommendations.append(self._recommendation_cls(
                    action=self._action_storage.get(action_id),
                    estimated_reward=estimated_reward[action_id],
                    uncertainty=uncertainty[action_id],
                    score=score[action_id],
                ))

        history_id = self._history_storage.add_history(context, recommendations)
        return history_id, recommendations

    def reward(self, history_id, rewards):
        """Reward the previous action with reward.

        Parameters
        ----------
        history_id : int
            The history id of the action to reward.

        rewards : dictionary
            The dictionary {action_id, reward}, where reward is a float.
        """

        # Update the model
        model = self._model_storage.get_model()
        total_action_reward = model['total_action_reward']
        action_times = model['action_times']
        for action_id, reward in six.viewitems(rewards):
            total_action_reward[action_id] += reward
            action_times[action_id] += 1
            model['n_rounds'] += 1
        self._model_storage.save_model(model)
        # Update the history
        self._history_storage.add_reward(history_id, rewards)

    def add_action(self, actions):
        """ Add new actions (if needed).

        Parameters
        ----------
        actions : iterable
            A list of Action objects for recommendation
        """
        self._action_storage.add(actions)

        model = self._model_storage.get_model()
        total_action_reward = model['total_action_reward']
        action_times = model['action_times']

        for action in actions:
            total_action_reward[action.id] = 1.0
            action_times[action.id] = 1
            model['n_rounds'] += 1

        self._model_storage.save_model(model)

    def remove_action(self, action_id):
        """Remove action by id.

        Parameters
        ----------
        action_id : int
            The id of the action to remove.
        """
        model = self._model_storage.get_model()
        model['n_rounds'] -= model['action_times'][action_id]
        del model['total_action_reward'][action_id]
        del model['action_times'][action_id]
        self._model_storage.save_model(model)
        self._action_storage.remove(action_id)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
from setuptools import setup, find_packages

description = "Bottle authentication, for Personal, Google, Twitter and "\
    "facebook."

setup(
    name='bottle-auth',
    version="0.3.3",
    description=description,
    author="Thiago Avelino",
    author_email="thiago@avelino.xxx",
    url='https://github.com/avelino/bottle-auth',
    packages=find_packages(),
    package_dir={'bottle_auth': 'bottle_auth'},

    install_requires=['webob', 'bottle', 'bottle-mongo', 'bottle-beaker'],
    classifiers=[
        'Environment :: Web Environment',
        'Environment :: Plugins',
        'Framework :: Bottle',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: MIT License',
        'Operating System :: OS Independent',
        'Programming Language :: Python',
        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
        'Topic :: Software Development :: Libraries :: Python Modules'
    ],
)


from typing import Any, Callable, MutableMapping, Optional

from tensorflow import keras

class HyperparameterScheduler(keras.callbacks.Callback):
    """Generic hyperparameter scheduler.

    !!! example
        ```python
        bop = lq.optimizers.Bop(threshold=1e-6, gamma=1e-3)
        adam = tf.keras.optimizers.Adam(0.01)
        optimizer = lq.optimizers.CaseOptimizer(
            (lq.optimizers.Bop.is_binary_variable, bop), default_optimizer=adam,
        )
        callbacks = [
            HyperparameterScheduler(lambda x: 0.001 * (0.1 ** (x // 30)), "gamma", bop)
        ]
        ```
    # Arguments
    optimizer: the optimizer that contains the hyperparameter that will be scheduled.
        Defaults to `self.model.optimizer` if `optimizer == None`.
    schedule: a function that takes an epoch index as input
        (integer, indexed from 0) and returns a new hyperparameter as output.
    hyperparameter: str. the name of the hyperparameter to be scheduled.
    verbose: int. 0: quiet, 1: update messages.
    """

    def __init__(
        self,
        schedule: Callable,
        hyperparameter: str,
        optimizer: Optional[keras.optimizers.Optimizer] = None,
        verbose: int = 0,
    ):
        super(HyperparameterScheduler, self).__init__()
        self.optimizer = optimizer
        self.schedule = schedule
        self.hyperparameter = hyperparameter
        self.verbose = verbose

    def set_model(self, model: keras.models.Model) -> None:
        super().set_model(model)
        if self.optimizer is None:
            # It is not possible for a model to reach this state and not have
            # an optimizer, so we can safely access it here.
            self.optimizer = model.optimizer

        if not hasattr(self.optimizer, self.hyperparameter):
            raise ValueError(
                f'Optimizer must have a "{self.hyperparameter}" attribute.'
            )

    def on_epoch_begin(
        self, epoch: int, logs: Optional[MutableMapping[str, Any]] = None
    ) -> None:
        hp = getattr(self.optimizer, self.hyperparameter)
        try:  # new API
            hyperparameter_val = keras.backend.get_value(hp)
            hyperparameter_val = self.schedule(epoch, hyperparameter_val)
        except TypeError:  # Support for old API for backward compatibility
            hyperparameter_val = self.schedule(epoch)

        keras.backend.set_value(hp, hyperparameter_val)

        if self.verbose > 0:
            print(
                f"Epoch {epoch + 1}: {self.hyperparameter} changing to {keras.backend.get_value(hp)}."
            )

    def on_epoch_end(
        self, epoch: int, logs: Optional[MutableMapping[str, Any]] = None
    ) -> None:
        logs = logs or {}
        hp = getattr(self.optimizer, self.hyperparameter)
        logs[self.hyperparameter] = keras.backend.get_value(hp)


import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

from logger import Logger
from datetime import datetime

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
from IPython import display
import os
from PIL import Image
from torch.utils.data.dataset import Dataset
from scipy.misc import imread
import pdb
# %matplotlib inline

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

if USE_GPU:
    print("="*80)
    print("Model is using GPU")
    print("="*80)

class NN(nn.Module):

    def __init__(self, input_size, hidden_size, output_size):
        super(NN, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.out = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        output = self.fc1(x)
        #output = F.torch.sigmoid(output)
        output = F.relu(output)
        output = self.out(output)
        return output

from torch.utils.data import DataLoader, Dataset
from torchvision import transforms, utils
import time
import pca_data_hu_ndl2_train_test as pca_data_hu

class DataSet(Dataset):

    def __init__(self, X, Y, transform=False):
        self.X = X
        self.Y = Y
        self.transform = transform

    def __len__(self):
        return len(self.X)

    def __getitem__(self, idx):
        x = self.X[idx].astype(np.float32)
        y = self.Y[idx].astype(np.float32)
        return torch.tensor(x, device=device), torch.tensor(y, device=device)

    def save(self, path):
        data = np.c_[self.X, self.Y]
        np.save(path, data)

    def load(self, path):
        Y = np.load(path).T
        self.Y = Y[:, None]

hu, PC, X_pca_test, X_pca, X, X_test, Y, Y_test = pca_data_hu.pca_data()

bch_sz = 2048

training_set = DataSet(X_pca, Y, transform=False)
training_generator = DataLoader(training_set, batch_size=bch_sz, shuffle=True)

test_set = DataSet(X_pca_test, Y_test, transform=False)
test_generator = DataLoader(test_set, batch_size=bch_sz, shuffle=True)

img, lab = training_set.__getitem__(0)
print('PCA input shape at the first row : {}'.format(img.size()))
print('label shape at the first row : {}'.format(lab.size()))
print(np.array(training_set).shape)
#print(np.array(test_set).shape)

train_loader_check = DataLoader(training_set, batch_size=bch_sz, shuffle=True)
train_iter_check = iter(train_loader_check)
print(type(train_iter_check))

images, labels = train_iter_check.next()

print('images shape on batch size = {}'.format(images.size()))
print('labels shape on batch size = {}'.format(labels.size()))
# Break
# import Ipython; Ipython.embed()

net = NN(PC, 100, 1)

if USE_GPU:
    net.cuda()

import pyro
from pyro.distributions import Normal, Uniform, Delta
from pyro.infer import SVI, Trace_ELBO
from pyro.optim import Adam
from pyro.infer import EmpiricalMarginal, SVI, Trace_ELBO, TracePredictive
from pyro.infer.mcmc import MCMC, NUTS
import pyro.optim as optim

log_softmax = nn.LogSoftmax(dim=1)

def model(x_data, y_data):

    fc1w_prior = Normal(loc=torch.zeros_like(net.fc1.weight), scale=torch.ones_like(net.fc1.weight))
    fc1b_prior = Normal(loc=torch.zeros_like(net.fc1.bias), scale=torch.ones_like(net.fc1.bias))

    outw_prior = Normal(loc=torch.zeros_like(net.out.weight), scale=torch.ones_like(net.out.weight))
    outb_prior = Normal(loc=torch.zeros_like(net.out.bias), scale=torch.ones_like(net.out.bias))

    priors = {'fc1.weight': fc1w_prior, 'fc1.bias': fc1b_prior,  'out.weight': outw_prior, 'out.bias': outb_prior}
    # lift module parameters to random variables sampled from the priors
    lifted_module = pyro.random_module("module", net, priors)
    # sample a regressor (which also samples w and b)
    lifted_reg_model = lifted_module()
    """
    lhat = log_softmax(lifted_reg_model(x_data))
    pyro.sample("obs", Categorical(logits=lhat), obs=y_data)
    """
    # run the regressor forward conditioned on inputs
    prediction_mean = lifted_reg_model(x_data).squeeze(-1)
    pyro.sample("obs", Normal(prediction_mean, 1),
                obs=y_data)
    return prediction_mean

softplus = torch.nn.Softplus()

"""
from pyro.contrib.autoguide import AutoDiagonalNormal
guide = AutoDiagonalNormal(model)
"""
def guide(x_data, y_data):

    # First layer weight distribution priors
    fc1w_mu = torch.randn_like(net.fc1.weight)
    fc1w_sigma = torch.randn_like(net.fc1.weight)
    fc1w_mu_param = pyro.param("fc1w_mu", fc1w_mu)
    fc1w_sigma_param = softplus(pyro.param("fc1w_sigma", fc1w_sigma))
    fc1w_prior = Normal(loc=fc1w_mu_param, scale=fc1w_sigma_param)
    # First layer bias distribution priors
    fc1b_mu = torch.randn_like(net.fc1.bias)
    fc1b_sigma = torch.randn_like(net.fc1.bias)
    fc1b_mu_param = pyro.param("fc1b_mu", fc1b_mu)
    fc1b_sigma_param = softplus(pyro.param("fc1b_sigma", fc1b_sigma))
    fc1b_prior = Normal(loc=fc1b_mu_param, scale=fc1b_sigma_param)
    # Output layer weight distribution priors
    outw_mu = torch.randn_like(net.out.weight)
    outw_sigma = torch.randn_like(net.out.weight)
    outw_mu_param = pyro.param("outw_mu", outw_mu)
    outw_sigma_param = softplus(pyro.param("outw_sigma", outw_sigma))
    outw_prior = Normal(loc=outw_mu_param, scale=outw_sigma_param).independent(1)
    # Output layer bias distribution priors
    outb_mu = torch.randn_like(net.out.bias)
    outb_sigma = torch.randn_like(net.out.bias)
    outb_mu_param = pyro.param("outb_mu", outb_mu)
    outb_sigma_param = softplus(pyro.param("outb_sigma", outb_sigma))
    outb_prior = Normal(loc=outb_mu_param, scale=outb_sigma_param)
    priors = {'fc1.weight': fc1w_prior, 'fc1.bias': fc1b_prior, 'out.weight': outw_prior, 'out.bias': outb_prior}

    lifted_module = pyro.random_module("module", net, priors)
    return lifted_module()

# Reducing Learning Rate.
# ReduceOnPlateau is not supported.
AdamArgs = { 'lr': 1e-2 }
optimizer = torch.optim.Adam
scheduler = pyro.optim.ExponentialLR({'optimizer': optimizer, 'optim_args': AdamArgs, 'gamma': 0.99995 })
svi = SVI(model, guide, scheduler, loss=Trace_ELBO(), num_samples=1000)

"""

#Fixed Learning Rate
optimizer = Adam({"lr": 0.01})
svi = SVI(model, guide, optimizer, loss=Trace_ELBO())
"""

num_iterations = 100
loss = 0
losses = []
for j in range(num_iterations):
    #print("Epoch ", j)
    loss = 0
    for batch_id, data_train in enumerate(training_generator):
        # calculate the loss and take a gradient step
        loss += svi.step(data_train[0], data_train[1][:,-1])
        #loss += svi.step(x, y)
    normalizer_train = len(training_generator.dataset)
    total_epoch_loss_train = loss / normalizer_train

    losses.append(total_epoch_loss_train)
    print("Epoch ", j, " Loss ", total_epoch_loss_train)

plt.plot(losses)
plt.title("ELBO")
plt.xlabel("step")
plt.ylabel("Epoch loss")

posterior = svi.run(data_train[0], data_train[1][:,-1])

# Break
#import Ipython; Ipython.embed()

def save_ask():
    save_model = input("save model > ")

    if save_model.lower().startswith('y'):
        experiment_id = input("Enter exp name, press return to use datetime> ")
        if not experiment_id:
            experiment_id = datetime.now().isoformat()

        if os.environ['HOSTNAME'] == 'fractal':
            SAVE_PATH = f'/hdd/bdhammel/checkpoints/bayes/{experiment_id}'
        else:
            SAVE_PATH = f'/usr/WS1/hammel1/proj/checkpoints/bayes/{experiment_id}'

        print("Saving to :", SAVE_PATH)
        pyro.get_param_store().save(SAVE_PATH + '.params')

        save_data = input("save data > ")
        if save_data.lower().startswith('y'):
            dataset = training_generator.dataset
            dataset.save(SAVE_PATH)

def save():
    experiment_id = datetime.now().isoformat()

    if os.environ['HOSTNAME'] == 'fractal':
        SAVE_PATH = f'/hdd/bdhammel/checkpoints/bayes/{experiment_id}'
    else:
        SAVE_PATH = f'/usr/WS1/hammel1/proj/checkpoints/bayes/{experiment_id}'

    print("Saving to :", SAVE_PATH)
    pyro.get_param_store().save(SAVE_PATH + '.params')

    dataset = training_generator.dataset
    dataset.save(SAVE_PATH)

save()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Aug 14 12:21:18 2018

@author: timothy
"""
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
from scipy import stats
from sklearn import linear_model
from sklearn.decomposition import PCA
from sklearn.metrics import mean_squared_error
from sklearn.pipeline import Pipeline
import statsmodels.formula.api as sm
from tabulate import tabulate

def linear_func(x, m, b):
    return m*x + b

def sigmoid_func(x, k, x0, A):

    return A / (1 + np.exp(-k*(x-x0)))

class RegressionModel():
    def __init__(self, feature_object):
        self.features = feature_object.encoded_features
        self.dependent_variable = feature_object.dependent_variable
        self.independent_variables = feature_object.continuous_independent_variables
        self.X_train = feature_object.training_features()[self.independent_variables]
        self.y_train = feature_object.training_features()[self.dependent_variable]
        self.X_test = feature_object.standardized_features()[self.independent_variables]
        self.y_test = feature_object.standardized_features()[self.dependent_variable]
        self.question_choices = feature_object.question_choices()
        self.encoders = feature_object.encoders()
        self.simulating_features = feature_object.simulating_features()
        self.question_stats = feature_object.independent_variable_stats()
        self.trained_model = self.trained_pipeline()
        self.y_pred = self.prediction()

    def trained_pipeline(self):
        ols = sm.GLS(self.y_train, self.X_train)
        model = ols.fit(maxiter=1000)
        return model

    def prediction(self):

        print(self.trained_model.predict(self.X_train))
        return self.trained_model.predict(self.X_test)

    def visualize_fit(self):

        fig, ax = plt.subplots()
        predicted = self.y_pred
        ax.scatter(self.y_test, predicted, edgecolors=(0, 0, 0))
        ax.plot([self.y_test.min(), self.y_test.max()],
                 [self.y_test.min(), self.y_test.max()], 'k--', lw=4)
        ax.set_xlabel('Measured')
        ax.set_ylabel('Predicted')

        return ax

    def visualize_goodness(self):
        fit_df = pd.DataFrame({'truth':self.y_test,
                               'prediction': self.y_pred})
        num_ticks = len(self.question_choices[self.dependent_variable])
        fig, ax = plt.subplots()
        for i, choice in enumerate(self.question_choices[self.dependent_variable]):
            encoded_choice = self.encoders[self.dependent_variable].transform([choice])[0]
            temp = fit_df[fit_df['truth'] == encoded_choice]['prediction'].copy()
            if temp.empty:
                pass
            else:
                n, bins, patches =\
                    plt.hist(temp, np.linspace(0,num_ticks -1 ,5*num_ticks),
                             density=False, alpha=0.5, label = choice)
        plt.xticks(np.linspace(0,num_ticks -1, num_ticks),
                   self.question_choices[self.dependent_variable],
                   rotation = 45)
        plt.legend(loc = 0)
        return ax
    def simulate_outcomes(self, independent_variable):

        outcome_range =\
            np.sort(self.y_test.unique())
        idx = self.independent_variables.index(independent_variable)

        simulation_df = self.simulating_features
        standard_features = [
            feat+'_std' for feat in self.independent_variables
        ]
        simulation_df['prediction'] = (
            self.trained_model
            .predict(simulation_df[standard_features])
        )

        indep_vals = (
            simulation_df
            .groupby(
                [independent_variable+'_val',
                 independent_variable]
            )['prediction']
            .mean().reset_index()
        ).sort_values(by=independent_variable)
        response_range = (
            indep_vals[independent_variable].tolist()
        )

        predictions = (
            indep_vals['prediction'].tolist()
        )
        question_choices = (
            indep_vals[independent_variable+'_val'].tolist()
        )
        fig, ax = plt.subplots()
        ax.scatter(response_range, predictions)
        popt, pcov = curve_fit(linear_func, response_range, predictions)
        slope = popt[0]
        intercept = popt[1]
        if intercept > 0:
             title = independent_variable + ": y =" + str(round(slope,3)) +\
                     "x +" + str(round(intercept,2))
        elif intercept > 0:
             title = independent_variable + ": y =" + str(round(slope,3)) +\
                     "x -" + str(-1*round(intercept,2))
        else:
             title = independent_variable + ": y =" + str(round(slope,3)) + "x"
        #print(ticks[indep_var])

        #ax.scatter(response_range, linear_func(response_range, slope, intercept))
        ax.plot(
            response_range,
            [linear_func(resp, slope, intercept) for resp in response_range]
        )
        #ax.set_xlabel('Varied '+indep_var)
        ax.set_ylabel('Prediction')
        ax.set_yticks(np.sort(outcome_range))
        ax.set_yticklabels([str(outcome)+' ' + choice for outcome, choice in
                            zip(outcome_range, self.question_choices[self.dependent_variable])])
        ax.set_xticks(np.sort(response_range))
        ax.set_xticklabels(self.question_choices[independent_variable], rotation=90)
        ax.set_title(title)
        return ax

    def print_results(self):

        print(self.trained_model.summary())

class MixedClassificationModel():
    def __init__(self, feature_object):
        self.features = feature_object.encoded_features
        self.dependent_variable = feature_object.dependent_variable
        self.independent_variables = feature_object.independent_variables
        self.continuous_independent_variables  = feature_object.continuous_independent_variables
        self.dummies = feature_object.dummies
        self.standardized_features = feature_object.standardized_features()
        self.training_features = feature_object.training_features()
        self.balanced = self.balance_training()
        self.X_train = self.balanced[self.independent_variables]
        self.y_train = self.balanced['outcome']
        self.X_test = self.standardized_features[self.independent_variables]
        self.y_test = self.standardized_features['outcome']
        self.question_choices = feature_object.question_choices()
        self.encoders = feature_object.encoders()
        self.simulating_features = feature_object.simulating_features()
        self.question_stats = feature_object.independent_variable_stats()
        self.trained_model = self.trained_pipeline()
        self.y_pred = self.prediction()

    def balance_training(self):

        pos_samples = len(self.training_features\
         [self.training_features['outcome'] == 1])

        neg_samples = len(self.training_features\
         [self.training_features['outcome'] == 0])

        sample_size = min(pos_samples, neg_samples)
        balanced =\
            pd.concat([self.training_features
                        [self.training_features['outcome'] == 0]
                        .sample(n=sample_size),
                       self.training_features
                        [self.training_features['outcome'] == 1]
                        .sample(n=sample_size)
                      ])
        return balanced

    def trained_pipeline(self):
        np.random.seed(44)
        log_reg = sm.Logit(self.y_train, self.X_train)
        param_array = np.zeros(len(self.independent_variables)+1)
        iterations = 5
        for i in range(0,iterations):
            model = log_reg.fit(
                maxiter=5000,
                avextol=.0001,
                epsilon=.1,
                full_output=1,
                disp=0
            )
            params = list(model.params)
            param_array = [
                a + b for a,b in zip(param_array,params)
            ]
        start = [p/iterations for p in param_array]
        model = log_reg.fit(
            start_params=start,
            maxiter=5000,
            avextol=.0001,
            epsilon=.1,
            full_output=1,
            disp=1
        )
        return model

    def prediction(self):
        return self.trained_model.predict(self.X_test)

    def visualize_fit(self):

        fig, ax = plt.subplots()
        ax.scatter(self.y_test, self.y_pred, edgecolors=(0, 0, 0), alpha = .2)

        ax.set_xlabel('Measured')
        ax.set_ylabel('Predicted')

        return ax

    def visualize_goodness(self):
        fit_df = pd.DataFrame({'truth':self.y_test,
                               'prediction': [prob_a for prob_a in
                                              self.y_pred]})
        num_ticks = 2
        outcome_labels = ['negative', 'positive']
        outcome_choices = [0, 1]
        fig, ax = plt.subplots()
        for i, choice in enumerate(outcome_labels):
            encoded_choice = outcome_choices[i]
            temp = fit_df[fit_df['truth'] == encoded_choice]['prediction'].copy()
            if temp.empty:
                pass
            else:
                n, bins, patches =\
                    plt.hist(temp, np.linspace(0,num_ticks -1 ,5*num_ticks),
                             density=False, alpha=0.5, label = choice)
        plt.xticks(np.linspace(0,num_ticks -1, num_ticks),
                   outcome_labels,
                   rotation = 45)
        plt.legend(loc = 0)
        return ax

    def simulate_continuous_outcomes(self, independent_variable):
        outcome_labels = ['negative', 'positive']
        outcome_choices = [0, 1]
        simulation_df = (
            self.simulating_features
            .sort_values(by=independent_variable)
            .reset_index()
        )
        standard_features = [
            feat+'_std' for feat in self.independent_variables
        ]
        simulation_df['prediction'] = (
            self.trained_model
            .predict(simulation_df[standard_features])
        )

        indep_vals = (
            simulation_df
            .groupby(
                [independent_variable+'_val',
                 independent_variable]
            )['prediction']
            .mean().reset_index()
        ).sort_values(by=independent_variable)
        response_range = (
            indep_vals[independent_variable].tolist()
        )
        predictions = (
            indep_vals['prediction'].tolist()
        )
        question_choices = (
            indep_vals[independent_variable+'_val'].tolist()
        )
        return {
            "response_range":response_range,
            "predictions": predictions,
            "question_choices": question_choices
        }

    def plot_simulations(self, independent_variable):
        outcome_labels = ['negative', 'positive']
        outcome_choices = [0, 1]
        sim_dict = self.simulate_continuous_outcomes(independent_variable)
        response_range = sim_dict['response_range']
        predictions = sim_dict['predictions']
        question_choices = sim_dict['question_choices']
        fig, ax = plt.subplots()
        ax.scatter(response_range, predictions)
        x0_start = np.mean(response_range)
        A_start = 1
        try:
            k_start = .5
            popt, pcov =\
                 curve_fit(sigmoid_func, response_range,
                           predictions, p0 = [k_start, x0_start, A_start],
                           maxfev=1000)
            k = popt[0]
            x0 = popt[1]
            A = popt[2]
        except RuntimeError:
            try:
                k_start = -.5
                popt, pcov =\
                     curve_fit(sigmoid_func, response_range,
                               predictions, p0 = [k_start, x0_start, A_start],
                               maxfev=2000)
                k = popt[0]
                x0 = popt[1]
                A = popt[2]
            except RuntimeError:
                try:
                    k_start = 0
                    A_start = .5
                    popt, pcov =\
                    curve_fit(sigmoid_func, response_range,
                              predictions, p0 = [k_start, x0_start, A_start],
                              maxfev=2000)
                    k = popt[0]
                    x0 = popt[1]
                    A = popt[2]
                except RuntimeError:
                    k = 0
                    x0 = 0
                    A = 1

        title = (independent_variable + "p(Positive|x) ="+str(round(A,2))+
            "/(1+exp(-"+str(round(k,2))+ "(x +"+ str(round(x0,2))+")))"
        )
        ax.plot(
            response_range,
            [sigmoid_func(resp, k, x0, A) for resp in response_range]
        )
        #ax.set_xlabel('Varied '+indep_var)
        ax.set_ylabel('Prediction')
        ax.set_yticks(np.sort(outcome_choices))
        ax.set_yticklabels([str(outcome)+' ' + choice for outcome, choice in
                            zip(outcome_choices, outcome_labels)])
        ax.set_xticks(np.sort(response_range))
        ax.set_xticklabels(question_choices, rotation=90)
        ax.set_title(title)
        return ax

    def simulate_binary_outcomes(self, independent_variable):
        outcome_labels = ['negative', 'positive']
        outcome_choices = [0, 1]

        simulation_df = (
            self.simulating_features
            .sort_values(by=independent_variable)
            .reset_index()
        )
        standard_features = [
            feat+'_std' for feat in self.independent_variables
        ]
        simulation_df['prediction'] = (
            self.trained_model
            .predict(simulation_df[standard_features])
        )

        indep_vals = (
            simulation_df
            .groupby(
                [independent_variable+'_val',
                 independent_variable]
            )['prediction']
            .mean().reset_index()
        ).sort_values(by=independent_variable)
        #print(indep_vals)
        response_range = (
            indep_vals[independent_variable].tolist()
        )
        predictions = (
            indep_vals['prediction'].tolist()
        )

        fig, ax = plt.subplots()
        ax.scatter(response_range, predictions)
        #ax.set_xlabel('Varied '+indep_var)
        ax.set_ylabel('Prediction')
        ax.set_yticks(np.sort(outcome_choices))
        ax.set_yticklabels([str(outcome)+' ' + choice for outcome, choice in
                            zip(outcome_choices, outcome_labels)])
        ax.set_xticks(np.sort(response_range))
        ax.set_xticklabels(['no','yes'], rotation=90)
        ax.set_title(independent_variable)
        return ax

    def print_results(self):

        print(self.trained_model.summary())

    def result_df(self):
        return self.trained_model.pvalues

    def odds_ratio(self):
        odds_increase = [
            int(round(100*(np.exp(param) - 1),0))
            for param in self.trained_model.params
        ]
        odds_ratio_table = pd.DataFrame(
            {'independent_variable':self.independent_variables,
             'odds_%_increase': odds_increase
            }
        )
        odds_ratio_table['mu'] = odds_ratio_table.apply(
            lambda row:
            round(self.question_stats[row['independent_variable']]['mu'], 2),
            axis=1
        )
        odds_ratio_table['std'] = odds_ratio_table.apply(
            lambda row:
            round(self.question_stats[row['independent_variable']]['std'],2),
            axis=1
        )
        #odds_ratio_table.to_string(index=False))
        return odds_ratio_table
        #print(np.exp(self.trained_model.params))


import time
import datetime
import re

class CronSchedule(object):
    _minutes = None
    _hours = None
    _doms = None # days of the month
    _months = None
    _dows = None # days of the week

    def __init__(self,cron_line):
        kwargs = parseCronLine(cron_line)

        self._minutes = kwargs.get('minutes')
        self._hours = kwargs.get('hours')
        self._doms = kwargs.get('doms')
        self._months = kwargs.get('months')
        self._dows = kwargs.get('dows')

    def __eq__(self,other):
        if not isinstance(other,CronSchedule):
            return False

        return (self._minutes == other._minutes and
                self._hours   == other._hours and
                self._doms    == other._doms and
                self._months  == other._months and
                self._dows    == other._dows)

    def _getNextMonth(self, current):
        # If the current month is a valid option, try to parse for the next valid day
        if current.month in self._months:
            try:
                return self._getNextDay(current)
            except NoMatch:
                pass

        # Find the next month if it occurs in the current year
        for month in self._months:
            if month > current.month:
                return self._getFirstDay(current.replace(month=month,
                                                         day=1,
                                                         hour=self._hours[0],
                                                         minute=self._minutes[0]))

        # No remaining months this year, go to next year
        return self._getFirstDay(current.replace(year=current.year+1,
                                                 month=self._months[0],
                                                 day=1,
                                                 hour=self._hours[0],
                                                 minute=self._minutes[0]))

    def _getFirstDay(self,current):
        all_doms = False
        all_dows = False
        fdom = current.replace(day=1)

        if len(self._doms) == 31:
            all_doms = True

        if len(self._dows) == 7:
            all_dows = True

        # All days of the week and days of the month, return day = 1
        if all_doms and all_dows:
            return fdom

        # All days of the week, return first day of the month
        if all_dows:
            return current.replace(day=self._doms[0])

        dows = self._dows

        if dows[0] == 0:
            import copy
            dows = copy.copy(dows)
            del dows[0]
            dows.append(7)

        current_dow = fdom.isoweekday()

        # If the current day of the week is in the days of the week, return
        if not all_dows and current_dow in dows:
            return fdom

        # If the first day of the month is specifically listed, return it
        if not all_doms and self._doms[0] == 1:
            return fdom

        dow_distance = None
        dom_distance = None

        distance = None

        if not all_dows:
            for dow in dows:
                if dow > current_dow:
                    dow_distance = dow-current_dow
                    break

        if dow_distance is None:
            dow_distance = dows[0]+7-current_dow

        if not all_doms:
            dom_distance = self._doms[0] - 1

        if not dow_distance is None and not dom_distance is None:
            if dow_distance <= dom_distance:
                distance = dow_distance
            else:
                distance = dom_distance

        if distance is None and not dow_distance is None:
            distance = dow_distance

        if distance is None and not dom_distance is None:
            distance = dom_distance

        return current.replace(day=distance + 1)

    def _getNextDay(self, current):
        all_doms = False
        all_dows = False

        if len(self._doms) == 31:
            all_doms = True

        if len(self._dows) == 7:
            all_dows = True

        dows = self._dows

        if dows[0] == 0:
            import copy
            dows = copy.copy(dows)
            del dows[0]
            dows.append(7)

        # If the current day is a valid option, try to parse for the next valid hour
        if ((all_doms and all_dows) or
            (current.day in self._doms and all_dows) or
            (current.isoweekday() in dows and not all_dows)):
            try:
                return self._getNextHour(current)
            except NoMatch:
                pass

        distance = None

        if all_doms and all_dows:
            distance = 1
        else:
            dow_distance = None
            dom_distance = None

            if not all_dows:
                current_dow = current.isoweekday()

                for dow in dows:
                    if dow > current_dow:
                        dow_distance = dow-current_dow
                        break

                if dow_distance is None:
                    dow_distance = dows[0]+7-current_dow

            if not all_doms:
                for dom in self._doms:
                    if dom > current.day:
                        dom_distance = dom - current.day
                        break

            if not dow_distance is None and dom_distance is not None:
                if dow_distance <= dom_distance:
                    distance = dow_distance
                else:
                    distance = dom_distance

            if distance is None and dow_distance is not None:
                distance = dow_distance

            if distance is None and dom_distance is not None:
                distance = dom_distance

            if distance is None or distance <= 0:
                raise NoMatch('no matching days in month')

        next_day = (current.replace(hour=self._hours[0],
                                    minute=self._minutes[0]) +
                    distance * datetime.timedelta(days=1))

        if next_day.month != current.month:
          raise NoMatch('no remaining days in the current month')

        return next_day

    def _getNextHour(self, current):
        if current.hour in self._hours:
            try:
                return self._getNextMinute(current)
            except NoMatch:
                pass

        for hour in self._hours:
            if hour > current.hour:
                return current.replace(hour=hour,minute=self._minutes[0])

        raise NoMatch('no remaining hours in the current day')

    def _getNextMinute(self, current):
        for minute in self._minutes:
            if minute > current.minute:
                return current.replace(minute=minute)

        raise NoMatch('no remaining minutes in the current hour')

    def getNextEntry(self,current=None):
        if current is None:
            current = datetime.datetime.now()

        if not isinstance(current,datetime.datetime):
            raise ValueError('current value must be a datetime.datetime object')

        return self._getNextMonth(current.replace(second=0,microsecond=0))

    def getDelayForNext(self):
        next = self.getNextEntry()

        return time.mktime(next.timetuple()) - time.time()

class InvalidCronLine(Exception):
  pass

class InvalidCronEntry(Exception):
  pass

class NoMatch(Exception):
  pass

_cronStepRe = re.compile('^\*/(?P<step>\d{1,2})$')
_cronRangeRe = re.compile('^(?P<begin>\d{1,2})-(?P<end>\d{1,2})$')
_cronRangeStepRe = re.compile('^(?P<begin>\d{1,2})-(?P<end>\d{1,2})/(?P<step>\d{1,2})$')

def parseCronLine(line):
      """
      Parse a standard cron string (minus the command) and return them as a
      dictionary. The syntax for this was pulled from the
      Wikipedia page: http://en.wikipedia.org/wiki/Cron
      Currently, there is no support for textual days of the week
      (i.e. Monday,Tuesday).
      """
      if not line:
          raise InvalidCronLine('Empty cron line provided')

      try:
          line = re.split('\s+',line.strip())
      except:
          raise InvalidCronLine('Cron line must be a string type')

      if len(line) != 5:
          raise InvalidCronLine('Improper number of elements encountered: %s' % len(line))

      schedule = {}

      schedule['minutes'] = parseCronEntry(line[0],0,59)
      schedule['hours']   = parseCronEntry(line[1],0,23)
      schedule['doms']    = parseCronEntry(line[2],1,31)
      schedule['months']  = parseCronEntry(line[3],1,12)
      schedule['dows']    = parseCronEntry(line[4],0,6)

      return schedule

def parseCronEntry(entry,min,max):
    """Parse a single cron entry for something like hours or minutes from a cron
    scheduling line.  The given min and max are used to verify that results are
    in the proper range. The following formats are supports:
    * => All in the available range
    */5 => Only those values in the available range that are divisible by five
    1-5 => The range of 1-5
    And any combination of the above using commas to separate the entries.
    """

    if not entry:
        raise InvalidCronEntry('Empty cron entry')

    try:
        min = int(min)
        max = int(max)
    except ValueError:
        raise ValueError('minimum and maximum should be convertible to integers')

    if min > max:
        raise ValueError('minimum must be less than or equal to maximum')

    if min < 0:
        raise ValueError('minimum must be non-negative')

    try:
        entry = entry.split(',')
    except AttributeError:
        raise InvalidCronEntry('Non-string cron-entry')

    total = set()

    for e in entry:
        try:
            int_val = int(e)
        except ValueError:
            pass
        else:
            total.add(int_val)
            continue

        begin = None
        end = None
        step = 1

        if e == '*':
            begin = min
            end = max + 1

        if begin is None:
            #If this match works, then it is of the form */int
            match = _cronStepRe.search(e)

            if not match is None:
                begin = min
                end = max + 1
                step = int(match.group('step'))

        if begin is None:
            match = _cronRangeRe.search(e)

            if not match is None:
                begin = int(match.group('begin'))
                end = int(match.group('end')) + 1
                step = 1

        if begin is None:
            match = _cronRangeStepRe.search(e)

            if not match is None:
                begin = int(match.group('begin'))
                end = int(match.group('end')) + 1
                step = int(match.group('step'))

        if (begin is not None and begin < end and step > 0 and
            begin >= min and end <= max + 1):

            # need to align the start properly
            while begin % step != 0 and begin < end:
                begin += 1

            if begin == end and begin % step != 0:
                raise InvalidCronEntry('Invalid range or step specified: %s-%s/%s' % (begin,end,step))

            total.update(range(begin,end,step))
        elif not begin is None:
            raise InvalidCronEntry('Invalid range or step specified: %s-%s/%s' % (begin,end,step))

    if len(total) == 0:
        raise InvalidCronEntry('Empty cron entry')

    total = list(total)
    total.sort()

    if total[0] < min or total[len(total)-1] > max:
        raise InvalidCronEntry('Value, %s-%s, out of allowed range: %s-%s' % (total[0],total[len(total)-1],min,max))

    return total


import asyncio
import copy
import logging
from dataclasses import dataclass
from typing import Dict, Optional

from quadradiusr_server.config import GameConfig
from quadradiusr_server.constants import QrwsCloseCode
from quadradiusr_server.db.base import Game
from quadradiusr_server.db.base import User
from quadradiusr_server.db.repository import Repository
from quadradiusr_server.game_state import GameState, Piece, Tile, GameBoard
from quadradiusr_server.notification import NotificationService
from quadradiusr_server.powers import PowerDefinition, PowerRandomizer
from quadradiusr_server.qrws_connection import BasicConnection, QrwsConnection
from quadradiusr_server.qrws_messages import Message, KickMessage, GameStateMessage, MoveMessage, \
    ActionResultMessage, GameStateDiffMessage, ApplyPowerMessage

@dataclass
class ActionResult:
    is_legal: bool
    reason: Optional[str] = None
    old_game_state: Optional[GameState] = None
    new_game_state: Optional[GameState] = None

class ActionApplicationContext:
    def __init__(self, game: Game) -> None:
        self._game = game
        self._old_game_state = copy.deepcopy(self._game.game_state_)

        # we need to make sure sqlalchemy will see the change
        game_state: GameState = copy.deepcopy(self._game.game_state_)
        self._game.game_state_ = game_state
        self._new_game_state = game_state

    @property
    def old_game_state(self):
        return self._old_game_state

    @property
    def new_game_state(self):
        return self._new_game_state

    @property
    def game_state(self):
        return self.new_game_state

    def legal_result(self):
        return ActionResult(
            is_legal=True,
            old_game_state=self.old_game_state,
            new_game_state=self.game_state,
        )

class GameInProgress:
    def __init__(self, game: Game, repository: Repository, config: GameConfig) -> None:
        self.config = config
        self.game_id = game.id_
        self.repository = repository

        self.player_connections: Dict[str, Optional[GameConnection]] = {
            game.player_a_id_: None,
            game.player_b_id_: None,
        }

        self.power_randomizer: PowerRandomizer = config.get_power_randomizer()
        self.power_definitions: Dict[str, PowerDefinition] = config.get_power_definitions()

    async def get_game(self) -> Game:
        return await self.repository.game_repository.get_by_id(self.game_id)

    def is_player_connected(self, player_id):
        return self.player_connections.get(player_id) is not None

    async def connect_player(self, connection: 'GameConnection'):
        player_id = connection.user_id
        if player_id not in self.player_connections:
            raise ValueError(
                f'User {player_id} is not part of the game {self.game_id}')

        old_connection = self.player_connections.get(player_id)
        if old_connection:
            await old_connection.kick()

        self.player_connections[player_id] = connection

        logging.info(f'User {connection.user_id} connected to game {self.game_id}')

    async def disconnect_player(self, connection: 'GameConnection'):
        player_id = connection.user_id
        if player_id in self.player_connections:
            self.player_connections[player_id] = None

        logging.info(f'User {connection.user_id} disconnected from game {self.game_id}')

    async def apply_power(
            self, player: User,
            power_id: str) -> ActionResult:
        ctx = ActionApplicationContext(await self.get_game())
        game_state: GameState = ctx.game_state

        if game_state.finished:
            return ActionResult(
                is_legal=False,
                reason='Game is finished',
            )

        if game_state.current_player_id != player.id_:
            return ActionResult(
                is_legal=False,
                reason='Not your turn',
            )

        if power_id not in game_state.board.powers:
            return ActionResult(
                is_legal=False,
                reason='Power does not exist',
            )

        power = game_state.board.powers[power_id]

        if power.tile_id is not None:
            return ActionResult(
                is_legal=False,
                reason='Power has not been captured',
            )

        if player.id_ not in power.authorized_player_ids:
            return ActionResult(
                is_legal=False,
                reason='You do not own the power',
            )

        pd_id = power.power_definition_id
        pd = self.power_definitions[pd_id]

        pd.apply(game_state, power_id)

        return ctx.legal_result()

    async def make_move(
            self, player: User,
            piece_id: str,
            tile_id: str) -> ActionResult:
        game = await self.get_game()
        ctx = ActionApplicationContext(game)
        # check move

        game_state: GameState = ctx.game_state

        tiles = game_state.board.tiles
        pieces = game_state.board.pieces
        other_player_id = game.get_other_player_id(player.id_)

        if game_state.finished:
            return ActionResult(
                is_legal=False,
                reason='Game is finished',
            )

        if game_state.current_player_id != player.id_:
            return ActionResult(
                is_legal=False,
                reason='Not your turn',
            )

        if piece_id not in pieces:
            return ActionResult(
                is_legal=False,
                reason='Piece does not exist',
            )
        if tile_id not in tiles:
            return ActionResult(
                is_legal=False,
                reason='Destination tile does not exist',
            )

        piece: Piece = pieces[piece_id]

        if piece.owner_id != player.id_:
            return ActionResult(
                is_legal=False,
                reason='Cannot move opponent\'s piece',
            )

        src_tile: Tile = tiles[piece.tile_id]
        dest_tile: Tile = tiles[tile_id]

        if dest_tile.elevation > src_tile.elevation + 1:
            return ActionResult(
                is_legal=False,
                reason='Destination tile too high',
            )

        # perform move
        game_state.moves_played += 1

        await self._capture_pieces(dest_tile, game_state.board)
        await self._capture_powers(dest_tile, piece, game_state.board)

        piece.tile_id = tile_id
        game_state.current_player_id = other_player_id

        if not any(piece.owner_id == other_player_id for piece in pieces.values()):
            game_state.finished = True
            game_state.winner_id = player.id_
        else:
            self._handle_power_spawning(game_state)

        return ctx.legal_result()

    async def _capture_pieces(
            self, dest_tile: Tile,
            board: GameBoard):
        captured_pieces = []
        for opid, other_piece in board.pieces.items():
            if board.tiles[other_piece.tile_id].position == dest_tile.position:
                captured_pieces.append(opid)
        for captured_piece in captured_pieces:
            del board.pieces[captured_piece]

    async def _capture_powers(
            self, dest_tile: Tile,
            piece: Piece,
            board: GameBoard):
        for power_id, power in board.powers.items():
            if power.tile_id is None:
                continue
            if board.tiles[power.tile_id].position == dest_tile.position:
                power.tile_id = None
                power.piece_id = piece.id
                power.authorized_player_ids.append(piece.owner_id)

    def _handle_power_spawning(self, game_state: GameState):
        self.power_randomizer.after_move(
            game_state, list(self.power_definitions.values()))

class GameConnection(BasicConnection):

    def __init__(
            self, qrws: QrwsConnection,
            game_in_progress: GameInProgress,
            user: User,
            notification_service: NotificationService,
            repository: Repository) -> None:
        super().__init__(qrws, user, notification_service, repository)
        self.game_in_progress = game_in_progress

    async def on_ready(self, user: User):
        game = await self.game_in_progress.get_game()
        game_state = game.game_state_
        serialized, etag = game_state.serialize_with_etag_for(user)
        await self.qrws.send_message(
            GameStateMessage(
                recipient_id=user.id_,
                game_state=serialized,
                etag=etag,
            ))

    async def handle_message(self, user: User, message: Message) -> bool:
        if await super().handle_message(user, message):
            return True

        if isinstance(message, MoveMessage):
            result = await self.game_in_progress.make_move(
                user,
                piece_id=message.piece_id,
                tile_id=message.tile_id,
            )

            if result.is_legal:
                logging.debug(
                    f'User {user.friendly_name} in game {self.game_in_progress.game_id} '
                    f'made a move {message.piece_id}->{message.tile_id}')
            else:
                logging.debug(
                    f'User {user.friendly_name} in game {self.game_in_progress.game_id} '
                    f'has tried to make an illegal move {message.piece_id}->{message.tile_id}, '
                    f'reason: {result.reason}')

            await self._post_action(result)
            return True
        elif isinstance(message, ApplyPowerMessage):
            result = await self.game_in_progress.apply_power(
                user,
                power_id=message.power_id,
            )

            if result.is_legal:
                logging.debug(
                    f'User {user.friendly_name} in game {self.game_in_progress.game_id} '
                    f'applied power {message.power_id}')
            else:
                logging.debug(
                    f'User {user.friendly_name} in game {self.game_in_progress.game_id} '
                    f'has illegally tried to apply a power {message.power_id}, '
                    f'reason: {result.reason}')

            await self._post_action(result)
            return True
        else:
            return False

    async def _post_action(self, action_result: ActionResult):
        async def on_commit(conn: GameConnection, result: ActionResult):
            await conn.qrws.send_message(ActionResultMessage(
                is_legal=result.is_legal,
                reason=result.reason,
            ))
            if not result.is_legal:
                return
            coroutines = []
            for player_id, conn in conn.game_in_progress.player_connections.items():
                if conn is None:
                    continue
                diff, etag_from, etag_to = GameState.serialize_diff_with_etag_for(
                    from_=result.old_game_state,
                    to=result.new_game_state,
                    user_id=player_id,
                )
                coroutines.append(conn.qrws.send_message(GameStateDiffMessage(
                    recipient_id=player_id,
                    game_state_diff=diff,
                    etag_from=etag_from,
                    etag_to=etag_to,
                )))
            await asyncio.gather(*coroutines)

        await self.repository.synchronize_transaction_on_commit(
            on_commit(self, action_result))

    async def kick(self):
        await self.qrws.send_message(KickMessage(
            reason='Connected from another location',
        ))
        await self.qrws.close(
            QrwsCloseCode.CONFLICT,
            'Connected from another location')


# -*- coding: utf-8 -*-
import scrapy

class GdpDebtSpider(scrapy.Spider):
    name = 'gdp_debt'
    allowed_domains = ['worldpopulationreview.com']
    start_urls = ['http://worldpopulationreview.com/countries/countries-by-national-debt/']

    def parse(self, response):
        countries = response.xpath("//table[@class='table table-striped']/tbody/tr")
        for country in countries:
            name = country.xpath(".//td[1]/a/text()").get()
            debt = country.xpath(".//td[2]/text()").get()
            population = country.xpath(".//td[3]/text()").get()

            yield {
                'Year' : 2019,
                'Country_Name': name,
                'National Debt to GDP Ratio': debt,
                'Country_Population': population
            }


#!/usr/bin/python
from RPLCD.i2c import CharLCD
from gpiozero import RotaryEncoder, Button, Buzzer
import threading
import os
import sys
import time

class One_Wire(threading.Thread):
    Device_Temp = ['00.0','00.0','00.0']
    Device_ID = []

    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        base_dir = '/sys/devices/w1_bus_master1/'
        dir = open(base_dir + 'w1_master_slaves' , 'r')
        for x in range(3):
            self.Device_ID.append(dir.readline()[:15])
        dir.close()

        while True:
            # Find 1-Wire sensor
            i = 0
            for x in self.Device_ID:
                self.Device_Temp[i] = self.read_temp(base_dir + x)
                i = i + 1

            time.sleep(2)

    def read_temp(self, device_value):

        # Read temp of sensor
        file = open(device_value + '/w1_slave', 'r')
        filecontent = file.read()
        file.close()

        # Format temp
        stringvalue = filecontent.split("\n")[1].split(" ")[9]
        temp = float(stringvalue[2:]) / 1000

        return (str('%3.1f' % temp))

class Stop_Watch(threading.Thread):
    Display_value = ['0','30','00']
    Timer_running = False
    Timer_value = 1800

    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        print("stop watch running")
        rotor = RotaryEncoder(16, 20)
        rotor.steps = 1800
        btn = Button(21, pull_up=False)

        done = threading.Event()

        btn.when_released = self.toggle_timer

        rotor.when_rotated_clockwise = self.rotate_clockwise
        rotor.when_rotated_counter_clockwise = self.rotate_counter_clockwise

        done.wait()

    def toggle_timer(self):
        if self.Timer_running == False:
            self.Timer_running = True
        else:
            self.Timer_running = False

        print("Timer_running: " + str(self.Timer_running))

    def rotate_clockwise(self):
        if self.Timer_value < 9000 and self.Timer_running == False:
            self.Timer_value = self.Timer_value + 60
            self.format_time()

    def rotate_counter_clockwise(self):
        if self.Timer_value > 60 and self.Timer_running == False:
            self.Timer_value = self.Timer_value - 60
            self.format_time()

    def format_time(self):
        hours = int(self.Timer_value / 3600)
        minutes = int((self.Timer_value-hours*3600) / 60)
        seconds = int((self.Timer_value-hours*3600-minutes*60))

        self.Display_value[0] = str(hours)
        self.Display_value[1] = str(minutes)
        self.Display_value[2] = str(seconds)

def main():

    try:

        thread_one_wire = One_Wire()
        thread_one_wire.start()

        thread_stop_watch = Stop_Watch()
        thread_stop_watch.start()

        lcd = CharLCD('PCF8574', 0x27)
        degree = (0b00010, 0b00101, 0b00101, 0b00010, 0b00000, 0b00000, 0b00000, 0b00000)
        lcd.create_char(0, degree)

        sandclock = (0b00000, 0b11111, 0b01010, 0b00100, 0b00100, 0b01010, 0b11111, 0b00000)
        lcd.create_char(1, sandclock)

        lcd.cursor_pos = (0, 0)
        lcd.write_string('Temp 1: 00.0\x00C')
        lcd.cursor_pos = (1, 0)
        lcd.write_string('Temp 2: 00.0\x00C')
        lcd.cursor_pos = (2, 0)
        lcd.write_string('Temp 3: 00.0\x00C')
        lcd.cursor_pos = (3, 0)
        lcd.write_string('Timer : 00:00:00')

        Display_ref = ['0','02','03']

        print("End program with CRLT+C !!")

        while(True):
            for i in range(3):
                lcd.cursor_pos = (i, 8)
                lcd.write_string(thread_one_wire.Device_Temp[i])

            if Display_ref[0] != thread_stop_watch.Display_value[0]:
                lcd.cursor_pos = (3, 9)
                Display_ref[0] = thread_stop_watch.Display_value[0]
                lcd.write_string(thread_stop_watch.Display_value[0])
                #print("treffer hour")

            if Display_ref[1] != thread_stop_watch.Display_value[1]:
                lcd.cursor_pos = (3, 11)
                Display_ref[1] = thread_stop_watch.Display_value[1]
                #print("treffer min")
                if len(thread_stop_watch.Display_value[1]) > 1:
                    lcd.write_string(thread_stop_watch.Display_value[1])
                else:
                    lcd.write_string("0" + thread_stop_watch.Display_value[1])

            if Display_ref[2] != thread_stop_watch.Display_value[2]:
                lcd.cursor_pos = (3, 14)
                Display_ref[2] = thread_stop_watch.Display_value[2]
                #print("treffer sec")
                if len(thread_stop_watch.Display_value[2]) > 1:
                    lcd.write_string(thread_stop_watch.Display_value[2])
                else:
                    lcd.write_string("0" + thread_stop_watch.Display_value[2])

            if thread_stop_watch.Timer_running == True:
                lcd.cursor_pos = (3, 18)
                lcd.write_string('\x01')
            else:
                lcd.cursor_pos = (3, 18)
                lcd.write_string(' ')

            time.sleep(.5)

    except Exception as err:
        print(err)

if __name__ == "__main__":
    main()


from collections import defaultdict
def solution(clothes):
  clothesDict = defaultdict(lambda:0)

  for value, key in clothes:
    clothesDict[key] += 1

  result = 1

  for key in clothesDict:
    result *= (clothesDict[key]+1)

  return result-1 #어떤 옷도 착용하지않는 경우 제외


from typing import List, Optional

class Solution:
    def findMin(self, nums: List[int]) -> Optional[int]:
        n = len(nums)
        if n == 1:
            return nums[0]
        if nums[0] < nums[-1]:
            return nums[0]

        for i in range(1,n):
            if nums[i] > nums[i+1]:
                return nums[i+1]

if __name__ == '__main__':
    s = Solution()
    print(s.findMin([3,4,5,1,2]))


import datetime
import json
import os

import boto3

def delete_recovery_point(client: boto3.client, vault_name: str, arn: str) -> None:
    "Deletes a recovery point with the given arn from a given vault"
    client.delete_recovery_point(BackupVaultName=vault_name, RecoveryPointArn=arn)

def get_old_recovery_points(
    client: boto3.client, vault_name: str, created_before: datetime.datetime
) -> list:
    "Returns a list of ARNs for all recovery points in the given vault created before a given date"
    response = client.list_recovery_points_by_backup_vault(
        BackupVaultName=vault_name, ByCreatedBefore=created_before
    )

    return response["RecoveryPoints"]

if __name__ == "__main__":
    client = boto3.client("backup")

    created_before = datetime.datetime.strptime(
        os.environ["CREATED_BEFORE"], "%Y-%m-%d"
    )
    vault_name = os.environ["VAULT_NAME"]

    recovery_points = get_old_recovery_points(client, vault_name, created_before)

    for recovery_point in recovery_points:
        delete_recovery_point(client, vault_name, recovery_point["RecoveryPointArn"])
        print("Deleted {}".format(recovery_point["RecoveryPointArn"]))


# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load in

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
from imageio import imread
import matplotlib.pyplot as plt

# Input data files are available in the "../input/" directory.
# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory

import os
print(os.listdir("../input"))

# Any results you write to the current directory are saved as output.
path_train = '../input/train/'
path_test = '../input/test/'

train_segmentation = pd.read_csv('../input/train_ship_segmentations.csv')
test_segmentation = pd.read_csv('../input/test_ship_segmentations.csv')
# now lets check the format of our sample submission file
sample_submission = pd.read_csv('../input/sample_submission.csv')
sample_submission.head()
# referecens for two kernel
# ref: https://www.kaggle.com/paulorzp/run-length-encode-and-decode
# ref: https://www.kaggle.com/inversion/run-length-decoding-quick-start

from skimage.morphology import label
def multi_rle_encode(img):
    labels = label(img[:, :, 0])
    return [rle_encode(labels==k) for k in np.unique(labels[labels>0])]

def rle_decode(mask_rle, shape=(768, 768)):
    '''
    mask_rle: run-length as string formated (start length)
    shape: (height,width) of array to return
    Returns numpy array, 1 - mask, 0 - background

    '''
    s = mask_rle.split()
    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]
    starts -= 1
    ends = starts + lengths
    img = np.zeros(shape[0]*shape[1], dtype=np.uint8)
    for lo, hi in zip(starts, ends):
        img[lo:hi] = 1
    return img.reshape(shape).T  # Needed to align to RLE direction

def rle_encode(img):
    '''
    img: numpy array, 1 - mask, 0 - background
    Returns run length as string formated
    '''
    pixels = img.T.flatten()
    pixels = np.concatenate([[0], pixels, [0]])
    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1
    runs[1::2] -= runs[::2]
    return ' '.join(str(x) for x in runs)

# ref https://www.kaggle.com/kmader/baseline-u-net-model-part-1
def masks_as_image(in_mask_list):
    # Take the individual ship masks and create a single mask array for all ships
    all_masks = np.zeros((768,768))
    for mask in img_mask:
        all_masks += rle_decode(mask)
    return all_masks
# now lets do some EDA on our training data
train_segmentation.head()
ImageId = '00021ddc3.jpg'
img = imread('../input/train/'+ImageId)
img_mask = train_segmentation.loc[train_segmentation['ImageId'] == ImageId, 'EncodedPixels']
print(img_mask)
print('Number os images with the same given ImageId:', len(img_mask))
img_mask = img_mask.tolist()
# Looking at this ImageId we can see that is not a rule that in the train data the id are unique,
all_masks_img0 = masks_as_image(img_mask)
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(16, 16))

# 'turn off' the axis for all subplots
for i in range(3):
    axes[i].axis('off')

# plot the original image
axes[0].imshow(img)
# plot the masks of the image
axes[1].imshow(all_masks_img0)
# plot the image and all masks with an alpha equals 0.4
axes[2].imshow(img)
axes[2].imshow(all_masks_img0, alpha=0.4)
plt.tight_layout(h_pad=0.1, w_pad=0.1)
plt.show()
fig_e, axes_e = plt.subplots(1, 2, figsize=(12,12))
axes_e[0].axis('off')
axes_e[1].axis('off')
axes_e[0].set_title('Image$_0$')
axes_e[0].imshow(all_masks_img0)
# encode the image loaded earlier
rle_img1 = multi_rle_encode(img)
# read the img from the rle_img1
img1 = rle_decode(rle_img1)
axes_e[1].set_title('Image$_1$')
axes_e[1].imshow(img1)
plt.tight_layout(h_pad=0.1, w_pad=0.1)
plt.show()


import sys
from collections import deque

input = sys.stdin.readline
T = int(input())
for _ in range(T):
  N, M = map(int, input().split())
  q = deque()
  temp = list(map(int, input().split()))
  for i in range(len(temp)):
    q.append((temp[i], i))

  cnt = 0
  while (q):
    max_val = q[0][0]
    max_idx = 0
    for idx, val in enumerate(q):
      if int(val[0]) > max_val:
        max_val = int(val[0])
        max_idx = idx
    for i in range(max_idx):
      q.append(q.popleft())
    x, y = q.popleft()
    cnt += 1
    if y == M:
      print(cnt)
      break


#!/usr/bin/env python3

"""Represent one leg of a trip."""

import datetime
import numpy
import shapely

__all__ = ["TripLeg"]

class TripLeg:
    """
    Represent one leg of a trip.

    This is a base class, use one the specific classes,
    e.g., TransitLeg, or DirectLeg
    """

    COLUMNS = [
        "transport_mode",
        "departure_time",
        "distance",
        "travel_time",
        "wait_time",
        "route",
        "geometry",
    ]

    def __init__(
        self,
        transport_mode=None,
        departure_time=numpy.datetime64("NaT"),
        distance=None,
        travel_time=datetime.timedelta(seconds=0),
        wait_time=datetime.timedelta(seconds=0),
        route=None,
        geometry=shapely.LineString(),
    ):
        """
        Represent one leg of a trip.

        This is a base class, use one the specific classes,
        e.g., TransitLeg, or DirectLeg

        Arguments
        =========
        transport_mode : r5py.TransportMode
            mode of transport this trip leg was travelled
        departure_time : datetime.datetime,
        distance : float
            distance covered by this trip leg, in metres
        travel_time : datetime.timedelta
            time spent travelling on this trip leg
        wait_time : datetime.timedelta
            time spent waiting for a connection on this trip leg
        route : str
            public transport route used for this trip leg
        geometry : shapely.LineString
            spatial representation of this trip leg
        """
        self.transport_mode = transport_mode
        self.departure_time = departure_time
        self.distance = distance
        self.travel_time = travel_time
        self.wait_time = wait_time
        self.route = route
        self.geometry = geometry

    def __add__(self, other):
        from .trip import Trip

        if isinstance(other, self.__class__):
            trip = Trip([self, other])
            return trip
        elif isinstance(other, Trip):
            other.legs = [self] + other.legs
            return other
        else:
            raise TypeError(
                f"unsupported operand type(s) for '+': '{type(other)}' and '{type(self)}'"
            )

    def __radd__(self, other):
        from .trip import Trip

        if other == 0:  # first iteration of sum()
            return self
        elif isinstance(other, Trip):
            other.legs.append(self)
            return other
        else:
            return self.__add__(other)

    def __gt__(self, other):
        if isinstance(other, TripLeg):
            return (self.travel_time + self.wait_time) > (
                other.travel_time + other.wait_time
            )

    def __ge__(self, other):
        if isinstance(other, TripLeg):
            return (self.travel_time + self.wait_time) >= (
                other.travel_time + other.wait_time
            )

    def __lt__(self, other):
        if isinstance(other, TripLeg):
            return (self.travel_time + self.wait_time) < (
                other.travel_time + other.wait_time
            )

    def __le__(self, other):
        if isinstance(other, TripLeg):
            return (self.travel_time + self.wait_time) <= (
                other.travel_time + other.wait_time
            )

    def __repr__(self):
        try:
            first_point = self.geometry.coords[0]
            last_point = self.geometry.coords[-1]
            _repr = (
                "<"
                f"{self.__class__.__name__}: "
                f"{self.transport_mode}, "
                f"{self.distance}m, "
                f"{self.travel_time.total_seconds():0.1f}s, "
                f"{first_point} -> {last_point}"
                ">"
            )
        except (AttributeError, IndexError):
            _repr = f"<{self.__class__.__name__}>"
        return _repr

    def as_table_row(self):
        """
        Return a table row (list) of this trip leg’s details.

        Returns
        =======
        list : detailed information about this trip leg: ``transport_mode``,
        ``departure_time``, ``distance``, ``travel_time``, ``wait_time``,
        ``route``, ``geometry``
        """
        return [getattr(self, column) for column in self.COLUMNS]


import logging

from pytos.common.base_types import XML_Object_Base, XML_List
from pytos.common.logging.definitions import XML_LOGGER_NAME
from pytos.common.definitions import xml_tags
from pytos.common.functions.xml import get_xml_text_value, get_xml_int_value, get_xml_node
from pytos.securetrack.xml_objects.rest.rules import Shadowed_Rule

logger = logging.getLogger(XML_LOGGER_NAME)

class Generic_Cleanup_List(XML_List):
    def __init__(self, count, total, score, cleanups):
        self.count = count
        self.total = total
        self.score = score
        super().__init__(xml_tags.Elements.CLEANUPS, cleanups)

    @classmethod
    def from_xml_node(cls, xml_node):
        """
        Initialize the object from a XML node.
        :param xml_node: The XML node from which all necessary parameters will be parsed.
        :type xml_node: xml.etree.Element
        """
        count = get_xml_int_value(xml_node, xml_tags.Elements.COUNT)
        total = get_xml_int_value(xml_node, xml_tags.Elements.TOTAL)
        score = get_xml_int_value(xml_node, xml_tags.Elements.SCORE)
        cleanups = []
        for user_node in xml_node.iter(tag=xml_tags.Elements.CLEANUP):
            cleanups.append(Generic_Cleanup.from_xml_node(user_node))
        return cls(count, total, score, cleanups)

class Generic_Cleanup(XML_Object_Base):
    def __init__(self, num_id, code, name, instances_total):
        self.id = num_id
        self.code = code
        self.name = name
        self.instances_total = instances_total
        super().__init__(xml_tags.Elements.CLEANUP)

    @classmethod
    def from_xml_node(cls, xml_node):
        """
        Initialize the object from a XML node.
        :param xml_node: The XML node from which all necessary parameters will be parsed.
        :type xml_node: xml.etree.Element
        """
        num_id = get_xml_int_value(xml_node, xml_tags.Elements.ID)
        code = get_xml_text_value(xml_node, xml_tags.Elements.CODE)
        name = get_xml_text_value(xml_node, xml_tags.Elements.NAME)
        instances_total = get_xml_int_value(xml_node, xml_tags.Elements.INSTANCES_TOTAL)
        return cls(num_id, code, name, instances_total)

class Cleanup_Set(XML_Object_Base):
    def __init__(self, shadowed_rules_cleanup=None):
        self.shadowed_rules_cleanup = shadowed_rules_cleanup
        super().__init__(xml_tags.Elements.CLEANUP_SET)

    @classmethod
    def from_xml_node(cls, xml_node):
        """
        Initialize the object from a XML node.
        :param xml_node: The XML node from which all necessary parameters will be parsed.
        :type xml_node: xml.etree.Element
        """
        shadowed_rules_cleanup = Shadowed_Rules_Cleanup.from_xml_node(
                get_xml_node(xml_node, xml_tags.Elements.SHADOWED_RULES_CLEANUP))
        return cls(shadowed_rules_cleanup)

class Shadowed_Rules_Cleanup(XML_Object_Base):
    def __init__(self, shadowed_rules=None):
        self.shadowed_rules = XML_List(xml_tags.Elements.SHADOWED_RULES, shadowed_rules)
        super().__init__(xml_tags.Elements.SHADOWED_RULES_CLEANUP)

    @classmethod
    def from_xml_node(cls, xml_node):
        """
        Initialize the object from a XML node.
        :param xml_node: The XML node from which all necessary parameters will be parsed.
        :type xml_node: xml.etree.Element
        """
        shadowed_rules = XML_List(xml_tags.Elements.SHADOWED_RULES)
        for shadowed_rule_node in xml_node.iter(tag=xml_tags.Elements.SHADOWED_RULE):
            shadowed_rules.append(Shadowed_Rule.from_xml_node(shadowed_rule_node))
        return cls(shadowed_rules)