#! /usr/bin/env python3

import os, json, sys, time, random
import numpy as np
import torch
from torch.optim import Adam
from  easydict import EasyDict
import matplotlib.pyplot as plt

from steves_models.steves_ptn import Steves_Prototypical_Network

from steves_utils.lazy_iterable_wrapper import Lazy_Iterable_Wrapper
from steves_utils.iterable_aggregator import Iterable_Aggregator
from steves_utils.ptn_train_eval_test_jig import  PTN_Train_Eval_Test_Jig
from steves_utils.torch_sequential_builder import build_sequential
from steves_utils.torch_utils import get_dataset_metrics, ptn_confusion_by_domain_over_dataloader
from steves_utils.utils_v2 import (per_domain_accuracy_from_confusion, get_datasets_base_path)
from steves_utils.PTN.utils import independent_accuracy_assesment

from steves_utils.stratified_dataset.episodic_accessor import Episodic_Accessor_Factory

from steves_utils.ptn_do_report import (
    get_jig_diagram,
    get_results_table,
    get_parameters_table,
    get_domain_accuracies,
)

required_parameters = {
    "experiment_name",
    "lr",
    "device",
    "seed",
    "dataset_seed",
    "labels_source",
    "labels_target",
    "domains_source",
    "domains_target",
    "num_examples_per_domain_per_label_source",
    "num_examples_per_domain_per_label_target",
    "n_shot",
    "n_way",
    "n_query",
    "train_k_factor",
    "val_k_factor",
    "test_k_factor",
    "n_epoch",
    "patience",
    "criteria_for_best",
    "x_transforms_source",
    "x_transforms_target",
    "episode_transforms_source",
    "episode_transforms_target",
    "pickle_name",
    "x_net",
    "NUM_LOGS_PER_EPOCH",
    "BEST_MODEL_PATH",
}

from steves_utils.CORES.utils import (
    ALL_NODES,
    ALL_NODES_MINIMUM_1000_EXAMPLES,
    ALL_DAYS
)

standalone_parameters = {}
standalone_parameters["experiment_name"] = "STANDALONE PTN"
standalone_parameters["lr"] = 0.001
standalone_parameters["device"] = "cuda"

standalone_parameters["seed"] = 1337
standalone_parameters["dataset_seed"] = 1337
standalone_parameters["labels_source"] = ALL_NODES_MINIMUM_1000_EXAMPLES
standalone_parameters["labels_target"] = ALL_NODES_MINIMUM_1000_EXAMPLES

standalone_parameters["domains_source"] = ALL_DAYS
standalone_parameters["domains_target"] = ALL_DAYS

standalone_parameters["num_examples_per_domain_per_label_source"]=100
standalone_parameters["num_examples_per_domain_per_label_target"]=100

standalone_parameters["n_shot"] = 3
standalone_parameters["n_way"]  = len(standalone_parameters["labels_source"])
standalone_parameters["n_query"]  = 2
standalone_parameters["train_k_factor"] = 1
standalone_parameters["val_k_factor"] = 2
standalone_parameters["test_k_factor"] = 2

standalone_parameters["n_epoch"] = 3

standalone_parameters["patience"] = 10
standalone_parameters["criteria_for_best"] = "target"

standalone_parameters["x_transforms_source"] = []
standalone_parameters["x_transforms_target"] = []
standalone_parameters["episode_transforms_source"] = []
standalone_parameters["episode_transforms_target"] = []

standalone_parameters["pickle_name"] = "cores.stratified_ds.2022A.pkl"

standalone_parameters["x_net"] =     [
    {"class": "nnReshape", "kargs": {"shape":[-1, 1, 2, 256]}},
    {"class": "Conv2d", "kargs": { "in_channels":1, "out_channels":256, "kernel_size":(1,7), "bias":False, "padding":(0,3), },},
    {"class": "ReLU", "kargs": {"inplace": True}},
    {"class": "BatchNorm2d", "kargs": {"num_features":256}},

    {"class": "Conv2d", "kargs": { "in_channels":256, "out_channels":80, "kernel_size":(2,7), "bias":True, "padding":(0,3), },},
    {"class": "ReLU", "kargs": {"inplace": True}},
    {"class": "BatchNorm2d", "kargs": {"num_features":80}},
    {"class": "Flatten", "kargs": {}},

    {"class": "Linear", "kargs": {"in_features": 80*256, "out_features": 256}}, # 80 units per IQ pair
    {"class": "ReLU", "kargs": {"inplace": True}},
    {"class": "BatchNorm1d", "kargs": {"num_features":256}},

    {"class": "Linear", "kargs": {"in_features": 256, "out_features": 256}},
]

# Parameters relevant to results
# These parameters will basically never need to change
standalone_parameters["NUM_LOGS_PER_EPOCH"] = 10
standalone_parameters["BEST_MODEL_PATH"] = "./best_model.pth"

# Uncomment these for ORACLE mode
from steves_utils.ORACLE.utils_v2 import (
    ALL_DISTANCES_FEET,
    ALL_RUNS,
    ALL_SERIAL_NUMBERS,
)
# standalone_parameters["labels_source"] = ALL_SERIAL_NUMBERS
# standalone_parameters["labels_target"] = ALL_SERIAL_NUMBERS
# standalone_parameters["domains_source"] = ALL_DISTANCES_FEET
# standalone_parameters["domains_target"] = ALL_DISTANCES_FEET
# standalone_parameters["num_examples_per_domain_per_label_source"]=1000
# standalone_parameters["num_examples_per_domain_per_label_target"]=100
# standalone_parameters["pickle_name"] = "oracle.stratified_ds.2022A.pkl"
# standalone_parameters["n_way"]  = len(standalone_parameters["labels_source"])

# Uncomment these for Metahan mode
# standalone_parameters["labels_source"] = list(range(19))
# standalone_parameters["labels_target"] = list(range(19))
# standalone_parameters["domains_source"] = [0]
# standalone_parameters["domains_target"] = [0]
# standalone_parameters["pickle_name"] = "metehan.stratified_ds.2022A.pkl"
# standalone_parameters["n_way"]  = len(standalone_parameters["labels_source"])
# standalone_parameters["num_examples_per_domain_per_label_source"]=200
# standalone_parameters["num_examples_per_domain_per_label_target"]=200
# Kludge for Metehan transfer
# standalone_parameters["n_way"]  = len(standalone_parameters["labels_target"])

# Set this to True if you want to run this template directly
STANDALONE = True
if STANDALONE:
    print("parameters not injected, running with standalone_parameters")
    parameters = standalone_parameters

if not 'parameters' in locals() and not 'parameters' in globals():
    raise Exception("Parameter injection failed")

#Use an easy dict for all the parameters
p = EasyDict(parameters)

supplied_keys = set(p.keys())

if  supplied_keys != required_parameters:
    print("Parameters are incorrect")
    if len(supplied_keys - required_parameters)>0: print("Shouldn't have:", str(supplied_keys - required_parameters))
    if len(required_parameters - supplied_keys)>0: print("Need to have:", str(required_parameters - supplied_keys))
    raise RuntimeError("Parameters are incorrect")

###################################
# Set the RNGs and make it all deterministic
###################################
np.random.seed(p.seed)
random.seed(p.seed)
torch.manual_seed(p.seed)

torch.use_deterministic_algorithms(True)

# TODO
# Required since we're pulling in 3rd party code
torch.set_default_dtype(torch.float64)

###################################
# Build the network(s)
# Note: It's critical to do this AFTER setting the RNG
###################################
x_net = build_sequential(p.x_net)

start_time_secs = time.time()

###################################
# Build the dataset
###################################

if p.x_transforms_source == []: x_transform_source = None
else: raise Exception("x_transform_source not implemented")

if p.x_transforms_target == []: x_transform_target = None
else: raise Exception("x_transform_target not implemented")

if p.episode_transforms_source == []: episode_transform_source = None
else: raise Exception("episode_transform_source not implemented")

if p.episode_transforms_target == []: episode_transform_target = None
else: raise Exception("episode_transform_target not implemented")

eaf_source = Episodic_Accessor_Factory(
    labels=p.labels_source,
    domains=p.domains_source,
    num_examples_per_domain_per_label=p.num_examples_per_domain_per_label_source,
    iterator_seed=p.seed,
    dataset_seed=p.dataset_seed,
    n_shot=p.n_shot,
    n_way=p.n_way,
    n_query=p.n_query,
    train_val_test_k_factors=(p.train_k_factor,p.val_k_factor,p.test_k_factor),
    pickle_path=os.path.join(get_datasets_base_path(), p.pickle_name),
    x_transform_func=x_transform_source,
    example_transform_func=episode_transform_source,

)
train_original_source, val_original_source, test_original_source = eaf_source.get_train(), eaf_source.get_val(), eaf_source.get_test()

eaf_target = Episodic_Accessor_Factory(
    labels=p.labels_target,
    domains=p.domains_target,
    num_examples_per_domain_per_label=p.num_examples_per_domain_per_label_target,
    iterator_seed=p.seed,
    dataset_seed=p.dataset_seed,
    n_shot=p.n_shot,
    n_way=p.n_way,
    n_query=p.n_query,
    train_val_test_k_factors=(p.train_k_factor,p.val_k_factor,p.test_k_factor),
    pickle_path=os.path.join(get_datasets_base_path(), p.pickle_name),
    # pickle_path=os.path.join(get_datasets_base_path(), "metehan.stratified_ds.2022A.pkl"),

    x_transform_func=x_transform_target,
    example_transform_func=episode_transform_target,
)
train_original_target, val_original_target, test_original_target = eaf_target.get_train(), eaf_target.get_val(), eaf_target.get_test()

# For CNN We only use X and Y. And we only train on the source.
# Properly form the data using a transform lambda and Lazy_Iterable_Wrapper. Finally wrap them in a dataloader

transform_lambda = lambda ex: ex[1] # Original is (<domain>, <episode>) so we strip down to episode only

train_processed_source = Lazy_Iterable_Wrapper(train_original_source, transform_lambda)
val_processed_source   = Lazy_Iterable_Wrapper(val_original_source, transform_lambda)
test_processed_source  = Lazy_Iterable_Wrapper(test_original_source, transform_lambda)

train_processed_target = Lazy_Iterable_Wrapper(train_original_target, transform_lambda)
val_processed_target   = Lazy_Iterable_Wrapper(val_original_target, transform_lambda)
test_processed_target  = Lazy_Iterable_Wrapper(test_original_target, transform_lambda)

datasets = EasyDict({
    "source": {
        "original": {"train":train_original_source, "val":val_original_source, "test":test_original_source},
        "processed": {"train":train_processed_source, "val":val_processed_source, "test":test_processed_source}
    },
    "target": {
        "original": {"train":train_original_target, "val":val_original_target, "test":test_original_target},
        "processed": {"train":train_processed_target, "val":val_processed_target, "test":test_processed_target}
    },
})

print(
    len(datasets.source.processed.train)
)


# -*- coding:utf-8 -*-
import argparse
import sys

from nsnqtlib.servers import serverlist
from nsnqtlib.db import mongodb
from nsnqtlib.utils import WindQuote
from nsnqtlib.db.fields import *
from nsnqtlib.config import DB_SERVER,DB_PORT,USER,PWD,AUTHDBNAME
from nsnqtlib.servers.serverlist import LOCAL_SERVER_IP,MONGODB_PORT_DEFAULT

class windbupdate():
    def __init__(self):
        pass

    #process action to get data for different market
    def update_security(self,args):
        local_wnd = WindQuote.WndQuery()
        local_wnd.connect()
        regular_fields = local_wnd.get_par_string(par_list_stock)

        sh600611 = local_wnd.get_history_data("600611.SH",regular_fields, "1992-08-12")

        local_db = mongodb.MongoDB(LOCAL_SERVER_IP,MONGODB_PORT_DEFAULT,None,None,None)
        ali_db   = mongodb.MongoDB(DB_SERVER,DB_PORT,USER,PWD,AUTHDBNAME)

        local_db.save_data("ml_security_table","600611",par_list_stock,sh600611)
        ali_db.save_data("ml_security_table","600611",par_list_stock,sh600611)

    def update_future(self,args):
        print("future updated")
    def update_fund(self,args):
        print("fund updated")
    def update_index(self,args):
        print("start to update index")
        local_wnd = WindQuote.WndQuery()
        local_wnd.connect()
        regular_fields = local_wnd.get_par_string(par_list_stock)

        sh000001 = local_wnd.get_history_data("000001.SH",regular_fields, "1990-12-19")
        sz399001 = local_wnd.get_history_data("399001.SZ",regular_fields, "1991-04-03")

        local_db = mongodb.MongoDB()
        ali_db   = mongodb.MongoDB(DB_SERVER,DB_PORT,USER,PWD,AUTHDBNAME)

        local_db.save_data("mlindex","000001",par_list_stock,sh000001)
        local_db.save_data("mlindex","399001",par_list_stock,sz399001)
        ali_db.save_data("mlindex","399001",par_list_stock,sz399001)

        print("update index done")

    def update_currency(self,args):
        print("currency updated")
    def update_general(self,args):
        print("--------general update is running-------")
        local_wnd = WindQuote.WndQuery()
        local_wnd.connect()
        security_general = local_wnd.wset("listedsecuritygeneralview","sectorid=a001010100000000")
        company_general = local_wnd.wset("listedcompanygenerayview","sectorid=a001010100000000")
        local_wnd.disconnect()

        #####################################################################################################
        local_db = mongodb.MongoDB()
        local_db.connect()
        local_db.save_data("general","security_table",security_general,FIELD_SECURITY_GENERAL)
        local_db.save_data("general","company_table",company_general,FIELD_COMPANY_GENERAL)
        # 10.19 stop here
        db = ldb_session.general

        j = 0
        security_general_size = len(security_general.Data[0])
        sys.stdout.write("Uploading to security_general: %d/%d   \r" %(j, security_general_size ))

        for j in range(security_general_size):
            db.security_general.update_one({"sec_code":security_general.Data[0][j]},
                {"$set":{
                    "sec_name":security_general.Data[1][j],
                    "close_price":security_general.Data[2][j],
                    "total_market_value":security_general.Data[3][j],
                    "mkt_cap_float":security_general.Data[4][j],
                    "trade_status":security_general.Data[5][j],
                    "last_trade_day":security_general.Data[6][j],
                    "ipo_day":security_general.Data[7][j],
                    "province":security_general.Data[8][j],
                    "sec_type":security_general.Data[9][j],
                    "listing_board":security_general.Data[10][j],
                    "exchange":security_general.Data[11][j] }})
            sys.stdout.write("Uploading to security_general: %d/%d   \r" %(j, security_general_size))

        j = 0
        company_general_size = len(company_general.Data[0])
        sys.stdout.write("Uploading to company_general: %d/%d   \r" %(j, company_general_size))

        for j in range(company_general_size):
            db.company_general.update_one({"company_name":company_general.Data[0][j]},
                {"$set":{
                    "inception_date":company_general.Data[1][j],
                    "ipo_date":company_general.Data[2][j],
                    "outstanding_shares":company_general.Data[3][j],
                    "sec_type":company_general.Data[4][j],
                    "regcapital":company_general.Data[5][j],
                    "chairman":company_general.Data[6][j],
                    "discloser":company_general.Data[7][j],
                    "address":company_general.Data[8][j],
                    "office":company_general.Data[9][j],
                    "zipcode":company_general.Data[10][j],
                    "telephone":company_general.Data[11][j],
                    "fax":company_general.Data[12][j],
                    "website":company_general.Data[12][j],
                    "comp_name_eng":company_general.Data[14][j] }})
            sys.stdout.write("Uploading to company_general: %d/%d   \r" %(j, company_general_size))

        local_db.disconnect()
        #ali_db   = mongodb.MongoDB(serverlist.ALIYUN_SERVERS_IP,serverlist.ALIYUN_MONGODB_PORT,
        #    serverlist.ALIYUN_DBUSER_NAME,serverlist.ALIYUN_DB_PWD)

    def parseargs(self):
        parser = argparse.ArgumentParser()

        action_list=["general","security","future","fund", "index", "currency"]
        parser.add_argument('action', type=str,nargs='*', #required=True,
                      help='Only action in {} are supported'.format(action_list))

        parser.add_argument("-n", "--name", type=str,
                      dest="name",
                      help="Name of query target. By default will query all avaible in specific market.")
        parser.add_argument("-s", "--start", type=str,
                      dest="start",
                      help="Start day of query")
        parser.add_argument("-e", "--end",type=str,
                      dest="end",
                      help="End day of query")

        parser.add_argument("-d", "--days", type=int, default=10,
                      dest="days",
                      help="Number of trading days from today need to query.Default day = 10")
        parser.add_argument('-v','--version', action='version', version='%(prog)s 0.1')
        #parser.add_argument('infile',nargs='?', type=argparse.FileType('r'),default=sys.stdin)

        args = parser.parse_args()

        return args

    def action(self,action,args):

        if action == "general":
            self.update_general(args)
        elif args == "security":
            self.update_security(args)
        elif args == "fund":
            self.update_fund(args)
        elif args == "future":
            self.update_future(args)
        elif args == "index":
            self.update_index(args)
        else:
            print("Action not defined. Please use -h to know supported market")

if __name__ == '__main__':
    wind = windbupdate()
    wind.update_security("")
#    args = wind.parseargs()
#    print(args)
#    for item in args.action:
#        wind.action(item,args)


import sys
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtWidgets import QFileDialog
from modelo import Modelo
from modelo import ErrorArchivo
from modelo import ErrorDirectorio
import webbrowser

class MainWindow():

    def __init__ (self):

        self.new_model = Modelo()

        app = QtWidgets.QApplication(sys.argv)
        self.frm_mainwindow = QtWidgets.QMainWindow()

        self.frm_mainwindow.setObjectName("frm_mainwindow")
        self.frm_mainwindow.setWindowModality(QtCore.Qt.ApplicationModal)
        self.frm_mainwindow.resize(463, 202)
        self.frm_mainwindow.setWindowFlags(QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint)
        self.frm_mainwindow.setMinimumSize(463, 202)
        self.frm_mainwindow.setMaximumSize(463, 202)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("img/calculator-solid.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.frm_mainwindow.setWindowIcon(icon)
        self.frm_mainwindow.setTabShape(QtWidgets.QTabWidget.Rounded)
        self.centralwidget = QtWidgets.QWidget(self.frm_mainwindow)
        self.centralwidget.setObjectName("centralwidget")
        self.lbl_0 = QtWidgets.QLabel(self.centralwidget)
        self.lbl_0.setGeometry(QtCore.QRect(10, 10, 61, 16))
        self.lbl_0.setObjectName("lbl_0")
        self.cmb_resolucion = QtWidgets.QComboBox(self.centralwidget)
        self.cmb_resolucion.setGeometry(QtCore.QRect(120, 10, 321, 22))
        self.cmb_resolucion.setEditable(False)
        self.cmb_resolucion.setPlaceholderText("")
        self.cmb_resolucion.setObjectName("cmb_resolucion")
        self.cmb_resolucion.addItem("Resolución General 5008/2021")
        self.cmb_resolucion.addItem("Resolución General 5076/2021")
        self.cmb_resolucion.addItem("Actualización RIPTE 2022")
        self.cmb_resolucion.addItem("Resolución General 5206/2022")
        self.cmb_resolucion.addItem("Resolución General 5280/2022")
        self.cmb_resolucion.addItem("Actualización RIPTE 2023")
        self.lbl_1 = QtWidgets.QLabel(self.centralwidget)
        self.lbl_1.setGeometry(QtCore.QRect(10, 50, 101, 16))
        self.lbl_1.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.lbl_1.setObjectName("lbl_1")
        self.txt_archivo = QtWidgets.QLineEdit(self.centralwidget)
        self.txt_archivo.setGeometry(QtCore.QRect(120, 50, 241, 20))
        self.txt_archivo.setObjectName("lineEdit")
        self.btn_abrir = QtWidgets.QPushButton(self.centralwidget, clicked=self.abrir_archivo)
        self.btn_abrir.setGeometry(QtCore.QRect(370, 50, 75, 23))
        self.btn_abrir.setObjectName("btn_abrir")
        self.lbl_2 = QtWidgets.QLabel(self.centralwidget)
        self.lbl_2.setGeometry(QtCore.QRect(10, 90, 101, 16))
        self.lbl_2.setObjectName("lbl_2")
        self.txt_directorio = QtWidgets.QLineEdit(self.centralwidget)
        self.txt_directorio.setGeometry(QtCore.QRect(120, 90, 241, 20))
        self.txt_directorio.setObjectName("lineEdit_2")
        self.btn_directorio = QtWidgets.QPushButton(self.centralwidget, clicked = self.directorio_salida)
        self.btn_directorio.setGeometry(QtCore.QRect(370, 90, 75, 23))
        self.btn_directorio.setObjectName("btn_directorio")
        self.btn_procesar = QtWidgets.QPushButton(self.centralwidget, clicked=self.procesar)
        self.btn_procesar.setGeometry(QtCore.QRect(370, 130, 75, 23))
        self.btn_procesar.setObjectName("btn_procesar")
        self.frm_mainwindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(self.frm_mainwindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 463, 21))
        self.menubar.setObjectName("menubar")
        self.menuArchivo = QtWidgets.QMenu(self.menubar)
        self.menuArchivo.setObjectName("menuArchivo")
        self.menuAyuda = QtWidgets.QMenu(self.menubar)
        self.menuAyuda.setObjectName("menuAyuda")
        self.frm_mainwindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(self.frm_mainwindow)
        self.statusbar.setObjectName("statusbar")
        self.frm_mainwindow.setStatusBar(self.statusbar)
        self.actionSalir = QtWidgets.QAction(self.frm_mainwindow)

        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap("img/sign-out-alt-solid.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionSalir.setIcon(icon1)
        self.actionSalir.setObjectName("actionSalir")
        self.actionSalir.triggered.connect(lambda:app.quit())
        self.actionFormato_xlsx = QtWidgets.QAction(self.frm_mainwindow)

        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap("img/rss-square-solid.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionFormato_xlsx.setIcon(icon2)
        self.actionFormato_xlsx.setObjectName("actionFormato_xlsx")
        self.actionFormato_xlsx.triggered.connect(lambda: webbrowser.open("help.html"))
        self.actionAcerca_de = QtWidgets.QAction(self.frm_mainwindow)

        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap("img/question-solid.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionAcerca_de.setIcon(icon3)
        self.actionAcerca_de.setObjectName("actionAcerca_de")
        self.menuArchivo.addAction(self.actionSalir)
        self.menuAyuda.addAction(self.actionFormato_xlsx)
        self.actionAcerca_de.triggered.connect(self.formulario_acerca)

        self.menuAyuda.addSeparator()
        self.menuAyuda.addAction(self.actionAcerca_de)
        self.menubar.addAction(self.menuArchivo.menuAction())
        self.menubar.addAction(self.menuAyuda.menuAction())

        QtCore.QMetaObject.connectSlotsByName(self.frm_mainwindow)

        _translate = QtCore.QCoreApplication.translate
        self.frm_mainwindow.setWindowTitle(_translate("frm_mainwindow", "Rango deducción ganancias - V 2.0"))
        self.lbl_0.setText(_translate("frm_mainwindow", "Resolución:"))
        self.lbl_1.setText(_translate("frm_mainwindow", "Archivo a procesar:"))
        self.btn_abrir.setText(_translate("frm_mainwindow", "Abrir"))
        self.lbl_2.setText(_translate("frm_mainwindow", "Directorio de salida:"))
        self.btn_directorio.setText(_translate("frm_mainwindow", "Directorio"))
        self.btn_procesar.setText(_translate("frm_mainwindow", "Procesar"))
        self.menuArchivo.setTitle(_translate("frm_mainwindow", "Archivo"))
        self.menuAyuda.setTitle(_translate("frm_mainwindow", "Ayuda"))
        self.actionSalir.setText(_translate("frm_mainwindow", "Salir"))
        self.actionFormato_xlsx.setText(_translate("frm_mainwindow", "Ayuda"))
        self.actionAcerca_de.setText(_translate("frm_mainwindow", "Acerca de..."))

        self.frm_mainwindow.show()
        sys.exit(app.exec_())

    def abrir_archivo(self):
        opcion = QFileDialog.Options()

        file_name = QFileDialog.getOpenFileName(self.frm_mainwindow,"Seleccionar archivo", "",
                                               "Archivos excel (*xlsx);; All Files (*)", options = opcion)
        self.txt_archivo.clear()
        self.txt_archivo.insert(file_name[0])

    def directorio_salida(self):
        opcion = QFileDialog.Options()
        directorio = QFileDialog.getExistingDirectory(self.frm_mainwindow,"Seleccionar directorio","",opcion)

        self.txt_directorio.clear()
        self.txt_directorio.insert(directorio)

    def procesar(self):
        try:
            if self.cmb_resolucion.currentText() == 'Resolución General 5008/2021':
                rg = 'RG_5008'
            elif self.cmb_resolucion.currentText() == 'Resolución General 5076/2021':
                rg = 'RG_5076'
            elif self.cmb_resolucion.currentText() == "Actualización RIPTE 2022":
                rg = 'RIPTE_2022'
            elif self.cmb_resolucion.currentText() == "Resolución General 5206/2022":
                rg = 'RG 5206/2022'
            elif self.cmb_resolucion.currentText() == "Resolución General 5280/2022":
                rg = 'RG 5280/2022'
            elif self.cmb_resolucion.currentText() == "Actualización RIPTE 2023":
                rg = 'RIPTE 2023'
            elif self.cmb_resolucion.currentText() == "Resolución General 5358/2023":
                rg = 'RG 5358/2023'

            if self.txt_archivo.text() == '':
                raise ErrorArchivo
            elif self.txt_directorio.text() == '':
                raise ErrorDirectorio

            archivo = self.txt_archivo.text()
            directorio = self.txt_directorio.text()

            self.new_model.leer_excel(archivo, directorio, rg, self.frm_mainwindow)

            self.txt_directorio.clear()
            self.txt_archivo.clear()

        except ErrorDirectorio:
            bug_icon = QtGui.QIcon()
            bug_icon.addPixmap(QtGui.QPixmap("img/bug-solid.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
            msg_box = QMessageBox()
            msg_box.setWindowIcon(bug_icon)
            msg_box.setText("Falta seleccionar el directorio de salida.")
            msg_box.setWindowTitle("Error")
            msg_box.setStandardButtons(QMessageBox.Ok)
            #msg_box.buttonClicked.connect(msgButtonClick)
            msg_box.exec()

        except ErrorArchivo:
            bug_icon = QtGui.QIcon()
            bug_icon.addPixmap(QtGui.QPixmap("img/bug-solid.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
            msg_box = QMessageBox()
            msg_box.setWindowIcon(bug_icon)
            msg_box.setText("Falta seleccionar el archivo a procesar o el mismo contiene un formato erroneo.")
            msg_box.setWindowTitle("Error")
            msg_box.setStandardButtons(QMessageBox.Ok)
            #msg_box.buttonClicked.connect(msgButtonClick)
            msg_box.exec()

    def formulario_acerca(self):

        frm_acerca = QtWidgets.QDialog()

        frm_acerca.setObjectName("frm_acerca")
        frm_acerca.resize(364, 131)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("img/info-circle-solid.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        frm_acerca.setWindowIcon(icon)
        self.lbl_0 = QtWidgets.QLabel(frm_acerca)
        self.lbl_0.setGeometry(QtCore.QRect(10, 10, 221, 16))
        self.lbl_0.setObjectName("lbl_0")
        self.lbl_1 = QtWidgets.QLabel(frm_acerca)
        self.lbl_1.setGeometry(QtCore.QRect(10, 30, 221, 16))
        self.lbl_1.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.lbl_1.setObjectName("lbl_1")
        self.lbl_2 = QtWidgets.QLabel(frm_acerca)
        self.lbl_2.setGeometry(QtCore.QRect(10, 50, 221, 16))
        self.lbl_2.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.lbl_2.setObjectName("lbl_2")
        self.lbl_3 = QtWidgets.QLabel(frm_acerca)
        self.lbl_3.setGeometry(QtCore.QRect(10, 70, 221, 16))
        self.lbl_3.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.lbl_3.setObjectName("lbl_3")
        self.btn_cerrar = QtWidgets.QPushButton(frm_acerca, clicked = frm_acerca.close)
        self.btn_cerrar.setGeometry(QtCore.QRect(280, 100, 75, 23))
        self.btn_cerrar.setObjectName("btn_cerrar")

        #QtCore.QMetaObject.connectSlotsByName(frm_acerca)

        _translate = QtCore.QCoreApplication.translate
        frm_acerca.setWindowTitle(_translate("frm_acerca", "Acerca de..."))
        self.lbl_0.setText(_translate("frm_acerca", "Rango deducciones ganancias - Versión 2.0"))
        self.lbl_1.setText(_translate("frm_acerca", "Escalas actualizadas al 01/01/2023"))
        self.lbl_2.setText(_translate("frm_acerca", "Desarrollado por: Jorge Eduardo Arrieta"))
        self.lbl_3.setText(_translate("frm_acerca", "Año, 2022 - Buenos Aires, Argentina"))
        self.btn_cerrar.setText(_translate("frm_acerca", "Cerrar"))

        frm_acerca.exec_()


from cs50 import get_int

def main():
  # Prompt user for integer
  n = get_int("n: ")

  # Check parity of integer
  if n % 2 == 0:
      print("gerade")
  else:
      print("ungerade")

if __name__ == "__main__":
    main()


from string import ascii_lowercase
import itertools
import toml
from threading import Thread
import os
import re

class Pwd_gen():
    """
    A class to handle generating passwords, by using permutations, upper/lower
    character combinations, replacement of characters, and more.
    """
    def __init__(self, config_fd):
        """
        Loads a specified configuration file.

        Parameters
        ----------
        config_fd : str
            File path of the configuration file.
        """
        self.config = toml.load(config_fd)
        if self.config['generating-rules']['use-regex']:
            self.regex = self.config['generating-rules']['regex']
        if self.config['generating-rules']['use-formats']:
            self.formats = self.config['generating-rules']['formats']
        elif self.config['generating-rules']['use-top-formats']:
            self.formats = self.__get_top_x_formats(x=self.config['generating-rules']['top-formats'])
        else:
            self.formats = False

    def upper_perms(self, word):
        """
        Change characters in word to upper characters. Changes single characters first:
        Test, tEst, teSt. Followed by all chars left to right: test, Test, TEst, TESt
        Followed by the inverse: TEST, tEST, teST, tesT.
        Removes duplicates before returning.
        Runs in three threads for speed.

        Parameters
        ----------
        word : str
            Word to base the new results on.

        Returns
        -------
        results : list
            Results from the three threads.
        """
        word = word.lower()
        self.results = []

        t1 = Thread(target=self.__single_c_upper, args=(word,))
        t2 = Thread(target=self.__c_upper_ltr, args=(word,))
        t3 = Thread(target=self.__c_upper_rtl, args=(word,))

        t1.start()
        t2.start()
        t3.start()

        t1.join()
        t2.join()
        t3.join()

        results = list(dict.fromkeys(self.results))
        self.result = []
        return results

    def reverse(self, word):
        """
        Reverse a word. For example, "word" turns into "drow".

        Parameters
        ----------
        word : str
            Word to reverse
        """
        reverse = word[::-1]
        return reverse

    def fraction(self, word):
        """
        Returns fractions of the given word. For example,
        for "word" it will return ['w', 'wo', 'wor']
        """
        fracs = []
        for i in range(len(word)):
            fracs.append(word[:i])
        return fracs

    def word_perms(self, words, nr_in_result=2,
                   add_cmmn_sep=True, add_cmmn_end=True):
        """
        Permutations of given words, using a given amount of  words per
        result. For example: words=['hello', 'world', '!'], nr_in_result=2
        would give results like: 'hello!', 'helloworld', '!world', etc.
        It also parses over itself, incrementing the number of words in the results until it
        reaches the nr_in_result.

        Parameters
        ----------
        words : list
            List of words to create permutations of.
        nr_in_result : int
            Number of words in the final results.
        add_cmmn_sep : bool, default = True
            Adds a commonly used separator to be used in the permutations.
        add_cmmn_end : bool, default = True
            Adds a commonly used end to be used in the permutations.

        Returns
        -------
        results : list
            Resulting permutations.
        """
        results = []
        for i in range(1, nr_in_result+1):
            perms = itertools.permutations([c for c in words], i)
            #perms = itertools.permutations([c for c in words], nr_in_result)
            if add_cmmn_sep:
                cmmn_sep = self.config['common-separators']['list']
                perms2 = itertools.permutations([c for c in words]+cmmn_sep, i+1)
                #perms2 = itertools.permutations([c for c in words]+cmmn_sep,
                #                                nr_in_result+1)
            if add_cmmn_end:
                cmmn_end = self.config['common-end']['list']
                #perms3 = itertools.permutations([c for c in words]+cmmn_end,
                #                                nr_in_result+1)
                perms3 = itertools.permutations([c for c in words]+cmmn_end, i+1)

            results += [''.join(p) for p in perms]+[''.join(p) for p in perms2]+\
                [''.join(p) for p in perms3]

        results = list(dict.fromkeys(results))
        self.result = []
        return results

    def rep_cmmn_chars(self, word: str, count=None, iterations=4):
        """
        Replace commonly replaced characters. Iterate the results a specified number of times
        and replace more characters in results. For example: 'start' becomes '5tart' and is
        added to results. After second iteration '5tart' becomes '57ar7', which is added to
        the results as well. In iterations, all previous results are used, exponentially increasing
        the nr of results. Results are filtered from duplicates at the end, making that 5
        iterations can provide the same nr of results as 100. However, this will make a
        significant difference in the processing time.

        Parameters
        ----------
        word : str
            Word to replace common characters from.
        count : int, default = None
            Nr of characters to replace every time. For example, if the word is 'all' and 'l'
            would be replaced by '|', having count=None would result in 'a||', while having count=1
            would result in 'a|l'.
        iterations : int, default = 4
            Number of times to parse previous results. This can allow words like 'all' to
            become words like '@||' for example.

        Returns
        -------
        results : list
            List of results
        """
        results = [word]

        for cmmn_char in self.config['common-replacements']:
            for char in self.config['common-replacements'][cmmn_char]:
                if not(count):
                    results.append(word.replace(cmmn_char, char))
                else:
                    results.append(word.replace(cmmn_char, char, count))

        results = list(dict.fromkeys(results)) # remove duplicates

        new_results_ph = []
        if iterations <= 1:
            iterations = 0
        else:
            iterations -= 1

        for i in range(iterations):
            new_results = []
            for cmmn_char in self.config['common-replacements']:
                for char in self.config['common-replacements'][cmmn_char]:
                    if not(count):
                        for j in range(len(results)):
                            new_results.append(results[j].replace(cmmn_char, char))
                    else:
                        for j in range(len(results)):
                            new_results.append(results[j].replace(cmmn_char, char,
                                                          count))
            new_results_ph += new_results

        results += new_results_ph
        results = list(dict.fromkeys(results)) # remove duplicates
        self.result = []
        return results

    def filter(self, results):
        """
        Filters the given results to only match the formats or regexes,
        specified in the configuration.
        The configuration allows users to specify formats or regexes, or use the top formats from
        the processed passwords, or not filter at all (not recommended).

        Parameters
        ----------
        results : list
            Results to be checked and filtered.

        Returns
        -------
        results : list
            Results from the filtering.
        """
        ctr=0
        reg_res = []
        new_results = []
        regexes = [re.compile(r) for r in self.regex]
        if self.regex:
            temp_results = results[:]
            for r in regexes:
                reg_res = list(filter(r.search, temp_results))

        form_res = []
        if self.formats:
            temp_results = results[:]
            form_res = []
            for res in temp_results:
                ctr += 1
                for format in self.formats:
                    if self.__check_if_format_match(word=res, format=format):
                        form_res.append(res)

        results = list(dict.fromkeys(form_res+reg_res))
        return results

    def __single_c_upper(self, word):
        """
        Takes a word and creates all possible combinations of this word with one upper character.
        For example, changes 'word' into 'Word', 'wOrd', 'woRd' and 'worD'. Adds all these words
        to the self.results member.

        Parameters
        ----------
        word : str
            Word to use for upper char combinations
        """
        for i in range(len(word)): # Single Upper chars
            new_word = word[:i] + word[i].upper() + word[i+1:]
            self.results.append(new_word)

    def __c_upper_ltr(self, word):
        """
        Takes a word and creates all possible combinations of this word with upper characters,
        from left to right.
        For example, changes 'word' into 'Word', 'WOrd', 'WORd' and 'WORD'. Adds all these words
        to the self.results member.

        Parameters
        ----------
        word : str
            Word to use for upper char combinations
        """
        for i in range(0, len(word)): # Upper chars, incrementing from left
            new_word = word[:i].upper() + word[i:]
            self.results.append(new_word)

    def __c_upper_rtl(self, word):
        """
        Takes a word and creates all possible combinations of this word with upper characters,
        from right to left.
        For example, changes 'word' into 'worD', 'woRD', 'wORD' and 'WORD'. Adds all these words
        to the self.results member.

        Parameters
        ----------
        word : str
            Word to use for upper char combinations
        """
        upper_w = word.upper()
        for i in range(0, len(upper_w)): #Upper chars, decrementing from left
            new_word = upper_w[:i].lower() + upper_w[i:]
            self.results.append(new_word)

    def __check_if_format_match(self, word, format):
        """
        Checks if the format of a word matches to a given format.

        Parameters
        ----------
        word : str
            Word to check for matching format.
        format : str
            Format to check against word.

        Returns
        -------
        .bool
            Returns True if matching, False if not.
        """
        if not(len(word) == len(format)):
            return False

        for i in range(len(word)):
            c = ''
            if word[i].isdigit():
                c='d'
            elif word[i].isalpha and word[i] in ascii_lowercase:
                if word[i].isupper():
                    c='C'
                else:
                    c='c'
            else:
                c='s'

            if not(c == format[i]):
                return False

        return True

    def __get_top_x_formats(self, x):
        """
        Gets the top x formats from processed passwords, where x is defined in
        the configuration file. These formats are used in filtering.

        Parameters
        ----------
        x : int
            The number of formats to retrieve.

        Returns
        -------
        formats : list
            The formats from processed passwords, to be used for filtering.
        """
        file_ = os.path.dirname(os.path.abspath(__file__))+'/'+self.config['processed-data']['dir']+self.config['processed-data']['format']
        formats = []

        with open(file_, 'r') as fd:
            lines = fd.readlines()[-x:]
            for line in lines:
                form, occ = line.split(' ')

                formats.append(form)

        return formats

if __name__=='__main__':
    pwd_gen = Pwd_gen('../config/entropass_conf.toml')
    #print(pwd_gen.word_perms(['harry', 'potter', 'quiditch', 'hedwig'], 2))
    #print(pwd_gen.upper_perms('test'))

    list_ = ['johann', 'johan']
    print(pwd_gen.filter(list_))


import streamlit as st
import pandas as pd
import base64
import re

regex_codigo = r'(?<={\s)(.*)(?=\s})'
regex_empresa = r'(?<=}\s)([A-Z]+?\s[A-Z]+)(?=\s)|(?<=}\s)([A-Z]+)(?=\s)'
regex_año = r'20\d{2}'

regex_ceco_inicio = r'^\d+\s\(\s+\)'
regex_ceco_fin = r'Total\s\(\d+\s\(\s+\)\)'

def si(a,b):
    """Recibe dos parametros, si el primero tiene largo 0, devolverá el segundo"""

    if len(a) > 0:
        return a
    else:
        return b

def rellenar(df, colname):
    """Función auxiliar, es genérica para arrastrar los valores no nulos de una columna"""
    df_tmp = df.copy()
    value = '-'
    for index, row in df_tmp.iterrows():

        if row[colname] != '-':
            value = row[colname]

        row[colname] = value
    return df_tmp

def proceso(archivo):
    """Función principal para procesar los archivos"""

    df = pd.read_excel(archivo, engine = 'openpyxl', skiprows = 1).iloc[:-1].fillna('-')
    df.columns = [str(i) for i in df.columns]

    ceco_idx = [x for x,y in df.iterrows() if re.findall(regex_ceco_inicio, y['Centro de costo'])]
    ceco_idx.append(df.shape[0])
    df_list = [df.iloc[ceco_idx[n-1]:ceco_idx[n]-1].assign(Ceco = re.findall(r'\d+', df['Centro de costo'].iloc[ceco_idx[n-1]])[0]) for n in range(1, len(ceco_idx))]

    #df_list.append(df.iloc[ceco_idx[-1]:df.shape[0]-1])

    df_acum = pd.DataFrame()
    for df_iter in df_list:

        df_out = df_iter.iloc[::-1]
        for i in df_iter.columns[:-3]:

            df_out = rellenar(df_out, i)
        df_acum = df_acum.append(df_out).iloc[::-1]
    df_acum = df_acum[df_acum['Descripcion del Recurso'] != '-']

    #Extraccion de atributos de la columna Activos.
    df_acum['Codigo'] = df_acum['Activos'].apply(lambda x: si(re.findall(regex_codigo, x), [''])[0])
    df_acum['Año'] = df_acum['Activos'].apply(lambda x: si(re.findall(regex_año, x), [''])[0])
    df_acum['Empresa'] = df_acum['Activos'].apply(lambda x: si([i for i in si(re.findall(regex_empresa, x), [''])[0] if len(i)>0],[''])[0])

    return df_acum

###########
archivo = st.file_uploader('Subir archivo')

if archivo:
    df = proceso(archivo)
    csv = df.to_csv(index=False)
    b64 = base64.b64encode(csv.encode()).decode()
    st.markdown(f'Descargar Csv', unsafe_allow_html=True)


numbers = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]
letters = ['a', 'b', 'c', 'd']
y = [10, 11, 20, 30, 40, 70, 80]

def get_length(list):
    count = 0
    while list != []:
        list.pop(0)
        count += 1
    return count

length = get_length(numbers)
print(length)


"""Tests for the main API."""
from unittest import mock
from unittest.mock import MagicMock

import numpy as np
import pytest

from arraytex import to_matrix
from arraytex import to_tabular
from arraytex.errors import DimensionMismatchError
from arraytex.errors import TooManyDimensionsError

class TestToMatrix:
    """Tests for the `to_matrix` function."""

    def test_default(self) -> None:
        """A formatted matrix is returned."""
        mat = np.array(
            [
                [1, 2, 3],
                [4, 5, 6],
            ]
        )

        out = to_matrix(mat)

        assert (
            out
            == r"""\begin{bmatrix}
1 & 2 & 3 \\
4 & 5 & 6 \\
\end{bmatrix}"""
        )

    def test_style(self) -> None:
        """An arg can be supplied to determine the style of the returned matrix."""
        mat = np.array(
            [
                [1, 2, 3],
                [4, 5, 6],
            ]
        )

        out = to_matrix(mat, style="p")

        assert (
            out
            == r"""\begin{pmatrix}
1 & 2 & 3 \\
4 & 5 & 6 \\
\end{pmatrix}"""
        )

    def test_decimal_format(self) -> None:
        """Decimal style of number formatting can be selected."""
        mat = np.array(
            [
                [1, 2, 3],
                [4, 5, 6],
            ]
        )

        out = to_matrix(mat, num_format=".2f")

        assert (
            out
            == r"""\begin{bmatrix}
1.00 & 2.00 & 3.00 \\
4.00 & 5.00 & 6.00 \\
\end{bmatrix}"""
        )

    def test_e_notation(self) -> None:
        """Scientific E notation can be selected."""
        mat = np.array(
            [
                [0.001, 0.002, 0.003],
                [0.004, 0.005, 0.006],
            ]
        )

        out = to_matrix(mat, num_format=".2e")

        assert (
            out
            == r"""\begin{bmatrix}
1.00\mathrm{e}{-03} & 2.00\mathrm{e}{-03} & 3.00\mathrm{e}{-03} \\
4.00\mathrm{e}{-03} & 5.00\mathrm{e}{-03} & 6.00\mathrm{e}{-03} \\
\end{bmatrix}"""
        )

    def test_e_notation_positive(self) -> None:
        """Scientific E notation can be selected with positive powers."""
        mat = np.arange(6).reshape(2, 3)

        out = to_matrix(mat, num_format=".2e")

        assert (
            out
            == r"""\begin{bmatrix}
0.00\mathrm{e}{+00} & 1.00\mathrm{e}{+00} & 2.00\mathrm{e}{+00} \\
3.00\mathrm{e}{+00} & 4.00\mathrm{e}{+00} & 5.00\mathrm{e}{+00} \\
\end{bmatrix}"""
        )

    def test_scientific_notation(self) -> None:
        """Scientific  can be selected."""
        mat = np.array(
            [
                [0.001, 0.002, 0.003],
                [0.004, 0.005, 0.006],
            ]
        )

        out = to_matrix(mat, num_format=".2e", scientific_notation=True)

        assert (
            out
            == r"""\begin{bmatrix}
1.00 \times 10^{-03} & 2.00 \times 10^{-03} & 3.00 \times 10^{-03} \\
4.00 \times 10^{-03} & 5.00 \times 10^{-03} & 6.00 \times 10^{-03} \\
\end{bmatrix}"""
        )

    def test_one_d(self) -> None:
        """One dimensional vectors are handled correctly."""
        mat = np.array([1, 2, 3])

        out = to_matrix(mat)

        assert (
            out
            == r"""\begin{bmatrix}
1 & 2 & 3 \\
\end{bmatrix}"""
        )

    def test_0_d(self) -> None:
        """0 dimensional vectors are handled correctly."""
        mat = np.array(1)

        out = to_matrix(mat)

        assert (
            out
            == r"""\begin{bmatrix}
1 \\
\end{bmatrix}"""
        )

    def test_3_d(self) -> None:
        """>2 dimensional arrays are handled correctly."""
        mat = np.arange(8).reshape(2, 2, 2)

        with pytest.raises(TooManyDimensionsError):
            to_matrix(mat)

class TestClipboard:
    """Tests for the `to_clp` arg."""

    @mock.patch("arraytex.utils.pyperclip", autospec=True)
    def test_success(self, mock_pyperclip: MagicMock) -> None:
        """Outputs are copied to the clipboard."""
        mat = np.array(1)

        to_matrix(mat, to_clp=True)

        mock_pyperclip.copy.assert_called_once_with(
            "\\begin{bmatrix}\n1 \\\\\n\\end{bmatrix}"
        )

class TestToTabular:
    """Tests for the `to_tabular` function."""

    def test_too_many_dimensions(self) -> None:
        """Error is thrown for too many dimensions."""
        mat = np.arange(8).reshape(2, 2, 2)

        with pytest.raises(TooManyDimensionsError):
            to_tabular(mat)

    def test_mismatch_col_align(self) -> None:
        """Error is thrown if wrong number of col_align items."""
        mat = np.arange(6).reshape(2, 3)

        with pytest.raises(DimensionMismatchError) as exc:
            to_tabular(mat, col_align=["c", "c"])

        assert str(exc.value) == (
            "Number of `col_align` items (2) doesn't match number of columns (3)"
        )

    def test_mismatch_col_names(self) -> None:
        """Error is thrown if wrong number of col_names items."""
        mat = np.arange(6).reshape(2, 3)

        with pytest.raises(DimensionMismatchError) as exc:
            to_tabular(mat, col_names=["1", "2"])

        assert str(exc.value) == (
            "Number of `col_names` items (2) doesn't match number of columns (3)"
        )

    def test_default(self) -> None:
        """A formatted matrix is returned."""
        mat = np.arange(1, 7).reshape(2, 3)

        out = to_tabular(mat)

        assert (
            out
            == r"""\begin{tabular}{c c c}
\toprule
Col 1 & Col 2 & Col 3 \\
\midrule
1 & 2 & 3 \\
4 & 5 & 6 \\
\bottomrule
\end{tabular}"""
        )

    def test_given_col_names(self) -> None:
        """User can supply col names."""
        mat = np.arange(1, 5).reshape(2, 2)

        out = to_tabular(mat, col_names=["1", "b"])

        assert (
            out
            == r"""\begin{tabular}{c c}
\toprule
1 & b \\
\midrule
1 & 2 \\
3 & 4 \\
\bottomrule
\end{tabular}"""
        )

    def test_given_col_align(self) -> None:
        """User can supply col_align list."""
        mat = np.arange(1, 5).reshape(2, 2)

        out = to_tabular(mat, col_align=["l", "r"])

        assert (
            out
            == r"""\begin{tabular}{l r}
\toprule
Col 1 & Col 2 \\
\midrule
1 & 2 \\
3 & 4 \\
\bottomrule
\end{tabular}"""
        )

    def test_one_dimensional(self) -> None:
        """One dimensinal vectors are expanded properly."""
        mat = np.arange(1, 4)

        out = to_tabular(mat)

        assert (
            out
            == r"""\begin{tabular}{c c c}
\toprule
Col 1 & Col 2 & Col 3 \\
\midrule
1 & 2 & 3 \\
\bottomrule
\end{tabular}"""
        )

    def test_0_d(self) -> None:
        """0 dimensional scalars are handled properly."""
        mat = np.array(1)

        out = to_tabular(mat)

        assert (
            out
            == r"""\begin{tabular}{c}
\toprule
Col 1 \\
\midrule
1 \\
\bottomrule
\end{tabular}"""
        )

    class TestIndex:
        """Tests for the `index` support."""

        def test_default(self) -> None:
            """`index` forms the row names."""
            index = ["Row 1", "Row 2"]
            mat = np.arange(1, 5).reshape(2, 2)

            out = to_tabular(mat, index=index)

            assert (
                out
                == r"""\begin{tabular}{l c c}
\toprule
Index & Col 1 & Col 2 \\
\midrule
Row 1 & 1 & 2 \\
Row 2 & 3 & 4 \\
\bottomrule
\end{tabular}"""
            )

        def test_bad_dimensions(self) -> None:
            """An error is raised if wrong dimension of `index`."""
            index = ["Row 1", "Row 2"]
            mat = np.arange(1, 4).reshape(3, 1)

            with pytest.raises(DimensionMismatchError) as exc:
                to_tabular(mat, index=index)

            assert str(exc.value) == (
                "Number of `index` items (2) doesn't match number of rows (3)"
            )

        def test_given_col_names(self) -> None:
            """A given index name as part of `col_names` is used."""
            index = ["Row 1", "Row 2"]
            col_names = ["My Index", "Col 1", "Col 2"]
            mat = np.arange(1, 5).reshape(2, 2)

            out = to_tabular(mat, index=index, col_names=col_names)

            assert (
                out
                == r"""\begin{tabular}{l c c}
\toprule
My Index & Col 1 & Col 2 \\
\midrule
Row 1 & 1 & 2 \\
Row 2 & 3 & 4 \\
\bottomrule
\end{tabular}"""
            )

        def test_given_col_align(self) -> None:
            """A given col align char can be used for the col index."""
            index = ["Row 1", "Row 2"]
            mat = np.arange(1, 5).reshape(2, 2)

            out = to_tabular(mat, index=index, col_align=["r", "c", "c"])

            assert (
                out
                == r"""\begin{tabular}{r c c}
\toprule
Index & Col 1 & Col 2 \\
\midrule
Row 1 & 1 & 2 \\
Row 2 & 3 & 4 \\
\bottomrule
\end{tabular}"""
            )

        def test_given_col_name_and_align(self) -> None:
            """A given col index name and align can be used for the index."""
            index = ["Row 1", "Row 2"]
            col_names = ["My Index", "Col 1", "Col 2"]
            col_align = ["r", "c", "c"]
            mat = np.arange(1, 5).reshape(2, 2)

            out = to_tabular(
                mat,
                col_align=col_align,
                col_names=col_names,
                index=index,
            )

            assert (
                out
                == r"""\begin{tabular}{r c c}
\toprule
My Index & Col 1 & Col 2 \\
\midrule
Row 1 & 1 & 2 \\
Row 2 & 3 & 4 \\
\bottomrule
\end{tabular}"""
            )

        def test_col_align_bad_dimensions(self) -> None:
            """Bad dimensions of `col_align` is caught."""
            index = ["Row 1", "Row 2"]
            col_names = ["My Index", "Col 1", "Col 2"]
            col_align = ["r", "c"]
            mat = np.arange(1, 5).reshape(2, 2)

            with pytest.raises(DimensionMismatchError) as exc:
                to_tabular(
                    mat,
                    col_align=col_align,
                    col_names=col_names,
                    index=index,
                )

            assert str(exc.value) == (
                "Number of `col_align` items (2) doesn't match number of columns (3)"
            )


#import datetime
#print(datetime.datetime.now())

#today = datetime.datetime.now()
#print("Day: ",today.day)
#print("Month: ", today.month)
#print("Year: ", today.year)
#print("Hour: ", today.hour)
#print("Minute: ", today.minute)
#print("Second: ", today.second)

#import time
#ltime = time.asctime(time.localtime(time.time()))
#print("Local current time is: ", ltime)
import calendar
year = 2022
month = 5
print(calendar.month(year, month))


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

from rome import Validator, ValidationError
from rome.language import _

class String(Validator):

    __errors__ = {'not_string': _("This is not a String"),
                  'empty': _('Please enter a value'),
                  'exact_length': _("Value length must be %(length)i exactly"),
                  'max_exceeded': _("Value length must be %(max)i or less"),
                  'min_not_reached': _("Value length must be %(min)i or more")}

    def __init__(self, *args, **kwargs):
        Validator.__init__(self, *args, **kwargs)
        self.max = kwargs.get('max', None)
        self.min = kwargs.get('min', None)
        self.empty = kwargs.get('empty', True)

        if self.max is not None and self.min is not None and self.min > self.max:
            raise ValueError("Max value must be greater than min")

    def validate(self, value):
        if not isinstance(value, basestring):
            self._validation_error('not_string')

        self.attr_validate(value)

        return value

    def attr_validate(self, value):
        if not self.empty and value == '':
            self._validation_error('empty')

        if self.max is not None:
            if self.min == self.max and self.max != len(value):
                self._validation_error('exact_length', length=self.max)
            if len(value) > self.max:
                self._validation_error('max_exceeded', max=self.max)

        if self.min is not None:
            if len(value) < self.min:
                self._validation_error('min_not_reached', min=self.min)

class Number(Validator):

    __errors__ = {'not_number': _('This is not a number')}

    def validate(self, value):
        try:
            f_result = float(value)
            try:
                i_result = int(value)
                return i_result if i_result == f_result else f_result
            except ValueError:
                pass
            return f_result
        except ValueError:
            self._validation_error('not_number')

class Int(Validator):

    __errors__ = {'not_int': _('This is not an integer')}

    def validate(self, value):
        if isinstance(value, int):
            return value
        self._validation_error('not_int')

class In(Validator):

    __errors__ = {'not_in': _('Value must be in list [%(values)s]')}

    def __init__(self, *args, **kwargs):
        Validator.__init__(self, *args, **kwargs)
        self._values = args

    def validate(self, value):
        if value not in self._values:
            self._validation_error('not_in', values=', '.join(self._values))
        return value

class Email(Validator):

    __errors__ = {'no_valid': _('This is not a valid email')}

    # RFC 2822
    REGEXP = '[a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])'

    def validate(self, value):
        if not re.match(self.REGEXP, value):
            self._validation_error('no_valid')
        return value


import RPi.GPIO as GPIO
import time
import os
from multiprocessing import Process

GPIO.setwarnings(False)
GPIO.cleanup()

gpio_pins = [0, 26, 19, 13, 6, 5, 27, 17, 4, 21, 20, 24, 23]
posi_pins = [9, 10, 11, 12]
numberToChars = ['abcdef', 'bc', 'abged', 'abgcd', 'bcfg', 'afgcd', 'acdefg', 'abc', 'abcdefg', 'abcdfg']
charPinMap = { 'e':1, 'd':2, 'c':4, 'g':5, 'b':6, 'f':7, 'a':8, 'dp':3 }

pin_clock = 18

def beep(second=1):
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(pin_clock, GPIO.OUT)
    try:
        while True:
            GPIO.output(pin_clock, GPIO.HIGH)
            time.sleep(second)
            GPIO.output(pin_clock, GPIO.LOW)
            time.sleep(second)
    except KeyboardInterrupt:
        print("Clock beep process stop")
        GPIO.cleanup()

def clear():
    GPIO.cleanup()
    GPIO.setmode(GPIO.BCM)
    for pin in range(1, 13):
        GPIO.setup(gpio_pins[pin], GPIO.OUT)
        if pin<10:
            GPIO.output(gpio_pins[pin], True)
        else:
            GPIO.output(gpio_pins[pin], False)

def showDigit(no, num, showDotPoint):
    if num<0 or num>9:
        return False

    clear()

    numChars = numberToChars[num]
    for i in range(len(numChars)):
        char = numChars[i]
        pin  = charPinMap[char]
        GPIO.output(gpio_pins[pin], False)

    if showDotPoint:
        pointPin = charPinMap['dp']
        GPIO.output(gpio_pins[pointPin], False)

    posi_pin = posi_pins[no-1]
    GPIO.output(gpio_pins[posi_pin], True)
    for pin in posi_pins:
        if pin != posi_pin:
            GPIO.output(gpio_pins[pin], False)

def run_digit(*args):
    t = 0.005
    try:
        while True:
            #print(int(int(time.strftime("%S", time.localtime(time.time()))) %10 ), False)
            showDigit(3, int(int(time.strftime("%M", time.localtime(time.time()))) /10 ), False)
            time.sleep(t)
            showDigit(4, int(int(time.strftime("%M", time.localtime(time.time()))) %10 ), True )
            time.sleep(t)
            showDigit(2, int(int(time.strftime("%S", time.localtime(time.time()))) /10 ), False)
            time.sleep(t)
            showDigit(1, int(int(time.strftime("%S", time.localtime(time.time()))) %10 ), False)
            time.sleep(t)

    except KeyboardInterrupt:
        print("Time display process stop!")
        GPIO.cleanup()

def main():
    p1 = Process(target = run_digit, args=("",))
    p1.start()
    p2 = Process(target = beep, args=(1.0,))
    p2.start()

if __name__ == "__main__":
    main()