import copy
from components.episode_buffer import EpisodeBatch
from modules.mixers.fop import FOPMixer
import torch.nn.functional as F
import torch as th
from torch.optim import RMSprop
import numpy as np
from torch.distributions import Categorical
from modules.critics.fop import FOPCritic
from utils.rl_utils import build_td_lambda_targets

class FOP_Learner:
    def __init__(self, mac, scheme, logger, args):
        self.args = args
        self.mac = mac
        self.logger = logger
        self.n_agents = args.n_agents
        self.n_actions = args.n_actions
        self.last_target_update_episode = 0
        self.critic_training_steps = 0

        self.log_stats_t = -self.args.learner_log_interval - 1

        self.critic1 = FOPCritic(scheme, args)
        self.critic2 = FOPCritic(scheme, args)

        self.mixer1 = FOPMixer(args)
        self.mixer2 = FOPMixer(args)

        self.target_mixer1 = copy.deepcopy(self.mixer1)
        self.target_mixer2 = copy.deepcopy(self.mixer2)

        self.target_critic1 = copy.deepcopy(self.critic1)
        self.target_critic2 = copy.deepcopy(self.critic2)

        self.agent_params = list(mac.parameters())
        self.critic_params1 = list(self.critic1.parameters()) + list(self.mixer1.parameters())
        self.critic_params2 = list(self.critic2.parameters()) + list(self.mixer2.parameters())

        self.p_optimiser = RMSprop(params=self.agent_params, lr=args.lr, alpha=args.optim_alpha, eps=args.optim_eps)
        self.c_optimiser1 = RMSprop(params=self.critic_params1, lr=args.c_lr, alpha=args.optim_alpha, eps=args.optim_eps)
        self.c_optimiser2 = RMSprop(params=self.critic_params2, lr=args.c_lr, alpha=args.optim_alpha, eps=args.optim_eps)

    def train_actor(self, batch: EpisodeBatch, t_env: int, episode_num: int):
        bs = batch.batch_size
        max_t = batch.max_seq_length
        terminated = batch["terminated"][:, :-1].float()
        mask = batch["filled"][:, :-1].float()
        mask[:, 1:] = mask[:, 1:] * (1 - terminated[:, :-1])
        mask = mask.repeat(1, 1, self.n_agents).view(-1)
        avail_actions = batch["avail_actions"]

        mac = self.mac
        alpha = max(0.05, 0.5 - t_env / 200000) # linear decay

        mac_out = []
        mac.init_hidden(batch.batch_size)
        for t in range(batch.max_seq_length):
            agent_outs = mac.forward(batch, t=t)
            mac_out.append(agent_outs)
        mac_out = th.stack(mac_out, dim=1)  # Concat over time

        # Mask out unavailable actions, renormalise (as in action selection)
        mac_out[avail_actions == 0] = 1e-10
        mac_out = mac_out/mac_out.sum(dim=-1, keepdim=True)
        mac_out[avail_actions == 0] = 1e-10

        pi = mac_out[:,:-1].clone()
        pi = pi.reshape(-1, self.n_actions)
        log_pi = th.log(pi)

        inputs = self.critic1._build_inputs(batch, bs, max_t)
        q_vals1 = self.critic1.forward(inputs)
        q_vals2 = self.critic2.forward(inputs)
        q_vals = th.min(q_vals1, q_vals2)

        pi = mac_out[:,:-1].reshape(-1, self.n_actions)
        entropies = - (pi * log_pi).sum(dim=-1)

        # policy target for discrete actions (from Soft Actor-Critic for Discrete Action Settings)
        pol_target = (pi * (alpha * log_pi - q_vals[:,:-1].reshape(-1, self.n_actions))).sum(dim=-1)

        policy_loss = (pol_target * mask).sum() / mask.sum()

        # Optimise
        self.p_optimiser.zero_grad()
        policy_loss.backward()
        agent_grad_norm = th.nn.utils.clip_grad_norm_(self.agent_params, self.args.grad_norm_clip)
        self.p_optimiser.step()

        if t_env - self.log_stats_t >= self.args.learner_log_interval:
            self.logger.log_stat("policy_loss", policy_loss.item(), t_env)
            self.logger.log_stat("agent_grad_norm", agent_grad_norm, t_env)
            self.logger.log_stat("pi_max", (pi.max(dim=1)[0] * mask).sum().item() / mask.sum().item(), t_env)
            self.logger.log_stat("alpha", alpha, t_env)
            self.logger.log_stat("ent", entropies.mean().item(), t_env)

    def train(self, batch: EpisodeBatch, t_env: int, episode_num: int, show_demo=False, save_data=None):
        self.train_actor(batch, t_env, episode_num)
        self.train_critic(batch, t_env)
        if (episode_num - self.last_target_update_episode) / self.args.target_update_interval >= 1.0:
            self._update_targets()
            self.last_target_update_episode = episode_num

    def train_critic(self, batch, t_env):
        bs = batch.batch_size
        max_t = batch.max_seq_length
        rewards = batch["reward"][:, :-1]
        actions = batch["actions"][:, :-1]
        terminated = batch["terminated"][:, :-1].float()
        mask = batch["filled"][:, :-1].float()
        mask[:, 1:] = mask[:, 1:] * (1 - terminated[:, :-1])
        avail_actions = batch["avail_actions"]
        actions_onehot = batch["actions_onehot"][:, :-1]
        states = batch["state"]

        mac = self.mac
        mixer1 = self.mixer1
        mixer2 = self.mixer2
        alpha = max(0.05, 0.5 - t_env / 200000) # linear decay

        mac_out = []
        mac.init_hidden(batch.batch_size)
        for t in range(batch.max_seq_length):
            agent_outs = mac.forward(batch, t=t)
            mac_out.append(agent_outs)
        mac_out = th.stack(mac_out, dim=1)  # Concat over time

        # Mask out unavailable actions
        mac_out[avail_actions == 0] = 0.0
        mac_out = mac_out/mac_out.sum(dim=-1, keepdim=True)
        mac_out[avail_actions == 0] = 1e-10

        t_mac_out = mac_out.clone().detach()
        pi = t_mac_out

        # sample actions for next timesteps
        next_actions = Categorical(pi).sample().long().unsqueeze(3)
        next_actions_onehot = th.zeros(next_actions.squeeze(3).shape + (self.n_actions,))
        if self.args.use_cuda:
            next_actions_onehot = next_actions_onehot.cuda()
        next_actions_onehot = next_actions_onehot.scatter_(3, next_actions, 1)

        pi_taken = th.gather(pi, dim=3, index=next_actions).squeeze(3)[:,1:]
        pi_taken[mask.expand_as(pi_taken) == 0] = 1.0
        log_pi_taken = th.log(pi_taken)

        target_inputs = self.target_critic1._build_inputs(batch, bs, max_t)
        target_q_vals1 = self.target_critic1.forward(target_inputs).detach()
        target_q_vals2 = self.target_critic2.forward(target_inputs).detach()

        # directly caculate the values by definition
        next_vs1 = th.logsumexp(target_q_vals1 / alpha, dim=-1) * alpha
        next_vs2 = th.logsumexp(target_q_vals2 / alpha, dim=-1) * alpha

        next_chosen_qvals1 = th.gather(target_q_vals1, dim=3, index=next_actions).squeeze(3)
        next_chosen_qvals2 = th.gather(target_q_vals2, dim=3, index=next_actions).squeeze(3)

        target_qvals1 = self.target_mixer1(next_chosen_qvals1, states, actions=next_actions_onehot, vs=next_vs1)
        target_qvals2 = self.target_mixer2(next_chosen_qvals2, states, actions=next_actions_onehot, vs=next_vs2)

        target_qvals = th.min(target_qvals1, target_qvals2)

        # Calculate td-lambda targets
        target_v = build_td_lambda_targets(rewards, terminated, mask, target_qvals, self.n_agents, self.args.gamma, self.args.td_lambda)
        targets = target_v - alpha * log_pi_taken.mean(dim=-1, keepdim=True)

        inputs = self.critic1._build_inputs(batch, bs, max_t)
        q_vals1 = self.critic1.forward(inputs)
        q_vals2 = self.critic2.forward(inputs)

        # directly caculate the values by definition
        vs1 = th.logsumexp(q_vals1 / alpha, dim=-1) * alpha
        vs2 = th.logsumexp(q_vals2 / alpha, dim=-1) * alpha

        q_taken1 = th.gather(q_vals1[:,:-1], dim=3, index=actions).squeeze(3)
        q_taken2 = th.gather(q_vals2[:,:-1], dim=3, index=actions).squeeze(3)

        q_taken1 = mixer1(q_taken1, states[:, :-1], actions=actions_onehot, vs=vs1[:, :-1])
        q_taken2 = mixer2(q_taken2, states[:, :-1], actions=actions_onehot, vs=vs2[:, :-1])

        td_error1 = q_taken1 - targets.detach()
        td_error2 = q_taken2 - targets.detach()

        mask = mask.expand_as(td_error1)

        # 0-out the targets that came from padded data
        masked_td_error1 = td_error1 * mask
        loss1 = (masked_td_error1 ** 2).sum() / mask.sum()
        masked_td_error2 = td_error2 * mask
        loss2 = (masked_td_error2 ** 2).sum() / mask.sum()

        # Optimise
        self.c_optimiser1.zero_grad()
        loss1.backward()
        grad_norm = th.nn.utils.clip_grad_norm_(self.critic_params1, self.args.grad_norm_clip)
        self.c_optimiser1.step()

        self.c_optimiser2.zero_grad()
        loss2.backward()
        grad_norm = th.nn.utils.clip_grad_norm_(self.critic_params2, self.args.grad_norm_clip)
        self.c_optimiser2.step()

        if t_env - self.log_stats_t >= self.args.learner_log_interval:
            self.logger.log_stat("loss", loss1.item(), t_env)
            self.logger.log_stat("grad_norm", grad_norm, t_env)
            mask_elems = mask.sum().item()
            self.logger.log_stat("td_error_abs", (masked_td_error1.abs().sum().item() / mask_elems), t_env)
            self.logger.log_stat("q_taken_mean",
                                 (q_taken1 * mask).sum().item() / (mask_elems * self.args.n_agents), t_env)
            self.logger.log_stat("target_mean", (targets * mask).sum().item() / (mask_elems * self.args.n_agents),
                                 t_env)
            self.log_stats_t = t_env

    def _update_targets(self):
        self.target_critic1.load_state_dict(self.critic1.state_dict())
        self.target_critic2.load_state_dict(self.critic2.state_dict())
        self.target_mixer1.load_state_dict(self.mixer1.state_dict())
        self.target_mixer2.load_state_dict(self.mixer2.state_dict())
        self.logger.console_logger.info("Updated target network")

    def cuda(self):
        self.mac.cuda()
        self.critic1.cuda()
        self.mixer1.cuda()
        self.target_critic1.cuda()
        self.target_mixer1.cuda()
        self.critic2.cuda()
        self.mixer2.cuda()
        self.target_critic2.cuda()
        self.target_mixer2.cuda()

    def save_models(self, path):
        self.mac.save_models(path)
        th.save(self.critic1.state_dict(), "{}/critic1.th".format(path))
        th.save(self.mixer1.state_dict(), "{}/mixer1.th".format(path))
        th.save(self.critic2.state_dict(), "{}/critic2.th".format(path))
        th.save(self.mixer2.state_dict(), "{}/mixer2.th".format(path))
        th.save(self.p_optimiser.state_dict(), "{}/agent_opt.th".format(path))
        th.save(self.c_optimiser1.state_dict(), "{}/critic_opt1.th".format(path))
        th.save(self.c_optimiser2.state_dict(), "{}/critic_opt2.th".format(path))

    def load_models(self, path):
        self.mac.load_models(path)
        self.critic1.load_state_dict(th.load("{}/critic1.th".format(path), map_location=lambda storage, loc: storage))
        self.critic2.load_state_dict(th.load("{}/critic2.th".format(path), map_location=lambda storage, loc: storage))
        # Not quite right but I don't want to save target networks
        self.target_critic1.load_state_dict(self.critic1.state_dict())
        self.target_critic2.load_state_dict(self.critic2.state_dict())

        self.mixer1.load_state_dict(th.load("{}/mixer1.th".format(path), map_location=lambda storage, loc: storage))
        self.mixer2.load_state_dict(th.load("{}/mixer2.th".format(path), map_location=lambda storage, loc: storage))

        self.p_optimiser.load_state_dict(th.load("{}/agent_opt.th".format(path), map_location=lambda storage, loc: storage))
        self.critic_optimiser1.load_state_dict(th.load("{}/critic_opt1.th".format(path), map_location=lambda storage, loc: storage))
        self.critic_optimiser2.load_state_dict(th.load("{}/critic_opt2.th".format(path), map_location=lambda storage, loc: storage))

    def build_inputs(self, batch, bs, max_t, actions_onehot):
        inputs = []
        inputs.append(batch["obs"][:])
        actions = actions_onehot[:].reshape(bs, max_t, self.n_agents, -1)
        inputs.append(actions)
        inputs.append(th.eye(self.n_agents, device=batch.device).unsqueeze(0).unsqueeze(0).expand(bs, max_t, -1, -1))
        inputs = th.cat([x.reshape(bs, max_t, self.n_agents, -1) for x in inputs], dim=-1)
        return inputs


from collections import OrderedDict
from typing import Literal

import numpy as np
from ta.trend import sma_indicator, ema_indicator

from model.strategies._mixin import StrategyMixin

class MovingAverage(StrategyMixin):
    """
    Strategy that uses a simple or exponential moving average as a trigger for
    going long or short, with no neutral position. When the price crosses over
    above the moving average it goes long, and vice versa when the price crosses below.

    Parameters
    ----------
    ma : int
        Moving average window.
    moving_av : Literal["sma", "ema"], optional
        Type of moving average (simple or exponential), by default 'sma'.
    data : pd.DataFrame, optional
        Dataframe of OHLCV data, by default None.
    **kwargs : dict, optional
        Additional keyword arguments to be passed to parent class, by default None.

    Attributes
    ----------
    params : OrderedDict
        Parameters for the strategy, by default {"ma": lambda x: int(x)}

    Methods
    -------
    update_data()
        Retrieves and prepares the data.
    _calculate_positions(data)
        Calculates positions based on strategy rules.
    get_signal(row)
        Returns signal based on current data.

    """
    def __init__(self, ma: int, moving_av: Literal["sma", "ema"] = 'sma', data=None, **kwargs):
        """
        Parameters
        ----------
        ma : int
            Moving average window.
        moving_av : Literal["sma", "ema"], optional
            Type of moving average (simple or exponential), by default 'sma'.
        data : pd.DataFrame, optional
            Dataframe of OHLCV data, by default None.
        **kwargs : dict, optional
            Additional keyword arguments to be passed to parent class, by default None.
        """
        self._ma = ma
        self._moving_av = moving_av

        self.params = OrderedDict(ma=lambda x: int(x))

        StrategyMixin.__init__(self, data, **kwargs)

    def __repr__(self):
        return "{}(symbol = {}, SMA = {})".format(self.__class__.__name__, self.symbol, self._ma)

    def update_data(self, data):
        """
        Updates the input data with additional columns required for the strategy.

        Parameters
        ----------
        data : pd.DataFrame
            OHLCV data to be updated.

        Returns
        -------
        pd.DataFrame
            Updated OHLCV data containing additional columns.
        """
        data = super().update_data(data)

        if self._moving_av == 'sma':
            data["SMA"] = sma_indicator(close=data[self.close_col], window=self._ma)
        elif self._moving_av == 'ema':
            data["SMA"] = ema_indicator(close=data[self.close_col], window=self._ma)
        else:
            raise('Method not supported')

        return data

    def _calculate_positions(self, data):
        """
        Calculates positions based on strategy rules.

        Parameters
        ----------
        data : pd.DataFrame
            OHLCV data.

        Returns
        -------
        pd.DataFrame
            OHLCV data with additional 'position' column containing -1 for short, 1 for long.
        """
        data["position"] = np.where(data["SMA"] > data[self.close_col], 1, 0)
        data["position"] = np.where(data["SMA"] < data[self.close_col], -1, data["position"])
        return data

    def get_signal(self, row=None):
        """
        Returns signal based on current data.

        Parameters
        ----------
        row : pd.Series, optional
            Row of OHLCV data to generate signal for, by default None.

        Returns
        -------
        int
            Signal (-1 for short, 1 for long, 0 for neutral).
        """
        if row is None:
            row = self.data.iloc[-1]

        if row["SMA"] > row[self.close_col]:
            return 1
        elif row["SMA"] < row[self.close_col]:
            return -1

        elif row["SMA"] == row[self.close_col]:
            return 0


import nltk
import sklearn
import sklearn_crfsuite
from sklearn_crfsuite import metrics
import os
nltk.download('averaged_perceptron_tagger')
def read_iob2_sents(in_iob2_file):
    sents = []
    with open(in_iob2_file, 'r', encoding = 'utf8') as f:
        for sent_iob2 in f.read().split('\n\n'):
            sent = []
            for raw in sent_iob2.split('\n'):
                if raw == '':
                    continue
                columns = raw.split('\t')
                sent.append(tuple(columns))
            sents.append(sent)
    return sents
train_sents = read_iob2_sents('train.iob2')
train_sents[0]

def sent2labels(sent): return [label for token, pos,label in sent]
def word2features(sent, i):
    word = sent[i][0]
    postag = sent[i][1]
    features = {
        'bias' : 1.0,
        'word': word,
        'word_len':len(word),
        'word[0:3]':word[0:3],
        'word[-3:]': word[-3:],
        'word[-2:]': word[-2:],
        'word.islower()': word.islower(),
        'word.isalnum()': word.isalnum(),
        'word.isupper()': word.isupper(),
        'word.isdigit()': word.isdigit(),
        'word.isalpha()': word.isalpha(),
        'word.istitle()': word.istitle(),
        'word.containspace()':' ' in word==True,
        'word.containdash()':'-' in word==True,
        'postag': postag,
        'postag[:2]': postag[:2],}
    if i > 0:
        word1 = sent[i-1][0]
        postag1= sent[i-1][1]
        features.update({
            '-1:word': word1,
            '-1:word_len':len(word1),
            '-1:word[0:3]':word1[0:3],
            '-1:word[-3:]': word1[-3:],
            '-1:word[-2:]': word1[-2:],
            '-1:word.islower()': word1.islower(),
            '-1:word.isalnum()': word1.isalnum(),
            '-1:word.isupper()': word1.isupper(),
            '-1:word.isdigit()': word1.isdigit(),
            '-1:word.isalpha()': word1.isalpha(),
            '-1:word.istitle()': word1.istitle(),
            '-1:word.containspace()':' ' in word==True,
            '-1:word.containdash()':'-' in word==True,
            '-1:postag': postag1,
            '-1:postag[:2]': postag1[:2]})
    else:
        features['BOS'] = True
    if i < len(sent)-1:
        word1 = sent[i+1][0]
        postag1 = sent[i+1][1]
        features.update({
            '+1:word': word1,
            '+1:word_len':len(word1),
            '+1:word[0:3]':word1[0:3],
            '+1:word[-3:]': word1[-3:],
            '+1:word[-2:]': word1[-2:],
            '+1:word.islower()': word1.islower(),
            '+1:word.isalnum()': word1.isalnum(),
            '+1:word.isupper()': word1.isupper(),
            '+1:word.isdigit()': word1.isdigit(),
            '+1:word.isalpha()': word1.isalpha(),
            '+1:word.istitle()': word1.istitle(),
            '+1:word.containspace()':' ' in word==True,
            '+1:word.containdash()':'-' in word==True,
            '+1:postag': postag1,
            '+1:postag[:2]': postag1[:2]})
    else:
        features['EOS'] = True
    return features

def sent2features(sent):
    return [word2features(sent, i) for i in range(len(sent))]
def appendpostagged(sent):
    data=[]
    for i, s in enumerate(sent):
        # Obtain the list of tokens in the document
        tokens = [t for t, label in s]
        # Perform POS tagging
        for token in tokens :
            if(token==''):
                tokens.remove('')
        tagged = nltk.pos_tag(tokens)
        print(tagged)
        # Take the word, POS tag, and its label
        data.append([(w, pos, label) for (w, label), (word, pos) in zip(s, tagged)])
    return data

def write_iob2(data, pred, out_iob2_file):
    with open(out_iob2_file, 'wb') as iob2_writer:
        for _data, _pred in zip(data, pred):
            for _tuple, _pred in zip(_data, _pred):
                iob2_writer.write(bytes('\t'.join(_tuple) + '\t' + _pred + '\n', encoding = 'utf8'))
            iob2_writer.write(bytes('\n', encoding = 'utf8'))

#main
print("go")
train_sents = read_iob2_sents('train.iob2')
train_sents = appendpostagged(train_sents)
#print(train_sents)
test_sents = read_iob2_sents('test.iob2')
test_sents = appendpostagged(test_sents)

X_train = [sent2features(s) for s in train_sents]
y_train = [sent2labels(s) for s in train_sents]

X_test = [sent2features(s) for s in test_sents]
y_test = [sent2labels(s) for s in test_sents]
print("openfile success")

crf = sklearn_crfsuite.CRF(
    algorithm='lbfgs',
    all_possible_states=True,
    all_possible_transitions=True,
    c1 =0.1,
    c2=0.1,
    linesearch='StrongBacktracking',
    max_iterations=8000,
    min_freq=0
)
print("start train")
crf.fit(X_train, y_train)
print("end and start pred")
y_pred = crf.predict(X_test)
print(y_pred)

labels = list(crf.classes_)
labels.remove('O')
sorted_labels = sorted(
    labels,
    key=lambda name: (name[1:], name[0])
)
print(metrics.flat_classification_report(y_test, y_pred, labels=sorted_labels, digits=3))
write_iob2(test_sents, y_pred, 'answer.iob2')


from selenium import webdriver
from selenium.webdriver.common.by import By
import os
currentDirectory = os.getcwd()
if not os.path.exists('scrape_results'):
    os.makedirs('scrape_results')

options = webdriver.ChromeOptions()
driver = webdriver.Chrome(chrome_options=options, executable_path=currentDirectory + "/chromedriver.exe")

# https://www.silverlakemobility.com/plugins/WebPlugIn?type=1-1&action={}&email=fongyoong8@gmail.com&data=hSiJpBIcBFFMPGRgXWckfA==
# https://www.silverlakemobility.com/plugins/WebPlugIn?type={}&email=fongyoong8@gmail.com&data=v6HVSSWR+DGJWVuqTGCNyQ==

for i in range(0, 1000, 1):
    driver.get('https://www.silverlakemobility.com/plugins/WebPlugIn?type={}&email=fongyoong8@gmail.com&data=v6HVSSWR+DGJWVuqTGCNyQ=='.format(i))
    body = driver.find_elements(By.XPATH, '//body')[0]
    children = body.find_elements_by_css_selector("*")
    if len(children) > 0:
        driver.save_screenshot("scrape_results\\{}.png".format(i))

driver.close()


# -*- coding: utf-8 -*-
import scrapy
import re
import urllib.parse
import codecs
from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError
from twisted.internet.error import TimeoutError,TCPTimedOutError

class Photo3Spider(scrapy.Spider):
    name = 'photo3'
    allowed_domains = ['68aiav.com']
    start_urls = ['http://www.68aiav.com/vodtag/无码破解版']

    def parse(self, response):
        title_li = response.xpath('//div[@class="wrap mt20"]//li')
        for title in title_li:
            item = {}
            item['title'] = title.xpath('.//h3/text()').extract_first()
            item['date'] = title.xpath('.//dt/text()').extract_first()
            item['href'] = title.xpath('.//a/@href').extract_first()
            item['href'] = 'http://www.68aiav.com' + item['href']
            item['img'] = "http:"+ title.xpath('.//img/@data-original').extract_first()

            yield scrapy.Request(
                item["href"],
                callback=self.play_url, #跳转到播放器页面
                meta = {"item":item}
            )

    def play_url(self,response):
        item = response.meta['item']
        # 全集
        item['play'] = response.xpath('//div[@class="wrap mt20"]//li/a/@href').extract_first()
        item['play'] = 'http://www.68aiav.com' +item['play']
        yield scrapy.Request(
            item['play'],
            callback=self.m3u8_url, #获取m3u8的地址
            meta={"item":item}
        )
    def m3u8_url(self,response):
        item = response.meta['item']
        # m3u8 = response.xpath('//')
        # 得到的url_1是%2f%50%75%62%6c%69%63%2f%7形式,
        # ?????有的时候会报这里有问题,有的时候不会??????
        url_if = re.findall('


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def estimate_wave_duration(df, region):
    region_data = df[df['Регион'] == region].copy()

    dates = region_data['Дата'].values
    cases = region_data['Заражений за день'].values

    # print("cases = ")
    # print(cases)

    rises = []  # список для длительности подъемов
    falls = []  # список для длительности спадов
    rise_values = []  # список значений в каждом подъеме
    fall_values = []  # список значений в каждом спаде

    current_wave = []  # текущая волна (подъем или спад)
    current_trend = None  # текущий тренд: 'rise' (подъем) или 'fall' (спад)

    for i in range(1, len(cases)):
        if cases[i] <= cases[i-1]:
            if current_trend != 'rise':  # Если тренд изменился, сохраняем предыдущую волну и начинаем новую
                if current_wave:
                    rises.append(len(current_wave))
                    rise_values.append(current_wave)
                current_wave = [cases[i-1], cases[i]]
                current_trend = 'rise'
            else:  # Продолжаем текущую волну
                current_wave.append(cases[i])
        else:
            if current_trend != 'fall':   # Если тренд изменился, сохраняем предыдущую волну и начинаем новую
                if current_wave:
                    falls.append(len(current_wave))
                    fall_values.append(current_wave)
                current_wave = [cases[i-1], cases[i]]
                current_trend = 'fall'
            else:  # Продолжаем текущую волну
                current_wave.append(cases[i])

    # Добавляем последнюю волну в соответствующий список
    if current_wave:
        if current_trend == 'rise':
            rises.append(len(current_wave))
            rise_values.append(current_wave)
        else:
            falls.append(len(current_wave))
            fall_values.append(current_wave)

    # Выводим длительности подъемов и спадов
    print("Длительность подъемов (в днях):", rises)
    print("Длительность спадов (в днях):", falls)

    print("Значения в подъемах:")
    for i, values in enumerate(rise_values):
        print("Подъем", i+1, ":", values)

    print("Значения в спадах:")
    for i, values in enumerate(fall_values):
        print("Спад", i+1, ":", values)

    # Строим график
    plt.plot(region_data['Дата'],cases)

    # Отображаем волны
    # for i, wave in enumerate(rise_values):
    #     x_wave = np.arange(len(wave))
    #     x_offset = np.sum(rises[:i])  # Смещение для горизонтальной оси
    #     plt.plot(x_wave + x_offset, wave, color='blue')

    # for i, wave in enumerate(fall_values):
    #     x_wave = np.arange(len(wave))
    #     x_offset = np.sum(rises[:len(rise_values)]) + np.sum(falls[:i])  # Смещение для горизонтальной оси
    #     plt.plot(x_wave + x_offset, wave, color='blue')

    plt.title(f'Длительность волн для региона: {region}')
    plt.xlabel('Дни')
    plt.ylabel('Значение')
    plt.xticks(rotation = 45)

    table_text = f'Rises: {rises}\nFalls: {falls}'
    plt.text(0.75, 0.95, table_text, transform=plt.gca().transAxes, fontsize=10, va='top')

    plt.show()


"""This utility lets you repack your data files in a flexible way.

Pass the flag -h to this for help on usage.

"""

import argparse
import sys
import warnings
from pathlib import Path
from time import perf_counter as clock
from time import process_time as cpuclock

import tables as tb

# Global variables
verbose = False
regoldindexes = True
createsysattrs = True

numpy_aliases = [
    'numeric',
    'Numeric',
    'numarray',
    'NumArray',
    'CharArray',
]

def newdst_group(dstfileh, dstgroup, title, filters):
    group = dstfileh.root
    # Now, create the new group. This works even if dstgroup == '/'
    for nodename in dstgroup.split('/'):
        if nodename == '':
            continue
        # First try if possible intermediate groups does already exist.
        try:
            group2 = dstfileh.get_node(group, nodename)
        except tb.exceptions.NoSuchNodeError:
            # The group does not exist. Create it.
            group2 = dstfileh.create_group(group, nodename,
                                           title=title,
                                           filters=filters)
        group = group2
    return group

def recreate_indexes(table, dstfileh, dsttable):
    listoldindexes = table._listoldindexes
    if listoldindexes != []:
        if not regoldindexes:
            if verbose:
                print("[I]Not regenerating indexes for table: '%s:%s'" %
                      (dstfileh.filename, dsttable._v_pathname))
            return
        # Now, recreate the indexed columns
        if verbose:
            print("[I]Regenerating indexes for table: '%s:%s'" %
                  (dstfileh.filename, dsttable._v_pathname))
        for colname in listoldindexes:
            if verbose:
                print("[I]Indexing column: '%s'. Please wait..." % colname)
            colobj = dsttable.cols._f_col(colname)
            # We don't specify the filters for the indexes
            colobj.create_index(filters=None)

def copy_leaf(srcfile, dstfile, srcnode, dstnode, title,
              filters, copyuserattrs, overwritefile, overwrtnodes, stats,
              start, stop, step, chunkshape, sortby, check_CSI,
              propindexes, upgradeflavors, allow_padding):
    # Open the source file
    srcfileh = tb.open_file(srcfile, 'r', allow_padding=allow_padding)
    # Get the source node (that should exist)
    srcnode = srcfileh.get_node(srcnode)

    # Get the destination node and its parent
    last_slash = dstnode.rindex('/')
    if last_slash == len(dstnode)-1:
        # print("Detected a trailing slash in destination node. "
        #       "Interpreting it as a destination group.")
        dstgroup = dstnode[:-1]
    elif last_slash > 0:
        dstgroup = dstnode[:last_slash]
    else:
        dstgroup = "/"
    dstleaf = dstnode[last_slash + 1:]
    if dstleaf == "":
        dstleaf = srcnode.name
    # Check whether the destination group exists or not
    if Path(dstfile).is_file() and not overwritefile:
        dstfileh = tb.open_file(dstfile, 'a',
                                pytables_sys_attrs=createsysattrs,
                                allow_padding=allow_padding)
        try:
            dstgroup = dstfileh.get_node(dstgroup)
        except Exception:
            # The dstgroup does not seem to exist. Try creating it.
            dstgroup = newdst_group(dstfileh, dstgroup, title, filters)
        else:
            # The node exists, but it is really a group?
            if not isinstance(dstgroup, tb.group.Group):
                # No. Should we overwrite it?
                if overwrtnodes:
                    parent = dstgroup._v_parent
                    last_slash = dstgroup._v_pathname.rindex('/')
                    dstgroupname = dstgroup._v_pathname[last_slash + 1:]
                    dstgroup.remove()
                    dstgroup = dstfileh.create_group(parent, dstgroupname,
                                                     title=title,
                                                     filters=filters)
                else:
                    raise RuntimeError("Please check that the node names are "
                                       "not duplicated in destination, and "
                                       "if so, add the --overwrite-nodes "
                                       "flag if desired.")
    else:
        # The destination file does not exist or will be overwritten.
        dstfileh = tb.open_file(dstfile, 'w', title=title, filters=filters,
                                pytables_sys_attrs=createsysattrs,
                                allow_padding=allow_padding)
        dstgroup = newdst_group(dstfileh, dstgroup, title="", filters=filters)

    # Finally, copy srcnode to dstnode
    try:
        dstnode = srcnode.copy(
            dstgroup, dstleaf, filters=filters,
            copyuserattrs=copyuserattrs, overwrite=overwrtnodes,
            stats=stats, start=start, stop=stop, step=step,
            chunkshape=chunkshape,
            sortby=sortby, check_CSI=check_CSI, propindexes=propindexes)
    except Exception:
        (type_, value, traceback) = sys.exc_info()
        print("Problems doing the copy from '%s:%s' to '%s:%s'" %
              (srcfile, srcnode, dstfile, dstnode))
        print(f"The error was --> {type_}: {value}")
        print("The destination file looks like:\n", dstfileh)
        # Close all the open files:
        srcfileh.close()
        dstfileh.close()
        raise RuntimeError("Please check that the node names are not "
                           "duplicated in destination, and if so, add "
                           "the --overwrite-nodes flag if desired.")

    # Upgrade flavors in dstnode, if required
    if upgradeflavors:
        if srcfileh.format_version.startswith("1"):
            # Remove original flavor in case the source file has 1.x format
            dstnode.del_attr('FLAVOR')
        elif srcfileh.format_version < "2.1":
            if dstnode.get_attr('FLAVOR') in numpy_aliases:
                dstnode.set_attr('FLAVOR', tb.flavor.internal_flavor)

    # Recreate possible old indexes in destination node
    if srcnode._c_classid == "TABLE":
        recreate_indexes(srcnode, dstfileh, dstnode)

    # Close all the open files:
    srcfileh.close()
    dstfileh.close()

def copy_children(srcfile, dstfile, srcgroup, dstgroup, title,
                  recursive, filters, copyuserattrs, overwritefile,
                  overwrtnodes, stats, start, stop, step,
                  chunkshape, sortby, check_CSI, propindexes,
                  upgradeflavors, allow_padding, use_hardlinks=True):
    """Copy the children from source group to destination group"""
    # Open the source file with srcgroup as root_uep
    srcfileh = tb.open_file(srcfile, 'r', root_uep=srcgroup,
                            allow_padding=allow_padding)
    #  Assign the root to srcgroup
    srcgroup = srcfileh.root

    created_dstgroup = False
    # Check whether the destination group exists or not
    if Path(dstfile).is_file() and not overwritefile:
        dstfileh = tb.open_file(dstfile, 'a',
                                pytables_sys_attrs=createsysattrs,
                                allow_padding=allow_padding)
        try:
            dstgroup = dstfileh.get_node(dstgroup)
        except tb.exceptions.NoSuchNodeError:
            # The dstgroup does not seem to exist. Try creating it.
            dstgroup = newdst_group(dstfileh, dstgroup, title, filters)
            created_dstgroup = True
        else:
            # The node exists, but it is really a group?
            if not isinstance(dstgroup, tb.group.Group):
                # No. Should we overwrite it?
                if overwrtnodes:
                    parent = dstgroup._v_parent
                    last_slash = dstgroup._v_pathname.rindex('/')
                    dstgroupname = dstgroup._v_pathname[last_slash + 1:]
                    dstgroup.remove()
                    dstgroup = dstfileh.create_group(parent, dstgroupname,
                                                     title=title,
                                                     filters=filters)
                else:
                    raise RuntimeError("Please check that the node names are "
                                       "not duplicated in destination, and "
                                       "if so, add the --overwrite-nodes "
                                       "flag if desired.")
    else:
        # The destination file does not exist or will be overwritten.
        dstfileh = tb.open_file(dstfile, 'w', title=title, filters=filters,
                                pytables_sys_attrs=createsysattrs,
                                allow_padding=allow_padding)
        dstgroup = newdst_group(dstfileh, dstgroup, title="", filters=filters)
        created_dstgroup = True

    # Copy the attributes to dstgroup, if needed
    if created_dstgroup and copyuserattrs:
        srcgroup._v_attrs._f_copy(dstgroup)

    # Finally, copy srcgroup children to dstgroup
    try:
        srcgroup._f_copy_children(
            dstgroup, recursive=recursive, filters=filters,
            copyuserattrs=copyuserattrs, overwrite=overwrtnodes,
            stats=stats, start=start, stop=stop, step=step,
            chunkshape=chunkshape,
            sortby=sortby, check_CSI=check_CSI, propindexes=propindexes,
            use_hardlinks=use_hardlinks)
    except Exception:
        (type_, value, traceback) = sys.exc_info()
        print("Problems doing the copy from '%s:%s' to '%s:%s'" %
              (srcfile, srcgroup, dstfile, dstgroup))
        print(f"The error was --> {type_}: {value}")
        print("The destination file looks like:\n", dstfileh)
        # Close all the open files:
        srcfileh.close()
        dstfileh.close()
        raise RuntimeError("Please check that the node names are not "
                           "duplicated in destination, and if so, add the "
                           "--overwrite-nodes flag if desired. In "
                           "particular, pay attention that root_uep is not "
                           "fooling you.")

    # Upgrade flavors in dstnode, if required
    if upgradeflavors:
        for dstnode in dstgroup._f_walknodes("Leaf"):
            if srcfileh.format_version.startswith("1"):
                # Remove original flavor in case the source file has 1.x format
                dstnode.del_attr('FLAVOR')
            elif srcfileh.format_version < "2.1":
                if dstnode.get_attr('FLAVOR') in numpy_aliases:
                    dstnode.set_attr('FLAVOR', tb.flavor.internal_flavor)

    # Convert the remaining tables with old indexes (if any)
    for table in srcgroup._f_walknodes("Table"):
        dsttable = dstfileh.get_node(dstgroup, table._v_pathname)
        recreate_indexes(table, dstfileh, dsttable)

    # Close all the open files:
    srcfileh.close()
    dstfileh.close()

def _get_parser():
    parser = argparse.ArgumentParser(
        description='''This utility is very powerful and lets you copy any
        leaf, group or complete subtree into another file.
        During the copy process you are allowed to change the filter
        properties if you want so. Also, in the case of duplicated pathnames,
        you can decide if you want to overwrite already existing nodes on the
        destination file. Generally speaking, ptrepack can be useful in may
        situations, like replicating a subtree in another file, change the
        filters in objects and see how affect this to the compression degree
        or I/O performance, consolidating specific data in repositories or
        even *importing* generic HDF5 files and create true PyTables
        counterparts.''')

    parser.add_argument(
        '-v', '--verbose', action='store_true',
        help='show verbose information',
    )
    parser.add_argument(
        '-o', '--overwrite', action='store_true', dest='overwritefile',
        help='overwrite destination file',
    )
    parser.add_argument(
        '-R', '--range', dest='rng', metavar='RANGE',
        help='''select a RANGE of rows (in the form "start,stop,step")
        during the copy of *all* the leaves.
        Default values are "None,None,1", which means a copy of all the
        rows.''',
    )
    parser.add_argument(
        '--non-recursive', action='store_false', default=True,
        dest='recursive',
        help='do not do a recursive copy. Default is to do it',
    )
    parser.add_argument(
        '--dest-title', dest='title', default='',
        help='title for the new file (if not specified, the source is copied)',
    )
    parser.add_argument(
        '--dont-create-sysattrs', action='store_false', default=True,
        dest='createsysattrs',
        help='do not create sys attrs (default is to do it)',
    )
    parser.add_argument(
        '--dont-copy-userattrs', action='store_false', default=True,
        dest='copyuserattrs',
        help='do not copy the user attrs (default is to do it)',
    )
    parser.add_argument(
        '--overwrite-nodes', action='store_true', dest='overwrtnodes',
        help='''overwrite destination nodes if they exist.
        Default is to not overwrite them''',
    )
    parser.add_argument(
        '--complevel', type=int, default=0,
        help='''set a compression level (0 for no compression, which is the
        default)''',
    )
    parser.add_argument(
        '--complib', choices=(
            "zlib", "lzo", "bzip2", "blosc", "blosc:blosclz",
            "blosc:lz4", "blosc:lz4hc", "blosc:zlib", "blosc:zstd",
            "blosc2", "blosc2:blosclz",
            "blosc2:lz4", "blosc2:lz4hc", "blosc2:zlib", "blosc2:zstd"
        ),
        default='zlib',
        help='''set the compression library to be used during the copy.
        Defaults to %(default)s''',
    )
    parser.add_argument(
        '--shuffle', type=int, choices=(0, 1),
        help='''activate or not the shuffle filter (default is active if
        complevel > 0)''',
    )
    parser.add_argument(
        '--bitshuffle', type=int, choices=(0, 1),
        help='activate or not the bitshuffle filter (not active by default)',
    )
    parser.add_argument(
        '--fletcher32', type=int, choices=(0, 1),
        help='''whether to activate or not the fletcher32 filter (not active
        by default)''',
    )
    parser.add_argument(
        '--keep-source-filters', action='store_true', dest='keepfilters',
        help='''use the original filters in source files.
        The default is not doing that if any of --complevel, --complib,
        --shuffle --bitshuffle or --fletcher32 option is specified''',
    )
    parser.add_argument(
        '--chunkshape', default='keep',
        help='''set a chunkshape.
        Possible options are: "keep" | "auto" | int | tuple.
        A value of "auto" computes a sensible value for the chunkshape of the
        leaves copied.  The default is to "keep" the original value''',
    )
    parser.add_argument(
        '--upgrade-flavors', action='store_true', dest='upgradeflavors',
        help='''when repacking PyTables 1.x or PyTables 2.x files, the flavor
        of leaves will be unset. With this, such a leaves will be serialized
        as objects with the internal flavor ('numpy' for 3.x series)''',
    )
    parser.add_argument(
        '--dont-regenerate-old-indexes', action='store_false', default=True,
        dest='regoldindexes',
        help='''disable regenerating old indexes.
        The default is to regenerate old indexes as they are found''',
    )
    parser.add_argument(
        '--sortby', metavar='COLUMN',
        help='''do a table copy sorted by the index in "column".
        For reversing the order, use a negative value in the "step" part of
        "RANGE" (see "-r" flag).  Only applies to table objects''',
    )
    parser.add_argument(
        '--checkCSI', action='store_true',
        help='force the check for a CSI index for the --sortby column',
    )
    parser.add_argument(
        '--propindexes', action='store_true',
        help='''propagate the indexes existing in original tables. The default
        is to not propagate them.  Only applies to table objects''',
    )
    parser.add_argument(
        '--dont-allow-padding', action='store_true',
        dest="dont_allow_padding",
        help='''remove the possible padding in compound types in source files.
        The default is to propagate it.  Only applies to table objects''',
    )
    parser.add_argument(
        'src', metavar='sourcefile:sourcegroup', help='source file/group',
    )
    parser.add_argument(
        'dst', metavar='destfile:destgroup', help='destination file/group',
    )

    return parser

def main():
    global verbose
    global regoldindexes
    global createsysattrs

    parser = _get_parser()
    args = parser.parse_args()

    # check arguments
    if args.rng:
        try:
            args.rng = eval("slice(" + args.rng + ")")
        except Exception:
            parser.error("Error when getting the range parameter.")

    if args.chunkshape.isdigit() or args.chunkshape.startswith('('):
        args.chunkshape = eval(args.chunkshape)

    if args.complevel < 0 or args.complevel > 9:
        parser.error(
            'invalid "complevel" value, it sould be in te range [0, 9]'
        )

    # Catch the files passed as the last arguments
    src = args.src.rsplit(':', 1)
    dst = args.dst.rsplit(':', 1)
    if len(src) == 1:
        srcfile, srcnode = src[0], "/"
    else:
        srcfile, srcnode = src
    if len(dst) == 1:
        dstfile, dstnode = dst[0], "/"
    else:
        dstfile, dstnode = dst

    if srcnode == "":
        # case where filename == "filename:" instead of "filename:/"
        srcnode = "/"

    if dstnode == "":
        # case where filename == "filename:" instead of "filename:/"
        dstnode = "/"

    # Ignore the warnings for tables that contains oldindexes
    # (these will be handled by the copying routines)
    warnings.filterwarnings("ignore", category=tb.exceptions.OldIndexWarning)

    # Ignore the flavors warnings during upgrading flavor operations
    if args.upgradeflavors:
        warnings.filterwarnings("ignore", category=tb.exceptions.FlavorWarning)

    # Build the Filters instance
    filter_params = (
        args.complevel,
        args.complib,
        args.shuffle,
        args.bitshuffle,
        args.fletcher32,
    )
    if (filter_params == (None,) * 4 or args.keepfilters):
        filters = None
    else:
        if args.complevel is None:
            args.complevel = 0
        if args.shuffle is None:
            if args.complevel > 0:
                args.shuffle = True
            else:
                args.shuffle = False
        if args.bitshuffle is None:
            args.bitshuffle = False
        if args.bitshuffle:
            # Shuffle and bitshuffle are mutually exclusive
            args.shuffle = False
        if args.complib is None:
            args.complib = "zlib"
        if args.fletcher32 is None:
            args.fletcher32 = False
        filters = tb.leaf.Filters(complevel=args.complevel,
                                  complib=args.complib, shuffle=args.shuffle,
                                  bitshuffle=args.bitshuffle,
                                  fletcher32=args.fletcher32)

    # The start, stop and step params:
    start, stop, step = None, None, 1  # Defaults
    if args.rng:
        start, stop, step = args.rng.start, args.rng.stop, args.rng.step

    # Set globals
    verbose = args.verbose
    regoldindexes = args.regoldindexes
    createsysattrs = args.createsysattrs

    # Some timing
    t1 = clock()
    cpu1 = cpuclock()
    # Copy the file
    if verbose:
        print("+=+" * 20)
        print("Recursive copy:", args.recursive)
        print("Applying filters:", filters)
        if args.sortby is not None:
            print("Sorting table(s) by column:", args.sortby)
            print("Forcing a CSI creation:", args.checkCSI)
        if args.propindexes:
            print("Recreating indexes in copied table(s)")
        print(f"Start copying {srcfile}:{srcnode} to {dstfile}:{dstnode}")
        print("+=+" * 20)

    allow_padding = not args.dont_allow_padding
    # Check whether the specified source node is a group or a leaf
    h5srcfile = tb.open_file(srcfile, 'r', allow_padding=allow_padding)
    srcnodeobject = h5srcfile.get_node(srcnode)

    # Close the file again
    h5srcfile.close()

    stats = {'groups': 0, 'leaves': 0, 'links': 0, 'bytes': 0, 'hardlinks': 0}
    if isinstance(srcnodeobject, tb.group.Group):
        copy_children(
            srcfile, dstfile, srcnode, dstnode,
            title=args.title, recursive=args.recursive, filters=filters,
            copyuserattrs=args.copyuserattrs, overwritefile=args.overwritefile,
            overwrtnodes=args.overwrtnodes, stats=stats,
            start=start, stop=stop, step=step, chunkshape=args.chunkshape,
            sortby=args.sortby, check_CSI=args.checkCSI,
            propindexes=args.propindexes,
            upgradeflavors=args.upgradeflavors,
            allow_padding=allow_padding,
            use_hardlinks=True)
    else:
        # If not a Group, it should be a Leaf
        copy_leaf(
            srcfile, dstfile, srcnode, dstnode,
            title=args.title, filters=filters,
            copyuserattrs=args.copyuserattrs,
            overwritefile=args.overwritefile, overwrtnodes=args.overwrtnodes,
            stats=stats, start=start, stop=stop, step=step,
            chunkshape=args.chunkshape,
            sortby=args.sortby, check_CSI=args.checkCSI,
            propindexes=args.propindexes,
            upgradeflavors=args.upgradeflavors,
            allow_padding=allow_padding,
        )

    # Gather some statistics
    t2 = clock()
    cpu2 = cpuclock()
    tcopy = t2 - t1
    cpucopy = cpu2 - cpu1
    if verbose:
        ngroups = stats['groups']
        nleaves = stats['leaves']
        nlinks = stats['links']
        nhardlinks = stats['hardlinks']
        nbytescopied = stats['bytes']
        nnodes = ngroups + nleaves + nlinks + nhardlinks

        print(
            "Groups copied:", ngroups,
            ", Leaves copied:", nleaves,
            ", Links copied:", nlinks,
            ", Hard links copied:", nhardlinks,
        )
        if args.copyuserattrs:
            print("User attrs copied")
        else:
            print("User attrs not copied")
        print(f"KBytes copied: {nbytescopied / 1024:.3f}")
        print(
            f"Time copying: {tcopy:.3f} s (real) {cpucopy:.3f} s "
            f"(cpu)  {cpucopy / tcopy:.0%}")
        print(f"Copied nodes/sec: {nnodes / tcopy:.1f}")
        print(f"Copied KB/s : {nbytescopied / tcopy / 1024:.0f}")


from fastapi import FastAPI
from typing import Optional
from pydantic import BaseModel
import sqlite3

class Item(BaseModel):
    project_name: str = None
    development_version: Optional[str] = None
    staging_version: Optional[str] = None
    production_version: Optional[str] = None

app = FastAPI()

@app.get("/api/health")
def get_health():
    return { "Status" : "Healthy" }

@app.post("/api/add_project/")
def add_project(item: Item):
    project_name, development_version = item.project_name, item.development_version
    staging_version, production_version =  item.staging_version, item.production_version

    data = (project_name, development_version, staging_version, production_version)
    try:
        connection = sqlite3.connect('../tagger.db')
        cursor = connection.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS tagger (
            project_name TEXT NOT NULL UNIQUE,
            development TEXT,
            staging TEXT,
            production TEXT,
            PRIMARY KEY(project_name)
            )
        ''')
        cursor.execute("INSERT INTO tagger values (?,?,?,?)", data)
        return {"Success": f"Project {project_name} Added"}
    except:
        print(f"Project {project_name} already exists")
        if development_version != None:
            cursor.execute('insert into tagger (project_name,development) values(?, ?) \
                           on conflict(project_name) do update set development=excluded.development',
                           (project_name, development_version))
            return {"Success": f"Project {project_name} development version updated to: {development_version}"}
        if staging_version != None:
            cursor.execute('insert into tagger (project_name,staging) values(?, ?) \
                           on conflict(project_name) do update set staging=excluded.staging', (project_name, staging_version))
            return {"Success": f"Project {project_name} staging version updated to: {staging_version}"}
        if production_version != None:
            cursor.execute('insert into tagger (project_name,production) values(?, ?) \
                           on conflict(project_name) do update set production=excluded.production', (project_name, production_version))
            return {"Success": f"Project {project_name} production version updated to: {production_version}"}
    finally:
        connection.commit()
        connection.close()

@app.get("/api/get_all_projects")
def get_all():
    try:
        connection = sqlite3.connect('../tagger.db')
        cursor = connection.cursor()
        cursor.execute("SELECT * FROM tagger")
        results = cursor.fetchall()
        for i in results:
            entry = [ {"Project_name": i[0],
                       "development" : i[1],
                       "staging": i[2],
                       "production": i[3]}
                       for i in results ]
        return entry
    except:
        return {"Error" : "Could not fetch projects"}
    finally:
        connection.commit()
        connection.close()

@app.get("/api/get_projects")
def get_all(name: str):
    proj_name = name
    try:
        connection = sqlite3.connect('tagger.db')
        cursor = connection.cursor()
        cursor.execute("SELECT * FROM tagger WHERE project_name LIKE ?", ('%'+proj_name+'%',))
        results = cursor.fetchall()
        for i in results:
            entry = [ {"Project_name": i[0], "development" : i[1], "staging": i[2], "production": i[3]} for i in results ]
        return entry
    except:
        return {"Error" : "Could not fetch projects"}
    finally:
        connection.commit()
        connection.close()


import pygame

pygame.init()

(WindowWidth, WindowHeight) = (800, 600)
vel = 5
vel_ballx = 3
vel_bally = 3
width = WindowWidth * 0.313
height = WindowHeight * 0.042
x1 = (WindowWidth / 2) - (width / 2)
y1 = 550
x2 = (WindowWidth / 2) - (width / 2)
y2 = 50 - height
x_ball = (WindowWidth / 2)
y_ball = (WindowHeight / 2)

screen = pygame.display.set_mode((WindowWidth, WindowHeight))
pygame.display.set_caption('Ping Pong 1.0')

running = True
while running:
    pygame.time.delay(16)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    keys = pygame.key.get_pressed()

    y_ball += vel_bally
    x_ball += vel_ballx

    if x_ball > WindowWidth or x_ball < 0:
        vel_ballx = -vel_ballx

    if y_ball >= y1 and x_ball >= x1 and x_ball <= x1 + width:
        vel_bally = vel_bally * -1

    if y_ball <= y2 + height and x_ball >= x2 and x_ball <= x2 + width:
        vel_bally = vel_bally * -1

    if y_ball < 0 or y_ball > WindowHeight:
        vel_bally = vel_bally * -1

    if keys[pygame.K_a]:
        x2 -= vel
        if x2 < 0:
            x2 = 0

    elif keys[pygame.K_d]:
        x2 += vel
        if x2 > 800 - width:
            x2 = 800 - width

    if keys[pygame.K_LEFT]:
        x1 -= vel
        if x1 < 0:
            x1 = 0

    elif keys[pygame.K_RIGHT]:
        x1 += vel
        if x1 > 800 - width:
            x1 = 800 - width

    screen.fill((255, 255, 255))
    pygame.draw.rect(screen, (0, 0, 0), (x1, y1, width, height))
    pygame.draw.rect(screen, (0, 0, 0), (x2, y2, width, height))
    pygame.draw.circle(screen, (205, 13, 13), (x_ball, y_ball), 12)
    pygame.display.flip()

pygame.quit()


import logging
import pdb
import random
import sys
import time

import numpy as np
import pandas as pd
from scipy.spatial import distance

from Benchmarks.POM3 import get_pom3
from Stats.pd_dominance import cull

def riot(M, num_anchor=30, num_random=1000):
    """
    check out IEEE CLOUD18 paper
    :param M:
    :param num_anchor:
    :param num_random:
    :return:
    """
    logging.debug("Working on model " + M.name + " @ div_conv.py::riot")

    anchors = M.init_random_pop(num_anchor)
    # add some diagonals also
    d = M.decNum
    for i in range(d):
        diag = M.init_random_pop(1, default_value=i / (d - 1))
        anchors = pd.merge(anchors, diag, how='outer')

    M.eval_pd_df(anchors)
    logging.debug("Evaluating %d anchors done" % anchors.shape[0])

    randoms = M.init_random_pop(num_random)

    DIST_MTX = pd.DataFrame(index=anchors.index, columns=randoms.index)
    for a in anchors.index:
        for r in randoms.index:
            DIST_MTX.loc[a, r] = distance.sqeuclidean(randoms.loc[r, M.decs], anchors.loc[a, M.decs])
    logging.debug("Distance in configuration spaces calc done.")

    # guessing the objectives. see JC oral slides final.pdf P44 at jianfeng.us
    for r in randoms.index:
        n, f = np.argmin(DIST_MTX[r].tolist()), np.argmax(DIST_MTX[r].tolist())

        nf = (anchors.loc[n] - anchors.loc[f])[M.decs].values
        nr = (anchors.loc[n] - randoms.loc[r])[M.decs].values
        rf = (randoms.loc[r] - anchors.loc[f])[M.decs].values

        pQ = np.dot(nf, nr) / np.dot(nf, rf)
        randoms.loc[r, M.objs] = anchors.loc[n, M.objs] + (pQ / (pQ + 1)) * (
                anchors.loc[f, M.objs] - anchors.loc[n, M.objs])

    # Hypothesis showing
    # randoms_cp = randoms.copy(deep=True)
    # randoms_cp[M.objs] = -1
    # M.eval_pd_df(randoms_cp)
    # logging.debug('The avg absolute guessing error is:')
    # error = np.average(np.abs(randoms[M.objs] - randoms_cp[M.objs]), axis=0)
    # logging.debug(error)

    # collecting and returning
    all_configs = pd.merge(anchors, randoms, how='outer')
    cleared, dominated = cull(all_configs[M.objs])

    res = all_configs.loc[cleared]
    M.eval_pd_df(res, force_eval_all=True)
    cleared, dominated = cull(res)
    return res.loc[cleared]

if __name__ == '__main__':
    logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', stream=sys.stdout,
                        level=logging.DEBUG)
    # model = get_xomo('flight')
    model = get_pom3('p3a')
    startat = time.time()

    for repeat in range(100):
        print(repeat)
        np.random.seed(545557)
        random.seed(545557)
        res = riot(model, num_random=50)
        print(res)
        if res.shape[0] < 2:
            pdb.set_trace()
        print('=' * 20)
    # write_results_to_txt("debug_writing", res, model, 'riot', runtime=time.time() - startat)


from elasticsearch import Elasticsearch
from elasticsearch.helpers import scan
from dotenv import load_dotenv
import os
import pandas as pd
from datetime import datetime, timedelta, timezone
from re import search

load_dotenv()

def get_elasticsearchdata():
    indexer_es = Elasticsearch([os.getenv("ELASTIC_URL")],
                               basic_auth=(os.getenv("ELASTIC_USERNAME"), os.getenv("ELASTIC_PASSWORD")),
                               verify_certs=False)
    print("Elasticsearch connection is successful")
    query = {
        "query": {
            "bool": {
                "must": [
                    {
                        "wildcard": {
                            "sitename": "**"
                        }
                    },
                    {
                        "range": {
                            "@timestamp": {
                                "gte": "now-1m"
                            }
                        }
                    }
                ]
            }
        }
    }

    part_of_index = scan(client=indexer_es,
                         query=query,
                         index=('*'),
                         raise_on_error=True,
                         preserve_order=False,
                         clear_scroll=True
                         )

    result = list(part_of_index)

    temp = []
    # We need only '_source', which has all the fields required.
    # This elimantes the elasticsearch metdata like _id, _type, _index.
    for hit in result:
        temp.append(hit['_source'])

    df = pd.DataFrame(temp)

    return df

df = get_elasticsearchdata()
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
pd.set_option('display.max_colwidth', None)
df.columns.values.tolist()

print(df)


from django.test import TestCase
from .models import Invitation

from django.contrib.auth.models import User

class InvitationTests(TestCase):

    @classmethod
    def setUpTestData(cls):
        # Set up non-modified objects used by all test methods
        from_user = User.objects.create(username='jimmy')
        to_user = User.objects.create(username='joe')
        Invitation.objects.create(from_user=from_user,
                                  to_user=to_user,
                                  message='I will crush you!'
                                  )

    def test_to_user(self):
        invitation = Invitation.objects.get(id=1)
        expected_user = f'{invitation.from_user}'
        self.assertEquals(expected_user, 'jimmy')

    def test_from_user(self):
        invitation =Invitation.objects.get(id=1)
        expected_user = f'{invitation.to_user}'
        self.assertEquals(expected_user, 'joe')

    def test_message(self):
        invitation = Invitation.objects.get(id=1)
        expected_message = f'{invitation.message}'
        self.assertEquals(expected_message, 'I will crush you!')


from datetime import datetime

from airflow import DAG
from airflow.providers.databricks.operators.databricks_repos import DatabricksReposCreateOperator

with DAG(
        dag_id='Create_DatabricksRepo',
        schedule_interval='@daily',
        start_date=datetime.now(),
        tags=['example'],
        catchup=False,
) as dag:
    repo_path = "/Repos/sc87291@inetpsa.com/airflow-repo"
    git_url = "https://github.com/lazkamal/dags_2.git"
    create_repo = DatabricksReposCreateOperator(task_id='create_repo', repo_path=repo_path, git_url=git_url, databricks_conn_id='databricks_default')


import os, sys
import time
import argparse
import numpy as np

import tensorflow as tf

sys.path.append('../')
from src import helper as hp
from src import fixed_parameters as fpf

parser = argparse.ArgumentParser(description='Run experiment')
parser.add_argument('-i','--chunk_id', type=int, help='Id of the current data chunk', required=True)
parser.add_argument('-n','--n_chunk', type=int, help='Total number of chunks', required=True)
parser.add_argument('-r','--root', type=str, help='Root for models and data', required=True)
parser.add_argument('-m','--mode', type=str, help='If test, will run on a small data to test. Else choose deploy', required=True)

def do_chunkify(lst,n):
    """get n chunk of equal length except last to cover lst"""
    return [lst[i::n] for i in range(n)]

def one_hot_encode(tokenized_input, n_chars):
    output = np.zeros([len(tokenized_input), n_chars])
    for j, token in enumerate(tokenized_input):
        output[j, int(token)] = 1
    return output

def get_tok_smi(smi, token_indices, max_len_model, start_char, end_char, pad_char):

    token_list = hp.smi_tokenizer(smi)
    token_list = [start_char] + token_list + [end_char]
    padding = [pad_char]*(max_len_model - len(token_list))
    token_list.extend(padding)
    token_list = np.array([token_indices[x] for x in token_list])

    return token_list

def get_tok_prot(prot, token_indices, max_len_model, start_char, end_char, pad_char):

    token_list = list(prot)
    token_list = [start_char] + token_list + [end_char]
    padding = [pad_char]*(max_len_model - len(token_list))
    token_list.extend(padding)
    token_list = np.array([token_indices[x] for x in token_list])

    return token_list

# SMILES fixed parameters
s_pad_char = fpf.s_pad_char
s_start_char = fpf.s_start_char
s_end_char = fpf.s_end_char
s_indices_token = fpf.s_indices_token
s_token_indices = fpf.s_token_indices

# PROTEIN fixed parameters
p_pad_char = fpf.p_pad_char
p_start_char = fpf.p_start_char
p_end_char = fpf.p_end_char
p_indices_token = fpf.p_indices_token
p_token_indices = fpf.p_token_indices

if __name__ == '__main__':

    start = time.time()

    ####################################
    # get back parameters
    args = vars(parser.parse_args())

    verbose = True
    chunk_id = args['chunk_id']
    n_chunk = args['n_chunk']
    root = args['root']
    mode = args['mode']

    if mode not in ['test', 'deploy']:
        raise ValueError('Your mode does not exist. Pick: test or deploy')

    if mode=='test':
        savepath = f'{root}results/'
    else:
        savepath = f'{root}results/test/'
    os.makedirs(savepath, exist_ok=True)

    if verbose: print('\nSTART PREDICTIONS')
    ####################################

    ####################################
    if mode=='test':
        all_smi = hp.read_with_pd(f'{root}data_clean/small.txt')
    elif mode=='deploy':
        all_smi = hp.read_with_pd(f'{root}data_clean/all_smi_clean.txt')

    if n_chunk>1:
        data_chunkified = do_chunkify(all_smi, n_chunk)

        # -1 because the arg to the bash script will go,
        # eg for 10, from 1 to 10 included
        # so we want idx 0 to 9
        chunk_id = chunk_id-1
        all_smi = data_chunkified[chunk_id]
        print(f'N of data in the current chunk: {len(all_smi)}')

    n_data = len(all_smi)
    max_len_model = 100+2
    p_n_chars = len(p_token_indices)
    s_n_chars = len(s_token_indices)

    s_data = np.empty([n_data, max_len_model])

    for i,s in enumerate(all_smi):
        s_data[i] = get_tok_smi(s, s_token_indices, max_len_model, s_start_char, s_end_char, s_pad_char)

    # protein
    protein = hp.read_with_pd(f'{root}data_clean/rcsb_pdb_6LZE.txt')
    p_max_len_model = 1000+2
    p_data = np.empty([1, p_max_len_model])

    p_data[0] = get_tok_prot(protein, p_token_indices, p_max_len_model, p_start_char,
                             p_end_char, p_pad_char)
    ####################################

    ####################################
    # start prediction
    n_model = 5
    all_y_preds = {}

    # we have only one prot
    p = p_data[0]
    p = one_hot_encode(list(p), p_n_chars)
    p = np.expand_dims(p, axis=0)

    for i in range(1,n_model+1):
        print(f'\ncurrent model: {i}')

        path = f'{root}A02_5repeats/{i}/16/last.h5'
        model = tf.keras.models.load_model(path)

        l_start = time.time()
        log_every = 200
        for n in range(n_data):
            print('current data ', n)
            if n%log_every==0 and n!=0:
                l_end = time.time()
                l_time = l_end - l_start
                print(f'current data: {n}')
                print(f'{log_every} data done in: {l_time:.05} seconds')
                l_start = time.time()

            s = s_data[n]
            s = one_hot_encode(list(s), s_n_chars)
            s = np.expand_dims(s, axis=0)

            logit = model.predict([p,s])
            pred = tf.nn.sigmoid(logit)
            fpred = float(pred.numpy())

            if all_smi[n] in all_y_preds:
                all_y_preds[all_smi[n]].append(fpred)
            else:
                all_y_preds[all_smi[n]] = [fpred]

        hp.save_pkl(f'{savepath}chunk_{chunk_id}_model_{i}_all_y_preds.pkl', all_y_preds)

    end = time.time()
    print(f'PREDICTIONS DONE in {end - start:.05} seconds')
    ####################################