from ast import literal_eval

from allura.command import base

from bson import ObjectId
from allura import model as M
from allura.lib import exceptions
from ming.odm import session

# Example usage:
# paster set-neighborhood-features development.ini
# 4f50c898610b270c92000286 max_projects 50

class SetNeighborhoodFeaturesCommand(base.Command):
    min_args = 4
    max_args = 4
    usage = "<ini file> <neighborhood> <feature> <value>"
    summary = "Change the neighborhood features\r\n" \
        "\t<neighborhood> - the neighborhood name or object id\r\n" \
        "\t<feature> - feature value to change options are max_projects, css, google_analytics, or private_projects\r\n" \
        "\t<value> - value to give the feature - see below for descriptions\r\n" \
        "\t    max_projects - maximum projects allowed in neighborhood - specify None for no limit\r\n" \
        "\t    css - type of css customization - use \"none\", \"picker\", or \"custom\".\r\n" \
        "\t    google_analytics - allow the user to use google analytics - True or False\r\n" \
        "\t    private_projects - allow private projects in the neighborhood - True or False"
    parser = base.Command.standard_parser(verbose=True)

    def command(self):
        self.basic_setup()
        n_id = self.args[1]
        n_feature = self.args[2]
        # try to get a bool or int val, otherwise treat it as a string
        try:
            n_value = literal_eval(self.args[3])
        except ValueError:
            n_value = self.args[3]
        if n_feature not in ["max_projects", "css", "google_analytics", "private_projects"]:
            raise exceptions.NoSuchNBFeatureError("%s is not a valid "
                                                  "neighborhood feature. The valid features are \"max_projects\", "
                                                  "\"css\", \"google_analytics\" and \"private_projects\"" % n_feature)

        n = M.Neighborhood.query.get(name=n_id)
        if not n:
            n = M.Neighborhood.query.get(_id=ObjectId(n_id))

        if not n:
            raise exceptions.NoSuchNeighborhoodError("The neighborhood %s "
                                                     "could not be found in the database" % n_id)
        else:
            if n_feature == "max_projects":
                if isinstance(n_value, int) or n_value is None:
                    n.features['max_projects'] = n_value
                else:
                    raise exceptions.InvalidNBFeatureValueError("max_projects must be "
                                                                "an int or None.")
            elif n_feature == "css":
                if n_value in ['none', 'custom', 'picker']:
                    n.features['css'] = n_value
                else:
                    raise exceptions.InvalidNBFeatureValueError("css must be "
                                                                "'none', 'custom', or 'picker'")
            elif n_feature == "google_analytics":
                if isinstance(n_value, bool):
                    n.features['google_analytics'] = n_value
                else:
                    raise exceptions.InvalidNBFeatureValueError("google_analytics must be "
                                                                "a boolean")
            else:
                if isinstance(n_value, bool):
                    n.features['private_projects'] = n_value
                else:
                    raise exceptions.InvalidNBFeatureValueError("private_projects must be "
                                                                "a boolean")
            session(M.Neighborhood).flush()


import os
import json
import time
import dgl
from argparse import ArgumentParser

import tqdm
import torch
from torch.utils.data import DataLoader
from transformers import (RobertaTokenizer, RobertaConfig, AdamW,
                          get_linear_schedule_with_warmup)
from transformers import (BertTokenizer, BertConfig, AdamW,
                          get_linear_schedule_with_warmup)
from model import OneIE
from graph import Graph
from config import Config
from data import IEDataset
from scorer import score_graphs
from util import generate_vocabs, load_valid_patterns, save_result, best_score_by_task

# configuration
parser = ArgumentParser()
parser.add_argument('-c', '--config', default='config/example.json')
parser.add_argument('-n', '--name', default='experiment')
parser.add_argument('-g', '--gpu', type=int, default=0)
args = parser.parse_args()
config = Config.from_json_file(args.config)
config.gpu_device = args.gpu
print("Run training on GPU " + str(config.gpu_device))

# set GPU device
use_gpu = config.use_gpu
if use_gpu and config.gpu_device >= 0:
    torch.cuda.set_device(config.gpu_device)

# output
output_dir = os.path.join(config.log_path, args.name)
if not os.path.exists(output_dir):
    os.mkdir(output_dir)
log_file = os.path.join(output_dir, 'log.txt')
with open(log_file, 'w', encoding='utf-8') as w:
    w.write(json.dumps(config.to_dict()) + '\n')
    print('Log file: {}'.format(log_file))
best_role_model = os.path.join(output_dir, 'best.role.mdl')
best_event_model = os.path.join(output_dir, 'best.event.mdl')
best_relation_model = os.path.join(output_dir, 'best.relation.mdl')
final_model = os.path.join(output_dir, 'final.mdl')

dev_role_result_file = os.path.join(output_dir, 'role.dev.json')
dev_event_result_file = os.path.join(output_dir, 'event.dev.json')
dev_relation_result_file = os.path.join(output_dir, 'relation.dev.json')

test_role_result_file = os.path.join(output_dir, 'role.test.json')
test_event_result_file = os.path.join(output_dir, 'event.test.json')
test_relation_result_file = os.path.join(output_dir, 'relation.test.json')

# datasets
model_name = config.bert_model_name

org_train_graphs, train_align, train_exist = torch.load(config.train_amr)
org_dev_graphs, dev_align, dev_exist = torch.load(config.dev_amr)
org_test_graphs, test_align, test_exist = torch.load(config.test_amr)
if config.use_gpu:
    train_graphs, dev_graphs, test_graphs = [], [], []
    for g in org_train_graphs:
        g_device = g.to(config.gpu_device)
        train_graphs.append(g_device)

    for g in org_dev_graphs:
        g_device = g.to(config.gpu_device)
        dev_graphs.append(g_device)

    for g in org_test_graphs:
        g_device = g.to(config.gpu_device)
        test_graphs.append(g_device)
else:
    train_graphs, dev_graphs, test_graphs = org_train_graphs, org_dev_graphs, org_test_graphs

if config.bert_model_name.startswith("roberta"):
    tokenizer = RobertaTokenizer.from_pretrained(model_name, do_lower_case=False)
else:
    tokenizer = BertTokenizer.from_pretrained(model_name,
                                              cache_dir=config.bert_cache_dir,
                                              do_lower_case=False)

# print(len(test_graphs))
train_set = IEDataset(config.train_file, train_graphs, train_align, train_exist, gpu=use_gpu,
                      relation_mask_self=config.relation_mask_self,
                      relation_directional=config.relation_directional,
                      symmetric_relations=config.symmetric_relations,
                      ignore_title=config.ignore_title)
dev_set = IEDataset(config.dev_file, dev_graphs, dev_align, dev_exist, gpu=use_gpu,
                    relation_mask_self=config.relation_mask_self,
                    relation_directional=config.relation_directional,
                    symmetric_relations=config.symmetric_relations)
test_set = IEDataset(config.test_file, test_graphs, test_align, test_exist, gpu=use_gpu,
                     relation_mask_self=config.relation_mask_self,
                     relation_directional=config.relation_directional,
                     symmetric_relations=config.symmetric_relations)

vocabs = generate_vocabs([train_set, dev_set, test_set])

train_set.numberize(tokenizer, vocabs)
dev_set.numberize(tokenizer, vocabs)
test_set.numberize(tokenizer, vocabs)

valid_patterns = load_valid_patterns(config.valid_pattern_path, vocabs)

batch_num = len(train_set) // config.batch_size
dev_batch_num = len(dev_set) // config.eval_batch_size + \
    (len(dev_set) % config.eval_batch_size != 0)
test_batch_num = len(test_set) // config.eval_batch_size + \
    (len(test_set) % config.eval_batch_size != 0)

# initialize the model
model = OneIE(config, vocabs, valid_patterns)
model.load_bert(model_name, cache_dir=config.bert_cache_dir)
if use_gpu:
    model.cuda(device=config.gpu_device)

if config.use_graph_encoder:
    graph_params = []
    for k in range(config.gnn_layers):
        for para in model.graph_encoder.gnn.gats[k].fc.parameters():
            graph_params.append(para)
        for para in model.graph_encoder.gnn.gats[k].attn_fc.parameters():
            graph_params.append(para)
        for para in model.graph_encoder.gnn.gats[k].edge_fc.parameters():
            graph_params.append(para)

    param_groups = [
        {
            'params': [p for n, p in model.named_parameters() if n.startswith('bert')],
            'lr': config.bert_learning_rate, 'weight_decay': config.bert_weight_decay
        },
        {
            'params': [p for n, p in model.named_parameters() if not n.startswith('bert')
                    and 'crf' not in n and 'global_feature' not in n],
            'lr': config.learning_rate, 'weight_decay': config.weight_decay
        },
        {
            'params': [p for n, p in model.named_parameters() if not n.startswith('bert')
                    and ('crf' in n or 'global_feature' in n)],
            'lr': config.learning_rate, 'weight_decay': 0
        },
        {
            'params': graph_params, 'lr': config.learning_rate, 'weight_decay': 0
        }
    ]

else:

    param_groups = [
        {
            'params': [p for n, p in model.named_parameters() if n.startswith('bert')],
            'lr': config.bert_learning_rate, 'weight_decay': config.bert_weight_decay
        },
        {
            'params': [p for n, p in model.named_parameters() if not n.startswith('bert')
                    and 'crf' not in n and 'global_feature' not in n],
            'lr': config.learning_rate, 'weight_decay': config.weight_decay
        },
        {
            'params': [p for n, p in model.named_parameters() if not n.startswith('bert')
                    and ('crf' in n or 'global_feature' in n)],
            'lr': config.learning_rate, 'weight_decay': 0
        }
    ]

optimizer = AdamW(params=param_groups)
schedule = get_linear_schedule_with_warmup(optimizer,
                                           num_warmup_steps=batch_num * config.warmup_epoch,
                                           num_training_steps=batch_num * config.max_epoch)

# model state
state = dict(model=model.state_dict(),
             config=config.to_dict(),
             vocabs=vocabs,
             valid=valid_patterns)

global_step = 0
global_feature_max_step = int(config.global_warmup * batch_num) + 1
print('global feature max step:', global_feature_max_step)
print("message passing level: ", config.lamda)

tasks = ['trigger', 'relation', 'role']
best_dev = {k: 0 for k in tasks}

for epoch in range(config.max_epoch):
    print('Epoch: {}'.format(epoch))

    # training set
    progress = tqdm.tqdm(total=batch_num, ncols=75,
                         desc='Train {}'.format(epoch))
    optimizer.zero_grad()
    train_loss = 0.0
    for batch_idx, batch in enumerate(DataLoader(
            train_set, batch_size=config.batch_size // config.accumulate_step,
            shuffle=True, drop_last=True, collate_fn=train_set.collate_fn)):

        loss = model(batch, epoch)
        loss = loss * (1 / config.accumulate_step)
        loss.backward()
        train_loss += loss.item()

        if (batch_idx + 1) % config.accumulate_step == 0:
            progress.update(1)
            global_step += 1
            torch.nn.utils.clip_grad_norm_(
                model.parameters(), config.grad_clipping)
            optimizer.step()
            schedule.step()
            optimizer.zero_grad()
    progress.close()
    print("Training Loss: --- " + str(train_loss / (batch_idx + 1)))

    # dev set
    progress = tqdm.tqdm(total=dev_batch_num, ncols=75,
                         desc='Dev {}'.format(epoch))
    best_dev_role_model = False
    best_dev_event_model = False
    best_dev_relation_model = False
    dev_gold_graphs, dev_pred_graphs, dev_sent_ids, dev_tokens = [], [], [], []
    for batch in DataLoader(dev_set, batch_size=config.eval_batch_size,
                            shuffle=False, collate_fn=dev_set.collate_fn):
        progress.update(1)
        graphs = model.predict(batch, epoch)
        if config.ignore_first_header:
            for inst_idx, sent_id in enumerate(batch.sent_ids):
                if int(sent_id.split('-')[-1]) < 4:
                    graphs[inst_idx] = Graph.empty_graph(vocabs)
        for graph in graphs:
            graph.clean(relation_directional=config.relation_directional,
                        symmetric_relations=config.symmetric_relations)
        dev_gold_graphs.extend(batch.graphs)
        dev_pred_graphs.extend(graphs)
        dev_sent_ids.extend(batch.sent_ids)
        dev_tokens.extend(batch.tokens)
    progress.close()
    dev_scores = score_graphs(dev_gold_graphs, dev_pred_graphs,
                              relation_directional=config.relation_directional)
    for task in tasks:
        if dev_scores[task]['f'] > best_dev[task]:
            best_dev[task] = dev_scores[task]['f']
            if task == 'role':
                print('Saving best role model')
                torch.save(state, best_role_model)
                best_dev_role_model = True
                save_result(dev_role_result_file,
                            dev_gold_graphs, dev_pred_graphs, dev_sent_ids,
                            dev_tokens)
            if task == 'trigger':
                print('Saving best event model')
                torch.save(state, best_event_model)
                best_dev_event_model = True
                save_result(dev_event_result_file,
                            dev_gold_graphs, dev_pred_graphs, dev_sent_ids,
                            dev_tokens)
            if task == 'relation':
                print('Saving best relation model')
                torch.save(state, best_relation_model)
                best_dev_relation_model = True
                save_result(dev_relation_result_file,
                            dev_gold_graphs, dev_pred_graphs, dev_sent_ids,
                            dev_tokens)

    # test set
    progress = tqdm.tqdm(total=test_batch_num, ncols=75,
                         desc='Test {}'.format(epoch))
    test_gold_graphs, test_pred_graphs, test_sent_ids, test_tokens = [], [], [], []
    for batch in DataLoader(test_set, batch_size=config.eval_batch_size, shuffle=False,
                            collate_fn=test_set.collate_fn):
        progress.update(1)
        graphs = model.predict(batch, epoch)
        if config.ignore_first_header:
            for inst_idx, sent_id in enumerate(batch.sent_ids):
                if int(sent_id.split('-')[-1]) < 4:
                    graphs[inst_idx] = Graph.empty_graph(vocabs)
        for graph in graphs:
            graph.clean(relation_directional=config.relation_directional,
                        symmetric_relations=config.symmetric_relations)
        test_gold_graphs.extend(batch.graphs)
        test_pred_graphs.extend(graphs)
        test_sent_ids.extend(batch.sent_ids)
        test_tokens.extend(batch.tokens)
    progress.close()
    test_scores = score_graphs(test_gold_graphs, test_pred_graphs,
                               relation_directional=config.relation_directional)

    if best_dev_role_model:
        save_result(test_role_result_file, test_gold_graphs, test_pred_graphs,
                    test_sent_ids, test_tokens)
    if best_dev_relation_model:
        save_result(test_relation_result_file, test_gold_graphs, test_pred_graphs,
                    test_sent_ids, test_tokens)
    if best_dev_event_model:
        save_result(test_event_result_file, test_gold_graphs, test_pred_graphs,
                    test_sent_ids, test_tokens)

    result = json.dumps(
        {'epoch': epoch, 'loss': train_loss / (batch_idx + 1), 'dev': dev_scores, 'test': dev_scores})
    with open(log_file, 'a', encoding='utf-8') as w:
        w.write(result + '\n')
    print('Log file', log_file)

torch.save(state, final_model)
print("Final model saved!")

print("================== Best Role Model ==================")
best_score_by_task(log_file, 'role')
print("================== Best Trigger Model ==================")
best_score_by_task(log_file, 'trigger')
print("================== Best Relation Model ==================")
best_score_by_task(log_file, 'relation')


from utils.connection import Connection
from utils.constants import *
import requests
from urllib.parse import unquote
from datetime import datetime

class Adder(Connection):

    def __init__(self):
        super().__init__()
        self.status = 1

    def add(self, link, idx=None):

        title = [x for x in link.split("/") if x != ""][-1].replace("-", " ")
        link = unquote(link.strip())
        req = requests.get(link)
        data = req.text

        chapters, index, logo_url = self.__parse_data(data)
        logo_url = unquote(logo_url)
        logo = self.__download_logo(logo_url)
        now = datetime.today().strftime('%Y-%m-%d %H:%M:%S')

        older = self.fetchall("SELECT * FROM `data` WHERE `link` = '%s'" % link)
        sinopsis = self.get_sinopsis(data)
        genres = self.get_genre(data)

        if len(older) == 0:
            query = "INSERT INTO `data`(`name`, `logo`, `description`, `genres`, `link`, `index`, `status`, `updated`) "
            query += "VALUES ('%s', '%s', '%s', '%s', '%s', '%s', %d, '%s')"

            self.execute(query % (title, logo, sinopsis, genres, link, index, self.status, now))
            manga_id = self.lastinsertid()

            if idx is None:
                self.__insert_chapters(manga_id, chapters)
            else:
                self.__insert_chapters(manga_id, chapters[(int(idx) - 1):])

            print()
            print("Finish Add %s" % title)
            print("Updated %d" % len(chapters))
        else:
            manga = older[0]
            if manga[4] in [0, 3]:
                print("Manga has been added")
                print("do you want to unarchieve it ?")
                x = input()
                if x in ["y", "yes", "Y", "Yes", "YES"]:
                    status = 0 if manga[4] == 0 else 2
                    self.execute("UPDATE `data` SET `status` = %d WHERE `mangaId` = %d" %(status, manga[0]))
            else:
                print("Manga has been added and not archieved")

    def __insert_chapters(self, manga_id, chapters):
        query = "INSERT INTO `queued`(`mangaId`, `link`) VALUES ('%d', '%s')"
        for x in chapters:
            self.execute(query % (manga_id, x))

    @staticmethod
    def __download_logo(link):
        link = link.strip()
        img = requests.get(link, stream=True)
        length = img.headers.get("content-length")
        ext = link.split("?")[0].split(".")[-1]
        filename = datetime.now().strftime("%Y%m%d%H%M%S") + "." + ext
        with open(LOGO_DIR + filename, "wb") as f:
            if length is None:
                print("[INFO] logo size: 0kb")
                f.write(img.content)
            else:
                dl = 0
                length = int(length)
                content_length = "{:.2f}".format(length / 1024)
                print("[INFO] logo size: %s kb" % content_length)
                for i in img.iter_content(chunk_size=1024):
                    dl += len(i)
                    f.write(i)
                    percent = "{:.2f}".format(dl * 100 / length)
                    print("[INFO] Download progress: %s%s" % (percent, "%"), end="\r")
        return LOGO_TEMP % filename

    @staticmethod
    def get_genre(data):
        try:
            if "komiku.id" in data:
                genre = data.replace("\"", "'").split("
")[1].split("
")[0].split("")
                genre = [x.split(">")[-1].strip() for x in genre[:-1]]
                return ", ".join(genre)
            else:
                genre = data.split("genres-content")[1].split("
")[0].split("")
                genre = [x.split(">")[-1] for x in genre]
                return ", ".join(genre)
        except:
            return ""

    @staticmethod
    def get_sinopsis(data):
        try:
            if "komiku.id" in data:
                sinopsis = data.replace("\"", "'").split("id='Sinopsis'")[1].split("
")[1].split("
")[0].strip()
                sinopsis = sinopsis.replace("\r", "").replace("\n", "
")
                sinopsis = sinopsis.replace("

", "
").replace("
", "").replace("
", "")
                return sinopsis.replace("'", "").replace("\"", "")
            else:
                return data.split("summary__content")[1].split("
")[1].split("
")[0]
        except:
            return ""

    @staticmethod
    def __parse_data(data):
        return [], 0, ""


import json
import os

def set_player_stats(treats, toys):
  data = { 'treats': { 'total': treats['total'], 'spent': treats['spent'] }, 'toys': toys }
  with open("stats.json", "w") as f:
    json.dump(data, f)

def get_player_stats(player):
  try:
    with open("stats.json", "r") as f:
      stats = json.load(f)
      player.treats = stats['treats']
      player.toys = stats['toys']
  except FileNotFoundError:
    set_player_stats(0, 10)
    player.treats = { 'total': 0, 'spent': 0 }
    player.toys = 10


import os
import pandas as pd
import numpy as np
from env import get_connection

"""
This is how you get rid of the Unnamed: 0 column:

#read_csv(filename, index_col=0)
#to_csv(filename, index=False)
"""

def get_titanic_data():
    filename = "titanic.csv"

    if os.path.isfile(filename):
        return pd.read_csv(filename)
    else:
        # read the SQL query into a dataframe
        df = pd.read_sql('SELECT * FROM passengers', get_connection('titanic_db'))

        # Write that dataframe to disk for later. Called "caching" the data for later.
        df.to_csv(filename, index=False)

        # Return the dataframe to the calling code
        return df

def get_iris_data():
    """
    This function reads the iris data from Codeup db into a df.
    """
    filename = "iris.csv"

    if os.path.isfile(filename):
        return pd.read_csv(filename)
    else:
        query = """
        SELECT sepal_length, sepal_width, petal_length, petal_width, species_name, species_id FROM measurements
        LEFT JOIN species USING (species_id);
        """
        # read the SQL query into a dataframe
        df = pd.read_sql(query, get_connection('iris_db'))

        # Write that dataframe to disk for later. Called "caching" the data for later.
        df.to_csv(filename, index=False)

        # Return the dataframe to the calling code
        return df

def get_telco_data():
    """
    This function reads the telco_churn data from Codeup db into a df.
    """
    filename = "telco_churn.csv"

    if os.path.isfile(filename):
        return pd.read_csv(filename)
    else:
        # read the SQL query into a dataframe
        query = """
        SELECT * FROM customers
        LEFT JOIN contract_types USING (contract_type_id)
        LEFT JOIN internet_service_types USING (internet_service_type_id)
        LEFT JOIN payment_types USING (payment_type_id);
        """
        df = pd.read_sql(query, get_connection('telco_churn'))

        # Write that dataframe to disk for later. Called "caching" the data for later.
        df.to_csv(filename, index=False)

        # Return the dataframe to the calling code
        return df


from tkinter import messagebox
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
import datetime

from timer import CountdownTimer, CountdownTimerError
from settings import Settings
from data_manager import DataManager

class BodyFrame(ttk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(bootstyle=DANGER, *args, **kwargs)
        self.pack(fill=BOTH, expand=YES)
        self._is_study_time = True
        self.data_manager = DataManager()
        self.study_seconds = self.data_manager.study_seconds
        self.break_seconds = self.data_manager.break_seconds

        self.countdown = CountdownTimer(self.study_seconds)
        self.running = ttk.BooleanVar(value=False)
        self.after_id = ttk.StringVar()
        self.elapsed = ttk.IntVar(value=self.study_seconds)
        self.stopwatch_text = ttk.StringVar(value=self.countdown.time_left())
        self.completed_rounds = 0
        self.tomato_counter = []

        self.images = [
            ttk.PhotoImage(
                name="tomato_counter_BW",
                file="images/tomate_BW_32px.png"
            ),
            ttk.PhotoImage(
                name="tomato_counter",
                file="images/tomate_32px.png"
            )
        ]

        self.build_mode_field()
        self.build_stopwatch_field()
        self.build_tomato_counter_field()

    def build_mode_field(self):
        container = ttk.Frame(master=self, bootstyle=DANGER)
        container.pack(fill=BOTH, expand=YES)

        self.mode_label = ttk.Label(
            master=container,
            text="Study time",
            font="-size 32",
            anchor=CENTER,
            bootstyle=(INVERSE, WARNING)
        )
        self.mode_label.pack(pady=(50, 20), ipadx=20)

    def build_stopwatch_field(self):
        container = ttk.Frame(master=self, bootstyle=DANGER)
        container.pack(fill=BOTH, expand=YES)

        self.stopwatch_label = ttk.Label(
            master=self,
            font="-size 64",
            anchor=CENTER,
            bootstyle=(INVERSE, DANGER),
            textvariable=self.stopwatch_text
        )
        self.stopwatch_label.pack(in_=container, side=TOP, fill=X, padx=20, pady=(0, 30))

        self.start_pause_button = ttk.Button(
            master=self,
            text="Start",
            command=self._start_pause_stopwatch,
            bootstyle=WARNING
        )
        self.start_pause_button.pack(in_=container, side=LEFT, expand=YES, fill=X, padx=10, pady=10)

        self.reset_button = ttk.Button(
            master=self,
            text="Reset",
            command=self.reset_step,
            bootstyle=SECONDARY
        )
        self.reset_button.pack(in_=container, side=LEFT, expand=YES, fill=X, padx=10, pady=10)

        self.config_button = ttk.Button(
            master=self,
            text="Config",
            command=self._open_config_window,
            bootstyle=SECONDARY
        )
        self.config_button.pack(in_=container, side=LEFT, expand=YES, fill=X, padx=10, pady=10)

    def build_tomato_counter_field(self):
        container = ttk.Frame(master=self, bootstyle=SECONDARY)
        container.pack(fill=BOTH, expand=YES, padx=10, pady=(0, 10))

        self.counter_label = ttk.Label(
            master=self,
            text="Counter:",
            font="-size 24",
            anchor=CENTER,
            bootstyle=(INVERSE, SECONDARY)
        )
        self.counter_label.pack(in_=container, side=LEFT, ipadx=10, ipady=10)

        for tomato in range(4):
            if tomato < self.completed_rounds:
                image_name = "tomato_counter"
            else:
                image_name = "tomato_counter_BW"

            self.tomato_counter.append(
                ttk.Label(
                    master=self,
                    image=image_name,
                    bootstyle=(INVERSE, SECONDARY)
                )
            )

        for tomato_intance in self.tomato_counter:
            tomato_intance.pack(in_=container, side=LEFT, expand=YES)

    def _start_pause_stopwatch(self):
        if self.countdown.is_running:
            self.pause()
        else:
            self.start()

    def start(self):
        self.countdown.start()
        self.reset_button.configure(state=DISABLED)
        self.start_pause_button.configure(bootstyle=SECONDARY, text="Pause")

        self.after_id.set(self.after(1, self.increment))

    def pause(self):
        self.countdown.pause()
        self.reset_button.configure(state=NORMAL)
        self.start_pause_button.configure(bootstyle=WARNING, text="Start")

        self.after_cancel(self.after_id.get())

    def increment(self):
        try:
            time_left = self.countdown.time_left()
            self.stopwatch_text.set(time_left)
            self.after_id.set(self.after(1000, self.increment))
        except CountdownTimerError:
            self._times_up()

    def _times_up(self):
        if self._is_study_time:
            self._study_time_is_up()
        else:
            self._break_time_is_up()

    def _study_time_is_up(self):
        self.completed_rounds += 1
        tomato_instance: ttk.Label = self.tomato_counter[self.completed_rounds - 1]
        tomato_instance.configure(image="tomato_counter")
        if self.completed_rounds == 4:
            self._study_is_complete()
        else:
            self.start_pause_button.configure(bootstyle=WARNING, state=NORMAL, text="Start")
            self.reset_button.configure(state=NORMAL)
            self.mode_label.configure(bootstyle=(INVERSE, PRIMARY), text="Break time")
            self._is_study_time = False
            self.reset_step()
            messagebox.showinfo("Parabéns!!!", "Você completou o tempo de estudo, aproveite para descansar!")

    def _break_time_is_up(self):
        self.start_pause_button.configure(bootstyle=WARNING, state=NORMAL, text="Start")
        self.reset_button.configure(state=NORMAL)
        self.mode_label.configure(bootstyle=(INVERSE, WARNING), text="Study time")
        self._is_study_time = True
        self.reset_step()
        messagebox.showinfo("Bora focar de novo?", "O tempo de descanso acabou! Siga no foco, você consegue!")

    def reset_step(self):
        if self._is_study_time:
            self.countdown = CountdownTimer(init_seconds=self.study_seconds)
        else:
            self.countdown = CountdownTimer(init_seconds=self.break_seconds)
        self.stopwatch_text.set(value=self.countdown.time_left())

    def _study_is_complete(self):
        self.start_pause_button.configure(state=DISABLED)
        self.reset_button.configure(state=DISABLED)
        self.config_button.configure(state=DISABLED)
        self.mode_label.configure(bootstyle=(INVERSE, SUCCESS), text="Complete!")
        messagebox.showinfo("Estudos concluídos!!!", "Parabéns por completar seus estudos. Agora descanse para que possa absorvê-los melhor!")

    def _open_config_window(self):
        self.settings = Settings(mother=self)
        self.settings.place_window_center()
        self.settings.iconbitmap("images/tomate_icon.ico")


from flask import Blueprint, jsonify, session, request, render_template, redirect, url_for
from flask_login import current_user
from app.models import Review, db
from app.forms.review_form import ReviewForm

review_routes = Blueprint('reviews', __name__)

# #GET ALL REVIEWS
@review_routes.route('/', methods=['GET'])
def get_all_reviews():
    reviews = Review.query.all()
    for review in reviews:
        return {'reviews' : [review.to_dict() for review in reviews]}

@review_routes.route('/<int:id>', methods=['GET'])
def get_review(id):
    review = Review.query.get(id)
    return review.to_dict()

@review_routes.route('/current', methods=['GET'])
def get_current_user_reviews():
    if current_user.is_authenticated:
        userId = current_user.id
        reviews = Review.query.filter(Review.user_id == userId).all()
        return {'reviews': [review.to_dict() for review in reviews]}

# #POST A REVIEW
@review_routes.route('/<int:teeshirtId>/review/', methods=['POST'])
def create_a_review(teeshirtId):
    # if current_user.is_authenticated:
        form = ReviewForm()
        # form['csrf_token'].data = request.cookies['csrf_token']
        data = form.data
        review = Review(
            review = data['review'],
            teeshirt_id = teeshirtId,
            user_id = current_user.id
            )
        db.session.add(review)
        db.session.commit()
        return review.to_dict()

# Test post route
@review_routes.route('/<int:teeshirtId>/review', methods=['GET'])
def render_review_form(teeshirtId):
    form = ReviewForm()
    return render_template('review_form.html', form=form, teeshirtId=teeshirtId)

# #EDIT A REVIEW
@review_routes.route('/<int:id>/update/', methods=['POST'])
def edit_review(id):
    # if current_user.is_authenticated:
        form = ReviewForm()
        # form['csrf_token'].data = request.cookies['csrf_token']
        data = form.data
        review_to_update = Review.query.get(id)
        # if review_to_update.user_id == current_user.id:
        review_to_update.review = data['review']
        db.session.commit()
        return review_to_update.to_dict()

@review_routes.route('/<int:id>/update', methods=['GET'])
def render_update_form(id):
    form = ReviewForm()
    review_to_update = Review.query.get(id)
    form.review.data = review_to_update.review  # Pre-fill the form with existing data
    return render_template('update_review_form.html', form=form, id=id)

# #DELETE A REVIEW
@review_routes.route('/delete/<int:id>', methods=['DELETE'])
def delete_review(id):
    # if (current_user.is_authenticated):
        review_to_delete = Review.query.get(id)
        # if review_to_delete.user_id == current_user.id:
        db.session.delete(review_to_delete)
        db.session.commit()
        return {'review': 'your review has been deleted'}


from paddle import Paddle # import de la classe Paddle
from ball import Ball # import de la classe Ball
from briques import Brick # import de la classe Brick
import time
import random
playingGame = False
bricks = [] # Création de la liste
new_game = False
score=0 # Initialisation du score
def setup():
    global paddle, ball # on déclare les variables paddle et ball comme globale
    size(805,600) # on défini la taille de la fenêtre
    paddle = Paddle() # on crée l'objet paddle
    ball = Ball() # on crée l'objet ball

    if new_game == True :
        randomizer = random.randint(110,200)
        randomizer2 = random.randint(4,8)
        randomizer3 = random.randint(20,40)
        randomizer4 = random.randint(150,245)
        ball.vel = PVector(1, 1)*randomizer2
        for x in range(5,width - 80,75):
            addBrick(x+37.5,50,3)
            addBrick(x+37.5,70,2)
            addBrick(x+37.5,90,1)
            addBrick(x+37.5,randomizer,4)
            addBrick(x+37.5,randomizer+randomizer3,4)

    else :
        for x in range(5,width - 80,75):
            addBrick(x+37.5,50,3)
            addBrick(x+37.5,70,2)
            addBrick(x+37.5,90,1)

# Fonctions créant et stockant les briques dans la liste
def addBrick(x,y,hits):
    brick = Brick(x,y,hits)
    bricks.append(brick)

def f_score(score) :
    """fonction permettant d'afficher le score"""
    fill("#FFFFFF") #couleur blanche
    textSize(25)
    text("score :",0,25)
    text(score,90,25)

def f_vie(vie) :
    """fonction permettant d'afficher le score"""
    fill("#FFFFFF") #couleur blanche
    textSize(25)
    text("vies :",700,25)
    text(ball.vie,775,25)

def draw():
    global playingGame, score
    background(0,0,0)
    #appel des méthodes pour le paddle
    paddle.display()
    f_score(score)
    f_vie(ball.vie)
    casser = True
    if ball.vie == 0 :  # permet de stopper le jeu quand les 3 vies sont utilisées
            playingGame = False
            fill("#FFFFFF") # couleur blanche
            textSize(30) # taille du texte
            text("Plus de vie, partie terminee",200,250) # message à afficher + sa position
            textSize(40)
            text("cliquez pour rejouer",200,300)
            if mousePressed :
                ball.vie = 3
                setup() # permet de recréer l'environnement (les briques + replacer le paddle)
                playingGame = True
    if ball.vie>0 and len(bricks)==0 :
        playingGame= False
        fill("#FFFFFF") #couleur blanche
        textSize(30)
        text("Bravo, tu as gagne !",250,200)
        textSize(30)
        text("cliquez pour rejouer dans un nouveau niveau",60,300)
        if mousePressed :
                ball.vie = 3
                new_game = True
                setup() # permet de recréer l'environnement (les briques + replacer le paddle)
                playingGame = True

    if playingGame:
        paddle.checkEdges()
        paddle.update()
    #appel des méthodes pour la balle

        ball.checkEdges()
        ball.update()
    ball.display()

    if (ball.meets_paddle(paddle)):
        if ball.dir.y > 0 :
            ball.dir.y *= -1
    for i in range(len(bricks)):
        bricks[i].display()

    #for i in range(len(bricks)-1,-1,-1):
    i=0
    while i < len(bricks) and i !=-1 :
        if ball.meets_brick(bricks[i]):
            score+=5
            bricks.pop(i) # détruit une brique i
            i=-2
        i+=1

def mousePressed():
    global playingGame
    playingGame = True

# détection des mouvements touches q et d
def keyPressed():
    if key == "q" or key == "Q":
        paddle.isMovingLeft = True
    elif key == "d" or key == "D":
        paddle.isMovingRight = True

#annulation des mouvements quand on relâche la touche
def keyReleased():
    paddle.isMovingRight = False
    paddle.isMovingLeft = False


from rest_framework import status
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from jwtauth.permissions import IsProfileCompleted, IsProfileCompleteOrReadOnly
from .serializer import (
    SubAccountBalanceSerializer,
    TransferHistorySerializer,
    WithdrawHistorySerializer,
    CoinSerializer,
)
from .models import SubAccountBalance, SubAccountDetails, WithdrawHistory, SubAccountDepositHistory
from rest_framework.views import APIView
from rest_framework.exceptions import APIException
from entropy.binance_api.generic import GenericAPI
from entropy.binance_api.master_account import MasterAccountAPI
import uuid
from .db_ops import DBOperations
from rest_framework.serializers import ValidationError
from itertools import chain
from django.shortcuts import get_object_or_404

class DepositAddress(APIView):
    permission_classes = [
        IsAuthenticated,
        IsProfileCompleteOrReadOnly,
    ]

    def post(self, request, *args, **kwargs):
        request_body_data = request.data
        logged_in_user = request.user.id
        # serializer for validating coin
        serializer = CoinSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            # this method will check the sub-account exists or not.
            # if exists then return sub-account details else create new sub account
            sub_account_data = SubAccountDetails.get_or_create(user_id=logged_in_user)
            generic_api = GenericAPI(
                api_key=sub_account_data.sub_account_api_key,
                api_secret=sub_account_data.sub_account_secret,
            )
            # fetching coin address from binance.
            coin_address = generic_api.get_deposit_address(
                coin=request.data["coin"]
            )
            return Response(
                coin_address,
                status=status.HTTP_200_OK,
            )
        except Exception as e:
            raise ValidationError("Please try after some time!")

class SubAccountBalanceView(APIView):
    permission_classes = [
        IsAuthenticated,
        IsProfileCompleteOrReadOnly,
    ]

    def get(self, request, *args, **kwargs):
        db_ops = DBOperations()

        try:
            # updating DB with latest balance
            db_ops.fetch_assets_balance_and_store_in_db(
                logged_in_user=request.user.id
            )
        except Exception as e:
            # dynamic message from serializer and e.message
            raise APIException("message")

        assets_balances = SubAccountBalance.objects.filter(
            account_id=request.user.id)
        serializer = SubAccountBalanceSerializer(
            assets_balances, many=True)
        return Response({"assets": serializer.data}, status=status.HTTP_200_OK)

class WithdrawView(APIView):
    permission_classes = [
        IsAuthenticated,
        IsProfileCompleteOrReadOnly
    ]

    def post(self, request, *args, **kwargs):

        # serializer for validating coin
        serializer = CoinSerializer(data={"coin" : request.data["asset"]})
        serializer.is_valid(raise_exception=True)

        db_ops = DBOperations()

        # update balance
        db_ops.fetch_assets_balance_and_store_in_db(request.user.id)

        updated_balance = get_object_or_404(SubAccountBalance,account_id=request.user.id, asset=request.data["asset"])

        master_api_obj = MasterAccountAPI()
        sub_account_data = SubAccountDetails.get_or_create(user_id=request.user.id)

        if updated_balance.available >= float(request.data["amount"]):
            # generating random client transaction ID
            client_transaction_id = str(uuid.uuid4()).replace("-", "")

            try:
                # sub-to-master
                transfer_res = master_api_obj.transfer(
                    asset=request.data["asset"],
                    amount=request.data["amount"],
                    fromId=sub_account_data.sub_account_id,
                    clientTranId=client_transaction_id,
                )
            except Exception as e:
                raise APIException(e.message)

            updated_transfer_history = db_ops.update_transfer_history(
                client_transaction_td=transfer_res["clientTranId"]
            )

            if len(updated_transfer_history) == 1:
                updated_transfer_history[0]["fake_id"] = transfer_res["clientTranId"]

            # save the updated_transfer_history into table
            serializer = TransferHistorySerializer(
                data=updated_transfer_history, many=True
            )
            serializer.is_valid(raise_exception=True)
            serializer.save()

            try:
                # master to wallet
                # NOTE: use this address for testing with LTC
                withdraw_response_id =  master_api_obj.withdraw(
                coin=request.data["asset"], address="LXAnLnBbsN5hjkebcSMB6ZdP8rUgUo9QqT", amount=request.data["amount"]
                )

                latest_withdraw_entry = db_ops.get_latest_withdraw(
                    withdraw_response_id=withdraw_response_id)

                # once matched perform entry in DB_table with one extra column called sub_account_id
                current_sub_account = SubAccountDetails.objects.get(
                    account_id=request.user.id)
                latest_withdraw_entry["sub_account_id"] = current_sub_account.sub_account_id
                serializer = WithdrawHistorySerializer(data=latest_withdraw_entry)
                serializer.is_valid(raise_exception=True)

                serializer.save()

                return Response(
                    {"message": "Withdraw request submitted"}, status=status.HTTP_200_OK
                )
            except Exception as e:
                try:
                    # transfer coins back to sub account from master account
                    master_api_obj.transfer(
                        asset=request.data["asset"], amount=request.data["amount"], toId=sub_account_data.sub_account_id
                    )

                    return Response(
                        {"message": "Coins are transfered back to the sub account"}, status=status.HTTP_200_OK
                    )
                except Exception as e:
                    # TODO: need to handle this case where witdraw request for master to wallet fails and we are unable to transfer coins back to sub account
                    raise APIException("Withdraw request failed")
        else:
            raise ValidationError("Insufficient Balance")

class WalletHistoryView(APIView):
    permission_classes = [
        IsAuthenticated,
        IsProfileCompleteOrReadOnly,
    ]

    def get(self, request, *args, **kwargs):
        # get withdraw history
        withdraw_history = WithdrawHistory.objects.extra(
            select={"timestamp": "apply_time"}
        ).values("coin", "amount", "order_type", "timestamp", "status")

        # get deposit history
        deposit_history = SubAccountDepositHistory.objects.extra(
            select={"timestamp": "deposited_at"}
        ).values("coin", "amount", "order_type", "timestamp", "status")

        # combined/chained two querysets
        report = chain(withdraw_history, deposit_history)

        # sort data according to timestamp
        return Response(sorted(
            list(report), key=lambda x: x["timestamp"], reverse=True
        ))


import sys

from string import ascii_lowercase as lowers

import networkx as nx

def setup():
     pass

def reachable1(src, dest):
     src = src.replace('S','a').replace('E','z')
     dest = dest.replace('S','a').replace('E','z')

     if src == 'z':
          return True

     maxdest = lowers[lowers.index(src)+1]

     return dest <= maxdest

def part1(filename):
     offsets = [ (-1, 0), (1, 0), (0, -1), (0, 1)]

     terrain = nx.DiGraph()

     terrain_strs = []
     for line in open(filename):
          terrain_strs.append(line.strip())

     rows = len(terrain_strs)
     cols = len(terrain_strs[0])

     for r in range(rows):
          for c in range(cols):
               for dr, dc in offsets:
                    if r+dr < 0 or r+dr >= rows or c+dc < 0 or c+dc >= cols:
                         continue
                    if reachable1(terrain_strs[r][c], terrain_strs[r+dr][c+dc]):
                         terrain.add_edge(
                              (r,c),
                              (r+dr,c+dc)
                         )
                    if terrain_strs[r][c] == 'S':
                         start_pos = (r,c)
                    elif terrain_strs[r][c] == 'E':
                         end_pos = (r,c)

     print(nx.shortest_path_length(terrain, start_pos, end_pos))

def part2(filename):
     offsets = [ (-1, 0), (1, 0), (0, -1), (0, 1)]

     terrain = nx.DiGraph()
     candidate_starts = []

     terrain_strs = []
     for line in open(filename):
          terrain_strs.append(line.strip().replace('S','a'))

     rows = len(terrain_strs)
     cols = len(terrain_strs[0])

     for r in range(rows):
          for c in range(cols):
               for dr, dc in offsets:
                    if r+dr < 0 or r+dr >= rows or c+dc < 0 or c+dc >= cols:
                         continue
                    if reachable1(terrain_strs[r][c], terrain_strs[r+dr][c+dc]):
                         terrain.add_edge(
                              (r,c),
                              (r+dr,c+dc)
                         )

                    if terrain_strs[r][c] == 'a':
                         candidate_starts.append((r,c))
                    elif terrain_strs[r][c] == 'E':
                         end_pos = (r,c)

     lens = []
     for start_pos in candidate_starts:
          try:
               lens.append(nx.shortest_path_length(terrain, start_pos, end_pos))
          except nx.NetworkXNoPath:
               continue
     print(sorted(lens)[0])

if __name__ == '__main__':
     setup()
     if sys.argv[1] == '1':
          part1(sys.argv[2])
     else:
          part2(sys.argv[2])


from typing import List

class Solution:
    def compress(self, chars: List[str]) -> int:
        start_len = len(chars)

        simb = chars[0]
        count = 0
        total_count = 0
        j = 0
        len_s = len(chars)
        while j < len_s:
            if chars[0] == simb:
                count += 1
            else:
                total_count += 1
                chars.append(simb)
                if count > 1:
                    total_count += len(str(count))
                    for i in str(count):
                        chars.append(i)
                simb = chars[0]
                count = 1
            j += 1
            chars.pop(0)

        chars.append(simb)
        if count > 1:
            total_count += len(str(count))
            for i in str(count):
                chars.append(i)
        #print(chars)
        #return chars

if __name__ == "__main__":
    chars = list(input().split(','))
    s = Solution()
    print(s.compress(chars))


# This Python file uses the following encoding: utf-8
import os, sys, time

from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog
from PySide6 import QtGui, QtCore
import glob

from createFileList import createFileList
from ui_form import Ui_improMeasure

def pbBasicInfoBrowseRootdir_clicked(self):
    # This code does not really work. It is only for intellisense
    # so that editor recognizes the functions and variables in self.
    if type(self) == type(None):
        self.ui = Ui_improMeasure()
    # This function allows user to set root directory by GUI file dialog
    folder = str(QFileDialog.getExistingDirectory(self, "Select Directory"))
    if len(folder) >= 1:
        self.ui.edRootdir.setText(folder)
        # display message
        self.ui.pteMsg.appendPlainText("# (%s) Set %s as the root directory." \
            % (time.strftime("%Y-%m-%d %H:%M:%S"), folder))
    else:
        # display message
        self.ui.pteMsg.appendPlainText("# (%s) Action cancelled. " \
            "Root directory unchanged." % (time.strftime("%Y-%m-%d %H:%M:%S")))

def pbBasicInfoBrowseConfig_clicked(self):
    # This code does not really work. It is only for intellisense
    # so that editor recognizes the functions and variables in self.
    if type(self) == type(None):
        self.ui = Ui_improMeasure()
    # This function allows user to set root directory by GUI file dialog
    filePathRet = QFileDialog.getOpenFileName(self, "Select configuration file", )
    filePath = filePathRet[0]
    if len(filePath) >= 1:
        self.ui.edConfig.setText(filePath)
        # display message
        self.ui.pteMsg.appendPlainText("# (%s) Set %s as the configuration file." \
            % (time.strftime("%Y-%m-%d %H:%M:%S"), filePath))
    else:
        # display message
        self.ui.pteMsg.appendPlainText("# (%s) Action cancelled. " \
            "Configuration file unchanged." % (time.strftime("%Y-%m-%d %H:%M:%S")))


import time
from binance_apis.client import get_spot_client
from utils.handler import get_current_coins,cancel_locked_orders,hold_or_sell_coins
from binance_apis.coins import coins_prices

client = get_spot_client()
acc_details=client.account()
if acc_details["canTrade"] and acc_details["accountType"] == "SPOT":
    free_coins, locked_coins = get_current_coins(acc_details)
    # Cancel all open orders
    count = 0
    while len(locked_coins) and count<10:
        cancel_locked_orders(client,locked_coins)
        count+=1
        time.sleep(2)
        acc_details = client.account()
        free_coins, locked_coins = get_current_coins(acc_details)
    # To decide whether to hold or sell the free coins
    hold_or_sell_coins(client,free_coins)


"""
Data resource for data exploration
"""
from flask import request, g
from flask_restful import Resource
from sqlalchemy import or_, extract, func, text

import meerkat_abacus.util.epi_week
from meerkat_abacus.model import Data
import meerkat_abacus.util as abacus_util
from meerkat_api.authentication import authenticate, is_allowed_location
from meerkat_api.extensions import db, api
from meerkat_api.resources.variables import Variables
from meerkat_api.util import fix_dates

def get_variables(category):
    """
    Get variables with category

    Args:
        category: category of variables to get
    Returns:
        names(dict): {key: variable}
    """
    variables_instance = Variables()
    variables = variables_instance.get(category)
    names = dict((str(v), variables[v]["name"]) for v in variables.keys())
    return names

def get_locations_by_level(level, only_loc):
    """
    Returns all the locations with the given level. If only_loc is
    given we only include children of only_loc.If we ask for the clinic
    level we also require that the clinic sends case reports

    Args:
        level: clinic, district or region
        only_loc: location to restrict wich locations are included

    Returns:
        names: {id: name}
    """
    locations = abacus_util.get_locations(db.session)
    names = {}
    for l in locations.values():
        if (l.level == level
            and (not only_loc or abacus_util.is_child(only_loc, l.id, locations))
            and (level != "clinic" or l.case_report)
        ):
            names[l.id] = l.name
    return names

class QueryVariable(Resource):
    """
    Create a table where all records have the variable, and is broken down by
    group_by. Start and end data gives option to specifiy time period

        We first construct the conditions for our database query.
        Need to take special care if the
        variable or the group_by is given in terms of locations
        as they need to be handeld differently.
        We then do the db-query and then assemble the return dictionary

    Args:\n
        variable: the variable all records need to fulfill\n
        group_by: category to group by\n
        start_date: start date\n
        end_date: end_date\n
        only_loc: If given retricts the data to that location\n
        use_ids: If use_ids is true we use variable_ids and not the
                 name as keys for the return\n
        additional_variables: a list of variable string id's to
                 include in the filter conditions\n

    Returns:\n
        data: {variable_1: {total: X, weeks: {12:X,13:X}}....}\n
    """
    decorators = [authenticate]

    def get(self, variable, group_by, start_date=None,
            end_date=None, only_loc=None, use_ids=None, date_variable=None, additional_variables=None,
            group_by_variables=None):

        variable = str(variable)
        if not only_loc:
            if "only_loc" in request.args:
                only_loc = request.args["only_loc"]
            else:
                only_loc = g.allowed_location
        if not is_allowed_location(only_loc, g.allowed_location):
            return {}

        start_date, end_date = fix_dates(start_date, end_date)
        if "use_ids" in request.args.keys() or use_ids:
            use_ids = True
        else:
            use_ids = False

        if date_variable:
            date_conditions = [func.to_date(
                Data.variables[date_variable].astext, "YYYY-MM-DDTHH-MI-SS") >= start_date,
            func.to_date(
                Data.variables[date_variable].astext, "YYYY-MM-DDTHH-MI-SS") < end_date]
        else:
            date_conditions = [Data.date >= start_date, Data.date < end_date]

        if "location" in variable:
            location_id = variable.split(":")[1]
            conditions = date_conditions + [or_(loc == location_id for loc in (
                Data.country, Data.zone, Data.region, Data.district, Data.clinic))]
        else:
            conditions = [Data.variables.has_key(variable)] + date_conditions
            if additional_variables:
            # add additional variable filters if there are and
                for i in additional_variables:
                    conditions.append(Data.variables.has_key(i))

            if only_loc:
                conditions += [or_(loc == only_loc for loc in (
                    Data.country, Data.zone, Data.region, Data.district, Data.clinic))]
        epi_year_start = meerkat_abacus.util.epi_week.epi_year_start_date(start_date)
        # Determine which columns we want to extract from the Data table
        columns_to_extract = [func.count(Data.id).label('value')]
        if date_variable:
            columns_to_extract.append(func.floor(
                extract('days', func.to_date(Data.variables[date_variable].astext, "YYYY-MM-DDTHH-MI-SS") -
                epi_year_start) / 7 + 1
            ).label("week"))
        else:
            columns_to_extract.append(
                func.floor(
                    extract('days', Data.date -
                            epi_year_start) / 7 + 1
                ).label("week")
            )
        # We want to add the columns to extract based on the group_by value
        # in addition we create a names dict that translates ids to names

        if "locations" in group_by:
            # If we have locations in group_by we also specify the level at
            #  which we want to group the locations, clinic, district or region
            if ":" in group_by:
                level = group_by.split(":")[1]
            else:
                level = "clinic"

            locations = abacus_util.get_locations(db.session)
            ids = locations.keys()
            names = get_locations_by_level(level, only_loc)

            columns_to_extract += [getattr(Data, level, None)]
            group_by_query = level
        else:
            if not group_by_variables:
                names = get_variables(group_by)
            else:
                names = group_by_variables
            if len(names) == 0:
                return {}
            ids = names.keys()
            for i in ids:
                columns_to_extract.append(
                    Data.variables.has_key(str(i)).label("id" + str(i)))
            group_by_query = ",".join(["id" + str(i) for i in ids])
        if use_ids:
            names = {vid: vid for vid in names.keys()}
        start_epi_week = abacus_util.epi_week.epi_week_for_date(start_date)[1]
        end_epi_week = abacus_util.epi_week.epi_week_for_date(end_date)[1]

        # How we deal with start and end dates in different years
        if start_date.year != end_date.year:
            end_epi_week += 53 * (end_date.year - start_date.year)

        # DB Query
        results = db.session.query(
            *tuple(columns_to_extract)
        ).filter(*conditions).group_by(text("week," + group_by_query))
        # Assemble return dict
        ret = {}
        for n in names.values():
            ret[n] = {"total": 0,
                      "weeks": {i: 0 for i in range(start_epi_week, end_epi_week + 1)}}

        for r in results:
            # r = (number, week, other_columns_to_extract
            if "locations" in group_by:
                # r[2] = location
                if r[2]:
                    ret[names[r[2]]]["total"] += r[0]
                    ret[names[r[2]]]["weeks"][int(r[1])] = int(r[0])
            else:
                # r[2:] are the ids that the record has
                for i, i_d in enumerate(ids):
                    if r[i + 2]:
                        ret[names[i_d]]["total"] += r[0]
                        ret[names[i_d]]["weeks"][int(r[1])] = int(r[0])
        return ret

class QueryCategory(Resource):
    """
    Create a contingency table with category1 x category2.
    Start and end data gives option to specifiy time period.

    We first construct the conditions for our database query.
    Need to take special care if any of the categories are locations
    locations as they need to be handeld differently.
    We then do the db-query and then assemble the return dictionary

    Args:\n
        variable: the variable all records need to fulfill\n
        group_by: category to group by\n
        start_date: start date\n
        end_date: end_date\n
        only_loc: restrict data to only this location\n
    Returns:\n
        data: {variable_11: {variable_21: number, variable_22: number ...},
               variable_12: {....\n
    """
    decorators = [authenticate]

    def get(self, group_by1, group_by2, start_date=None,
            end_date=None, only_loc=None):

        if not only_loc:
            if "only_loc" in request.args:
                only_loc = request.args["only_loc"]
            else:
                only_loc = g.allowed_location
        if not is_allowed_location(only_loc, g.allowed_location):
            return {}
        start_date, end_date = fix_dates(start_date, end_date)
        use_ids = False
        if "use_ids" in request.args.keys():
            use_ids = True

        # Assemble conditions and columns to query
        conditions = []
        if only_loc:
            conditions += [or_(loc == only_loc for loc in (
                Data.country, Data.zone, Data.region, Data.district, Data.clinic))]

        columns_to_query = [Data.categories[group_by1].astext, Data.categories[group_by2].astext]
        if "locations" in group_by1:
            if ":" in group_by1:
                level = group_by1.split(":")[-1]
            else:
                level = "clinic"
            names1 = get_locations_by_level(level, only_loc)
            ids1 = list(names1.keys())
            columns_to_query += [getattr(Data, level)]
            names2 = get_variables(group_by2)
            ids2 = names2.keys()
            conditions += [or_(Data.variables.has_key(str(i)) for i in ids2)]

        elif "locations" in group_by2:
            if ":" in group_by2:
                level = group_by2.split(":")[-1]
            else:
                level = "clinic"
            names2 = get_locations_by_level(level, only_loc)
            ids2 = list(names2.keys())
            columns_to_query += [getattr(Data, level)]
            names1 = get_variables(group_by1)
            ids1 = names1.keys()
            conditions += [or_(Data.variables.has_key(str(i)) for i in ids1)]

        else:
            names1 = get_variables(group_by1)
            ids1 = names1.keys()
            conditions += [or_(Data.variables.has_key(str(i)) for i in ids1)]
            names2 = get_variables(group_by2)
            ids2 = names2.keys()
            conditions += [or_(Data.variables.has_key(str(i)) for i in ids2)]

        if use_ids:
            names1 = {vid: vid for vid in names1.keys()}
            names2 = {vid: vid for vid in names2.keys()}
        conditions += [Data.date >= start_date, Data.date < end_date]

        # DB query
        conn = db.engine.connect()

        query = db.session.query(
            *tuple(columns_to_query)
        ).filter(*conditions)
        res = conn.execute(query.statement).fetchall()

        ret = {}
        # Assemble return dict
        # while True:
        #     chunk = res.fetchmany(500)
        #     if not chunk:
        #         break
        for row in res:
            i1 = row[0]
            i2 = row[1]
            if "locations" in group_by1:
                i1 = row[2]
            if "locations" in group_by2:
                i2 = row[2]
            if i1 and i2:
                ret.setdefault(names1[i1], {}).setdefault(
                    names2[i2], 0)
                ret[names1[i1]][names2[i2]] += 1
        # We also add rows and columns with zeros
        for n1 in names1.values():
            for n2 in names2.values():
                if n1 in ret:
                    if n2 not in ret[n1]:
                        ret[n1][n2] = 0
                else:
                    ret.setdefault(n1, {})
                    ret[n1][n2] = 0
        return ret

api.add_resource(QueryVariable,
                 "/query_variable/<variable>/<group_by>",
                 "/query_variable/<variable>/<group_by>"
                 "/<start_date>/<end_date>")
api.add_resource(QueryCategory,
                 "/query_category/<group_by1>/<group_by2>",
                 "/query_category/<group_by1>/<group_by2>/<only_loc>",
                 "/query_category/<group_by1>/<group_by2>"
                 "/<start_date>/<end_date>",
                 "/query_category/<group_by1>/<group_by2>"
                 "/<start_date>/<end_date>/<only_loc>")


from typing import Any, Callable, Dict, List, Union

from vcr import VCR, mode

from .patch import get_overridden_build

def scrub_header(unwanted_headers: List[str]) -> Callable:
    def before_record_response(response: Union[Dict, Any]) -> Union[Dict, Any]:
        if isinstance(response, dict):
            try:
                response_str = (
                    response.get("body", {}).get("string", b"").decode("utf-8")
                )
                if "Rate limit reached for" in response_str:
                    # don't record rate-limiting responses
                    return None
            except UnicodeDecodeError:
                pass  # ignore if we can't parse response

            for unwanted_header in unwanted_headers:
                if unwanted_header in response["headers"]:
                    response["headers"].pop(unwanted_header)
        return response

    return before_record_response

default_vcr = VCR(
    path_transformer=VCR.ensure_suffix(".yaml"),
    filter_headers=[
        "User-Agent",
        "Accept",
        "Accept-Encoding",
        "Connection",
        "Content-Length",
        "Content-Type",
        # OpenAI request headers we don't want
        "authorization",
        "X-OpenAI-Client-User-Agent",
        "OpenAI-Organization",
    ],
    filter_query_parameters=["api_key"],
    before_record_response=scrub_header(
        [
            # OpenAI response headers we don't want
            "Server",
            "access-control-allow-origin",
            "alt-svc",
            "openai-organization",
            "openai-version",
            "strict-transport-security",
            "x-ratelimit-limit-requests",
            "x-ratelimit-limit-tokens",
            "x-ratelimit-remaining-requests",
            "x-ratelimit-remaining-tokens",
            "x-ratelimit-reset-requests",
            "x-ratelimit-reset-tokens",
            "x-request-id",
        ]
    ),
    match_on=("method", "scheme", "host", "port", "path", "query", "body", "headers"),
    record_mode=mode.ONCE,
)

use_cassette = default_vcr.use_cassette

__all__ = [
    "get_overridden_build",
]


#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Script para pre-procesar los datos de time-stamping para el método de a-Rossi.

Lee los archivos y genera las historias pedidas.
alfa_rossi_preprocesamiento() es la función principal.
"""

import numpy as np
import matplotlib.pyplot as plt
import os
import seaborn as sns
sns.set()

import sys
sys.path.append('../../')

from CinePy.modules.io_modules import read_timestamp_list, read_timestamp_list_ascii
from CinePy.modules.estadistica import timestamp_to_timewindow

plt.style.use('paper')

def corrige_roll_over(datas_con_rollover):
    """
    Corrige el roll-over en los datos adquiridos

    El roll-over se produce por la resolución finita del contador utilizado.
    Esta corrección se basa en el casteo que hace python: al usar np.diff()
    hace un roll-over para mantenerse en el tipo de dato original. Al usar
    np.sumsum() pasa al siguiente tipo de datos más grande uint64.

    Parámetros
    ----------
        datas_con_rollover : list of numpy.ndarray
            Cada elemento de la lista son los datos leídos del archivo

    Resultados
    ----------
        data_sin_rollover : list of numpy.ndarray
            Cada elemento son los datos con roll-over corregido

    """
    if not isinstance(datas_con_rollover, list):
        _es_lista = False
        datas_con_rollover = [datas_con_rollover]
    else:
        _es_lista = True

    data_sin_rollover = []
    for i, data in enumerate(datas_con_rollover):
        print('Corrigiendo roll-over del archivo [{}]'.format(i))
        print('Tipo de dato inicial: {}'.format(data.dtype))
        # Obtengo los tiempos entre pulsos
        _dts = np.diff(data)
        # Sumo todos los intervalos
        _data_acum = np.cumsum(_dts)
        # Agrego el primer punto como t=0
        _data_acum = np.insert(_data_acum, 0, 0)
        print('Tipo de dato final {}'.format(_data_acum.dtype))
        print('-' * 50)
        data_sin_rollover.append(_data_acum)

    if not _es_lista:
        data_sin_rollover = data_sin_rollover[0]
    return data_sin_rollover

def grafica_mediciones_cuentas(nombres, dt_s, tb=12.5e-9, tipo='binario'):
    """
    Grafica las tasas de cuentas para los archivos con timestamping

    Parámetros
    ----------
        nombres : list of strings
            Nombres de los archivos que se quieren graficar
        dt_s: float  [segundos]
            Duración del intervalo en donde se cuentan los pulsos
        tb : float [segundos]
            Tiempo del reloj que se utilizó para el timestamping
        formato_datos : string, opcional ('binario', 'ascii')
            Formato en que están escritos los datos en los archivos

    """
    dt_s = np.float64(dt_s)
    if tipo == 'binario':
        # Se leen los datos del archivo
        data_con_rollover, _ = read_timestamp_list(nombres)
        # Se corrige el roll-over
        datos = corrige_roll_over(data_con_rollover)
    elif tipo == 'ascii':
        datos = read_timestamp_list_ascii(nombres)
    datos_binned, ts = timestamp_to_timewindow(datos, dt_s, 'segundos',
                                               'segundos', tb)

    # Graficación
    fig1, ax1 = plt.subplots(1, 1)
    for datos, t, nombre in zip(datos_binned, ts, nombres):
        _lab_str = os.path.split(nombre)[-1]
        ax1.plot(t, datos, label=_lab_str)

    ax1.set_xlabel('Tiempo [s]')
    if dt_s == 1.0:
        ax1.set_ylabel('Cuentas [cps]')
    else:
        ax1.set_ylabel('Cuentas por dt [1/s]')
    ax1.grid(True)
    ax1.legend(loc='best')
    plt.show()
    return None

def separa_en_historias(time_stamped_data, N_historias):
    """
    Separa los datos de tiempo entre pulsos en historias.

    Lo hace considerando que cada historia tendrá (estadísticamente) la misma
    duración temporal.
    Podría haberse separado por cantidad de pulsos.

    Parámetros
    ----------
        time_stamped_data : numpy nd.array
            Datos con los tiempos de lelgada de cada pulso.
            Ya debe tener corregido el roll-over
        N_historias : integer
            Cantidad de historias en que se quiere separar los datos

    Resultados
    ----------
        historias : list of numpy array
            Cada elemento es una historia. Todas comienzan en t=0.
    """
    _t_maximo = time_stamped_data[-1]
    # Tiempo que durará cada historia (estadísticamente, pues no necesariamente
    # habrá un pulso al tiempo calculado
    _t_historia = _t_maximo / N_historias
    # Cada historia pasa a estar caracterizado por un mismo número
    # (0, 1, ..., N_historias -1)
    _bloques = time_stamped_data // _t_historia
    # El último pulso crea una historia espúrea (a veces por errores de
    # redondeo no aparece)
    if int(_bloques[-1]) == N_historias:
        print('*** [separa_en_historias()]: Se eliminna el último pulso '
              + 'para evitar una historia extra')
        _bloques = np.delete(_bloques, -1)
    # Busca los índices en donde se cambian de historia (cambia el número con
    # que están caracterizadas)
    _index_historias = np.where(_bloques[:-1] != _bloques[1:])[0]
    # Sumo uno para que sea más fácil aplicar el slice
    _index_historias += 1
    # Controla que haya pulsos en todas las historias
    # En un proceso estacionario debería suceder siempre
    if _index_historias.size < N_historias - 1:
        print('Hay historias que no tienen pulsos. Revisar')
        quit()
    # Índices del comienzo de cada historia
    # (Se aagrega 0 al comienzo para definir la primer historia)
    _index_start = np.insert(_index_historias, 0, 0)
    # Índices del final de cada historia
    # Se agrega al final el índice máximo que para definir la última historia)
    _index_end = np.insert(_index_historias, _index_historias.size,
                           time_stamped_data.size)
    # Se construyen las historias en una lista
    historias = []
    for start, end in zip(_index_start, _index_end):
        # Todas comenzarán en t=0
        historia = time_stamped_data[start:end] - time_stamped_data[start]
        historias.append(historia)

    return historias

def separa_en_historias_lista(time_stamped_datas, N_historias):
    """ Wrapper de `separa_en_historias` para ser aplicado en listas """
    historias_list = []
    for t_s_data in time_stamped_datas:
        historias_list.append(separa_en_historias(t_s_data, N_historias))
    return historias_list

def convierte_dtype_historias(historias):
    """
    Convierte al tipo de dato de menor tamaño posible

    Se asume que todas las historias tendrán el mismo dtype
    """

    # Asumo que todas las historias tienen el mismo tipo de datos
    # (vienen de un mismo numpy array)
    in_dtype = historias[0].dtype
    print('Convirtiendo tipo de datos de las historias')
    print('Tipo de datos originales : {}'.format(in_dtype))
    # Máximo valor de cada historias
    _hist_max = []
    for historia in historias:
        _hist_max.append(historia[-1])
    _hist_max = np.asarray(_hist_max)
    # Fuerzo a que todas las historias tengan el mismo tipo de datos.
    # Se podría optimizar relajando esta condición.
    if all(_hist_max < 2**32 / 2 - 1):
        _new_dtype = 'int32'
    if all(_hist_max < 2**32):
        _new_dtype = 'uint32'
    if all(_hist_max < 2**64 / 2 - 1):
        _new_dtype = 'int64'
    elif all(_hist_max < 2**64):
        _new_dtype = 'uint64'
    else:
        _new_dtype = 'float64'
    # Se hace la conversión
    new_historias = []
    for historia in historias:
        new_historias.append(np.asarray(historia, dtype=_new_dtype))
    print('Tipo de dato convrtido: {}'.format(_new_dtype))
    print('-' * 50)

    return new_historias

def convierte_dtype_historias_lista(list_historias):
    """ Wrapper de `convierte_dtype_historias` para aplicarlo en listas """

    new_list_historias = []
    for i, historias in enumerate(list_historias):
        print("Archivo [{}]".format(i))
        new_list_historias.append(convierte_dtype_historias(historias))
    return new_list_historias

def inspeccion_historias(historias, tb=12.5e-9):
    """
    Verifica los datos separados en historias

    Parámetros
    ---------
        historias : list of numpy.ndarray
        tb : double
            Tiempo de cada pulso de reloj utilizado

    Resultados
    ----------
        pulsos_historia : list
            Cantidad de pulsos de cada historia
        tiempos_historia : list
            Duración de cada historia (utilizando tb para convertir)
    """

    print('Cantidad de historias: {}'.format(len(historias)))
    pulsos_historia = []    # Cantidad de pulsos en cada historia
    tiempos_historia = []   # Duranción de cada historia
    tasa_historia = []      # Tasa de cuentas de cada historia
    for historia in historias:
        pulsos_historia.append(historia.size)
        tiempos_historia.append(historia[-1])
        tasa_historia.append(historia.size / historia[-1])
    # Cantidad total de pulsos
    _pul_tot = np.sum(pulsos_historia)
    print('Pulsos totales : {}'.format(_pul_tot))
    # Duración de cada historia en unidades de tb
    _mean_t = np.mean(tiempos_historia) * tb
    _std_t = np.std(tiempos_historia, ddof=1) * tb
    _mean_R = np.mean(tasa_historia) / tb
    _std_R = np.std(tasa_historia, ddof=1) / tb
    print('Duración de cada historia: {} +/- {}'.format(_mean_t, _std_t))
    print('Tasa de cada historia: {} +/- {}'.format(_mean_R, _std_R))
    print('(Utilizando {} como unidad temporal)'.format(tb))
    print('-' * 50)
    return pulsos_historia, tiempos_historia

def inspeccion_historias_list(list_historias, tb=12.5e-9):
    """ Wrapper de `inspeccion_historias` para aplicarlo en listas """

    lista_pulsos_historia = []
    lista_tiempos_historia = []
    for i, historias in enumerate(list_historias):
        print('Inspección del archivo [{}]'.format(i))
        _pulsos, _tiempos = inspeccion_historias(historias, tb)
        lista_pulsos_historia.append(_pulsos)
        lista_tiempos_historia.append(_tiempos)
    return lista_pulsos_historia, lista_tiempos_historia

def alfa_rossi_preprocesamiento(nombres, Nhist, tb, formato_datos='binario'):
    """
    Función que genera las historias para todos los archivos leídos

    Hace las correcciones del roll-over y busca el menor tipo de dato con que
    pueden ser guardadas las historias.

    Parámetros
    ----------
        nombres : list of strings
            Nombres de los archivos con los datos de timestamping
        Nhist : integer
            Cantidad de historias
        tb : float
            Tiempo de duración de cada pulso de reloj. Equivalentemente, es
            el factor que convierte a los datos en segundos. Puede ser tb=1
            cuando se trabaja con datos en ascii (asumiendo que están en s)
        formato_datos : string, opcional ('binario', 'ascii')
            Para distinguir si se leen archivos  en binario (mediciones) o
            en ascii (simulaciones)

    Resultados
    ----------
        data_historias : list of list of numpy.ndarray
            Para cada archivo leido se genera una lista con las Nhist creadas.
            Cada historia podrá tener formato uint32, uint64 o float64
            dependiendo del tamaño.
        data_sin_rollover : list of numpy.ndarray
            Cada elemento son los datos leidos con el roll-over corregido.
            Sólo está para debuggear.
        data_con_rollover : list of numpy.ndarray
            Cada elemento son los datos leidos directamente del archivo.
            Sólo está para debuggear.
    """

    if formato_datos == 'binario':
        # Se leen los datos del archivo binario
        data_con_rollover, _header = read_timestamp_list(nombres)
        # Se corrige el roll-over
        data_sin_rollover = corrige_roll_over(data_con_rollover)
        # Separe el vector con los tiempos en historias
        data_historias = separa_en_historias_lista(data_sin_rollover, Nhist)
        # Busca el tipo de dato de menor tamaño
        data_historias = convierte_dtype_historias_lista(data_historias)
    elif formato_datos == 'ascii':
        data_con_rollover = []  # En este caso no existe
        # Se leen los datos del archivo en ascii
        data_sin_rollover = read_timestamp_list_ascii(nombres)
        # Separe el vector con los tiempos en historias
        data_historias = separa_en_historias_lista(data_sin_rollover, Nhist)
    else:
        print('Formato de dato especificado no disponible')
        quit()

    # Para verificar cómo quedó todo (opcional)
    inspeccion_historias_list(data_historias, tb)
    print('Fin del pre-procesamiento')
    print('-' * 50)

    return data_historias, data_sin_rollover, data_con_rollover

def grafica_timestamping(nombres, data_con_ro, data_sin_ro, data_bloques,
                         tipo):
    """
    Grafica los datos de timestamping luego del reprocesamiento

    Parámetros
    ----------
        nombres : (list of) strings
            Nombres de los archivos que se quieren leer
        data_con_ro : numpy array
            Datos originales presente en el archivo
        data_sin_ro : numpy array
            Datos con conrrección del roll-over
        data_bloques : list of numpy array
            Datos de timestamping separados por historias
        tipo : string, opcional ('binario', 'ascii')
            Formato en que están escritos los datos en los archivos

    Resultados
    ----------
        figs : figures handles
            Para seguir editando los gráficos. Notar que habría que cambiar
            el órden del plt.show().
    """
    if isinstance(nombres, list):
        _es_lista = True
    else:
        _es_lista = False
        nombres = [nombres]

    figs = {}
    if tipo == 'binario':
        for idx, nombre in enumerate(nombres):
            figs[idx], ax1 = plt.subplots(1, 1)
            ax1.plot(data_con_ro[idx], 'k.', label='Sin corrección roll-over')
            ax1.plot(data_sin_ro[idx], 'r.', label='Con corrección roll-over')
            for j, data in enumerate(data_bloques[idx]):
                ax1.plot(data, '-', label=('' if j == 0 else '_')
                         + 'Historias')
            ax1.set_xlabel('Índices')
            ax1.set_ylabel('Tiempo [pulsos de contador]')
            ax1.set_title(os.path.split(nombre)[-1])
            ax1.grid(True)
            ax1.legend(loc='best')
    elif tipo == 'ascii':
        for idx, nombre in enumerate(nombres):
            figs[idx], ax1 = plt.subplots(1, 1)
            ax1.plot(data_sin_ro[idx], 'k.', label='Datos originales')
            for j, data in enumerate(data_bloques[idx]):
                ax1.plot(data, '-', label=('' if j == 0 else '_')
                         + 'Historias')
            ax1.set_xlabel('Índices')
            ax1.set_ylabel('Tiempo [s]')
            ax1.set_title(os.path.split(nombre)[-1])
            ax1.grid(True)
            ax1.legend(loc='best')
    else:
        print('Formato de dato especificado no disponible')
        quit()

    if not _es_lista:
        figs = figs[0]

    plt.show()
    return figs

if __name__ == '__main__':

    # -------------------------------------------------------------------------
    # Parámetros de entrada
    # -------------------------------------------------------------------------
    # Archivos a leer
    nombres = [
               '../datos/medicion04.a.inter.D1.bin',
               '../datos/medicion04.a.inter.D2.bin',
               # '../datos/medicion04.a.inter.D1.txt',
              ]
    tipo = 'binario'
    Nhist = 200
    if tipo == 'binario':
        tb = 12.5e-9
    elif tipo == 'ascii':
        tb = 1.0
    # -------------------------------------------------------------------------
    grafica_mediciones_cuentas(nombres, 1.0, tb, tipo)
    # -------------------------------------------------------------------------
    data_bloques, data_sin_ro, data_con_ro = \
        alfa_rossi_preprocesamiento(nombres, Nhist, tb, formato_datos=tipo)
    fig = grafica_timestamping(nombres, data_con_ro, data_sin_ro,
                               data_bloques, tipo)
    #