from __future__ import absolute_import, division, print_function
import os, time

class mpi_logger(object):
  """A class to facilitate each rank writing to its own log file and (optionally) to a special log file for timing"""

  def __init__(self, params=None, mpi_helper=None):
    self.mpi_helper = mpi_helper
    if self.mpi_helper == None:
      from xfel.merging.application.mpi_helper import mpi_helper
      self.mpi_helper = mpi_helper()

    if params:
      self.set_log_file_paths(params)
    else:
      self.main_log_file_path = None
      self.rank_log_file_path = None
      self.timing_file_path = None

    self.log_buffer = self.main_log_buffer = '' # a buffer for handling a run-time case, when the file path isn't known yet, so we need to store the output somewhere

    self.timing_table = dict()

  def set_log_file_paths(self, params):

    if params.output.prefix:
      main_filename = params.output.prefix + "_main"
      aux_filename_prefix = params.output.prefix + "_"
    else:
      main_filename = "main"
      aux_filename_prefix = ""

    self.main_log_file_path = os.path.join(params.output.output_dir, main_filename + ".log")
    self.rank_log_file_path = os.path.join(params.output.output_dir, aux_filename_prefix + 'rank_%06d_%06d.log'%(self.mpi_helper.size, self.mpi_helper.rank))

    if params.output.do_timing:
      self.timing_file_path = os.path.join(params.output.output_dir, aux_filename_prefix + 'timing_%06d_%06d.log'%(self.mpi_helper.size, self.mpi_helper.rank))
    else:
      self.timing_file_path = None

    if params.output.log_level==0: # level 0 is most verbose, so for debug, catch err and out
      import sys, io
      expand_dir = os.path.expandvars(params.output.output_dir)
      os.makedirs(expand_dir, exist_ok=True)
      out_path = os.path.join(expand_dir,"rank_%d.out"%self.mpi_helper.rank)
      err_path = os.path.join(expand_dir,"rank_%d.err"%self.mpi_helper.rank)
      sys.stdout = io.TextIOWrapper(open(out_path,'ab', 0), write_through=True)
      sys.stderr = io.TextIOWrapper(open(err_path,'ab', 0), write_through=True)

  def log(self, message, rank_prepend=True):
    '''Log a rank message'''
    message = time.ctime(self.mpi_helper.time()) + " " + message

    # Prepend the message with the rank index - helpful for grep-ping
    if rank_prepend:
      rank_message = "Rank %d: %s\n"%(self.mpi_helper.rank, message)
    else:
      rank_message = message + "\n"
    self.log_buffer += rank_message

    if self.rank_log_file_path == None:
      return # the file path hasn't been parsed yet, so just keep the output in the buffer

    log_file_handle = open(self.rank_log_file_path, 'a')
    log_file_handle.write(self.log_buffer)
    self.log_buffer = ''
    log_file_handle.close()

  def info(self, message, *args):
    self.log("INFO: "+str(message) % args)

  def debug(self, message, *args):
    self.log("DEBUG: "+str(message) % args)

  def main_log(self, message):
    '''Log a message to the main log file'''

    self.main_log_buffer += (message + '\n')

    if self.main_log_file_path == None:
      return

    log_file_handle = open(self.main_log_file_path, 'a')
    log_file_handle.write(self.main_log_buffer)
    self.main_log_buffer = ''
    log_file_handle.close()

  def log_step_time(self, step, step_finished=False):
    '''Log elapsed time for an execution step'''

    step = step.replace(' ', '_') # for easier log file post-processing

    if not step_finished: # a step has started - cache its start time and return
      if not step in self.timing_table:
        self.timing_table[step] = dict({'single_step':dict({'start':self.mpi_helper.time(), 'elapsed':0.0}),
                                        'cumulative': 0.0
                                       }
                                      )
      else: # if a step is executed repeatedly - re-use the existent step key
        self.timing_table[step]['single_step']['start'] = self.mpi_helper.time()
      return

    # a step has finished - calculate its elapsed and cumulative time (the latter is needed when the step is executed repeatedly)
    #self.mpi_helper.comm.barrier()

    if not step in self.timing_table:
      assert False, "A step has finished, but doesn't have its starting time entry"
      return

    self.timing_table[step]['single_step']['elapsed'] = self.mpi_helper.time() - self.timing_table[step]['single_step']['start']
    self.timing_table[step]['cumulative'] += self.timing_table[step]['single_step']['elapsed']

    # log the elapsed time - single step and cumulative
    if self.timing_file_path == None:
      return

    log_file = open(self.timing_file_path,'a')
    log_file.write("RANK %d %s: %f s %f s\n"%(self.mpi_helper.rank, step, self.timing_table[step]['single_step']['elapsed'], self.timing_table[step]['cumulative']))
    log_file.close()


#!/usr/bin/env python

import sys
import calendar

cur_temp = -999
month_with_max = None
city_with_max = None

def get_month_name(month_number):
    return calendar.month_name[int(month_number)]

def print_out(month_param, city_param):
    print("('%s', '%s')" % (get_month_name(month_param), city_param))

for line in sys.stdin:
    month, city, temperature = line.strip().split("\t")
    temperature = int(temperature)

    if month_with_max is not None and month_with_max != month:
        print_out(month_with_max, city_with_max)
        cur_temp = -999

    if temperature > cur_temp:
        cur_temp = temperature
        month_with_max = month
        city_with_max = city

# Print do ultimo registro
if month_with_max is not None:
    print_out(month_with_max, city_with_max)


import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import f1_score
import matplotlib.pyplot as plt
import seaborn as sns

data = pd.read_csv(r'C:\Users\User\OneDrive - The American College of Greece\Documents\Εξόρυξη Δεδομένων\healthcare-dataset-stroke-data.csv')
replace_gender = {'gender':{'Male':0, "Female":1, 'Other':2}}
replace_ever_married = {'ever_married':{'Yes':0, 'No':1}}
replace_work_type = {'work_type':{'Private':0, 'Self-employed':1, 'children':2, 'Govt_job':3, 'Never_worked':4}}
replace_Residence_type = {'Residence_type':{'Urban':0, 'Rural':1}}
replace_smoking_status = {'smoking_status':{'formerly smoked':0, 'never smoked':1, 'smokes':2, 'Unknown':3}}

data[data.isnull().any(axis=1)]#cleanup null values

data = data.replace(replace_gender)
data = data.replace(replace_ever_married)
data = data.replace(replace_work_type)
data = data.replace(replace_Residence_type)
data = data.replace(replace_smoking_status)

data.pop('id')
data.pop('smoking_status')
data.pop('bmi')
data_stroke = data.pop('stroke')

clf = RandomForestClassifier(random_state=0)
clf.fit(data.head(3577),data_stroke.head(3577))
X = clf.predict(data.tail(1533))
np.savetxt('test.out',X, fmt="%d")
A = f1_score( data_stroke.tail(1533),X, average=None)

print(A)


from datetime import *
import smtplib, ssl

class Usuario:
    def __init__(self,doc,nombre=None,apellido=None,direccion=None,tel=None,correo=None,ciudad=None,fe_n=None,fe_a=None,fe_da=None,vac="No"):
        self.__documento = doc
        self.__nombre = nombre
        self.__apellido = apellido
        self.__direccion = direccion
        self.__telefono = tel
        self.__correo = correo
        self.__ciudad = ciudad
        self.__fecha_nac = fe_n
        self.__fecha_afil = fe_a
        self.__fecha_desafil = fe_da
        self.__vacunado = vac

## Setters and getters
    def getDocumento(self):
        return self.__documento
    def setDocumento(self,d):
        self.__documento = d

    def getNombre(self):
        return self.__nombre
    def setNombre(self,d):
        self.__nombre = d

    def getApellido(self):
        return self.__apellido
    def setApellido(self,d):
        self.__apellido = d

    def getDireccion(self):
        return self.__direccion
    def setDireccion(self,d):
        self.__direccion = d

    def getTelefono(self):
        return self.__telefono
    def setTelefono(self,d):
        self.__telefono = d

    def getCorreo(self):
        return self.__correo
    def setCorreo(self,d):
        self.__correo = d

    def getCiudad(self):
        return self.__ciudad
    def setCiudad(self,d):
        self.__ciudad = d

    def getFechaNacimiento(self):
        return self.__fecha_nac
    def setFechaNacimiento(self,d):
        self.__fecha_nac = d

    def getFechaAfiliacion(self):
        return self.__fecha_afil
    def setFechaAfiliacion(self,d):
        self.__fecha_afil = d

    def getFechaDesafiliacion(self):
        return self.__fecha_desafil
    def setFechaDesafiliacion(self,d):
        self.__fecha_desafil = d

    def getVacunado(self):
        return self.__vacunado
    def setVacunado(self,d):
        self.__vacunado = d
#
    def validar_opciones_menu(self,entrada:str,opciones:list):
        '''Función que se encarga de validar que las opciones ingresadas en los menús sean las correspondientes para cada uno.
        Retorna un booleano indicando si la entrada se encuentra o no en las opciones.'''
        #Ciclo que itera por la lista de opciones
        for opc in opciones:
            if entrada == opc:
                return True #Al iterar por la lista, si la entrada se encuentra en esta, retorna verdadero
        return False #En caso de que la entrada no se encuentre en la lista, retorna falso.

    def __validar_nombres(self,var):
        nums = ["0","1","2","3","4","5","6","7","8","9"]
        valido = True
        for i in var:
            if i in nums:
                valido = False
                break
        return valido

    def validar_tipo_tamaño(self,d,clase:str,tipo:str):
        '''Verifica que la clase, tipo y tamaño del dato ingresado corresponda con los parámetros indicados respectivamente.
        Clase = entero, cadena.
        Tipo = documento,telefono,cant_recibida,idplan,edad,nombre,direccion,correo,idlote.

        '''
        if clase == "entero":
            # En el caso de los enteros se hace un try para convertir la cadena.
            # Si esto no se puede realizar, se dirige al except.
            try:
                dato = int(d)
                #Si el dato se puede convertir se selecciona el tipo y dependiendo de este, se verifica el tamaño.
                if tipo == "documento":
                    if len(d) < 5 or len(d) > 12:
                        print("Documento no válido (ingrese un documento entre 5 y 12 dígitos)")
                        return False
                elif tipo == "telefono":
                    if len(d) < 6 or len(d) > 12:
                        print("Número de telefono no válido (ingrese un número entre 6 y 12 dígitos)")
                        return False
                elif tipo == "cant_recibida":
                    if int(d) < 1 or len(d) > 6:
                        print("Cantidad no válida (ingrese una cantidad entre 1 y 6 dígitos)")
                        return False
                elif tipo == "idplan":
                    if int(d) < 1 or len(d) > 2:
                        print("Número de plan no válido (ingrese un número entre 1 y 2 dígitos)")
                        return False
                elif tipo == "edad":
                    if int(d) < 1 or len(d) > 3:
                        print("Edad no válida (ingrese un número entre 1 y 3 dígitos)")
                        return False
                elif tipo == "tiempo":
                    if len(d) > 3:
                        print("Longitud de campo inválida (máximo 8 caracteres)")
                        return False
                elif tipo == "hora":
                    if int(d) > 23:
                        print("Hora no válida (máximo 23 horas)")
                        return False
                elif tipo == "minutos":
                    if int(d) > 59:
                        print("Minutos no válidos (máximo 59 minutos)")
                        return False
                return True
            except:
                print("Tipo de dato incorrecto, debe ingresar un número entero.")
                return False

        elif clase == "cadena":
            if tipo == "nombre":
                if not self.__validar_nombres(str(d)):
                    print("Este dato no puede contener números.")
                    return False
                elif len(d) > 20:
                    print("Longitud de campo inválida (máximo 20 caracteres)")
                    return False

            elif tipo == "direccion":
                if len(d) > 20:
                    print("Longitud de campo inválida (máximo 20 caracteres)")
                    return False

            elif tipo == "correo":
                if len(d) > 35:
                    print("Longitud de campo inválida (máximo 35 caracteres)")
                    return False

            elif tipo == "idlote":
                if len(d) > 12:
                    print("Longitud de campo inválida (máximo 12 caracteres)")
                    return False
            return True

    def __pedir_fecha(self,tipo, numero=0):
        '''Pide diferentes fechas y las retorna con el formato DD/MM/AAAA en una tupla'''
        #Dependiendo del tipo y número de la fecha, se imprime el mensaje y se establece la información de las preguntas correspondientes.
        d = date.today()
        f_v = False

        while f_v == False:
            if tipo == "lotes":
                print("FECHA DE VENCIMIENTO")
                info = ("Día de vencimiento: ", "Mes de vencimiento: ", "Año de vencimiento: ")
            elif tipo == "planes":
                if numero == 0:
                    print("FECHA DE INICIO DEL PLAN")
                    info = ("Día de inicio: ", "Mes de inicio: ", "Año de inicio: ")
                elif numero == 1:
                    print("FECHA DE FINALIZACIÓN DEL PLAN")
                    info = ("Día de fin: ", "Mes de fin: ", "Año de fin: ")
            elif tipo == "registro":
                print("FECHA DE NACIMIENTO DEL USUARIO")
                info = ("Día de nacimiento: ", "Mes de nacimiento: ", "Año de nacimiento: ")
            elif tipo == "citas":
                print("FECHA DE INICIO DE LA PROGRAMACIÓN")
                info = ("Día de inicio: ", "Mes de inicio: ", "Año de inicio: ")

            #Se pregunta el día, mes y año según la información antes seleccionada.
            nice = False
            while not nice:
                try:
                    dia = int(input(info[0]))
                    if dia > 0:
                        nice = True
                except ValueError:
                    print("Ingrese un número.")
                    nice = False
            nice = False
            while not nice:
                try:
                    mes = int(input(info[1]))
                    if mes > 0:
                        nice = True
                except ValueError:
                    print("Ingrese un número.")
                    nice = False
            nice = False
            while not nice:
                try:
                    año = int(input(info[2]))
                    nice = True
                except ValueError:
                    print("Ingrese un número.")
                    nice = False
                    continue
                if len(str(año)) != 4:
                    nice = False
                    print("Ingrese los 4 dígitos del año.")
            try:
                fecha = date(año, mes, dia)
                f_v = True
            except ValueError:
                print("La fecha ingresada no existe\n \n")
                f_v = False
                continue
            if f_v == True and fecha < d and tipo != "registro":
                f_v = False
                print("La fecha ingresada es anterior a hoy\n")
                continue
            elif f_v == True and tipo == "registro" and fecha > d:
                f_v = False
                print("Revise la fecha de nacimiento \n")
            elif f_v == True and tipo == "lotes" and fecha == d:
                f_v = False
                print("El lote vence hoy...\n")
        return fecha

    def __convertir_fecha(self,f):
        f = str(f)
        f_c = f[8]+f[9]+"/"+f[5]+f[6]+"/"+f[0]+f[1]+f[2]+f[3]
        return f_c

    def __validar_identificacion(self,documento,tabla_info,cursor,estado=0):
        '''Verifica que el documento o identificación no se encuentre ya en la base de datos.
        Retorna falso si el documento está en la base de datos y retorna verdadero si no lo está.'''
        cursor.execute("SELECT * FROM " + tabla_info) #Se seleccionan todos los datos de la tabla que se le pase a la función
        tabla = cursor.fetchall() #Se crea la lista con todos los datos de la tabla
        for t in tabla:
            doc = t[0]
            if doc == documento:
                if estado == 1:
                    if t[9] == "activo":
                        return False, True
                    else:
                        return False, False
                else:
                    return False #Al iterar por la lista, si el documento de entrada coincide con el documento que se encuentra en la base de datos, retorna falso
        if estado == 1:
            return True, False
        else:
            return True #Si el documento no se encuentra en la tabla, retorna verdadero

    def consulta(self):
        print('============================================================')
        print("NÚMERO DE DOCUMENTO: ",self.getDocumento())
        print("NOMBRE: ",self.getNombre())
        print("APELLIDO: ",self.getApellido())
        print("DIRECIIÓN: ",self.getDireccion())
        print("TELÉFONO: ",self.getTelefono())
        print("CORREO: ",self.getCorreo())
        print("CIUDAD: ",self.getCiudad())
        print("FECHA NACIMIENTO: ",self.getFechaNacimiento())
        print("FECHA AFILIACIÓN: ",self.getFechaAfiliacion())
        print("FECHA DESAFILIACIÓN: ",self.getFechaDesafiliacion())
        print("¿FUE VACUNADO?: ",self.getVacunado())
        print('============================================================')

    def afiliacion(self,documento,con):
        nombre = input("Ingrese el nombre del usuario: ").title()
        while not self.validar_tipo_tamaño(nombre,"cadena","nombre"):
            nombre = input("Ingrese un nombre válido: ").title()

        apellido = input("Ingrese los apellidos de " + nombre +" : ").title()
        while not self.validar_tipo_tamaño(apellido,"cadena","nombre"):
            apellido = input("Ingrese un apellido válido: ").title()

        direccion = input("Ingrese la direción de residencia de "+ nombre + " " + apellido[0] + "." + " : ")
        while not self.validar_tipo_tamaño(direccion,"cadena","direccion"):
            direccion = input("Ingrese una dirección válida: ")

        tel = input("ingrese el telefono de "+ nombre + " " + apellido[0] + "." + " : ")
        while not self.validar_tipo_tamaño(tel,"entero","telefono"):
            tel = input("Ingrese un teléfono válido: ")

        correo = input("ingrese el correo de "+ nombre + " " + apellido[0] + "." + " : ")
        while not self.validar_tipo_tamaño(correo,"cadena","correo"):
            correo = input("Ingrese un correo válido: ")
        ar = False  # Validación de correo 1: ¿Tiene arroba?
        pun = False  # Validación de correo 2: ¿Tiene punto?
        f_ar = True  #Validación de correo 3: ¿El arroba va primero?
        if correo == '@.' or correo == '.@':
            print("Ingrese un correo válido: ")
        else:
            for i in correo:
                if i == '@':
                    ar = True
                    break
            for i in correo:
                if i == '.':
                    pun = True
                    break
            for i in correo:
                if i == '@':
                    f_ar = True
                    break
                elif i == ".":
                    f_ar = False
                    break
                else:
                    continue

        while ar == False or pun == False or f_ar == False:
            print("Correo no válido")
            if ar == False:
                print("Falta '@' en su correo")
            if pun == False:
                print("Falta '.' en su correo")
            elif f_ar == False:
                print("El '@' debe ubicarse primero")
            correo = input("ingrese el correo de " + nombre + " " + apellido[0] + "." + " : ")
            for i in correo:
                if i == '@':
                    ar = True
                    break
            for i in correo:
                if i == '.':
                    pun = True
                    break
            for i in correo:
                if i == '@':
                    f_ar = True
                    break
                elif i == ".":
                    f_ar = False
                    break
                else:
                    continue

        ciudad = input("¿En qué ciudad se ubica "+ nombre + " " + apellido[0] + "." + "? : ").title()
        while not self.validar_tipo_tamaño(ciudad,"cadena","nombre"):
            ciudad = input("Ingrese una ciudad válida: ").title()

        fe_n = self.__convertir_fecha(self.__pedir_fecha("registro"))
        fe_a = self.__convertir_fecha(date.today())

        fe_da = "activo"
        vac = "no"

        self.setDocumento(documento)
        self.setNombre(nombre)
        self.setApellido(apellido)
        self.setDireccion(direccion)
        self.setTelefono(tel)
        self.setCorreo(correo)
        self.setCiudad(ciudad)
        self.setFechaNacimiento(fe_n)
        self.setFechaAfiliacion(fe_a)
        self.setFechaDesafiliacion(fe_da)
        self.setVacunado(vac)

        afiliado = (self.getDocumento(),self.getNombre(),self.getApellido(),self.getDireccion(),self.getTelefono(),self.getCorreo(),self.getCiudad(),self.getFechaNacimiento(),self.getFechaAfiliacion(),self.getFechaDesafiliacion(),self.getVacunado())
        con.execute('''INSERT INTO USUARIOS_EPS_SALUDUN('NUMERO_DE_DOCUMENTO', 'NOMBRES', 'APELLIDOS', 'DIRECCIÓN','TELEFONO','CORREO_ELECTRONICO','CIUDAD','FECHA_DE_NACIMIENTO','FECHA_DE_AFILIACIÓN','FECHA_DE_DESAFILIACIÓN' ,'VACUNADO')VALUES(?,?,?,?,?,?,?,?,?,?,?)''', afiliado)
        con.commit()

    def desafiliar(self,cursor,con):
        identificacion = input("Ingrese el número de documento del usuario: ")
        valido = False
        while not valido:
            if not self.validar_tipo_tamaño(self,identificacion,"entero","documento"):
                identificacion = input("Ingrese un número de documento válido: ")
            else:
                valido = True

            if valido:
                afiliado,activo = self.__validar_identificacion(self,int(identificacion),"USUARIOS_EPS_SALUDUN",cursor,1)
                if afiliado:
                    print("Este número de documento no se encuentra en la base de datos.")
                    eleccion = input('''¿Qué desea hacer ahora?
                a- Ingresar un nuevo documento
                b- Volver\n''').lower()
                    while eleccion != "a" and eleccion != "b":
                        eleccion = input("Introduzca una opción válida:\n")

                    if eleccion == "a":
                        valido = False
                        identificacion = input("Ingrese el número de documento del usuario: ")
                    elif eleccion == "b":
                        return

                elif not activo:
                    print("El usuario se encuentra desafiliado.")
                    eleccion = input('''¿Qué desea hacer ahora?
                a- Ingresar un nuevo documento
                b- Volver\n''').lower()
                    while eleccion != "a" and eleccion != "b":
                        eleccion = input("Introduzca una opción válida:\n")

                    if eleccion == "a":
                        valido = False
                        identificacion = input("Ingrese el número de documento del usuario: ")
                    elif eleccion == "b":
                        return

                else:
                    valido = True

        # Se realiza pregunta de confirmación de la desafiliación
        confirmacion = input("¿Está seguro que desea desafiliar al usuario con identificación {}? (Reponda 'si' si está seguro y 'no' si no lo está.)\n".format(identificacion)).upper()
        while confirmacion != 'SI' and confirmacion != 'NO':
            confirmacion = input("Introduzca una opción válida: ").upper()

        if confirmacion == "SI":
            f = datetime.now() #Se establece la fecha que está en el sistema
            fecha = str(f.day).rjust(2,"0") + "/" + str(f.month).rjust(2,"0") + "/" + str(f.year) #La fecha se coloca en el formato solicitado
            # Se actualiza la tabla de usuarios con la fecha correspondiente.
            cursor.execute("UPDATE USUARIOS_EPS_SALUDUN SET FECHA_DE_DESAFILIACIÓN = '{}' WHERE NUMERO_DE_DOCUMENTO= {}".format(fecha,int(identificacion)))
            print("EL USUARIO CON DOCUMENTO {} HA SIDO DESAFILIADO.".format(identificacion))
            print('============================================================')
            con.commit()

    def act_vac(self,cursor,con):
        documento = input("Ingrese el número de documento del usuario: ")
        valido = False
        while not valido:
            if not self.validar_tipo_tamaño(self,documento,"entero","documento"):
                documento = input("Ingrese un número de documento válido: ")
            else:
                valido = True

            if valido:
                afiliado,activo = self.__validar_identificacion(self,int(documento),"USUARIOS_EPS_SALUDUN",cursor,1)
                if afiliado:
                    print("Este número de documento no se encuentra en la base de datos.")
                    eleccion = input('''¿Qué desea hacer ahora?
                a- Ingresar un nuevo documento
                b- Volver\n''').lower()
                    while eleccion != "a" and eleccion != "b":
                        eleccion = input("Introduzca una opción válida:\n")

                    if eleccion == "a":
                        valido = False
                        documento = input("Ingrese el número de documento del usuario: ")
                    elif eleccion == "b":
                        return

                elif not activo:
                    print("El usuario se encuentra desafiliado.")
                    eleccion = input('''¿Qué desea hacer ahora?
                a- Ingresar un nuevo documento
                b- Volver\n''').lower()
                    while eleccion != "a" and eleccion != "b":
                        eleccion = input("Introduzca una opción válida:\n")

                    if eleccion == "a":
                        valido = False
                        documento = input("Ingrese el número de documento del usuario: ").rjust(12)
                    elif eleccion == "b":
                        return

                else:
                    valido = True

        actualizar = ("UPDATE USUARIOS_EPS_SALUDUN SET VACUNADO = 'si' WHERE NUMERO_DE_DOCUMENTO = "+ documento )
        cursor.execute(actualizar)
        con.commit()
        print('USUARIO CON NUMERO DE DOCUMENTO: ',documento, 'HA SIDO ACTUALIZADO')
        print('============================================================')

class Lotes(Usuario):
    def __init__(self,idlote = None,fab = None,tipo_vac = "",cant_recibida = None,cant_vac_usadas = None,cant_sin_asignar = None,dosis = "", temperatura ="", efectividad = "",tiempo_proteccion = None, fecha_venc = ""):
        self.__idlote = idlote
        self.__fab = fab
        self.__efectividad = efectividad
        self.__dosis = dosis
        self.__temperatura = temperatura
        self.__tipo_vac = tipo_vac
        self.__cant_recibida = cant_recibida
        self.__cant_vac_usadas = cant_vac_usadas
        self.__cant_sin_asignar = cant_sin_asignar
        self.__tiempo_proteccion = tiempo_proteccion
        self.__fecha_venc = fecha_venc

## Setters and getters
    def getidlote(self):
        return self.__idlote
    def setidlote(self,d):
        self.__idlote = d
    def getfab(self):
        return self.__fab
    def setfab(self,d):
        self.__fab = d
    def getefectividad(self):
        return self.__efectividad
    def setDocumento(self,d):
        self.__efectividad = d
    def getdosis(self):
        return self.__dosis
    def setdosis(self,d):
        self.__dosis = d
    def gettemperatura(self):
        return self.__temperatura
    def settemperatura(self,d):
        self.__temperatura = d
    def gettipo_vac(self):
        return self.__tipo_vac
    def settipo_vac(self,d):
        self.__tipo_vac = d
    def getcant_recibida(self):
        return self.__cant_recibida
    def setcant_recibida(self,d):
        self.__cant_recibida = d
    def getcant_vac_usadas(self):
        return self.__cant_vac_usadas
    def setcant_vac_usadas(self,d):
        self.__cant_vac_usadas = d
    def getcant_sin_asignar(self):
        return self.__cant_sin_asignar
    def setcant_sin_asignar(self,d):
        self.__cant_sin_asignar = d
    def gettiempo_proteccion(self):
        return self.__tiempo_proteccion
    def settiempo_proteccion(self,d):
        self.__tiempo_proteccion = d
    def getfecha_venc(self):
        return self.__fecha_venc
    def setfecha_venc(self,d):
        self.__fecha_venc = d
#
    def nombrar_lote(self,cursor,con):
        '''Se introducen los datos y se crea un nuevo lote en la base de datos.'''
        # Se verifica que el número del lote sea válido y que no se encuentre en la base de datos.
        self.__idlote = input("Número de lote: ").upper()
        valido = False
        while not valido:
            if not Usuario.validar_tipo_tamaño(self,self.__idlote,"cadena","documento"):
                self.__idlote = input("Ingrese un número de lote válido: ")
            else:
                valido = True

            if valido:
                if not self._Usuario__validar_identificacion(self.__idlote,"USUARIOS_EPS_SALUDUN",cursor,con):
                    print("Este número de lote ya se encuentra en la base de datos.")
                    eleccion = input('''¿Qué desea hacer ahora?
                a- Ingresar un nuevo documento
                b- Volver\n''').lower()
                    while not Usuario.validar_opciones_menu(eleccion,["a","b"]):
                        eleccion = input("Introduzca una opción válida:\n")

                    if eleccion == "a":
                        valido = False
                        self.__idlote = input("Ingrese el número de lote: ").upper()
                    elif eleccion == "b":
                        return
                else:
                    valido = True

    def identificar_fab(self):
        self.__fab = input('Fabricante(Sinovac-Pfizer-Moderna-Sputnik V-AstraZeneca-Sinopharm-Covaxim): ').title()
        while not Usuario.validar_opciones_menu(Usuario,self.__fab,["Sinovac","Pfizer","Moderna","Sputnik V","Astrazeneca","Sinopharm","Covaxim"]):
            self.__fab = input("Introduzca una opción válida. ").title()
            # Dependiendo del fabricante ingresado se establecen las propiedades por defecto según cada uno.
        if self.__fab == "Sinovac":
            self.__efectividad = "50,5"
            self.__dosis = "2"
            self.__temperatura = "2-8 C"
            self.__tipo_vac = "Virus desactivado"
        elif self.__fab == "Pfizer":
            self.__efectividad = "95"
            self.__dosis = "2"
            self.__temperatura = "-70 C"
            self.__tipo_vac = "ARNm"
        elif self.__fab == "Moderna":
            self.__efectividad = "94,5"
            self.__dosis = "2"
            self.__temperatura = "-20 C"
            self.__tipo_vac = "ARNm"
        elif self.__fab == "Sputnik V":
            self.__efectividad = "91,6"
            self.__dosis = "2"
            self.__temperatura = "2-8 C"
            self.__tipo_vac = "Vector viral"
        elif self.__fab == "Astrazeneca":
            self.__efectividad = "82,4"
            self.__dosis = "2"
            self.__temperatura = "2-8 C"
            self.__tipo_vac = "Vectror viral"
        elif self.__fab == "Sinopharm":
            self.__efectividad = "79"
            self.__dosis = "2"
            self.__temperatura = "2-8 C"
            self.__tipo_vac = "Vector viral"
        elif self.__fab == "Covaxim":
            self.__efectividad = "78"
            self.__dosis = "2"
            self.__temperatura = "2-8 C"
            self.__tipo_vac = "Virus desactivado"

    def cantidad_recibida(self):
        self.__cant_recibida = input("Cantidad recibida: ")
        while not Usuario.validar_tipo_tamaño(self,self.__cant_recibida,"entero","cant_recibida"):
            self.__cant_recibida = input("Ingrese una cantidad válida: ")

        self.__cant_vac_usadas = "0"*len(self.__cant_recibida)
        self.__cant_sin_asignar = self.__cant_recibida

    def tiempo_proteccion(self):
        self.__tiempo_proteccion = input("Tiempo de protección (indíquelo en el número de meses): ")
        while not Usuario.validar_tipo_tamaño(self,self.__tiempo_proteccion,"entero","cant_recibida"):
            self.__tiempo_proteccion = input("Ingrese una cantidad válida: ")

        self.__fecha_venc = self._Usuario__convertir_fecha(self._Usuario__pedir_fecha("lotes"))

    def registrar_datos(self,cursor,connection):
        # Los datos se guardan en una tupla y se insertan en la base de datos.
        datos = (self.getidlote(),self.getfab(),self.gettipo_vac(),self.getcant_recibida(),self.getcant_vac_usadas(),self.getcant_sin_asignar(),self.getdosis(),self.gettemperatura(),self.getefectividad(),self.gettiempo_proteccion(),self.getfecha_venc())
        print(datos)
        cursor.execute('''INSERT INTO LOTES_VACUNAS (NÚMERO_DE_LOTE, FABRICANTE, TIPO_DE_VACUNA, CANTIDAD_RECIBIDA, CANTIDAD_VACUNAS_USADAS, CANTIDAD_VACUNAS_SIN_ASIGNAR, DÓSIS_NECESARIAS, TEMPERATURA_DE_ALMACENAMIENTO, EFECTIVIDAD_IDENTIFICADA, TIEMPO_DE_PROTECCIÓN, FECHA_DE_VENCIMIENTO) VALUES(?,?,?,?,?,?,?,?,?,?,?)''', datos)
        print("CREACIÓN DEL LOTE", self.__idlote, "EXITOSA.")
        connection.commit()

    def consulta(self):
        print('============================================================')
        print("NÚMERO DE LOTE: ",self.getidlote())
        print("FABRICANTE: ",self.getfab())
        print("TIPO DE VACUNA: ",self.gettipo_vac())
        print("CANTIDAD RECIBIDA: ",self.getcant_recibida())
        print("CANTIDAD VACUNAS USADAS: ",self.getcant_vac_usadas())
        print("CANTIDAD VACUNAS SIN ASIGNAR: ",self.getcant_sin_asignar())
        print("DÓSIS NECESARIAS: ",self.getdosis())
        print("TEMPERATURA DE ALMACENAMIENTO: ",self.gettemperatura())
        print("EFECTIVIDAD IDENTIFICADA: ",self.getefectividad())
        print("TIEMPO DE PROTECCIÓN: ",self.gettiempo_proteccion())
        print("FECHA DE VENCIMIENTO: ",self.getfecha_venc())
        print('============================================================')

class Planes(Usuario):
    def __init__(self,idplan = None,edad_min= None,edad_max = None,fe_in=None,fe_f=None):
        self.__idplan = idplan
        self.__edad_min = edad_min
        self.__edad_max = edad_max
        self.__fe_in = fe_in
        self.__fe_f = fe_f

## Setters and getters
    def getIdplan(self):
        return self.__idplan
    def setIdplan(self,d):
        self.__idplan = d

    def getEdad_min(self):
        return self.__edad_min
    def setEdad_min(self,d):
        self.__edad_min = d

    def getEdad_max(self):
        return self.__edad_max
    def setEdad_max(self,d):
        self.__edad_max = d

    def getFe_in(self):
        return self.__fe_in
    def setFe_in(self, d):
        self.__fe_in = d

    def getFe_f(self):
        return self.__fe_f
    def setFe_f(self,d):
        self.__fe_f = d
#
    def numero_plan(self,cursor):

        '''Se ingresan los datos y se crea un nuevo plan en la base de datos.'''
        # Verificación del id del plan.
        self.__idplan = input("Ingrese el número del plan: ")
        valido = False
        while not valido:
            if not Usuario.validar_tipo_tamaño(self,self.__idplan,"entero","idplan"):
                self.__idplan = input("Ingrese un número de plan válido: ")
            else:
                valido = True

            if valido:
                if not self._Usuario__validar_identificacion(int(self.__idplan),"PLANES_VACUNACIÓN",cursor):
                    print("Este número de documento ya se encuentra en la base de datos.")
                    eleccion = input('''¿Qué desea hacer ahora?
                a- Ingresar un nuevo documento
                b- Volver\n''').lower()
                    while not Usuario.validar_opciones_menu(Usuario,eleccion,["a","b"]):
                        eleccion = input("Introduzca una opción válida:\n")

                    if eleccion == "a":
                        valido = False
                        self.__idplan = input("Ingrese el número de documento del usuario: ")
                    elif eleccion == "b":
                        return
                else:
                    valido = True

    def edades_plan(self,cursor):
        while True:
            self.__edad_min = input("Edad mínima del plan: ")
            while not Usuario.validar_tipo_tamaño(self,self.__edad_min,"entero","edad"):
                self.__edad_min = input("Ingrese una edad válida: ")

            self.__edad_max = input("Edad máxima del plan: ")
            ver = False
            # Se verifica que la edad máxima sea mayor a la edad mínima.
            while not ver:
                while not Usuario.validar_tipo_tamaño(self,self.__edad_max,"entero","edad"):
                    self.__edad_max = input("Ingrese una edad válida: ")
                if int(self.__edad_max) <= int(self.__edad_min):
                    print("La edad máxima es menor o igual a la edad mínima")
                    self.__edad_max = input("Introduzca una edad válida: ")
                else:
                    ver = True
            cursor.execute("SELECT * FROM PLANES_VACUNACIÓN")
            tabla = cursor.fetchall()
            v_edad = True
            for plan in tabla:
                if int(self.__edad_max) >= plan[1] and int(self.__edad_max) <= plan[2]:
                    print("Este rango de edad ya tiene un plan asignado.")
                    print(f"{plan[1]} - {plan[2]}")
                    v_edad = False
                    break
                elif int(self.__edad_min) <= plan[2] and int(self.__edad_max) >= plan[1]:
                    print("Este rango de edad ya tiene un plan asignado.")
                    print(f"{plan[1]} - {plan[2]}")
                    v_edad = False
                    break
            if v_edad:
                break

    def tiempo_del_plan(self):
        self.__fe_in = self._Usuario__pedir_fecha("planes")
        # Se verifica que la fecha de fin sea posterior a la fecha de inicio del plan.
        self.__fe_f = self._Usuario__pedir_fecha("planes",1)
        while self.__fe_f <= self.__fe_in:
            print("La fecha final del plan debe ser posterior a la fecha inicial: ")
            self.__fe_f = Usuario.__pedir_fecha(self,"planes", 1)

    def registrar_datos(self,cursor,con):
        # Se colocan los datos en una tupla y se insertan en la base de datos.
        datos = (self.getIdplan(), self.getEdad_min(), self.getEdad_max(), self._Usuario__convertir_fecha(self.getFe_in()), self._Usuario__convertir_fecha(self.getFe_f()))
        print(datos)
        cursor.execute('''INSERT INTO PLANES_VACUNACIÓN ('NÚMERO_DEL_PLAN', 'EDAD_MÍNIMA', 'EDAD_MÁXIMA', 'FECHA_INICIO', 'FECHA_FIN') VALUES(?,?,?,?,?)''', datos)
        con.commit()

    def consulta(self):
        info = (': ', ': ', ': ', ': ', ': ')
        print('============================================================')
        print("NÚMERO DEL PLAN: ",self.getIdplan())
        print("EDAD MÍNIMA: ",self.getEdad_min())
        print("EDAD MÁXIMA: ",self.getEdad_max())
        print("FECHA DE INICIO: ",self.getFe_in())
        print("FECHA DE FIN: ",self.getFe_f())
        print('============================================================')

class Citas(Usuario):
    def __init__(self, doc, ciudad=None, idlote=None, fe_prog=None, hora_prog=None, nombre=None, apellido=None, direccion=None, tel=None, correo=None,  fab=None):
        self.__documento = doc
        self.__ciudad = ciudad
        self.__idlote = idlote
        self.__fecha_prog = fe_prog
        self.__hora_prog = hora_prog
        self.__nombre = nombre
        self.__apellido = apellido
        self.__direccion = direccion
        self.__telefono = tel
        self.__correo = correo
        self.__fabricante = fab

## Setters and getters
    def getDocumento(self):
        return self.__documento
    def setDocumento(self,d):
        self.__documento = d

    def getNombre(self):
        return self.__nombre
    def setNombre(self,d):
        self.__nombre = d

    def getApellido(self):
        return self.__apellido
    def setApellido(self,d):
        self.__apellido = d

    def getDireccion(self):
        return self.__direccion
    def setDireccion(self,d):
        self.__direccion = d

    def getTelefono(self):
        return self.__telefono
    def setTelefono(self,d):
        self.__telefono = d

    def getCorreo(self):
        return self.__correo
    def setCorreo(self,d):
        self.__correo = d

    def getCiudad(self):
        return self.__ciudad
    def setCiudad(self,d):
        self.__ciudad = d

    def getNumLote(self):
        return self.__idlote
    def setNumLote(self,d):
        self.__idlote = d

    def getFechaProgramada(self):
        return self.__fecha_prog
    def setFechaProgramada(self,d):
        self.__fecha_prog = d

    def getHoraProgramada(self):
        return self.__hora_prog
    def setHoraProgramada(self,d):
        self.__hora_prog = d

    def getFabricante(self):
        return self.__fabricante
    def setFabricante(self,d):
        self.__fabricante = d
#
    def enviar_correo(self):
        username = "epsun2021@gmail.com"
        password = ("EPSUN123")

        # Conexión
        context = ssl.create_default_context()

        with smtplib.SMTP_SSL("smtp.gmail.com", 465, context = context) as server:
            server.login(username, password)

            mensaje = ('''CONFIRMACION CITA EPS SALUDUN\n\nApreciado(a) {} {} se ha programado su cita de vacunacion para la fecha\n\t{} a las {}.'''.format(self.getNombre(),self.getApellido(),self.getFechaProgramada(),self.getHoraProgramada()))
            try:
                server.sendmail(username, self.getCorreo(), mensaje)
            except:
                pass

    def crear_cita(self,cursor,con):
        usuario = (self.getDocumento(),self.getCiudad(),self.getNumLote(),self.getFechaProgramada(),self.getHoraProgramada(),self.getNombre(),self.getApellido(),self.getDireccion(),self.getTelefono(),self.getCorreo(),self.getFabricante())
        con.execute('''INSERT INTO CITAS_VACUNACION('NUMERO_DE_DOCUMENTO','CIUDAD_DE_VACUNACION','NUMERO_DE_LOTE','FECHA_PROGRAMADA','HORA_PROGRAMADA','NOMBRES','APELLIDOS', 'DIRECCION','TELEFONO','CORREO', 'FABRICANTE')VALUES(?,?,?,?,?,?,?,?,?,?,?)''', usuario)
        self.enviar_correo()
        con.commit()

    def verificar_fecha_hora(self,cursor):
        '''Verifica que la fecha y hora ingresados no se encuentren en la base de datos.
        Retorna falso si ya lo está y verdadero si no.'''
        cursor.execute("SELECT * FROM CITAS_VACUNACION")
        citas = cursor.fetchall() # Crea la tabla con las citas
        for c in citas: # Itera por todas las citas de la tabla y verifica fecha y hora.
            if c[3] == self.getFechaProgramada():
                if c[4] == self.getHoraProgramada():
                    return False
        return True

    def consulta(self):
        print('============================================================')
        print('NUMERO DE DOCUMENTO: ',self.getDocumento())
        print('CIUDAD DE VACUNACIÓN: ',self.getCiudad())
        print('NUMERO DE LOTE: ',self.getNumLote())
        print('FECHA PROGRAMDA: ',self.getFechaProgramada())
        print('HORA PROGRAMADA: ',self.getHoraProgramada())
        print('NOMBRES: ',self.getNombre())
        print('APELLIDOS: ',self.getApellido())
        print('DIRECCIÓN: ',self.getDireccion())
        print('TELEFONO ',self.getTelefono())
        print('CORREO ',self.getCorreo())
        print('FABRICANTE ',self.getFabricante())
        print('============================================================')

    def consulta_programacion(self,cursor):
        '''Imprime la información de toda la programación de vacunación, permitiendo ordenarla por cualquier campo.'''
        cursor.execute("SELECT * FROM CITAS_VACUNACION")
        tabla_citas = cursor.fetchall() # Se establece la tabla como se encuentra por defecto.
        self.__dibujar_tabla(self,tabla_citas)  # Se imprime la tabla.
        a = input("Presiona ENTER para continuar.")
        salida = False
        # Entra en el ciclo en donde se encuentran las opciones de ordenamiento y de salida.
        while not salida:
            eleccion = input('''¿Desea organizar la información por otro campo? Si es así escoja una de las siguientes opciones:
            1- Número de documento              7- Apellido del usuario
            2- Ciudad de vacunación             8- Dirección
            3- Número de lote                   9- Teléfono
            4- Fecha de programación            10- Correo
            5- Hora de programación             11- Fabricante de la vacuna
            6- Nombre del usuario

            Si desea volver al menú presione E.\n''')

            while not self.validar_opciones_menu(self,str(eleccion),["1","2","3","4","5","6","7","8","9","10","11","e","E"]):
                eleccion = input("Introduzca una opción válida: ")
            if eleccion == "e" or eleccion == "E":
                return
            else:
                # Se solicita el orden (ascendente o descendente).
                orden = input("¿Deseas ordenarlo de forma ascendente (ingresa 1) o descendente (ingresa 2)?\n")
                while not self.validar_opciones_menu(self,orden,["1","2"]):
                    orden = input("Introduzca una opción válida: ")
                if orden == "1":
                    orden = "ASC"
                else:
                    orden = "DESC"
                # En una tabla se almacenan los datos de la tabla.
                lista = ('NUMERO_DE_DOCUMENTO','CIUDAD_DE_VACUNACION','NUMERO_DE_LOTE','FECHA_PROGRAMADA','HORA_PROGRAMADA','NOMBRES','APELLIDOS', 'DIRECCION','TELEFONO','CORREO', 'FABRICANTE')
                # Se itera en el número de datos.
                for i in range(1,12):
                    # Si la variable i coincide con la elección tomada por el usuario, este es el campo por el que se va a ordenar.
                    if int(eleccion) == i:
                        cursor.execute("SELECT * FROM CITAS_VACUNACION ORDER BY {} {}".format(lista[i-1],orden))
                        self.__dibujar_tabla(self,cursor.fetchall())    # Se dibuja la tabla ya ordenada.
                        a = input("Presiona ENTER para continuar.")

    def __dibujar_tabla(self,tabla:list):
        '''Dibuja la tabla, al consultar la programación de vacunas completa.'''
        # Encabezado de la tabla
        print('='*120)
        print('DOCUMENTO \tCIUDAD_DE_VACUNACION\tNUMERO_DE_LOTE\tFECHA_PROGRAMADA\tHORA_PROGRAMADA\t\tNOMBRES')
        print('-'*120)
        # Itera por los registros de la tabla, imprimiendo los primeros datos de todos los usuarios.
        for t in tabla:
            print(str(t[0]).ljust(12)+"\t"+str(t[1]).ljust(20)+"\t"+str(t[2]).ljust(12)+"  \t"+str(t[3]).ljust(12)+"      \t"+str(t[4]).ljust(5)+"          \t\t"+str(t[5]).ljust(20))
        # Segundo encabezado
        print('='*120)
        print('APELLIDOS           \tDIRECCION           \tTELEFONO    \tCORREO                        \tFABRICANTE')
        print('-'*120)
        # Itera e imprime el resto de los datos de todos los usuarios.
        for t in tabla:
            print(str(t[6]).ljust(20)+"\t"+str(t[7]).ljust(20)+"\t"+str(t[8]).ljust(10)+"\t"+str(t[9]).ljust(30)+"\t"+str(t[10]))
        print('-'*120)


"""
Create statistical summaries command

"""
from __future__ import absolute_import, print_function, division

import logging
import os
from datetime import datetime
from functools import partial
from itertools import groupby

import click
from dateutil import tz
from pathlib import Path

import datacube
from datacube.ui import task_app

_LOG = logging.getLogger(__name__)

APP_NAME = 'datacube-ncml'

def get_filename(config, cell_index, year=None):
    if year is None:
        file_path_template = str(Path(config['location'], config['ncml_path_template']))
    else:
        file_path_template = str(Path(config['location'], config['partial_ncml_path_template']))
    return file_path_template.format(tile_index=cell_index, start_time=year)

def make_ncml_tasks(index, config, cell_index=None, year=None, cell_index_list=None, **kwargs):
    product = config['product']

    query = {}
    if year is not None:
        query['time'] = datetime(year=year, month=1, day=1), datetime(year=year + 1, month=1, day=1)

    gw = datacube.api.GridWorkflow(index=index, product=product.name)

    if cell_index_list is None:
        if cell_index is not None:
            cell_index_list = [cell_index]
        else:
            cell_index_list = []

    for requested_cell_index in cell_index_list:
        cells = gw.list_cells(product=product.name, cell_index=requested_cell_index, **query)
        for (found_cell_index, tile) in cells.items():
            output_filename = get_filename(config, found_cell_index, year)
            yield dict(tile=tile,
                       cell_index=found_cell_index,
                       output_filename=output_filename)

def make_ncml_config(index, config, export_path=None, nested_years=None, **query):
    config['product'] = index.products.get_by_name(config['output_type'])

    config['nested_years'] = nested_years if nested_years is not None else []

    if export_path is not None:
        config['location'] = export_path

    if not os.access(config['location'], os.W_OK):
        _LOG.warning('Current user appears not have write access output location: %s', config['location'])
    return config

def get_history_attribute(config, task):
    return '{dt} {user} {app} ({ver}) {args}  # {comment}'.format(
        dt=datetime.now(tz.tzlocal()).isoformat(),
        user=os.environ.get('USERNAME') or os.environ.get('USER'),
        app=APP_NAME,
        ver=datacube.__version__,
        args=', '.join([config['app_config_file'],
                        str(config['version']),
                        task['output_filename'],
                        str(task['cell_index'])]),
        comment='Created NCML file to aggregate multiple NetCDF files along the time dimension'
    )

def do_ncml_task(config, task):
    tile = task['tile']
    nested_years = config['nested_years']

    def get_sources_filepath(sources):
        year = int(str(sources.time.values.astype('datetime64[Y]')))
        if year in nested_years:
            file_path_template = str(Path(config['location'], config['partial_ncml_path_template']))
            return file_path_template.format(tile_index=task['cell_index'], start_time=year), True

        return str(sources.item()[0].local_path), False

    header_attrs = dict(date_created=datetime.today().isoformat(),
                        history=get_history_attribute(config, task))

    file_locations = []
    for (file_location, is_nested_ncml), sources in groupby(tile.sources, get_sources_filepath):
        file_locations.append(file_location)
        if is_nested_ncml:
            write_ncml_file(file_location, [str(source.item()[0].local_path) for source in sources], header_attrs)

    write_ncml_file(task['output_filename'], file_locations, header_attrs)

def write_ncml_file(ncml_filename, file_locations, header_attrs):
    filename = Path(ncml_filename)
    temp_filename = Path().joinpath(*filename.parts[:-1]) / '.tmp' / filename.parts[-1]

    if temp_filename.exists():
        temp_filename.unlink()

    try:
        temp_filename.parent.mkdir(parents=True)
    except OSError:
        pass

    netcdf_def = """
        <netcdf xmlns="http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2" location="{path}">
            <remove name="dataset" type="variable" />
            <remove name="dataset_nchar" type="dimension" />
        </netcdf>"""

    with open(str(temp_filename), 'w') as ncml_file:
        ncml_file.write('<netcdf xmlns="http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2">\n')

        for key, value in header_attrs.items():
            ncml_file.write("  <attribute name='{key}' type='string' value='{value}' />\n".format(key=key, value=value))

        ncml_file.write('  <aggregation dimName="time" type="joinExisting">\n')

        for file_location in file_locations:
            ncml_file.write(netcdf_def.format(path=file_location))

        ncml_file.write('  </aggregation>\n')
        ncml_file.write('</netcdf>\n')

    if filename.exists():
        filename.unlink()

    temp_filename.rename(filename)

@click.group(name=APP_NAME, help='NCML creation utility')
@datacube.ui.click.version_option
def ncml_app():
    pass

#: pylint: disable=invalid-name
command_options = datacube.ui.click.compose(
    datacube.ui.click.config_option,
    datacube.ui.click.pass_index(app_name=APP_NAME),
    datacube.ui.click.logfile_option,
    task_app.cell_index_option,
    task_app.cell_index_list_option,
    task_app.queue_size_option,
    task_app.load_tasks_option,
    task_app.save_tasks_option,
    datacube.ui.click.executor_cli_options,
    click.option('--export-path', 'export_path',
                 help='Write the stacked files to an external location instead of the location in the app config',
                 default=None,
                 type=click.Path(exists=True, writable=True, file_okay=False)),
)

@ncml_app.command(short_help='Create an ncml file')
@command_options
@click.argument('app_config')
@task_app.task_app(make_config=make_ncml_config, make_tasks=make_ncml_tasks)
def full(index, config, tasks, executor, queue_size, **kwargs):
    """Create ncml files for the full time depth of the product

    e.g. datacube-ncml full <app_config_yaml>
    """
    click.echo('Starting datacube ncml utility...')

    task_func = partial(do_ncml_task, config)
    task_app.run_tasks(tasks, executor, task_func, None, queue_size)

@ncml_app.command(short_help='Create a full ncml file with nested ncml files for particular years')
@command_options
@click.argument('app_config')
@click.argument('nested_years', nargs=-1, type=click.INT)
@task_app.task_app(make_config=make_ncml_config, make_tasks=make_ncml_tasks)
def nest(index, config, tasks, executor, queue_size, **kwargs):
    """Create ncml files for the full time, with nested ncml files covering the given years

    e.g. datacube-ncml nest <app_config_yaml> 2016 2017

    This will refer to the actual files (hopefully stacked), and make ncml files for the given (ie unstacked) years.
    Use the `update` command when new data is added to a year, without having to rerun for the entire time depth.
    """
    click.echo('Starting datacube ncml utility...')

    task_func = partial(do_ncml_task, config)
    task_app.run_tasks(tasks, executor, task_func, None, queue_size)

@ncml_app.command(short_help='Update a single year ncml file')
@command_options
@click.argument('app_config')
@click.argument('year', type=click.INT)
@task_app.task_app(make_config=make_ncml_config, make_tasks=make_ncml_tasks)
def update(index, config, tasks, executor, queue_size, **kwargs):
    """Update a single year ncml file

    e.g datacube-ncml <app_config_yaml> 1996

    This can be used to update an existing ncml file created with `nest` when new data is added.
    """
    click.echo('Starting datacube ncml utility...')

    task_func = partial(do_ncml_task, config)
    task_app.run_tasks(tasks, executor, task_func, None, queue_size)

if __name__ == '__main__':
    ncml_app()


# -*- coding: utf-8 -*-
# %%
"""Clase con la cual se gestiona una lista de analistas."""
import os
import pandas as pd
import numpy as np
import copy
from datetime import datetime

from parsers import Parser

# %%=============================================================================
# CLASE
# =============================================================================
class GestorAnalistas:
    """
    Clase que permite gestionar una lista de analistas.
    """

    # =========================================================================
    # CONSTRUCTOR
    # =========================================================================
    def __init__(self, lista_analistas):

        if not isinstance(lista_analistas, list):
            lista_analistas = [lista_analistas]

        self.lista_analistas = lista_analistas

    def predecir_individuos_por_filtro(
            self,
            d_filtros,
            usar_train=True,
            usar_test=True,
            usar_demo=True,
            calcular_proba=True,
    ):
        pass

    # Voting
    def predecir_por_voting(self,
                            d_filtros,
                            funcion_scorer,
                            parametros_scorer,
                            usar_train=True,
                            usar_test=True,
                            usar_demo=True,
                            corte=0.5,
                            voting='hard',
                            ):

        # Calculo las proba solo si es soft
        calcular_proba = (voting == 'soft')

        # Calculo las predicciones
        r = self.predecir_individuos_por_filtro(
            d_filtros,
            usar_train=usar_train,
            usar_test=usar_test,
            usar_demo=usar_demo,
            calcular_proba=calcular_proba,
        )
        res = {}

        if voting == 'hard':

            preds_train = r['y_predict_train']
            preds_test = r['y_predict_test']
            preds_demo = r['y_predict_demo']

            if usar_train:
                y_predict_train = (preds_train.mean(axis=1) > corte).astype(float)
                res.update(y_predict_train=y_predict_train)

            if usar_test:
                y_predict_test = (preds_test.mean(axis=1) > corte).astype(float)
                res.update(y_predict_test=y_predict_test)

            if usar_demo:
                y_predict_demo = (preds_demo.mean(axis=1) > corte).astype(float)
                res.update(y_predict_demo=y_predict_demo)

        elif voting == 'soft':

            preds_train = r['preds_proba_train_1']
            preds_test = r['preds_proba_test_1']
            preds_demo = r['preds_proba_demo_1']

            if usar_train:
                y_predict_train = (preds_train.mean(axis=1) > corte).astype(float)
                res.update(y_predict_train=y_predict_train)

            if usar_test:
                y_predict_test = (preds_test.mean(axis=1) > corte).astype(float)
                res.update(y_predict_test=y_predict_test)

            if usar_demo:
                y_predict_demo = (preds_demo.mean(axis=1) > corte).astype(float)
                res.update(y_predict_demo=y_predict_demo)

        else:
            raise TypeError('Error en la defincion del parametro voting.')

        # Calculo las metricas si la funcion_scorer no es None
        if funcion_scorer is not None:

            d = dict(funcion_scorer=funcion_scorer, parametros_scorer=parametros_scorer)

            if usar_train and self.y_train is not None:
                metr_tr = self.calcular_metrica2(y_true=self.y_train, y_predict=y_predict_train, **d)
                res.update(dict(metrica_train=metr_tr))

            if usar_test and self.y_test is not None:
                metr_te = self.calcular_metrica2(y_true=self.y_test, y_predict=y_predict_test, **d)
                res.update(dict(metrica_test=metr_te))

            if usar_demo and self.y_demo is not None:
                metr_de = self.calcular_metrica2(y_true=self.y_demo, y_predict=y_predict_demo, **d)
                res.update(dict(metrica_demo=metr_de))

        return res


# Write your solution here
def no_vowels(s):
    l = []
    l.extend(s)
    nl = []
    for i in range(len(l)):
        if l[i] != 'a' and l[i] != 'e' and l[i] != 'i' and l[i] != 'o' and l[i] != 'u':
            nl.append(l[i])
    s = ''
    for i in nl:
        s += i
    return s


import os

_LOG_DIR = 'weekcook/particle/train'

def main():
    target_filename = 'train_20190515.txt'
    target_file = os.path.join(_LOG_DIR, target_filename)

    with open(target_file, 'r', encoding='utf-8') as r:
        lines = r.readlines()

    # tags = []
    # for idx, line in enumerate(lines):
    #     line = line.replace('\n', '')
    #     print(idx)
    #     print(line)
    #     tag = line.split('/')[1]
    #     tags.append(tag)

    # print('tags')
    # print(tags)

    tags = [line.replace('\n', '').split('/')[1] for line in lines if line != '']
    tags_set = set(tags)
    print('tags_set')
    print(tags_set)
    print('number of tags')
    print(len(tags_set))

if __name__ == '__main__':
    main()


from datetime import datetime
from optparse import make_option
import json

from django.core.management import BaseCommand, CommandError

from hearthstonearenastats.app.card.models import Card, Patch

class Command(BaseCommand):
    option_list = BaseCommand.option_list + (
        make_option(
            '-f', '--filename',
            action='store',
            type='string',
            dest='filename',
        ),
    )

    def handle(*args, **options):
        if not options.get('filename'):
            raise CommandError("Path to JSON card database required.")
        file_name = options.get('filename')
        with open(file_name) as f:
            cards_json = json.load(f)
        patch = cards_json['meta']
        cards = cards_json['cards']
        patch_obj, created = get_or_create_patch(patch)
        if not created:
            return
        else:
            bulk_insert_cards(patch_obj, cards)

def get_or_create_patch(patch):
    """Given a spec of a patch, get or create it in the database.

    Args:
      - patch: a dictionary containing information about a patch.

    Returns:
      (patch_object, created)
    """
    patch_version = patch['patch']
    patch_obj, created = Patch.objects.get_or_create(
        patch_version=patch_version
    )
    if created:
        patch_obj.db_update_date = datetime.utcnow()
        patch_obj.save()
    return patch_obj, created

def bulk_insert_cards(patch_obj, cards):
    """Given a patch object, and a list of cards, put them in the db.

    Args:
      - patch_obj: a reference to a Patch object.
      - cards: a list of cards, imported from a json db.
    """
    Card.objects.bulk_create([
        Card(
            patch=patch_obj,
            name=card['name'],
            description=card['description'],
            hero=card['hero'],
            category=card['category'],
            quality=card['quality'],
            race=card['race'],
            card_set=card['set'],
            mana=card['mana'],
            attack=card['attack'],
            health=card['health'],
            collectible=card['collectible'],
        )
        for card in cards
    ])


import hashlib
from multiprocessing import Process

from nanoservice import Responder
from nanoservice import Requester
from nanoservice import encoder
from nanoservice import Authenticator

def check(res, expected):
    assert res == expected

def start_service(addr, encoder, authenticator=None):
    """ Start a service with options """

    s = Responder(addr, encoder=encoder,
                  authenticator=authenticator, timeouts=(3000, 3000))
    s.register('none', lambda: None)
    s.register('divide', lambda x, y: x / y)
    s.register('upper', lambda dct: {k: v.upper() for k, v in dct.items()})
    s.start()

# ------------------

TESTS = [
    (('divide', [10, 2]), 5.0),
    (('none', []), None),
    (('upper', [{'a': 'a'}]), {'a': 'A'})
]

def test_encoding():
    """ Test encoding with defferent options """
    address = 'ipc:///tmp/test-encoders.sock'

    authenticators = [
        None,
        Authenticator('my-secret', hashlib.sha256)]

    encoders = [
        encoder.JSONEncoder(),
        encoder.MsgPackEncoder(),
        encoder.PickleEncoder()]

    for test, expected in TESTS:
        for enc in encoders:
            for authenticator in authenticators:
                method, args = test

                # Start process
                proc = Process(
                    target=start_service,
                    args=(address, enc, authenticator))
                proc.start()

                # Create client
                client = Requester(
                    address, encoder=enc,
                    authenticator=authenticator, timeouts=(3000, 3000))

                # Test
                res, err = client.call(method, *args)
                client.socket.close()
                proc.terminate()
                yield check, res, expected
                # self.assertEqual(expected, res)


'''The lower bound for Comparison based sorting algorithm (Merge Sort, Heap Sort, Quick-Sort .. etc) is Ω(nLogn), i.e., they cannot do better than nLogn. Counting sort is a linear time sorting algorithm that sort in O(n+k) time when elements are in the range from 1 to k.

What if the elements are in the range from 1 to n2?

We can't use counting sort because counting sort will take O(n2) which is worse than comparison-based sorting algorithms. Can we sort such an array in linear time?

Radix Sort is the answer. The idea of Radix Sort is to do digit by digit sort starting from least significant digit to most significant digit. Radix sort uses counting sort as a subroutine to sort.'''
def countSort(arr,exp):
    n=len(arr)

    count=[0 for i in range(256)]
    output=[0 for i in range(n)]

    for i in range(n):
        index=arr[i]//exp
        count[index%10]+=1

    for i in range(len(count)):
        count[i]+=count[i-1]

    i=n-1
    while i>=0:
        output[count[arr[i]]-1]=arr[i]
        count[arr[i]]-=1
        i-=1

    for i in range(n):
        arr[i]=output[i]
def radixSort(arr):
    maximum=max(arr)
    exp=1
    while maximum/exp>1:
        countSort(arr,exp)
        exp*=10
arr=[0,6,2,1,4,2,0,4,3]
radixSort(arr)
print(arr)
#[0, 0, 1, 2, 2, 3, 4, 4, 6]

'''Let there be d digits in input integers. Radix Sort takes O(d*(n+b)) time where b is the base for representing numbers, for example, for the decimal system, b is 10. What is the value of d? If k is the maximum possible value, then d would be O(logb(k)). So overall time complexity is O((n+b) * logb(k)). Which looks more than the time complexity of comparison-based sorting algorithms for a large k. Let us first limit k. Let k <= nc where c is a constant. In that case, the complexity becomes O(nLogb(n)). But it still doesn’t beat comparison-based sorting algorithms.
What if we make the value of b larger?. What should be the value of b to make the time complexity linear? If we set b as n, we get the time complexity as O(n). In other words, we can sort an array of integers with a range from 1 to nc if the numbers are represented in base n (or every digit takes log2(n) bits). '''


import pathlib
from time import perf_counter
import math
from ezdxf.addons import MengerSponge
from ezdxf.addons import r12writer
from ezdxf.render.forms import sphere, circle, translate

CWD = pathlib.Path("~/Desktop/Outbox").expanduser()
if not CWD.exists():
    CWD = pathlib.Path(".")

# ------------------------------------------------------------------------------
# This example shows how to use the fast DXF R12 writer add-on.
#
# docs: https://ezdxf.mozman.at/docs/addons/r12writer.html
# ------------------------------------------------------------------------------

def menger_sponge(filename, level=1, kind=0):
    t0 = perf_counter()
    sponge = MengerSponge(level=level, kind=kind).mesh()
    t1 = perf_counter()
    print(f"Build menger sponge <{kind}> in {t1 - t0:.5f}s.")

    with r12writer(filename) as r12:
        r12.add_polyface(sponge.vertices, sponge.faces, color=1)
    print(f'saved as "{filename}".')

def polymesh(filename, size=(10, 10)):
    m, n = size  # rows, cols
    dx = math.pi / m * 2
    dy = math.pi / n * 2
    vertices = []
    for x in range(m):  # rows second
        z1 = math.sin(dx * x)
        for y in range(n):  # cols first
            z2 = math.sin(dy * y)
            z = z1 * z2
            vertices.append((x, y, z))
    with r12writer(filename) as r12:
        r12.add_polymesh(vertices, size=size, color=1)
    print(f'saved as "{filename}".')

def polyface_sphere(filename):
    mesh = sphere(16, 8, quads=True)
    with r12writer(filename) as r12:
        r12.add_polyface(mesh.vertices, mesh.faces, color=1)
    print(f'saved as "{filename}".')

def polylines(filename):
    with r12writer(filename) as r12:
        r12.add_polyline_2d(circle(8), color=1, closed=False)
        r12.add_polyline_2d(
            translate(circle(8), vec=(3, 0)), color=3, closed=True
        )
        r12.add_polyline_2d(
            [(0, 4), (4, 4, 1), (8, 4, 0, 0.2, 0.000001), (12, 4)],
            format="xybse",
            start_width=0.1,
            end_width=0.1,
            color=5,
        )
    print(f'saved as "{filename}".')

if __name__ == "__main__":
    menger_sponge(CWD / "menger_sponge_r12.dxf", level=2)
    polymesh(CWD / "polymesh.dxf", size=(20, 10))
    polyface_sphere(CWD / "sphere.dxf")
    polylines(CWD / "polylines.dxf")


#!/usr/bin/python

#If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
#Find the sum of all the multiples of 3 or 5 below 1000.
def solve(length):
    total = 0
    for x in range(1, length):
        if x%3 == 0 or x%5 == 0:
            total += x
    return total


import logging
from json import JSONDecodeError
from typing import Callable
from typing import Optional
from typing import Union

from cryptojwt.key_jar import KeyJar

from idpyoidc.client.entity import Entity
from idpyoidc.client.exception import ConfigurationError
from idpyoidc.client.exception import OidcServiceError
from idpyoidc.client.exception import ParseError
from idpyoidc.client.service import REQUEST_INFO
from idpyoidc.client.service import SUCCESSFUL
from idpyoidc.client.service import Service
from idpyoidc.client.util import do_add_ons
from idpyoidc.client.util import get_deserialization_method
from idpyoidc.configure import Configuration
from idpyoidc.context import OidcContext
from idpyoidc.exception import FormatError
from idpyoidc.message import Message
from idpyoidc.message.oauth2 import is_error_message
from requests import request

__author__ = "Roland Hedberg"

logger = logging.getLogger(__name__)

class ExpiredToken(Exception):
    pass

# =============================================================================

class Client(Entity):
    client_type = "oauth2"

    def __init__(
        self,
        keyjar: Optional[KeyJar] = None,
        config: Optional[Union[dict, Configuration]] = None,
        services: Optional[dict] = None,
        httpc: Optional[Callable] = None,
        httpc_params: Optional[dict] = None,
        context: Optional[OidcContext] = None,
        upstream_get: Optional[Callable] = None,
        key_conf: Optional[dict] = None,
        entity_id: Optional[str] = "",
        verify_ssl: Optional[bool] = True,
        jwks_uri: Optional[str] = "",
        client_type: Optional[str] = "",
        **kwargs
    ):
        """

        :type client_type: str
        :param client_type: What kind of client this is. Presently 'oauth2' or 'oidc'
        :param keyjar: A py:class:`idpyoidc.key_jar.KeyJar` instance
        :param config: Configuration information passed on to the
            :py:class:`idpyoidc.client.service_context.ServiceContext`
            initialization
        :param httpc: A HTTP client to use
        :param httpc_params: HTTP request arguments
        :param services: A list of service definitions
        :param jwks_uri: A jwks_uri
        :return: Client instance
        """

        if config is None:
            config = {}

        if client_type:
            self.client_type = client_type
        elif config and "client_type" in config:
            client_type = self.client_type = config["client_type"]
        else:
            client_type = self.client_type

        if verify_ssl is False:
            # just ignore verify_ssl until it goes away
            if httpc_params:
                httpc_params["verify"] = False
            else:
                httpc_params = {"verify": False}

        jwks_uri = jwks_uri or config.get("jwks_uri", "")

        Entity.__init__(
            self,
            keyjar=keyjar,
            config=config,
            services=services,
            jwks_uri=jwks_uri,
            httpc=httpc,
            httpc_params=httpc_params,
            client_type=client_type,
            context=context,
            upstream_get=upstream_get,
            key_conf=key_conf,
            entity_id=entity_id,
        )

        self.httpc = httpc or request

        if isinstance(config, Configuration):
            _add_ons = config.conf.get("add_ons")
        else:
            _add_ons = config.get("add_ons")

        if _add_ons:
            do_add_ons(_add_ons, self._service)

    def do_request(
        self,
        request_type: str,
        response_body_type: Optional[str] = "",
        request_args: Optional[dict] = None,
        behaviour_args: Optional[dict] = None,
        **kwargs
    ):
        _srv = self._service[request_type]

        _info = _srv.get_request_parameters(request_args=request_args, **kwargs)

        if not response_body_type:
            response_body_type = _srv.response_body_type

        logger.debug("do_request info: {}".format(_info))

        try:
            _state = kwargs["state"]
        except Exception:
            _state = ""
        return self.service_request(
            _srv, response_body_type=response_body_type, state=_state, **_info
        )

    def set_client_id(self, client_id):
        self.get_context().set("client_id", client_id)

    def get_response(
        self,
        service: Service,
        url: str,
        method: Optional[str] = "GET",
        body: Optional[dict] = None,
        response_body_type: Optional[str] = "",
        headers: Optional[dict] = None,
        **kwargs
    ):
        """

        :param url:
        :param method:
        :param body:
        :param response_body_type:
        :param headers:
        :param kwargs:
        :return:
        """
        try:
            resp = self.httpc(method, url, data=body, headers=headers, **self.httpc_params)
        except Exception as err:
            logger.error("Exception on request: {}".format(err))
            raise

        if 300 <= resp.status_code < 400:
            return {"http_response": resp}

        if resp.status_code < 300:
            if "keyjar" not in kwargs:
                kwargs["keyjar"] = self.get_attribute("keyjar")
            if not response_body_type:
                response_body_type = service.response_body_type

            if response_body_type == "html":
                return resp.text

            if body:
                kwargs["request_body"] = body

        return self.parse_request_response(service, resp, response_body_type, **kwargs)

    def service_request(
        self,
        service: Service,
        url: str,
        method: Optional[str] = "GET",
        body: Optional[dict] = None,
        response_body_type: Optional[str] = "",
        headers: Optional[dict] = None,
        **kwargs
    ) -> Message:
        """
        The method that sends the request and handles the response returned.
        This assumes that the response arrives in the HTTP response.

        :param service: The Service instance
        :param url: The URL to which the request should be sent
        :param method: Which HTTP method to use
        :param body: A message body if any
        :param response_body_type: The expected format of the body of the
            return message
        :param httpc_params: Arguments for the HTTP client
        :return: A cls or ResponseMessage instance or the HTTP response
            instance if no response body was expected.
        """

        if headers is None:
            headers = {}

        logger.debug(REQUEST_INFO.format(url, method, body, headers))

        try:
            response = service.get_response_ext(
                url, method, body, response_body_type, headers, **kwargs
            )
        except AttributeError:
            response = self.get_response(
                service, url, method, body, response_body_type, headers, **kwargs
            )

        if "error" in response:
            pass
        else:
            service.update_service_context(response, key=kwargs.get("state"), **kwargs)
        return response

    def parse_request_response(self, service, reqresp, response_body_type="", state="", **kwargs):
        """
        Deal with a self.httpc response. The response are expected to
        follow a special pattern, having the attributes:

            - headers (list of tuples with headers attributes and their values)
            - status_code (integer)
            - text (The text version of the response)
            - url (The calling URL)

        :param service: A :py:class:`idpyoidc.client.service.Service` instance
        :param reqresp: The HTTP request response
        :param response_body_type: If response in body one of 'json', 'jwt' or
            'urlencoded'
        :param state: Session identifier
        :param kwargs: Extra keyword arguments
        :return:
        """

        # if not response_body_type:
        #     response_body_type = self.response_body_type

        if reqresp.status_code in SUCCESSFUL:
            logger.debug('response_body_type: "{}"'.format(response_body_type))
            _deser_method = get_deserialization_method(reqresp)

            if _deser_method != response_body_type:
                logger.warning(
                    "Not the body type I expected: {} != {}".format(
                        _deser_method, response_body_type
                    )
                )
            if _deser_method in ["json", "jwt", "urlencoded"]:
                value_type = _deser_method
            else:
                value_type = response_body_type

            logger.debug("Successful response: {}".format(reqresp.text))

            try:
                return service.parse_response(reqresp.text, value_type, state, **kwargs)
            except Exception as err:
                logger.error(err)
                raise
        elif reqresp.status_code in [302, 303]:  # redirect
            return reqresp
        elif reqresp.status_code == 500:
            logger.error("(%d) %s" % (reqresp.status_code, reqresp.text))
            raise ParseError("ERROR: Something went wrong: %s" % reqresp.text)
        elif 400 <= reqresp.status_code < 500:
            logger.error("Error response ({}): {}".format(reqresp.status_code, reqresp.text))
            # expecting an error response
            _deser_method = get_deserialization_method(reqresp)
            if not _deser_method:
                _deser_method = "json"

            try:
                err_resp = service.parse_response(reqresp.text, _deser_method)
            except (FormatError, ValueError):
                if _deser_method != response_body_type:
                    try:
                        err_resp = service.parse_response(reqresp.text, response_body_type)
                    except (OidcServiceError, FormatError, ValueError):
                        raise OidcServiceError(
                            "HTTP ERROR: %s [%s] on %s"
                            % (reqresp.text, reqresp.status_code, reqresp.url)
                        )
                else:
                    raise OidcServiceError(
                        "HTTP ERROR: %s [%s] on %s"
                        % (reqresp.text, reqresp.status_code, reqresp.url)
                    )
            except JSONDecodeError:  # So it's not JSON assume text then
                err_resp = {"error": reqresp.text}

            err_resp["status_code"] = reqresp.status_code
            return err_resp
        else:
            logger.error("Error response ({}): {}".format(reqresp.status_code, reqresp.text))
            raise OidcServiceError(
                "HTTP ERROR: %s [%s] on %s" % (reqresp.text, reqresp.status_code, reqresp.url)
            )

def dynamic_provider_info_discovery(client: Client, behaviour_args: Optional[dict] = None):
    """
    This is about performing dynamic Provider Info discovery

    :param behaviour_args:
    :param client: A :py:class:`idpyoidc.client.oidc.Client` instance
    """

    if client.client_type == "oidc" and client.get_service("provider_info"):
        service = "provider_info"
    elif client.client_type == "oauth2" and client.get_service("server_metadata"):
        service = "server_metadata"
    else:
        raise ConfigurationError("Can not do dynamic provider info discovery")

    _context = client.get_context()
    try:
        _context.set("issuer", _context.config["srv_discovery_url"])
    except KeyError:
        pass

    response = client.do_request(service, behaviour_args=behaviour_args)
    if is_error_message(response):
        raise OidcServiceError(response["error"])


from __future__ import absolute_import, division, print_function
from operator import itemgetter
from libtbx.utils import Usage
import fileinput
import sys, os

def run(command_name=os.environ.get(
          "LIBTBX_DISPATCHER_NAME", "libtbx.nequal")):
  if (sys.argv[1:] in [["-h"], ["--help"]]):
    raise Usage("%s [file ...]" % command_name + """
  Similar to the Unix uniq command, but each output line is
  prefixed with the number of identical consecutive lines.
  Example command:
    grep Warning log_file | sort | %s
  Example output:
    12: Warning: missing file'
     9: Warning: missing directory'
     1: Warning: unknown file
    Number of lines shown: 3
    Sum of counts: 22""" % command_name)
  buffer = []
  prev = None
  n = 0
  for line in fileinput.input():
    if (prev is None):
      prev = line
      n = 1
    elif (line != prev):
      buffer.append((n, prev))
      prev = line
      n = 1
    else:
      n += 1
  if (n != 0):
    buffer.append((n, prev))
  if (len(buffer) != 0):
    buffer.sort(key=itemgetter(0,1))
    sum_n = 0
    n_fmt = "%%%dd: " % len("%d" % buffer[0][0])
    for n,line in buffer:
      sys.stdout.write(n_fmt % n + line)
      sum_n += n
    print("Number of lines shown:", len(buffer))
    print("Sum of counts:", sum_n)

if (__name__ == "__main__"):
  run()


import pandas as pd
import numpy as np
import copy

def select_neighbors(node, level, rel_df):
  # Outgoing links only!
  links = rel_df[(rel_df['Source'] == node) & (rel_df['Level'] == level)]
  return links['Target'].values

def select_community_members(node, level, rel_df, aux_df):
  everyone_but_node = rel_df[rel_df['Source'] != node]['Source'].unique()

  community = aux_df[aux_df['docid'] == node].iloc[0]['ModularityClass']
  members = aux_df[(aux_df['docid'].isin(everyone_but_node)) & (aux_df['ModularityClass'] == community)]

  member_identifiers = members['docid']

  filtered_rel_df = rel_df[(rel_df['Source'] == node) & (rel_df['Target'].isin(member_identifiers))]

  # print("Members of the community:")
  # print(members)
  # print("Relationships")
  # print(filtered_rel_df)
  # print("Are they the same thing? ", len(members) == len(filtered_rel_df))

  if level > 0:
    return select_neighbors(node, level, filtered_rel_df)
  else:
    # People who belong to the community but whom node does not know.

    # remove from member_identifiers the students that appear in filtered_rel_df.
    unknown_members = []
    for member in member_identifiers:
      if member not in filtered_rel_df['Target'].values:
        unknown_members.append(member)

    return unknown_members

def change_to_value(change):
  if change == 'increased':
      return 1
  elif change == 'decreased':
      return -1
  else:
      return 0

def get_reverse_row(df, row):
  source = row['Source']
  target = row['Target']

  return get_reverse(df, source, target)

def get_reverse(df, source, target):
  filtered = df[(df['Source'] == target) & (df['Target'] == source)]

  if len(filtered) == 0:
    return None

  return filtered.iloc[0]

def apply_null_transformation(df):
  # we're assuming this is a "ready-to-use" network

  students = df['Source'].unique()

  weight_distribution = dict(df['Weight'].value_counts())
  total_weight = sum(weight_distribution.values())
  weight_probabilities = [v / total_weight for v in weight_distribution.values()]

  null_model = df.copy(deep=True)

  for student in students:
    filtered_df = df[df['Source'] == student]
    filtered_students = students[students != student]

    replacements = np.random.choice(filtered_students, len(filtered_df), replace=False)

    # print(student)
    for i, (key, row) in enumerate(filtered_df.iterrows()):
      # print(i, key)
      # print(filtered_students)
      null_model.at[key, 'Target'] = replacements[i]

      new_weight = np.random.choice(list(weight_distribution.keys()), 1, p=weight_probabilities)
      null_model.at[key, 'Level'] = new_weight[0]
      null_model.at[key, 'Weight'] = new_weight[0]

  print(null_model)
  return null_model

def to_undirected(df, save_to=None):
  records = []

  for i, row in df.iterrows():
    reverse = get_reverse_row(df, row)
    has_reverse = True

    if reverse is None:
      # print("This person:")
      # print(row)
      # print("has no reverse link. Assuming zero.")

      has_reverse = False
      reverse = {
        'Level': 0,
        'Change': 0,
      }

    average_level = (row['Level'] + reverse['Level']) / 2.0
    average_change = (row['Change'] + reverse['Change']) / 2.0

    row_dict = row.to_dict()
    row_dict['Level'] = average_level
    row_dict['Weight'] = average_level
    row_dict['Change'] = average_change
    records.append(row_dict)

    if has_reverse == False:
      reverse_row_dict = copy.deepcopy(row_dict)
      reverse_row_dict['Source'] = row_dict['Target']
      reverse_row_dict['Target'] = row_dict['Source']

      # print("Adding reverse:")
      # print(reverse_row_dict)
      records.append(reverse_row_dict)

  undirected_df = pd.DataFrame.from_records(records)
  # print("Undirected")
  # print(undirected_df)

  if save_to is not None:
    undirected_df.to_csv(save_to, index=None)
    print("Results written to: ", save_to)

  return undirected_df


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

import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import pmdarima as pm
import math
import plotly.graph_objs as go
from datetime import datetime
import warnings
warnings.filterwarnings("ignore")
from statsmodels.tsa.stattools import adfuller
plt.rcParams.update({"figure.figsize": (10, 6), "figure.dpi": 120})

# SNIPPET 5.1: page 79
def getWeights(d, size):
    # thres > 0 drops insignificant weights
    w = [1.]
    for k in range(1, size):
        w_ = -w[-1] / k * (d-k+1)
        w.append(w_)
    w = np.array(w[::-1]).reshape(-1, 1)
    return w

# SNIPPET 5.2: page 82
def fracDiff(series, d, thres=.01):
    """
    Increasing width window, with treatment of NaNs
    Note 1: For thres=1, nothing is skipped.
    Note 2: d can be any positive fractional, not necessarily bounded [0, 1].
    """
    #1) Compute weights for the longest series
    w = getWeights(d, series.shape[0])
    #2) Determine initial calcs to be skipped based on weight-loss threshold
    w_ = np.cumsum(abs(w))
    w_/= w_[-1]
    skip = w_[w_ > thres].shape[0]
    #3) Apply weights to values
    df = {}
    for name in series.columns:
        seriesF, df_ = series[[name]].fillna(method='ffill').dropna(), pd.Series()
        for iloc in range(skip, seriesF.shape[0]):
            loc = seriesF.index[iloc]
            if not np.isfinite(series.loc[loc, name]): continue # exclude NAs
            # figure out this line
            df_[loc] = np.dot(w[-(iloc+1):, :].T, seriesF.loc[:loc])[0, 0]
        df[name]=df_.copy(deep=True)
    df = pd.concat(df, axis=1)
    return df

# SNIPPET5.3: page 83
def getWeights_FFD(d, thres):
    w, k = [1.], 1
    while True:
        w_ = -w[-1] / k*(d-k+1)
        if abs(w_) < thres: break
        w.append(w_); k+=1
    return np.array(w[::-1]).reshape(-1, 1)

def fracDiff_FFD(series, d, thres=1e-4):
    # Constant width window (new solution)
    series = series[~series.index.duplicated(keep='first')]
    w = getWeights_FFD(d, thres)
    width = len(w)-1
    df = {}
    for i, name in enumerate(series.columns):
        seriesF, df_ = series[[name]].fillna(method='ffill').dropna(), pd.Series(dtype='float64')
        for iloc1 in range(width, seriesF.shape[0]):
            loc0, loc1 = seriesF.index[iloc1-width], seriesF.index[iloc1]
            if not np.isfinite(series.iloc[iloc1, i]): continue # exclude NAs
            try:
                df_[loc1] = np.dot(w.T, seriesF.loc[loc0:loc1])[0, 0]
            except:
                df_[loc1] = np.dot(w.T, seriesF.loc[loc0:loc1][1:])[0, 0]

        df[name] = df_.copy(deep=True)
    df = pd.concat(df, axis=1)
    return df

def plot_frac_actual_price(actual_price: pd.DataFrame(), frac_price: pd.DataFrame(), plot_size: int, column: str):
    """
    Plot actual price and fractional differentiated price.

    Args:
        actual_price:(pandas DataFrame) full dataset of actual price. bar data
        frac_price: (pandas DataFrame) full data of fractional differentiated price data
        plot_size: length of plot you wish to show. Max is length of frac_price
        column: (str) price you wish to see ('Open', 'High', 'Low', 'Close')
    """
    plt.rcParams.update({'figure.figsize': (10, 6), 'figure.dpi': 120})
    column = column
    plot_size = plot_size
    d1 = actual_price[column].values
    d2 = frac_price[column].values

    fig, ax1 = plt.subplots()
    color = 'tab:red'
    ax1.set_xlabel('Time (Year)')
    ax1.set_ylabel('Price', color=color)
    ax1.plot(range(len(d1)-plot_size, len(d1)), d1[-plot_size:], color=color)
    ax1.tick_params(axis='y', labelcolor=color)

    ax2 = ax1.twinx()
    color = 'tab:blue'
    ax2.set_ylabel('Frac Diff', color=color)
    ax2.plot(range(len(d1)-plot_size, len(d1)), d2[-plot_size:], color=color)
    ax2.tick_params(axis='y', labelcolor=color)
    plt.tight_layout()
    plt.show()

# SNIPPET 5.4: page 85
def plotMinFrac(data):
    out = pd.DataFrame(columns=["adfstat", 'P-val', 'Lags', 'n0bs', '95% conf', 'corr'])
    df0 = data
    for d in np.linspace(0.1, 0.5, 41):
        df1 = df0[["Close"]].resample('1D').last() # downcast to daily obs
        df2 = fracDiff(df1, d, thres=.01)
        corr = np.corrcoef(df1.loc[df2.index, 'Close'], df2["Close"])[0, 1]
        df2 = adfuller(df2["Close"], maxlag=1, regression='c', autolag=None)
        out.loc[d] = list(df2[:4]) + [df2[4]['5%']] + [corr] # with critical value
    out[["adfstat", "corr"]].plot(secondary_y='adfstat')
    plt.axhline(out['95% conf'].mean(), linewidth=1, color='r', linestyle='dotted')
    plt.show()
    print(out)
    return

def plotMinFracFFD(data, thres):
    out = pd.DataFrame(columns=["adfstat", 'P-val', 'Lags', 'n0bs', '95% conf', 'corr'])
    df0 = data
    for d in np.linspace(0, 1, 11):
        df1 = df0[["Close"]].resample('1D').last() # downcast to daily obs
        df2 = fracDiff_FFD(df1, d, thres=thres)
        #print(f"Weight vector size (d={d}): {weight.shape}")
        corr = np.corrcoef(df1.loc[df2.index, 'Close'], df2["Close"])[0, 1]
        df2 = adfuller(df2["Close"], maxlag=1, regression='c', autolag=None)
        out.loc[d] = list(df2[:4]) + [df2[4]['5%']] + [corr] # with critical value
    out[["adfstat", "corr"]].plot(secondary_y='adfstat')
    plt.axhline(out['95% conf'].mean(), linewidth=1, color='r', linestyle='dotted')
    plt.show()
    print(out)
    return


import numpy as np
from stockfish import Stockfish

import numpy as np
from stockfish import Stockfish

def board_pos_to_chess(row, col):
    return chr(ord('a') + row) + str(col + 1)

class controller():
    def __init__(self, path):
        self.stockfish = Stockfish(path=path)

        self.current_board = np.zeros(shape=(8, 8))
        self.current_board[:, 0:2] = 1
        self.current_board[:, -2:] = 2

        print(self.current_board)

    def update_board(self, board):
        # returns -1 if error
        # returns 0 if no move was made
        # returns 1 if move was made
        differences = []

        for i in range(board.shape[0]):
            for j in range(board.shape[1]):
                if board[i, j] != self.current_board[i, j]:
                    differences.append((i, j))

        if len(differences) > 3:
            return -1, "Too many moved pieces"

        elif len(differences) == 0:
            return 0, "No Change"

        elif len(differences) < 2:
            return -1, "piece missing"

        elif len(differences) == 2: # one piece is moved
            # must be 1 new empty square

            if board[differences[0]] != 0 and board[differences[1]] != 0:
                return -1, "invalid or too many moves"

            # swap differences if first one is not empty (source square)
            if board[differences[0]] != 0:
                differences[0], differences[1] = differences[1], differences[0]

            move = board_pos_to_chess(*differences[0]) + board_pos_to_chess(*differences[1])

            print(move)

            if self.stockfish.is_move_correct(move):
                self.stockfish.make_moves_from_current_position([move])
                self.current_board = np.copy(board)

                return 1, move

            else:
                return -1, "invalid move from current position"

        elif len(differences) == 3: # check if en pessant
            # should now be 2 new empty squares
            # the source will be the color with a piece remaining

            empty_count = 0
            remaining = 0

            for i in range(len(differences)):
                if board[differences[i]] == 0:
                    empty_count += 1
                else:
                    # will only run once in correct cases
                    remaining = i

            if empty_count != 2:
                return -1, "invalid or too many moves"

            source = None
            target = differences[remaining]

            for i in range(len(differences)):
                # found correct source
                if self.current_board[differences[i]] == board[differences[remaining]]:
                        source = differences[i]

            if target is None:
                return -1, "invalid or too many moves"

            move = board_pos_to_chess(*source) + board_pos_to_chess(*target)

            print(move)

            if self.stockfish.is_move_correct(move):
                self.stockfish.make_moves_from_current_position([move])
                self.current_board = np.copy(board)

                return 1, move

            else:
                return -1, "invalid move from current position"

    def update_board(board):
        pass

controller()