#encoding:utf-8
from django.utils import simplejson
from dajaxice.decorators import dajaxice_register
from dajax.core import Dajax
from Siniestro.models import AvisoSiniestro, BienAfectadoAviosoSiniestro,\
    Inspeccion, ActaSiniestro
from Constancias.models import Constancia
from django.db import connections
import datetime
from Solicitud.models import Solicitud, DescripcionBienActaVerificacion,\
    RelacionAnexaActaVerificacion
from smtpd import program
from Programas.models import Programa, CoberturaPrograma, Cobertura
from Cotizador.models import Cotizador, CotizadorCobertura
from ConexosAgropecuarios.models import Persona, Telefono, PersonalApoyo
from django.db.models import Q
from Direcciones.models import Direccion
from dajaxice.utils import deserialize_form
from Endoso.models import DescripcionBienEndosoAD

@dajaxice_register
def CargarActasSiniestro(request): #Metodo que obtiene las actas de siniestro para su dictamen

    cursor = connections['siobicx'].cursor()
    sql_string = "SELECT * FROM vdictamensiniestro"
    cursor.execute(sql_string)
    actasSiniestro = cursor.fetchall()

    return simplejson.dumps({'ActasSiniestro':actasSiniestro})

@dajaxice_register
def obtener_avisos_siniestro(request):# Método que obtiene los avisos de siniestros de la base de datos.
    dajax = Dajax()
    try:
        cursor = connections['siobicx'].cursor()
        sql_string = "SELECT * FROM vlistadoavisossiniestro"
        cursor.execute(sql_string)
        result = cursor.fetchall()

    except:
        return dajax.json()

    return simplejson.dumps({'avisos':result})

@dajaxice_register
def buscar_constancia(request, buscar): # Funcion que obtiene la busqueda de las constancias vigentes para realizar un aviso de siniestro.

    constancias = Constancia.objects.filter(Q(FolioConstancia__contains = buscar), VigenciaFin__gte = datetime.datetime.now())
    datosConstancia = list()
    causaSiniestro = list()

    if constancias:
        for constancia in constancias:

            solicitud = Solicitud.objects.filter(IdSolicitud = constancia.Solicitud_id)[0]
            programa = Programa.objects.filter(IdPrograma = solicitud.Programa_id)[0]
            persona = Persona.objects.filter(IdPersona = solicitud.PersonaAsegurada_id)[0]

            if persona.TipoPersona == 'F':
                nombreAsegurado = persona.PrimerNombre + ' ' + persona.SegundoNombre + ' ' + persona.ApellidoPaterno + ' ' + persona.ApellidoMaterno
            else:
                nombreAsegurado = persona.RazonSocial

            vigenciaConstancia = constancia.VigenciaInicio.strftime("%d/%m/%Y") + " al " + constancia.VigenciaFin.strftime("%d/%m/%Y")
            datosConstancia.append({'IdConstancia':constancia.IdConstancia, 'Constancia':constancia.FolioConstancia,'Ejercicio':programa.Ejercicio,'Asegurado':nombreAsegurado,
                                    'Vigencia':vigenciaConstancia, 'CausaSiniestro':causaSiniestro, 'IdPrograma':programa.IdPrograma})

    return simplejson.dumps({'constancias':datosConstancia})

@dajaxice_register
def buscar_causa_siniestro_programa(req, idPrograma): # Metodo que nos permite buscar la causa del siniestro, mediante los riesgos del programa.
    causaSiniestro = list()

    cotizadorEncontrado = Cotizador.objects.filter(Programa_id = idPrograma)

    if cotizadorEncontrado:
        cotizadorCoberturaEncontradas = CotizadorCobertura.objects.filter(Cotizador_id = cotizadorEncontrado[0].IdCotizador)

        if cotizadorCoberturaEncontradas:
            for cotizadorCobertura in cotizadorCoberturaEncontradas:
                coberturaProgramaEncontrada = CoberturaPrograma.objects.filter(IdCoberturaPrograma = cotizadorCobertura.CoberturaPrograma_id)

                if coberturaProgramaEncontrada:
                    for cotizadorPrograma in coberturaProgramaEncontrada:
                        coberturaEncontrada = Cobertura.objects.using('catalogos').filter(IdCobertura = cotizadorPrograma.IdCobertura)

                        if coberturaEncontrada:

                            causaSiniestro.append({'IdCotizadorCobertura':cotizadorCobertura.IdCotizadorCobertura, 'Descripcion':coberturaEncontrada[0].Descripcion, 'Deducible':str(cotizadorCobertura.Deducible),
                                                   'ParticipacionAPerdida':str(cotizadorCobertura.ParticipacionAPerdida)})

    return simplejson.dumps({'causaSiniestro':causaSiniestro})

@dajaxice_register
def buscar_persona_avisa(req, datosBuscar): # Metodo que nos permite buscar en la tabla personas, filtrando mediante la variable datosBuscar.
    dajax = Dajax()
    try:
        datos = list()
        personas = Persona.objects.filter((Q(Rfc__contains=datosBuscar) | Q(PrimerNombre__contains=datosBuscar) | Q(SegundoNombre__contains=datosBuscar) | Q(ApellidoPaterno__contains=datosBuscar) | Q(ApellidoMaterno__contains=datosBuscar)), Q(TipoPersona='F'))

        for persona in personas:
            nombre = persona.PrimerNombre + " " + persona.SegundoNombre + " " + persona.ApellidoPaterno + " " + persona.ApellidoMaterno
            datos.append({'IdPersona':persona.IdPersona,'Nombre':nombre,'Rfc':persona.Rfc})

    except:
        return dajax.json()

    return simplejson.dumps({'personas':datos})

@dajaxice_register
def buscar_persona_tecnico(req, datosBuscar): # Metodo que nos permite obtener los tecnicos del fondo.
    dajax = Dajax()

    try:
        cursor = connections['siobicx'].cursor()
        sql_string = 'SELECT * FROM vpersonalapoyo WHERE Nombre Like ' + "'%" + str(datosBuscar) + "%'"
        cursor.execute(sql_string)
        tecnicos = cursor.fetchall()

    except:
        return dajax.json()

    return simplejson.dumps({'tecnicos':tecnicos})

@dajaxice_register
def guardar_aviso_siniestro(request, formulario, horaAviso, horaSiniestro, bienes, tieneAumento, causaSiniestro, idCotizadorCobertura, deducible, participacionAPerdida):# Metodo que nos permite guardar el aviso de siniestro obteniendo la informacion del formulario obtenida del js.
    dajax = Dajax()

    if bienes == '':
        list_bienes = ''
    else:
        array_convertido_string = simplejson.dumps(bienes, separators=('|',','))
        list_bienes = array_convertido_string.replace('[', '').replace(']', '').replace('"', '').split(';')

    formulario_deserializado = deserialize_form(formulario)

    if formulario_deserializado.get('varIdAvisoSiniestro') == '':

        fechaAviso = formulario_deserializado.get('dtpFechaAviso');
        fechaSiniestro = formulario_deserializado.get('dtpFechaSiniestro');

        if formulario_deserializado.get('varIdPersonaAvisa') == '':
            idPersonaAvisa = None
        else:
            idPersonaAvisa = formulario_deserializado.get('varIdPersonaAvisa')

        if formulario_deserializado.get('txtOtros') == '':
            viaAviso = formulario_deserializado.get('cmbViaAviso')
            otravia = False
        else:
            viaAviso = formulario_deserializado.get('txtOtros')
            otravia = True

        avisoSiniestro = AvisoSiniestro(Constancia_id = formulario_deserializado.get('varIdConstancia'),
                                        FechaAviso = datetime.datetime.strptime(fechaAviso,'%d/%m/%Y').strftime('%Y-%m-%d'),
                                        FechaSiniestro = datetime.datetime.strptime(fechaSiniestro,'%d/%m/%Y').strftime('%Y-%m-%d'),
                                        PersonaAvisa_id = idPersonaAvisa, NombreAvisa = formulario_deserializado.get('txtQuienAvisa').upper(),
                                        ViaAviso = viaAviso.upper(),
                                        DescripcionBienAfectado = formulario_deserializado.get('txtBien').upper(), PersonaTecnico_id = formulario_deserializado.get('varIdTecnico'),
                                        HoraAviso = str(horaAviso), HoraSiniestro = str(horaSiniestro), OtraVia = otravia, TipoAviso = formulario_deserializado.get('cmbTipoAviso'),
                                        CausaSiniestro = str(causaSiniestro.upper()), IdStatusAvisoSiniestro = int(formulario_deserializado.get('varStatusAviso')),
                                        CausaAgravante = str(formulario_deserializado.get('txtCausaAgravante').upper()), CotizadorCobertura_id = idCotizadorCobertura, Deducible = deducible,
                                        ParticipacionAPerdida = participacionAPerdida)
        avisoSiniestro.save();

        folioAviso = 'AVS-' + str(avisoSiniestro.IdAvisoSiniestro)

        AvisoSiniestro.objects.filter(IdAvisoSiniestro = avisoSiniestro.IdAvisoSiniestro).update(FolioAviso = folioAviso)

        if list_bienes != '':
            for bien in list_bienes:
                guardar_bienes_afectados(avisoSiniestro.IdAvisoSiniestro, bien, tieneAumento)

        dajax.add_data(avisoSiniestro.IdAvisoSiniestro, 'mensajeGuardadoSiniestro')

    else:
        actualizar_aviso(formulario_deserializado, horaAviso, horaSiniestro, list_bienes, tieneAumento, causaSiniestro, idCotizadorCobertura, deducible, participacionAPerdida)

    return dajax.json()

def guardar_bienes_afectados(id_aviso, bien, tieneAumento): # Metodo que nos permite guardar el bien afectado en la tabla de la base de datos mediante el id del aviso de siniestro.
    dajax = Dajax()

    list_bien = bien.split(',')
    bienAfectado = BienAfectadoAviosoSiniestro(AvisoSiniestro_id = id_aviso, IdBienConstancia = list_bien[1], NumeroBienAfectado = list_bien[2], EstadoDelDano = (list_bien[3]).upper(), Descripcion = (list_bien[4]).upper(), TieneAumento = bool(tieneAumento))
    bienAfectado.save()

    return dajax.json()

def actualizar_aviso(formulario, horaAviso, horaSiniestro, bienesAfectados, tieneAumento, causaSiniestro, idCotizadorCobertura, deducible, participacionAPerdida):# Metodo que nos permite actualizar los datos del aviso de siniestro pasando el formulario y las horas de siniestro y aviso.
    dajax = Dajax()

    fechaAviso = formulario.get('dtpFechaAviso');
    fechaSiniestro = formulario.get('dtpFechaSiniestro');

    if formulario.get('varIdPersonaAvisa') == '' or formulario.get('varIdPersonaAvisa') == 'None':
        idPersonaAvisa = None
    else:
        idPersonaAvisa = formulario.get('varIdPersonaAvisa')

    if formulario.get('txtOtros') == '':
        viaAviso = formulario.get('cmbViaAviso')
        otravia = False
    else:
        viaAviso = formulario.get('txtOtros')
        otravia = True

    AvisoSiniestro.objects.filter(IdAvisoSiniestro = formulario.get('varIdAvisoSiniestro')).update(Constancia = int(formulario.get('varIdConstancia')),
                                        FechaAviso = datetime.datetime.strptime(fechaAviso,'%d/%m/%Y').strftime('%Y-%m-%d'),
                                        FechaSiniestro = datetime.datetime.strptime(fechaSiniestro,'%d/%m/%Y').strftime('%Y-%m-%d'),
                                        PersonaAvisa = idPersonaAvisa, NombreAvisa = formulario.get('txtQuienAvisa').upper(),
                                        ViaAviso = viaAviso.upper(), DescripcionBienAfectado = formulario.get('txtBien').upper(),
                                        PersonaTecnico = formulario.get('varIdTecnico'), HoraAviso = str(horaAviso),
                                        HoraSiniestro = str(horaSiniestro), OtraVia = otravia, TipoAviso = formulario.get('cmbTipoAviso'),
                                        CausaSiniestro = str(causaSiniestro.upper()), IdStatusAvisoSiniestro = int(formulario.get('varStatusAviso')),
                                        CausaAgravante = str(formulario.get('txtCausaAgravante').upper()), CotizadorCobertura = idCotizadorCobertura, Deducible = deducible,
                                        ParticipacionAPerdida = participacionAPerdida)

    actualizar_bienes_afectados(bienesAfectados, formulario.get('varIdAvisoSiniestro'), tieneAumento)

    return dajax.json()

def actualizar_bienes_afectados(bienesAfectados, id_aviso, tieneAumento): # Metodo que nos permite actualizar los bienes afectado en la base de datos.
    dajax = Dajax()

    bienesAfectadosGuardado = BienAfectadoAviosoSiniestro.objects.filter(AvisoSiniestro_id = id_aviso)

    for bienPorDiaEliminar in bienesAfectadosGuardado:
        encontradoAEliminar = 1
        for bienABuscar in bienesAfectados:
            list_bien = bienABuscar.split(',')
            if list_bien[0] != '':
                if int(list_bien[0]) == bienPorDiaEliminar.IdBienAfectado:
                    encontradoAEliminar = 0
                    break

        if encontradoAEliminar == 1:
            eliminar_bien_afectado(bienPorDiaEliminar.IdBienAfectado)

    for bien in bienesAfectados:
        list_bien = bien.split(',')
        if list_bien[0] == '':
            guardar_bienes_afectados(id_aviso, bien, tieneAumento)
        else:
            actualizar_bien_afectado(list_bien)

    return dajax.json()

def actualizar_bien_afectado(bien): # Método que nos permite actualizar la informacion de un bien afectado.
    BienAfectadoAviosoSiniestro.objects.filter(IdBienAfectado = bien[0]).update(NumeroBienAfectado = (bien[2]).upper(), EstadoDelDano = (bien[3]).upper(), Descripcion = (bien[4]).upper())

def eliminar_bien_afectado(idBienAfectado): # Método que recibe el id del bien afectado para eliminarlo de la base de datos.
    dajax = Dajax()
    BienAfectadoAviosoSiniestro.objects.filter(IdBienAfectado = idBienAfectado).delete()

    return dajax.json()

@dajaxice_register
def obtener_descripcion_bienes_constancia(req, id_constancia): # Método que obtiene los bienes de la constancia seleccionada, si la constancia ya tiene un aumento o disminucion los toma de la tabla DescripcionBienEndosoAD.
    dajax = Dajax()
    datos = list()
    tieneAumento = False

    try:
        descripcionBienAD = DescripcionBienEndosoAD.objects.filter(Constancia_id = id_constancia, Utilizado = 1)

        if descripcionBienAD:
            tieneAumento = True

            for descripcion in descripcionBienAD:
                sumaBien = descripcion.Cantidad * descripcion.ValorUnitario
                datos.append({'IdDescripcion':descripcion.IdDescripcionBienEndosoAD,'NombreEquipo':descripcion.NombreEquipo,'Cantidad':str(descripcion.Cantidad),'ValorUnitario':str(descripcion.ValorUnitario),'SumaBien':str(sumaBien)})

        else:
            constancia = Constancia.objects.filter(IdConstancia = id_constancia)[0]
            relacionAnexaActaVerificacion = RelacionAnexaActaVerificacion.objects.filter(Solicitud_id = constancia.Solicitud_id)[0]
            descripcionAnexaActaVerificacion = DescripcionBienActaVerificacion.objects.filter(RelacionAnexaActaVerificacion_id = relacionAnexaActaVerificacion.IdRelacionAnexaActaVerificacion)

            for descripcion in descripcionAnexaActaVerificacion:
                sumaBien = descripcion.Cantidad * descripcion.ValorUnitario
                datos.append({'IdDescripcion':descripcion.IdDescripcionBienActaVerificacion,'NombreEquipo':descripcion.NombreEquipo,'Cantidad':str(descripcion.Cantidad),'ValorUnitario':str(descripcion.ValorUnitario),'SumaBien':str(sumaBien)})
    except:
        return dajax.json()

    return simplejson.dumps({'descripcionBienes':datos, 'tieneAumento':tieneAumento})

@dajaxice_register
def obtener_bienes_afectados(req, id_aviso, id_constancia): # Método que obtiene los bienes afectados mediante el id del aviso de siniestro.
    dajax = Dajax()
    datos = list()

    try:

        descripcionesBienAD = DescripcionBienEndosoAD.objects.filter(Constancia_id = id_constancia)

        if descripcionesBienAD:
            for descripcionBienAD in descripcionesBienAD:

                bienesAfectados = BienAfectadoAviosoSiniestro.objects.filter(AvisoSiniestro_id = id_aviso)

                for bienAfectado in bienesAfectados:
                    if descripcionBienAD.IdDescripcionBienEndosoAD == bienAfectado.IdBienConstancia:
                        idBienAfectado = bienAfectado.IdBienAfectado
                        noBienesAfectados = bienAfectado.NumeroBienAfectado
                        estadoDanos = bienAfectado.EstadoDelDano
                        descripcion = bienAfectado.Descripcion

                sumaBien = descripcionBienAD.Cantidad * descripcionBienAD.ValorUnitario
                datos.append({'IdBienAfectado':idBienAfectado, 'IdDescripcion':descripcionBienAD.IdDescripcionBienEndosoAD, 'Nombre':descripcionBienAD.NombreEquipo, 'Cantidad':str('{:,}'.format(descripcionBienAD.Cantidad)),
                                        'ValorUnitario':str('{:,}'.format(descripcionBienAD.ValorUnitario)), 'SumaAsegurada':str('{:,}'.format(sumaBien)), 'NoBienesAfectados':noBienesAfectados, 'EstadoDanos':estadoDanos,
                                        'Descripcion':descripcion})
                idBienAfectado = ''
                noBienesAfectados = ''
                estadoDanos = ''
                descripcion = ''
        else:

            constancia = Constancia.objects.filter(IdConstancia = id_constancia)[0]
            relacionAnexaActaVerificacion = RelacionAnexaActaVerificacion.objects.filter(Solicitud_id = constancia.Solicitud_id)[0]
            descripcionBienActaVerificacion = DescripcionBienActaVerificacion.objects.filter(RelacionAnexaActaVerificacion_id = relacionAnexaActaVerificacion.IdRelacionAnexaActaVerificacion)

            for descripcionBien in descripcionBienActaVerificacion:

                bienesAfectados = BienAfectadoAviosoSiniestro.objects.filter(AvisoSiniestro_id = id_aviso)

                for bienAfectado in bienesAfectados:
                    if descripcionBien.IdDescripcionBienActaVerificacion == bienAfectado.IdBienConstancia:
                        idBienAfectado = bienAfectado.IdBienAfectado
                        noBienesAfectados = bienAfectado.NumeroBienAfectado
                        estadoDanos = bienAfectado.EstadoDelDano
                        descripcion = bienAfectado.Descripcion

                sumaBien = descripcionBien.Cantidad * descripcionBien.ValorUnitario
                datos.append({'IdBienAfectado':idBienAfectado, 'IdDescripcion':descripcionBien.IdDescripcionBienActaVerificacion, 'Nombre':descripcionBien.NombreEquipo, 'Cantidad':str(descripcionBien.Cantidad),
                                    'ValorUnitario':str(descripcionBien.ValorUnitario), 'SumaAsegurada':str(sumaBien), 'NoBienesAfectados':str(noBienesAfectados), 'EstadoDanos':estadoDanos,
                                    'Descripcion':descripcion})
                idBienAfectado = ''
                noBienesAfectados = ''
                estadoDanos = ''
                descripcion = ''
    except:
        return dajax.json()

    return simplejson.dumps({'bienesAfectados':datos})

@dajaxice_register
def guardar_estado_aviso(request, idAviso, idStatus):# Metodo que nos permite actualizar el status del aviso de siniestro.
    dajax = Dajax()

    AvisoSiniestro.objects.filter(IdAvisoSiniestro = idAviso).update(IdStatusAvisoSiniestro = idStatus)

    return dajax.json()

@dajaxice_register
def obtener_avisos_siniestro_terminados(req, buscar): # Metodo que obtiene los avisos de siniestro terminados, para mostrarlos en el modal de busqueda y poder seleccionar uno en la platilla inspeccion.html
    dajax = Dajax()
    try:

        cursor = connections['siobicx'].cursor()
        sql_string = "SELECT * FROM vavisosiniestrocompletado WHERE FolioAviso like '%" + buscar + "%'"
        cursor.execute(sql_string)
        result = cursor.fetchall()

    except:
        return dajax.json()

    return simplejson.dumps({'avisos':result})

@dajaxice_register
def obtener_bienes_seleccionados_afectados(req, id_aviso, id_constancia): # Método que obtiene solo los bienes afectados para mostrarlos en reportes.
    dajax = Dajax()
    datos = list()

    try:

        descripcionesBienAD = DescripcionBienEndosoAD.objects.filter(Constancia_id = id_constancia)

        if descripcionesBienAD:
            for descripcionBienAD in descripcionesBienAD:

                bienesAfectados = BienAfectadoAviosoSiniestro.objects.filter(AvisoSiniestro_id = id_aviso)

                for bienAfectado in bienesAfectados:
                    if descripcionBienAD.IdDescripcionBienEndosoAD == bienAfectado.IdBienConstancia:
                        idBienAfectado = bienAfectado.IdBienAfectado
                        noBienesAfectados = bienAfectado.NumeroBienAfectado
                        estadoDanos = bienAfectado.EstadoDelDano
                        descripcion = bienAfectado.Descripcion

                        sumaBien = descripcionBienAD.Cantidad * descripcionBienAD.ValorUnitario
                        datos.append({'IdBienAfectado':idBienAfectado, 'IdDescripcion':descripcionBienAD.IdDescripcionBienEndosoAD, 'Nombre':descripcionBienAD.NombreEquipo, 'Cantidad':'{:,}'.format(descripcionBienAD.Cantidad),
                                                'ValorUnitario':'{:,}'.format(descripcionBienAD.ValorUnitario), 'SumaAsegurada':'{:,}'.format(sumaBien), 'NoBienesAfectados':noBienesAfectados, 'EstadoDanos':estadoDanos,
                                                'Descripcion':descripcion})
                idBienAfectado = ''
                noBienesAfectados = ''
                estadoDanos = ''
                descripcion = ''
        else:

            constancia = Constancia.objects.filter(IdConstancia = id_constancia)[0]
            relacionAnexaActaVerificacion = RelacionAnexaActaVerificacion.objects.filter(Solicitud_id = constancia.Solicitud_id)[0]
            descripcionBienActaVerificacion = DescripcionBienActaVerificacion.objects.filter(RelacionAnexaActaVerificacion_id = relacionAnexaActaVerificacion.IdRelacionAnexaActaVerificacion)

            for descripcionBien in descripcionBienActaVerificacion:

                bienesAfectados = BienAfectadoAviosoSiniestro.objects.filter(AvisoSiniestro_id = id_aviso)

                for bienAfectado in bienesAfectados:
                    if descripcionBien.IdDescripcionBienActaVerificacion == bienAfectado.IdBienConstancia:
                        idBienAfectado = bienAfectado.IdBienAfectado
                        noBienesAfectados = bienAfectado.NumeroBienAfectado
                        estadoDanos = bienAfectado.EstadoDelDano
                        descripcion = bienAfectado.Descripcion

                        sumaBien = descripcionBien.Cantidad * descripcionBien.ValorUnitario
                        datos.append({'IdBienAfectado':bienAfectado.IdBienAfectado, 'IdDescripcion':descripcionBien.IdDescripcionBienActaVerificacion, 'Nombre':descripcionBien.NombreEquipo, 'Cantidad':descripcionBien.Cantidad,
                                            'ValorUnitario':str(descripcionBien.ValorUnitario), 'SumaAsegurada':str(sumaBien), 'NoBienesAfectados':bienAfectado.NumeroBienAfectado, 'EstadoDanos':bienAfectado.EstadoDelDano,
                                            'Descripcion':bienAfectado.Descripcion})
                idBienAfectado = ''
                noBienesAfectados = ''
                estadoDanos = ''
                descripcion = ''

    except:
        return dajax.json()

    return simplejson.dumps({'bienesAfectados':datos})

@dajaxice_register
def obtener_tecnicos(req, buscar): # Metodo que nos permite llamar a la vista vpersonalapoyo para encontrar los tecnicos de seguros para asignarles inspecciones.
    dajax = Dajax()
    try:

        cursor = connections['siobicx'].cursor()
        sql_string = "SELECT Rfc, Nombre, IdPersonalApoyo FROM vpersonalapoyo WHERE Nombre like '%" + buscar + "%' And Cargo='TECNICO DE CAMPO'"
        cursor.execute(sql_string)
        result = cursor.fetchall()

    except:
        return dajax.json()

    return simplejson.dumps({'tecnicos':result})

@dajaxice_register
def guardar_inpeccion(request, formulario):# Metodo que nos permite guardar la solicitud de inspección obteniendo la informacion del formulario obtenida del js.
    dajax = Dajax()

    formulario_deserializado = deserialize_form(formulario)

    if formulario_deserializado.get('varIdInspeccion') == '':
        inspeccion = Inspeccion(AvisoSiniestro_id = int(formulario_deserializado.get('varIdAvisoSiniestro')),
                                        Constancia_id = int(formulario_deserializado.get('varIdConstancia')),
                                        PersonalApoyo_id = int(formulario_deserializado.get('varIdPersonalApoyo')),
                                        FechaInspeccion = datetime.datetime.now().strftime("%Y-%m-%d"))

        inspeccion.save();

        folioInspeccion = 'IS-' + str(inspeccion.IdInspeccion)

        Inspeccion.objects.filter(IdInspeccion = inspeccion.IdInspeccion).update(FolioInspeccion = folioInspeccion)

        dajax.add_data(inspeccion.IdInspeccion, 'mensajeGuardadoInspeccion')

    else:
        actualizar_inspeccion(formulario_deserializado)

    return dajax.json()

def actualizar_inspeccion(formulario): # Método que permite actualizar los datos de la inspección en la base de datos.
    dajax = Dajax()

    Inspeccion.objects.filter(IdInspeccion = formulario.get('varIdInspeccion')).update(PersonalApoyo_id = formulario.get('varIdPersonalApoyo'))

    return dajax.json()

@dajaxice_register
def obtener_listado_inspecciones(req): # Método que obtiene las inspecciones mediante la vista vlistadoinspecciones que se encuentran guardadas en la base de datos.
    dajax = Dajax()
    try:

        cursor = connections['siobicx'].cursor()
        sql_string = "SELECT * FROM vlistadoinspecciones"
        cursor.execute(sql_string)
        result = cursor.fetchall()

    except:
        return dajax.json()

    return simplejson.dumps({'inspecciones':result})

@dajaxice_register
def guardar_acta_siniestro(request, formulario):# Metodo que nos permite guardar la informacion obtenida del acta de siniestro.
    dajax = Dajax()

    formulario_deserializado = deserialize_form(formulario)

    #if formulario_deserializado.get('varI') == '':
    actaSiniestro = ActaSiniestro(Inspeccion_id = int(formulario_deserializado.get('varIdInspeccion')),
                                    TipoAviso = formulario_deserializado.get('txtTipoAviso'),
                                    FechaSiniestro = datetime.datetime.strptime(formulario_deserializado.get('txtFechaSiniestro'),'%d/%m/%Y').strftime('%Y-%m-%d'),
                                    MontoDano = formulario_deserializado.get('txtMontoDano'))

    actaSiniestro.save();

    folioActaSiniestro = 'ACS-' + str(actaSiniestro.IdActaSiniestro)

    ActaSiniestro.objects.filter(IdActaSiniestro = actaSiniestro.IdActaSiniestro).update(FolioActaSiniestro = folioActaSiniestro)

    dajax.add_data(actaSiniestro.IdActaSiniestro, 'mensajeGuardadoActaSiniestro')

    #else:
    #    actualizar_inspeccion(formulario_deserializado)

    return dajax.json()


#m_str = list(input().strip().split(','))
#res = 0
# for i in range(len(m_str)):
#     if len(m_str[i]) > 1:
#         if m_str[i].isdigit():
#             sum = int(m_str[i][0]) + int(m_str[i][-1])
#             if sum > 8:
#                 res += 1
# print(res)
count = 0
for i in range(1,10001):
    if i % 3 == 0 and i % 7 ==0 and i % 11 == 0:
        count += 1
print(10000-count)


from __future__ import print_function

import sys
import shutil
import subprocess

import universalmutator.mutator as mutator

import universalmutator.python_handler as python_handler
# import universalmutator.python3_handler as python3_handler
import universalmutator.c_handler as c_handler
import universalmutator.cpp_handler as cpp_handler
import universalmutator.java_handler as java_handler
import universalmutator.swift_handler as swift_handler
import universalmutator.rust_handler as rust_handler
import universalmutator.go_handler as go_handler
import universalmutator.solidity_handler as solidity_handler

def nullHandler(tmpMutantName, mutant, sourceFile, uniqueMutants):
    return "VALID"

def cmdHandler(tmpMutantName, mutant, sourceFile, uniqueMutants):
    global cmd

    with open(".um.mutant_output", 'w') as file:
        r = subprocess.call([cmd.replace("MUTANT", tmpMutantName)],
                            shell=True, stderr=file, stdout=file)
    if r == 0:
        return "VALID"
    else:
        return "INVALID"

def main():
    global cmd

    try:
        import custom_handler
    except BaseException:
        pass

    args = sys.argv

    languages = {".c": "c",
                 ".cpp": "cpp",
                 ".c++": "cpp",
                 ".py": "python",
                 ".java": "java",
                 ".swift": "swift",
                 ".rs": "rust",
                 ".go": "go",
                 ".sol": "solidity"}

    print("*** UNIVERSALMUTATOR ***")

    if "--help" in args:
        print("USAGE: mutate <sourcefile> [<language>] [<rule1> <rule2>...]",
              "[--noCheck] [--cmd <command string>] [--mutantDir <dir>]",
              "[--lines <coverfile> [--tstl]]")
        print()
        print("       --noCheck: skips compilation/comparison and just generates mutant files")
        print("       --cmd executes command string, replacing MUTANT with the mutant name, and uses return code")
        print("             to determine mutant validity")
        print("       --mutantDir: directory to put generated mutants in; defaults to current directory")
        print("       --lines: only generate mutants for lines contained in <coverfile>")
        print("       --tstl: <coverfile> is TSTL output")
        print()
        print("Currently supported languages: ", ", ".join(list(set(languages.values()))))
        sys.exit(0)

    noCheck = False
    if "--noCheck" in args:
        noCheck = True
        args.remove("--noCheck")

    cmd = None
    try:
        cmdpos = args.index("--cmd")
    except ValueError:
        cmdpos = -1

    tstl = False
    if "--tstl" in args:
        tstl = True
        args.remove("--tstl")

    if cmdpos != -1:
        cmd = args[cmdpos + 1]
        args.remove("--cmd")
        args.remove(cmd)

    sourceFile = args[1]
    ending = "." + sourceFile.split(".")[-1]

    lineFile = None
    try:
        linepos = args.index("--lines")
    except ValueError:
        linepos = -1

    if linepos != -1:
        lineFile = args[linepos + 1]
        args.remove("--lines")
        args.remove(lineFile)

    if lineFile is not None:
        with open(lineFile) as file:
            if not tstl:
                lines = map(int, file.read().split())
            else:
                lines = []
                for l in file:
                    if "LINES" in l:
                        if sourceFile not in l:
                            continue
                        db = l.split("[")[1]
                        d = db[:-2].split(",")
                        for line in d:
                            lines.append(int(line))

    mdir = ""
    try:
        mdirpos = args.index("--mutantDir")
    except ValueError:
        mdirpos = -1

    if mdirpos != -1:
        mdir = args[mdirpos + 1]
        args.remove("--mutantDir")
        args.remove(mdir)
        mdir += "/"

    handlers = {"python": python_handler,
                "c": c_handler,
                "c++": cpp_handler,
                "cpp": cpp_handler,
                "java": java_handler,
                "swift": swift_handler,
                "rust": rust_handler,
                "go": go_handler,
                "solidity": solidity_handler}

    cLikeLanguages = [
        "c",
        "java",
        "swift",
        "cpp",
        "c++",
        "rust",
        "solidity",
        "go"]

    try:
        handlers["custom"] == custom_handler
    except BaseException:
        pass

    sourceFile = args[1]
    ending = "." + sourceFile.split(".")[-1]

    if len(args) < 3:
        language = languages[ending]
        otherRules = []
    else:
        if ".rules" in args[2]:
            language = languages[ending]
            otherRules = args[2:]
        else:
            language = args[2]
            otherRules = args[3:]

    if language not in handlers:
        if language.lower() in handlers:
            language = language.lower()

    base = (".".join((sourceFile.split(".")[:-1]))).split("/")[-1]

    if language in cLikeLanguages:
        otherRules.append("c_like.rules")

    rules = ["universal.rules", language + ".rules"] + otherRules

    source = []

    with open(sourceFile, 'r') as file:
        for l in file:
            source.append(l)

    mutants = mutator.mutants(source, rules=rules)

    print(len(mutants), "MUTANTS GENERATED BY RULES")

    validMutants = []
    invalidMutants = []
    redundantMutants = []
    uniqueMutants = {}

    if not noCheck:
        if cmd is not None:
            handler = cmdHandler
        elif language == "none":
            handler = nullHandler
        else:
            handler = handlers[language].handler
    else:
        handler = nullHandler

    mutantNo = 0
    for mutant in mutants:
        if (lineFile is not None) and mutant[0] not in lines:
            # skip if not a line to mutate
            continue
        tmpMutantName = "tmp_mutant" + ending
        print("PROCESSING MUTANT:",
              str(mutant[0]) + ":", source[mutant[0] - 1][:-1], " ==> ", mutant[1][:-1], end="...")
        mutator.makeMutant(source, mutant, tmpMutantName)
        mutantResult = handler(
            tmpMutantName,
            mutant,
            sourceFile,
            uniqueMutants)
        print(mutantResult, end=" ")
        mutantName = mdir + base + ".mutant." + str(mutantNo) + ending
        if mutantResult == "VALID":
            print("[written to", mutantName + "]", end=" ")
            shutil.copy(tmpMutantName, mutantName)
            validMutants.append(mutant)
            mutantNo += 1
        elif mutantResult == "INVALID":
            invalidMutants.append(mutant)
        elif mutantResult == "REDUNDANT":
            redundantMutants.append(mutant)
        print()

    print(len(validMutants), "VALID MUTANTS")
    print(len(invalidMutants), "INVALID MUTANTS")
    print(len(redundantMutants), "REDUNDANT MUTANTS")

if __name__ == '__main__':
    main()


import firebase_admin
from firebase_admin import auth, credentials
from firebase_admin.auth import AuthError
from rest_framework.response import Response
from rest_framework.status import HTTP_401_UNAUTHORIZED

from ..models import FirebaseUser
from api.models import Analytics
from config.const import ERROR_INVALID_TOKEN, ERROR_NO_TOKEN, \
    ERROR_REVOKED_TOKEN, status_error
from config.settings import DEBUG, PATH_TO_FIREBASE
from utils.functions import mass_mail

def verify_token_id(f):
    def wrapped_f(self, request, *args, **kwargs):
        token = ''
        for header, value in request.META.items():
            if header == 'HTTP_AUTHORIZATION':
                token = value.split()[-1]

        # Если токен не передан, то вернуть ошибку 0
        if not token:
            return Response(ERROR_NO_TOKEN, HTTP_401_UNAUTHORIZED)

        # Если токен тестового юзера, то возвращаем найденый/созданый тестовый токен
        if token == 'mitrofanov_test_user':
            self.decoded_token = {'uid': token}
            token, created = FirebaseUser.objects.get_or_create(token=token)
            return f(self, request, *args, **kwargs)

        # Проверка на истёкший, отозваный токен
        try:
            decoded_token = auth.verify_id_token(token, check_revoked=True)
        except ValueError:
            return Response(ERROR_INVALID_TOKEN, HTTP_401_UNAUTHORIZED)
        except AuthError:
            return Response(ERROR_REVOKED_TOKEN, HTTP_401_UNAUTHORIZED)

        updated_values = {}
        updated_values['email'] = decoded_token['email']
        updated_values['email_verified'] = decoded_token['email_verified']
        updated_values['name'] = decoded_token['name']

        token, created = FirebaseUser.objects.update_or_create(
            token=decoded_token['uid'],
            defaults=updated_values
        )

        if not DEBUG and created:
            analytics, created_a = Analytics.objects.get_or_create(id=1)
            analytics.new_users = analytics.new_users + 1
            analytics.save()
            if analytics.new_users in Analytics.achive_users:
                mass_mail(
                    'Поздравляю! Мы прошли ещё один этап.',
                    'Это было сложно, но мы справились. У нас стало больше пользователей.\n'
                    'Наше текущее количество пользователей: ' + str(analytics.new_users) + '\n'
                    'Наше текущее количество заметок:       ' + str(analytics.new_notes) + '\n'
                    'Это сообщение было сгенерировано автоматически, не отвечайте на него :)'
                )

        self.decoded_token = decoded_token
        return f(self, request, *args, **kwargs)
    return wrapped_f


"""Pipeline nodes for transforming data. Used across different pipelines."""

from typing import Sequence, List, Dict, Any, cast, Callable, Optional, Union
from functools import reduce, partial, update_wrapper
import re
from datetime import timezone

import pandas as pd
import numpy as np
from dateutil import parser

from augury.settings import INDEX_COLS
from .base import _validate_required_columns, _validate_no_dodgy_zeros

DATE_STRING_REGEX = re.compile(r"\d{4}\-\d{2}\-\d{2}")

def convert_to_data_frame(
    *data: List[Dict[str, Any]]
) -> Union[List[pd.DataFrame], pd.DataFrame]:
    """
    Convert JSON data in the form of a list of dictionaries into a data frame.

    Params
    ------
    data (sequence of list of dictionaries): Data received from a JSON data set.

    Returns
    -------
    Sequence of pandas.DataFrame
    """
    if len(data) == 0:
        return pd.DataFrame()

    data_frames = [pd.DataFrame(datum) for datum in data]

    # TODO: Quick fix for empty roster data not having a 'date' column.
    # We should figure out a better way to handle empty data such that
    # its shape/columns are consistent, but one doesn't occur to me at the moment.
    for df in data_frames:
        if "date" in df.columns:
            df.loc[:, "date"] = pd.to_datetime(df["date"])

    return data_frames if len(data_frames) > 1 else data_frames[0]

def _combine_data_horizontally(*data_frames: Sequence[pd.DataFrame]):
    # We need to sort by length (going from longest to shortest), then keeping first
    # duplicated column to make sure we don't lose earlier values of shared columns
    # (e.g. dropping match data's 'date' column in favor of the betting data 'date'
    # column results in lots of NaT values, because betting data only goes back to 2010)
    sorted_data_frames: List[pd.DataFrame] = sorted(data_frames, key=len, reverse=True)
    joined_data_frame = pd.concat(sorted_data_frames, axis=1, sort=False)
    duplicate_columns = joined_data_frame.columns.duplicated(keep="first")

    combined_data_frame = joined_data_frame.loc[:, ~duplicate_columns].copy()

    # Due to vagaries in data updates, sometimes the longest data set is missing
    # the most-recent round which results in blank rows for required columns
    # like data and round_number. So, we make sure to fill those values
    # with the data set that has the most-recent data just to be sure.
    max_date_data_frame = max(sorted_data_frames, key=lambda df: df["date"].max())

    for column in set(joined_data_frame.columns[duplicate_columns]):
        combined_data_frame.loc[:, [column]] = combined_data_frame[column].fillna(
            max_date_data_frame[column]
        )

    filled_data_frame = combined_data_frame.fillna(0)

    _validate_no_dodgy_zeros(filled_data_frame)

    return filled_data_frame

def _append_data_frames(
    acc_data_frame: pd.DataFrame, curr_data_frame: pd.DataFrame
) -> pd.DataFrame:
    # Assumes the following:
    #     - All data frames have a date column
    #     - Data frames are sorted by date in ascending order
    #       (the data frames themselves, not their rows)
    #     - Data frames have all data for a given date (i.e. all matches played
    #         on a date, not 1 of 3, which would result in missing data)
    max_accumulated_date = acc_data_frame[  # pylint: disable=unused-variable
        "date"
    ].max()
    sliced_current_data_frame = curr_data_frame.query("date > @max_accumulated_date")

    return pd.concat([acc_data_frame, sliced_current_data_frame], axis=0, sort=False)

def _combine_data_vertically(*data_frames: Sequence[pd.DataFrame]):
    if len(data_frames) == 1:
        return data_frames[0]

    valid_data_frames = [
        df for df in cast(Sequence[pd.DataFrame], data_frames) if df.size
    ]
    sorted_data_frames = sorted(valid_data_frames, key=lambda df: df["date"].min())
    combined_data_frame = reduce(_append_data_frames, sorted_data_frames).fillna(0)

    _validate_no_dodgy_zeros(combined_data_frame)

    return combined_data_frame

# TODO: Combining vertically vs horizontally are so different that it doesn't make
# sense for both to be contained within a single node. Make combine_data_vertically
# and combine_data_horizontally their own nodes.
def combine_data(axis: int = 0) -> pd.DataFrame:
    """Concatenate data frames from multiple sources into one data frame.

    Params
    ------
    data_frames (list of pandas.DataFrame): Data frames to be concatenated.
    axis (0 or 1, defaults to 0): Whether to concatenate by rows (0) or columns (1).

    Returns
    -------
    Concatenated data frame.
    """
    assert axis in [0, 1], f"Expected axis to be 0 or 1, but recieved {axis}"

    if axis == 0:
        return _combine_data_vertically

    return _combine_data_horizontally

def _filter_by_date(
    start_date: str, end_date: str, data_frame: pd.DataFrame
) -> pd.DataFrame:
    REQUIRED_COLS = {"date"}
    _validate_required_columns(REQUIRED_COLS, data_frame.columns)

    start_datetime = parser.parse(  # pylint: disable=unused-variable
        start_date
    ).replace(tzinfo=timezone.utc)
    end_datetime = parser.parse(end_date).replace(  # pylint: disable=unused-variable
        hour=11, minute=59, second=59, tzinfo=timezone.utc
    )

    return data_frame.query("date >= @start_datetime & date <= @end_datetime")

def filter_by_date(
    start_date: str, end_date: str
) -> Callable[[pd.DataFrame], pd.DataFrame]:
    """Filter data frames by the given start and end_dates.

    Params
    ------
    start_date (str, YYYY-MM-DD): Earliest date (inclusive) for match data.
    end_date (str, YYYY-MM-DD): Latest date (inclusive) for match data.

    Returns
    -------
    Callable function that filters data frames by the given dates.
    """
    if not DATE_STRING_REGEX.match(start_date) or not DATE_STRING_REGEX.match(end_date):
        raise ValueError(
            f"Expected date string parameters start_date ({start_date}) and "
            f"end_date ({end_date}) to be of the form YYYY-MM-DD."
        )

    return update_wrapper(
        partial(_filter_by_date, start_date, end_date), _filter_by_date
    )

def _replace_col_names(team_type: str, oppo_team_type: str) -> Callable[[str], str]:
    return lambda col_name: (
        col_name.replace(f"{team_type}_", "").replace(f"{oppo_team_type}_", "oppo_")
    )

def _team_data_frame(data_frame: pd.DataFrame, team_type: str) -> pd.DataFrame:
    is_home_team = team_type == "home"

    if is_home_team:
        oppo_team_type = "away"
        at_home_col = np.ones(len(data_frame))
    else:
        oppo_team_type = "home"
        at_home_col = np.zeros(len(data_frame))

    return (
        data_frame.rename(columns=_replace_col_names(team_type, oppo_team_type))
        .assign(at_home=at_home_col)
        .set_index(INDEX_COLS, drop=False)
        # Renaming index cols 'None' to avoid warnings about duplicate column names
        .rename_axis([None] * len(INDEX_COLS))
    )

def convert_match_rows_to_teammatch_rows(
    match_row_data_frame: pd.DataFrame,
) -> pd.DataFrame:
    """
    Reshape data frame from one match per row to one team-match combination per row.

    This results in a data frame with two rows per match, one each for the home
    and away team.

    Params
    ------
    match_row_data_frame (pandas.DataFrame): Data frame to be transformed.

    Returns
    -------
    pandas.DataFrame
    """
    REQUIRED_COLS: List[str] = [
        "home_team",
        "away_team",
        "year",
        "round_number",
        "date",
    ]

    _validate_required_columns(REQUIRED_COLS, match_row_data_frame.columns)

    team_data_frames = [
        _team_data_frame(match_row_data_frame, "home"),
        _team_data_frame(match_row_data_frame, "away"),
    ]

    return (
        pd.concat(team_data_frames, join="inner")
        .sort_values("date", ascending=True)
        # Various finals matches have been draws and replayed,
        # and sometimes home/away is switched requiring us to drop duplicates
        # at the end.
        # This eliminates some matches from Round 15 in 1897, because they
        # played some sort of round-robin tournament for finals, but I'm
        # not too worried about the loss of that data.
        .drop_duplicates(subset=INDEX_COLS, keep="last")
        .drop("match_id", axis=1, errors="ignore")
        .sort_index()
    )

def _validate_no_duplicated_columns(data_frame: pd.DataFrame) -> None:
    are_duplicate_columns = data_frame.columns.duplicated()

    assert not are_duplicate_columns.any(), (
        "The data frame with 'oppo' features added has duplicate columns."
        "The offending columns are: "
        f"{data_frame.columns[are_duplicate_columns]}"
    )

def _oppo_features(data_frame: pd.DataFrame, cols_to_convert) -> Optional[pd.DataFrame]:
    if not any(cols_to_convert):
        return None

    oppo_cols = {col_name: f"oppo_{col_name}" for col_name in cols_to_convert}
    oppo_col_names = oppo_cols.values()
    column_translations = {**{"oppo_team": "team"}, **oppo_cols}

    return (
        data_frame.reset_index(drop=True)
        .loc[:, ["year", "round_number", "oppo_team"] + list(cols_to_convert)]
        # We switch out oppo_team for team in the index,
        # then assign feature as oppo_{feature_column}
        .rename(columns=column_translations)
        .set_index(INDEX_COLS)
        .sort_index()
        .loc[:, list(oppo_col_names)]
    )

def _cols_to_convert_to_oppo(
    data_frame: pd.DataFrame,
    match_cols: List[str] = [],
    oppo_feature_cols: List[str] = [],
) -> List[str]:
    if any(oppo_feature_cols):
        return oppo_feature_cols

    return [col for col in data_frame.columns if col not in match_cols]

def _add_oppo_features_node(
    data_frame: pd.DataFrame,
    match_cols: List[str] = [],
    oppo_feature_cols: List[str] = [],
) -> pd.DataFrame:
    cols_to_convert = _cols_to_convert_to_oppo(
        data_frame, match_cols=match_cols, oppo_feature_cols=oppo_feature_cols
    )

    REQUIRED_COLS: List[str] = INDEX_COLS + ["oppo_team"] + cols_to_convert

    _validate_required_columns(REQUIRED_COLS, data_frame.columns)

    transform_data_frame = (
        data_frame.copy()
        .set_index(INDEX_COLS, drop=False)
        .rename_axis([None] * len(INDEX_COLS))
        .sort_index()
    )

    concated_data_frame = pd.concat(
        [transform_data_frame, _oppo_features(transform_data_frame, cols_to_convert)],
        axis=1,
    )

    _validate_no_duplicated_columns(concated_data_frame)

    return concated_data_frame

def add_oppo_features(
    match_cols: List[str] = [], oppo_feature_cols: List[str] = []
) -> Callable[[pd.DataFrame], pd.DataFrame]:
    """Calculate team-based features for opposition teams and append them to the data.

    The team columns to use for creating oppo columns are based on match_cols
    (non-team-specific columns to ignore) or oppo_feature_cols
    (team-specific features to add 'oppo' versions of). Including both column arguments
    will raise an error.

    Params
    ------
    match_cols (list of strings): Names of columns to ignore (calculates oppo
        features for all columns not listed).
    oppo_feature_cols (list of strings): Names of columns to add oppo features of
        (ignores all columns not listed)

    Returns
    -------
    Function that takes pandas.DataFrame and returns another pandas.DataFrame
        with 'oppo_' columns added.
    """
    if any(match_cols) and any(oppo_feature_cols):
        raise ValueError(
            "To avoid conflicts, you can't include both match_cols "
            "and oppo_feature_cols. Choose the shorter list to determine which "
            "columns to skip and which to turn into opposition features."
        )

    return update_wrapper(
        partial(
            _add_oppo_features_node,
            match_cols=match_cols,
            oppo_feature_cols=oppo_feature_cols,
        ),
        _add_oppo_features_node,
    )

def finalize_data(
    data_frame: pd.DataFrame, index_cols: List[str] = INDEX_COLS
) -> pd.DataFrame:
    """Perform final data cleaning after all other data transformation steps.

    Params
    ------
    data_frame (pandas.DataFrame): Data frame that has been cleaned & transformed.

    Returns
    -------
    pandas.DataFrame that's ready to be fed into a machine-learning model.
    """
    final_data_frame = (
        data_frame.astype({"year": int})
        .fillna(0)
        .set_index(index_cols, drop=False)
        .rename_axis([None] * len(index_cols))
        .sort_index()
    )

    _validate_no_dodgy_zeros(final_data_frame)

    return final_data_frame

def _sort_data_frame_columns_node(
    category_cols: List[str], data_frame: pd.DataFrame
) -> pd.DataFrame:
    if category_cols is None:
        numeric_data_frame = data_frame.select_dtypes(
            include=["number", "datetimetz", "datetime"]
        ).fillna(0)
        category_data_frame = data_frame.drop(numeric_data_frame.columns, axis=1)
    else:
        category_data_frame = data_frame[category_cols]
        numeric_data_frame = data_frame.drop(category_data_frame.columns, axis=1)

    sorted_data_frame = pd.concat([category_data_frame, numeric_data_frame], axis=1)

    assert set(data_frame.columns) == set(
        sorted_data_frame.columns
    ), "Sorting data frames should not add or subtract columns."

    return sorted_data_frame

# TODO: This is for sorting columns in preparation for the ML pipeline.
# It should probably be at the start of that pipeline instead of at the end of the data
# pipeline
def sort_data_frame_columns(
    category_cols: Optional[List[str]] = None,
) -> Callable[[pd.DataFrame], pd.DataFrame]:
    """Sort data frame columns such that categories are grouped together on the left.

    Params
    ------
    category_cols: Explicitly define category columns to put on the left.
        If omitted, category columns are identified by their dtype not being
        'number' or a type of 'datetime'.
    """
    return update_wrapper(
        partial(_sort_data_frame_columns_node, category_cols),
        _sort_data_frame_columns_node,
    )

def clean_full_data(*data_frames: pd.DataFrame) -> pd.DataFrame:
    """Clean up data frames created from 'final_<pipeline>_data' JSON files."""
    # Need to convert dates from string to datetime for later
    # calculations/comparisons
    return [
        data_frame.assign(date=pd.to_datetime(data_frame["date"])).set_index(
            INDEX_COLS, drop=False
        )
        for data_frame in data_frames
    ]


"""
The first two consecutive numbers to have two distinct
prime factors are:

14 = 2 × 7
15 = 3 × 5

The first three consecutive numbers to have three
distinct prime factors are:

644 = 2² × 7 × 23
645 = 3 × 5 × 43
646 = 2 × 17 × 19.

Find the first four consecutive integers to have four
distinct prime factors each. What is the first of these
numbers?
"""
import time

# driver function for our program
def main():
    """
    """
    start_time = time.time()
    num = 0

    end_time = time.time() - start_time
    print("Found %s in %2f seconds." % (num, end_time))

# While not required, it is considered good practice to have
# a main function and use this syntax to call it.
if __name__ == "__main__":
    main()


import math
a, b = map(int, input().split())
ans = {1}

def calc_gcd(x, y):
    if y == 0: return x
    return calc_gcd(y, x%y)

def factrize(z):
    i = 2
    set = {1}
    while i*i <= z:
        while z%i==0:
            z//=i
            set.add(i)
        i+=1
    if z > 1:
        set.add(z)
    return set

gcd = calc_gcd(a, b)
print(len(factrize(gcd)))


import time
import warnings

import matplotlib.pyplot as plt
import pandas as pd

import pusion as p
from pusion.evaluation.evaluation import Evaluation
from pusion.input_output.file_input_output import *
from pusion.util.transformer import *

warnings.filterwarnings('error')  # halt on warning

pd.set_option('display.max_rows', 500)
pd.set_option('display.max_columns', 500)
pd.set_option('display.width', 1000)

eval_id = time.strftime("%Y%m%d-%H%M%S")

random_state = 1

# dataset_files = [
#     # 'datasets/Time-SE-ResNet_lr0.01_bs128_ep24_1.pickle',                   # 0  --  4 classes (MC)
#     # 'datasets/Time-SE-ResNet_lr0.01_bs128_ep04_2.pickle',                   # 1  --  4 classes (MC)
#     # 'datasets/Time-SE-ResNet_lr0.01_bs128_ep24_3.pickle',                   # 2  -- 16 classes (MC) (2)
#     # 'datasets/Time-SE-ResNet_lr0.01_bs128_ep70_4.pickle',                   # 3  -- 16 classes (MC) (2)
#     # 'datasets/Time-SE-ResNet_lr0.01_bs128_ep70_5.pickle',                   # 4  --  2 classes (MC)
#     # 'datasets/IndRnn_Classification_lr0.001_bs128_ep35_1.pickle',           # 5  -- 16 classes (MC)
#     # 'datasets/IndRnn_Classification_lr0.001_bs128_ep35_2.pickle',           # 6  --  4 classes (MC)
#     # 'datasets/IndRnn_Classification_lr0.001_bs128_ep35_3.pickle',           # 7  --  4 classes (MC)
#
#     # 'datasets/Time-SE-ResNet_MultiClass_MultiLabel_ep24_1.pickle',          # 8  --  9 classes (ML)
#     # 'datasets/Time-SE-ResNet_MultiClass_MultiLabel_ep70_2.pickle',          # 9  --  9 classes (ML)
#
#     # 'datasets/generated_multiclass_classification_classifier_0.pickle',     # 10 --  5 classes (MC)
#     # 'datasets/generated_multiclass_classification_classifier_1.pickle',     # 11 --  5 classes (MC)
#     # 'datasets/generated_multiclass_classification_classifier_2.pickle',     # 12 --  5 classes (MC)
#     # 'datasets/generated_multiclass_classification_classifier_3.pickle',     # 13 --  5 classes (MC)
#     # 'datasets/generated_multiclass_classification_classifier_4.pickle',     # 14 --  5 classes (MC)
#
#     # 'datasets/Time-SE-ResNet_2_lr0.01_bs128_ep70_1.pickle',                 # 15 -- 16 classes (MC) (2)
#     # 'datasets/Time-SE-ResNet_2_lr0.01_bs128_ep70_2.pickle',                 # 16 -- 16 classes (MC) (1)
#     # 'datasets/Time-SE-ResNet_2_lr0.01_bs128_ep70_3.pickle',                 # 17 -- 16 classes (MC) (1)
#     # 'datasets/Time-SE-ResNet_performance.pickle'                            # 18 -- 16 classes (MC) (1)
# ]

dataset_files = [
    # '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_01.pickle',
    # '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_02.pickle',  # (e1)
    # '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_03.pickle',  # (e1)
    # '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_04.pickle',  # (e1)
    #
    # '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_05.pickle',
    # '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_06.pickle',
    # '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_07.pickle',

    # '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_08.pickle',
    # '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_09.pickle',
    '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_10.pickle',
    '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_11.pickle',
    '/int/DFF_DL_data/models_for_fusion/Time-SE-ResNet_DF3_12.pickle',
]

data = load_pickle_files_as_data(dataset_files)

y_ensemble_valid = [np.roll(data[i]['Y_test_predictions'], shift=1, axis=1) for i in range(len(dataset_files))]
y_ensemble_valid = tensorize(y_ensemble_valid)
y_valid = np.roll(data[0]['Y_test'], shift=1, axis=1)

y_ensemble_test = [np.roll(data[i]['Y_test_for_fusion_predictions'], shift=1, axis=1) for i in range(len(dataset_files))]
y_ensemble_test = tensorize(y_ensemble_test)
y_test = np.roll(data[0]['Y_test_for_fusion'], shift=1, axis=1)

coverage = []
cr = False

np.random.seed(random_state)

eval_metrics = [
    p.PerformanceMetric.MEAN_CONFIDENCE,
]

print("============= Ensemble ===============")
eval_classifiers = Evaluation(*eval_metrics)
if cr:
    eval_classifiers.set_instances(['Ensemble'])
    eval_classifiers.evaluate_cr_decision_outputs(
        y_test, multilabel_prediction_tensor_to_decision_tensor(y_ensemble_test), coverage)
else:
    eval_classifiers.set_instances([('ResNet ' + str(i + 1)) for i in range(len(y_ensemble_test))])
    # eval_classifiers.evaluate(y_test, multilabel_prediction_tensor_to_decision_tensor(y_ensemble_test))
    eval_classifiers.evaluate(y_test, y_ensemble_test)

print(eval_classifiers.get_report())

# print("Diversity:")
# print("pairwise correlation:\t", pairwise_correlation(y_ensemble_test, y_test))
# print("pairwise disagreement:\t", pairwise_disagreement(y_ensemble_test, y_test))
# print("pairwise double fault:\t", pairwise_double_fault(y_ensemble_test, y_test))
# print("pairwise kappa statistic:\t", pairwise_kappa_statistic(y_ensemble_test, y_test))
# print("pairwise q statistic:\t", pairwise_q_statistic(y_ensemble_test, y_test))

# ---- Mean confidence on continuous ensemble outputs
# eval_classifiers_confidence = Evaluation(p.PerformanceMetric.MEAN_CONFIDENCE)
# if cr:
#     eval_classifiers_confidence.set_instances(['Ensemble'])
#     eval_classifiers_confidence.evaluate_cr_decision_outputs(y_test, y_ensemble_test, coverage)
# else:
#     eval_classifiers_confidence.set_instances([('ResNet ' + str(i + 1)) for i in range(len(y_ensemble_test))])
#     eval_classifiers_confidence.evaluate(y_test, y_ensemble_test)
#
# print(eval_classifiers_confidence.get_report())

# ---- GenericCombiner -------------------------------------------------------------------------------------------------
dp = p.DecisionProcessor(p.Configuration(method=p.Method.GENERIC))
dp.set_parallel(False)
if cr:
    dp.set_coverage(coverage)
dp.train(y_ensemble_valid, y_valid)
dp.combine(y_ensemble_test)

# ---- Evaluate all combiners
print("========== GenericCombiner ===========")
eval_combiner = Evaluation(*eval_metrics)
eval_combiner.set_instances(dp.get_combiners())
eval_combiner.set_runtimes(dp.get_multi_combiner_runtimes())
# multi_comb_decision_outputs = multilabel_prediction_tensor_to_decision_tensor(dp.get_multi_combiner_decision_output())
multi_comb_decision_outputs = dp.get_multi_combiner_decision_output()
combs = dp.get_combiners()
if cr:
    eval_combiner.evaluate_cr_multi_combiner_decision_outputs(y_test, multi_comb_decision_outputs)
else:
    eval_combiner.evaluate(y_test, multi_comb_decision_outputs)
print(eval_combiner.get_report())

# ---- Mean confidence on continuous combiner outputs
# eval_combiner_confidence = Evaluation(p.PerformanceMetric.MEAN_CONFIDENCE)
# eval_combiner_confidence.set_instances(dp.get_combiners())
# multi_comb_continuous_outputs = dp.get_multi_combiner_decision_output()
# if cr:
#     eval_combiner_confidence.evaluate_cr_multi_combiner_decision_outputs(y_test, multi_comb_continuous_outputs)
# else:
#     eval_combiner_confidence.evaluate(y_test, multi_comb_continuous_outputs)
# print(eval_combiner_confidence.get_report())

# # ---- ROC curves for classifiers
# for i, do in enumerate(y_ensemble_test):
#     skplt.metrics.plot_roc_curve(multilabel_assignments_to_labels(y_test), do)
#     save(plt, "000_classifier_" + str(i) + "_roc_curve", eval_id + "/roc")
#
# # ---- ROC curves for combiners with continuous outputs
# for do, comb in zip(multi_comb_continuous_outputs, dp.get_combiners()):
#     if determine_assignment_type(do) == p.AssignmentType.CONTINUOUS:
#         # skplt.metrics.plot_roc_curve(multilabel_assignments_to_labels(y_test), do)
#         # plt.title(comb.SHORT_NAME)
#         # save(plt, "001_combiner_" + comb.SHORT_NAME + "_roc_curve", eval_id + "/roc")
#         print("CONT. OUT: ", comb.SHORT_NAME)

# === Plots ============================================================================================================
meanprops = dict(markerfacecolor='black', markeredgecolor='white')
plt.rc('axes', titlesize=12)     # fontsize of the axes title
plt.rc('axes', labelsize=12)    # fontsize of the x and y labels

def extend_y_ticks(plot):
    y_ticks = plot.yticks()[0].tolist()
    step = y_ticks[-1] - y_ticks[-2]
    y_ticks.insert(0, y_ticks[0] - step)
    y_ticks.append(y_ticks[-1] + step)
    return y_ticks

def extend_y_ticks_upper_bound(plot):
    y_ticks = plot.yticks()[0].tolist()
    step = y_ticks[-1] - y_ticks[-2]
    y_ticks.append(y_ticks[-1] + step)
    return y_ticks

def extend_x_ticks_upper_bound(plot):
    x_ticks = plot.xticks()[0].tolist()
    step = x_ticks[-1] - x_ticks[-2]
    x_ticks.append(x_ticks[-1] + step)
    return x_ticks

# --- Ensemble convergence ---------------------------------------------------------------------------------------------
SMALL_SIZE = 8
MEDIUM_SIZE = 10
BIGGER_SIZE = 12

plt.rc('font', size=BIGGER_SIZE)          # controls default text sizes
plt.rc('axes', titlesize=SMALL_SIZE)     # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_SIZE)    # fontsize of the x and y labels
plt.rc('xtick', labelsize=SMALL_SIZE)    # fontsize of the tick labels
plt.rc('ytick', labelsize=SMALL_SIZE)    # fontsize of the tick labels
plt.rc('legend', fontsize=SMALL_SIZE)    # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE)  # fontsize of the figure title

for i in range(len(dataset_files)):
    plt.figure()
    plt.plot(data[i]['accuracy'], color="#0d79c0", label="Training")
    plt.plot(data[i]['val_accuracy'], color="#dd6733", label="Validierung")
    plt.xlabel('Epochen', fontweight='bold', labelpad=15)
    plt.ylabel('Trefferquote', fontweight='bold', labelpad=15)
    plt.legend(loc="lower right")
    plt.grid()
    plt.tight_layout()
    save(plt, "010_ensemble_classifier_" + str(i) + "_epochs_accuracy", eval_id)
    plt.close()

# --- Ensemble performance ---------------------------------------------------------------------------------------------
classifiers_accuracies = [t[1] for t in eval_classifiers.get_instance_performance_tuples(p.PerformanceMetric.MEAN_CONFIDENCE)]

bar1 = np.around(classifiers_accuracies, 3)
barWidth = 0.13
r1 = np.arange(len(bar1))

plt.figure()
rect1 = plt.bar(r1, bar1, color='#7a9fc2', width=barWidth, edgecolor='white', label="Mittlere Konfidenz")
plt.xlabel('Ensemble', fontweight='bold', labelpad=15)
plt.xticks(r1, [str(instance) for instance in eval_classifiers.get_instances()])
plt.ylabel('Wertung', fontweight='bold', labelpad=15)
plt.yticks(np.arange(0, 1.1, .1))
plt.ylim((0, 1.2))
plt.bar_label(rect1, padding=3, rotation=90)

plt.legend(loc="upper right")
plt.tight_layout()
save(plt, "100_classifier_scores_grouped", eval_id)
plt.close()

# --- Combiners performance --------------------------------------------------------------------------------------------
combiners_accuracies = [t[1] for t in eval_combiner.get_instance_performance_tuples(p.PerformanceMetric.MEAN_CONFIDENCE)]
bar1 = np.around(combiners_accuracies, 3)

barWidth = 0.13
r1 = np.arange(len(bar1))

plt.figure(figsize=(12, 5.5))
rect1 = plt.barh(r1, bar1, color='#7a9fc2', height=barWidth, edgecolor='white', label="Mittlere Konfidenz")

plt.ylabel('Fusionsmethoden', fontweight='bold', labelpad=15)
plt.yticks(r1, [comb.SHORT_NAME for comb in eval_combiner.get_instances()])
# plt.xlim(.5, np.max(r1) + 1.5)
plt.xlabel('Mittlere Konfidenz', fontweight='bold', labelpad=15)
# plt.xticks(np.arange(.0, 1.1, .1))
# plt.xlim((.6, 1.0))
axes = plt.gca()
# axes.set_xlim([.6, None])
axes.set(xlim=[.6, None])

# plt.axis([0.6, 1.0, -.5, len(bar1) - .5])

# plt.xticks(extend_x_ticks_upper_bound(plt))
plt.bar_label(rect1, padding=3)
plt.tight_layout()
save(plt, "101_combiner_scores_grouped", eval_id)
plt.close()

# --- Performance difference -------------------------------------------------------------------------------------------

classifiers_max_accuracy = np.max(classifiers_accuracies)
# classifiers_max_balanced_multilabel_accuracy_score = np.max(classifiers_balanced_multilabel_accuracy_scores)
# classifiers_max_micro_jaccard_score = np.max(classifiers_micro_jaccard_scores)
# classifiers_max_macro_f1_score = np.max(classifiers_macro_f1_scores)

difference_accuracies = np.array(combiners_accuracies) - classifiers_max_accuracy
# difference_balanced_multilabel_accuracy_scores = np.array(combiners_balanced_multilabel_accuracy_scores) - classifiers_max_balanced_multilabel_accuracy_score
# difference_micro_jaccard_scores = np.array(combiners_micro_jaccard_scores) - classifiers_max_micro_jaccard_score
# difference_macro_f1_scores = np.array(combiners_macro_f1_scores) - classifiers_max_macro_f1_score

bar1 = np.around(difference_accuracies, 3)
# bar2 = np.around(difference_balanced_multilabel_accuracy_scores, 3)
# bar3 = np.around(difference_micro_jaccard_scores, 3)
# bar4 = np.around(difference_macro_f1_scores, 3)

barWidth = 0.13
r1 = np.arange(len(bar1))
# r2 = [x + barWidth for x in r1]
# r3 = [x + barWidth for x in r2]
# r4 = [x + barWidth for x in r3]

plt.figure(figsize=(12, 5.5))
plt.axhline(y=0, color='gray', linestyle='-', linewidth=1)

rect1 = plt.bar(r1, bar1, color='#7a9fc2', width=barWidth, edgecolor='white', label="Mittlere Konfidenz")
# rect2 = plt.bar(r2, bar2, color='#7d2150', width=barWidth, edgecolor='white', label="Balancierte Trefferquote")
# rect3 = plt.bar(r3, bar3, color='#b55b53', width=barWidth, edgecolor='white', label="Micro Jaccard-Score")
# rect4 = plt.bar(r4, bar4, color='#197435', width=barWidth, edgecolor='white', label="Macro F1-Score")

plt.xlabel('Fusionsmethoden', fontweight='bold', labelpad=15)
# plt.xticks([r + barWidth * 1.5 for r in range(len(bar1))], [comb.SHORT_NAME for comb in eval_combiner.get_instances()])
plt.xticks(r1, [comb.SHORT_NAME for comb in eval_combiner.get_instances()])
plt.xlim(-.5, np.max(r1) + 1.5)
plt.ylabel('Wertung (Differenz)', fontweight='bold', labelpad=15)
plt.yticks(extend_y_ticks(plt))

plt.bar_label(rect1, padding=3, rotation=90)
# plt.bar_label(rect2, padding=3, rotation=90)
# plt.bar_label(rect3, padding=3, rotation=90)
# plt.bar_label(rect4, padding=3, rotation=90)

plt.legend(loc="lower right")
plt.tight_layout()
save(plt, "102_combiner_score_differences_grouped", eval_id)
plt.close()

# --- Performance improvement ------------------------------------------------------------------------------------------

classifiers_max_accuracy = np.max(classifiers_accuracies)
# classifiers_max_balanced_multilabel_accuracy_score = np.max(classifiers_balanced_multilabel_accuracy_scores)
# classifiers_max_micro_jaccard_score = np.max(classifiers_micro_jaccard_scores)
# classifiers_max_macro_f1_score = np.max(classifiers_macro_f1_scores)

difference_accuracies = (np.array(combiners_accuracies) - classifiers_max_accuracy).clip(min=0)
# difference_balanced_multilabel_accuracy_scores = (np.array(combiners_balanced_multilabel_accuracy_scores) -
#                                    classifiers_max_balanced_multilabel_accuracy_score).clip(min=0)
# difference_micro_jaccard_scores = (np.array(combiners_micro_jaccard_scores) - classifiers_max_micro_jaccard_score).clip(min=0)
# difference_macro_f1_scores = (np.array(combiners_macro_f1_scores) - classifiers_max_macro_f1_score).clip(min=0)

combiners = list(eval_combiner.get_instances())

for i, perf in reversed(list(enumerate(difference_accuracies))):
    if difference_accuracies[i] == 0:
        difference_accuracies = np.delete(difference_accuracies, i)
        # difference_balanced_multilabel_accuracy_scores = np.delete(difference_balanced_multilabel_accuracy_scores, i)
        # difference_micro_jaccard_scores = np.delete(difference_micro_jaccard_scores, i)
        # difference_macro_f1_scores = np.delete(difference_macro_f1_scores, i)
        del combiners[i]

if len(combiners) > 0:
    bar1 = np.around(difference_accuracies, 3)
    # bar2 = np.around(difference_balanced_multilabel_accuracy_scores, 3)
    # bar3 = np.around(difference_micro_jaccard_scores, 3)
    # bar4 = np.around(difference_macro_f1_scores, 3)

    barWidth = 0.13
    r1 = np.arange(len(bar1))
    # r2 = [x + barWidth for x in r1]
    # r3 = [x + barWidth for x in r2]
    # r4 = [x + barWidth for x in r3]

    plt.figure(figsize=(12, 5.5))
    rect1 = plt.bar(r1, bar1, color='#7a9fc2', width=barWidth, edgecolor='white', label="Mittlere Konfidenz")
    # rect2 = plt.bar(r2, bar2, color='#7d2150', width=barWidth, edgecolor='white', label="Balancierte Trefferquote")
    # rect3 = plt.bar(r3, bar3, color='#b55b53', width=barWidth, edgecolor='white', label="Micro Jaccard-Score")
    # rect4 = plt.bar(r4, bar4, color='#197435', width=barWidth, edgecolor='white', label="Macro F1-Score")

    plt.xlabel('Fusionsmethoden', fontweight='bold', labelpad=15)
    # plt.xticks([r + barWidth * 1.5 for r in range(len(bar1))], [comb.SHORT_NAME for comb in combiners])
    plt.xticks(r1, [comb.SHORT_NAME for comb in combiners])
    plt.xlim(-.5, np.max(r1) + 2)
    plt.ylabel('Wertung (Differenz)', fontweight='bold', labelpad=15)
    plt.yticks(extend_y_ticks_upper_bound(plt))

    plt.bar_label(rect1, padding=3, rotation=90)
    # plt.bar_label(rect2, padding=3, rotation=90)
    # plt.bar_label(rect3, padding=3, rotation=90)
    # plt.bar_label(rect4, padding=3, rotation=90)

    plt.legend(loc="lower right")
    plt.tight_layout()
    save(plt, "103_combiner_score_positive_improvement_grouped", eval_id)
    plt.close()

# --- Mean confidences -------------------------------------------------------------------------------------------------
rounded_cls_accuracies = np.around(
    [t[1] for t in eval_classifiers.get_instance_performance_tuples(p.PerformanceMetric.MEAN_CONFIDENCE)], 3)
rounded_comb_accuracies = np.around(
    [t[1] for t in eval_combiner.get_instance_performance_tuples(p.PerformanceMetric.MEAN_CONFIDENCE)], 3)

accuracies = np.flip(np.append(rounded_cls_accuracies, rounded_comb_accuracies))

fig, ax = plt.subplots()
index = np.arange(len(accuracies))

rect1 = plt.barh(index, accuracies, 0.13, color='black')

ax.set(xlim=[0.9, 1.02])

y_ticks = np.flip(np.append([str(instance) for instance in eval_classifiers.get_instances()],
                            [comb.SHORT_NAME for comb in eval_combiner.get_instances()]))

plt.yticks(index, y_ticks)

plt.xlabel('Mittlere Konfidenz', fontweight='bold', labelpad=15)
plt.ylabel('Fusionsmethoden / Ensemble', fontweight='bold', labelpad=15)

plt.axhline(y=3.5, color='black', linestyle='-', linewidth=0.5)
plt.bar_label(rect1, padding=3)

# ax.xaxis.set_major_locator(MaxNLocator(prune='upper'))
plt.setp(ax.get_xticklabels()[-1], visible=False)

plt.tight_layout()
# plt.show()
save(plt, "104_all_mean_confidences", eval_id)
plt.close()

# === Confusion matrices ===============================================================================================

# if not cr:
#     # labels=np.arange(4)
#     cm = confusion_matrix(multiclass_assignments_to_labels(y_test), multiclass_assignments_to_labels(y_test))
#     display = ConfusionMatrixDisplay(confusion_matrix=cm)  # display_labels=np.arange(4)
#     display.plot(cmap='binary')
#     # plt.title("Ground Truth")
#     plt.xlabel('vorhergesagte Klassen', fontweight='bold', labelpad=15)
#     plt.ylabel('wahre Klassen', fontweight='bold', labelpad=15)
#     save(plt, "000_ground_truth_confusion_matrix", eval_id + "/cm")
#     plt.close()
#
# if not cr:
#     for i, dt in enumerate(y_ensemble_test):
#         # labels=np.arange(4)
#         cm = confusion_matrix(multiclass_assignments_to_labels(y_test), multiclass_assignments_to_labels(dt))
#         display = ConfusionMatrixDisplay(confusion_matrix=cm)  # display_labels=np.arange(4)
#         display.plot(cmap='binary')
#         # plt.title("Classifier " + str(i))
#         plt.xlabel('vorhergesagte Klassen', fontweight='bold', labelpad=15)
#         plt.ylabel('wahre Klassen', fontweight='bold', labelpad=15)
#         save(plt, "001_classifier_" + str(i) + "_confusion_matrix", eval_id + "/cm")
#         plt.close()
#
# if not cr:
#     for i, comb in enumerate(eval_combiner.get_instances()):
#         cm = confusion_matrix(multiclass_assignments_to_labels(y_test),
#                               multiclass_assignments_to_labels(multi_comb_decision_outputs[i]))  # labels=np.arange(4)
#         display = ConfusionMatrixDisplay(confusion_matrix=cm)  # display_labels=np.arange(4)
#         display.plot(cmap='binary')
#         # plt.title(comb.SHORT_NAME)
#         plt.xlabel('vorhergesagte Klassen', fontweight='bold', labelpad=15)
#         plt.ylabel('wahre Klassen', fontweight='bold', labelpad=15)
#         save(plt, "002_" + str(i) + "_" + comb.SHORT_NAME + "_combiner_confusion_matrix", eval_id + "/cm")
#         plt.close()

# === Combiner runtimes ================================================================================================
plt.rcParams.update({'font.size': 13})

runtime_matrix = np.nan_to_num(eval_combiner.get_runtime_matrix())
combiners_train_runtimes = np.around(runtime_matrix[:, 0], 4)
combiners_combine_runtimes = np.around(runtime_matrix[:, 1], 4)
combiners_names = [c.SHORT_NAME for c in eval_combiner.get_instances()]

# --- Train runtimes ---------------------------------------------------------------------------------------------------
non_zero_indexes = np.nonzero(combiners_train_runtimes)[0]
combiners_train_non_zero_runtimes = combiners_train_runtimes[non_zero_indexes]
combiners_non_zero_names = [combiners_names[i] for i in non_zero_indexes]

# remove outliers
max_index = np.argmax(combiners_train_non_zero_runtimes)
combiners_train_non_zero_runtimes = np.delete(combiners_train_non_zero_runtimes, max_index)
del combiners_non_zero_names[max_index]

df = pd.DataFrame({'combiners_non_zero_names': combiners_non_zero_names,
                   'combiners_train_non_zero_runtimes': combiners_train_non_zero_runtimes})
df_sorted = df.sort_values('combiners_train_non_zero_runtimes')

plt.figure()
bar1 = plt.bar('combiners_non_zero_names', 'combiners_train_non_zero_runtimes', data=df_sorted, color='#93c6ed',
               width=.75)
plt.xlabel("Fusionsmethode", fontweight='bold', labelpad=15)
plt.ylabel("Laufzeit (s)", fontweight='bold', labelpad=15)
plt.bar_label(bar1, padding=3)
plt.tight_layout()
save(plt, "z90_train_runtime_comparison", eval_id)
plt.close()

# --- horizontal bar plot
plt.figure()
bar1 = plt.barh('combiners_non_zero_names', 'combiners_train_non_zero_runtimes', data=df_sorted, color='#b5670e',
                height=.2)
plt.xlabel("Laufzeit (s)", fontweight='bold', labelpad=15)
plt.ylabel("Fusionsmethode", fontweight='bold', labelpad=15)
plt.xticks(extend_x_ticks_upper_bound(plt))
plt.bar_label(bar1, padding=3)
plt.tight_layout()
save(plt, "z91_train_runtime_comparison_h", eval_id)
plt.close()

# --- Combine runtimes -------------------------------------------------------------------------------------------------

df = pd.DataFrame({'combiners_names': combiners_names, 'combiners_combine_runtimes': combiners_combine_runtimes})
df_sorted = df.sort_values('combiners_combine_runtimes')

plt.figure(figsize=(8, 4.8))
bar1 = plt.bar('combiners_names', 'combiners_combine_runtimes', data=df_sorted, color='#006aba', width=.75)
plt.xlabel("Fusionsmethoden", fontweight='bold', labelpad=15)
plt.ylabel("Laufzeit (s)", fontweight='bold', labelpad=15)
plt.bar_label(bar1, padding=3)
plt.tight_layout()
save(plt, "z92_combine_runtime_comparison", eval_id)
plt.close()

# --- horizontal bar plot
plt.figure()
bar1 = plt.barh('combiners_names', 'combiners_combine_runtimes', data=df_sorted, color='#915006', height=.4)
plt.xlabel("Laufzeit (s)", fontweight='bold', labelpad=15)
plt.ylabel("Fusionsmethode", fontweight='bold', labelpad=15)
plt.xticks(extend_x_ticks_upper_bound(plt))
plt.bar_label(bar1, padding=3)
plt.tight_layout()
save(plt, "z93_combine_runtime_comparison_h", eval_id)
plt.close()

save_evaluator(__file__, eval_id)
print("Evaluation", eval_id, "done.")