# -*- encoding: utf-8 -*-
import distutils.core
from django.shortcuts import render_to_response, render, redirect
from django.template import RequestContext, Context
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.views.decorators.csrf import csrf_exempt
from django.core.validators import validate_email
from django.template.loader import get_template
from django.http import HttpResponse
from slugify import slugify
from django import template
from django.conf import settings
from django.db.models import Q
from threading import Thread
from django.template.loader import get_template
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.db.models import Case, When, Max, F, Count, Value, Q, Sum, BooleanField, TextField, IntegerField

from datetime import datetime, date, timedelta
from django.utils import timezone

import json
import xlrd
import xlwt
import collections
import shutil
import os
import time

from academico.function import HORA_INI, HORA_FINI, UPDATE_CURSOS_PATH
from curso.form import CursoForm, CursoParteDosForm, ModuloForm, ClaseForm, MaterialForm
from curso.models import Curso, Modulo, Clase, Material
from integrado.models import Usuario, Ficha, Pais, Region, Comuna, Producto
from integrado.function import elige_choices, filtros_cabecera
from academico.models import AlumnoFicha, AlumnoCurso, Alumno, Sala, Horario
from integrado.form import AlumnoCursoForm, FiltroFichasForm, FiltroCursosForm
from academico.form import HorarioForm, UsuarioDocenteForm

# ------------------------------------
# ******* AREA ACADEMICA *********
# ------------------------------------
def fichas(request, tipo_fichas=""):
    listado_fichas = []

    tipo_de_fichas = int(tipo_fichas)

    filtroFichasForm = FiltroFichasForm()

    # -------------------------------------------------
    # bloque hecho para los filtros de las paginas
    query_set = Ficha.objects.filter(ficha_tipocurso=tipo_fichas)
    if request.POST:
        lst_query = filtros_cabecera(request)
        for l in lst_query:
            filtroFichasForm.fields[l['key']].initial = l['value']

            if l['key'] == 'ficha_estado':
                query_set = query_set.filter(ficha_estado=l['value'])
            if l['key'] == 'curso':
                query_set = query_set.filter(curso_id=l['value'])
            if l['key'] == 'vendedor':
                query_set = query_set.filter(usuario_id=l['value'])
    # -------------------------------------------------

    for f in query_set.order_by("ficha_id"):

        ficha_estado = elige_choices(Ficha.FICHA_ESTADO, f.ficha_estado)
        ficha_tipocurso = elige_choices(Ficha.TIPO_CURSO, f.ficha_tipocurso)
        estado_ficha = elige_choices(Ficha.OPCIONES, f.ficha_activo)

        alumno_ficha = AlumnoFicha.objects.filter(ficha=f).exclude(af_activo='N').annotate(
            cantidad_fichas=Count('alumno')).filter(cantidad_fichas__gt=0)

        # solo muestra las fichas con alumnos
        if len(alumno_ficha) > 0 and f.ficha_procesocreacion >= 1:

            try:
                producto = f.producto.prod_nombre.title()
            except:
                producto = ""

            try:
                curso = f.curso.cur_nombre.title()
            except:
                curso = ""

            try:
                cliente = f.cliente.cli_nombre.title()
            except:
                cliente = ""

            listado_fichas.append({
                'ficha_id': f.ficha_id,
                'ficha_estado': ficha_estado,
                'id_ficha_estado': f.ficha_estado,
                'producto': producto,
                'curso': curso,
                'ficha_tipocurso': ficha_tipocurso,
                'cliente': cliente,
                'cantidad': len(alumno_ficha),
                'usuario': "{n} {ap}".format(n=f.usuario.usu_nombre.title(), ap=f.usuario.usu_apepat.title()),
                'ficha_activo': f.ficha_activo,
                'estado_ficha': estado_ficha
            })

    lst_profesores = Usuario.objects.filter(usu_tipousuario=5).exclude(usu_usuarioactivo='N')

    titulo = ''
    if tipo_fichas == '1':
        titulo = 'E-learning'
    elif tipo_fichas == '2':
        titulo = 'Presenciales'
    elif tipo_fichas == '3':
        titulo = 'Semi-Presenciales'

    data = {
        'usuario': request.session["dicUsuario"],
        'listado_fichas': listado_fichas,
        'tipo_de_fichas': tipo_de_fichas,
        'AlumnoCursoForm': AlumnoCursoForm(),
        'lst_profesores': lst_profesores,
        'titulo': titulo,
        'filtroFichasForm': filtroFichasForm,
        'tipo_fichas': tipo_fichas,
    }
    return render(request, 'integrado/page/fichas.html', data)

# --------------------
# by osbustaman
# con esta función se asocician los alumnos al curso,
@csrf_exempt
def ajax_asociar_alumno_curso(request):
    the_error = ""
    lst_alumno_curso = []

    date_ini = datetime.strptime(request.POST['fecha_ini'], '%d/%m/%Y').date()
    date_fin = datetime.strptime(request.POST['fecha_fin'], '%d/%m/%Y').date()

    # try:
    ficha = Ficha.objects.get(ficha_id=request.POST['ficha_id'])

    if ficha.ficha_tipocurso==1:
        ac_diasdelasemana = '0:1:2:3:4:5:6'
    else:
        ac_diasdelasemana = request.POST['ac_diasdelasemana']

    ficha.ficha_ini = date_ini
    ficha.ficha_fin = date_fin
    ficha.ficha_diasalasemana = ac_diasdelasemana
    try:
        ficha.ficha_valorhoraprofesor = request.POST['id_valor_hora']
    except:
        ficha.ficha_valorhoraprofesor = 0
    ficha.save()

    alumno_ficha = AlumnoFicha.objects.filter(ficha=ficha).exclude(af_activo='N')
    for af in alumno_ficha:
        ac = AlumnoCurso()

        ac.alumno = af.alumno
        ac.curso = af.ficha.curso
        ac.ficha = af.ficha
        ac.ac_fechainicio = date_ini
        ac.ac_fechafinalizacioncurso = date_fin
        ac.ac_activo = "S"

        ac.save()

    is_save = True

    try:
        ficha.profesor = Usuario.objects.get(usu_id=request.POST['profesores'])
    except:
        pass
    ficha.ficha_estado = 2
    ficha.save()
    # except Exception as e:
    #     is_save = False
    #     the_error = "type error: " + str(e)

    # *****************************************************************************
    # aqui envio las fechas entre un rango de fechas
    # con ciertos dias de la semana por defecto
    # con el timedelta manejo la creacion de las fechas
    # y con range hago el recorrido

    inicio = date_ini
    fin = date_fin
    contador_fecha = 0
    lst_fechas = []

    listado_dias_semana = ficha.ficha_diasalasemana
    lista_fechas = [inicio + timedelta(days=d) for d in range((fin - inicio).days + 1)]
    for f in lista_fechas:
        la_fecha = f
        # pregunto si el numero de dia de la semana corresponde al numero del dia por defecto
        if str(int(datetime.weekday(la_fecha))+1) in listado_dias_semana.split(':'):
            contador_fecha += 1
            lst_fechas.append({
                'contador_fecha':contador_fecha,
                'fecha':la_fecha
            })
    # *****************************************************************************

    datos = {
        'lst_fechas':lst_fechas,
        'ficha_id': ficha.ficha_id,
    }

    file_template = 'academico/ajax/ajax_horario.html'
    template = get_template(file_template)
    el_html = template.render(datos)

    html = {
        'is_save': is_save,
        'the_error': the_error,
        'el_html':el_html,
        'ficha_id':ficha.ficha_id,
        'estado_nuevo': elige_choices(Ficha.FICHA_ESTADO, ficha.ficha_estado),
    }
    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

# --------------------
# by osbustaman
# con esta función se asocician los alumnos al curso,
@csrf_exempt
def ajax_datos_academico_ficha(request):

    lst_ficha = []

    ficha = Ficha.objects.get(ficha_id=request.POST['ficha_id'])
    ac_diasdelasemana = ficha.ficha_diasalasemana
    valorhoraprofesor = ficha.ficha_valorhoraprofesor
    tipomoneda = ficha.ficha_tipomoneda

    fecha_ini = datetime.strftime(ficha.ficha_ini, "%d/%m/%Y")
    fecha_fin = datetime.strftime(ficha.ficha_fin, "%d/%m/%Y")

    # *****************************************************************************
    # aqui envio las fechas entre un rango de fechas
    # con el timedelta manejo la creacion de las fechas
    # y con range hago el recorrido

    inicio = ficha.ficha_ini
    fin = ficha.ficha_fin
    contador_fecha = 0
    lst_fechas = []
    try:
        profesor = ficha.profesor.to_string
    except:
        profesor = ""

    listado_dias_semana = ficha.ficha_diasalasemana
    lista_fechas = [inicio + timedelta(days=d) for d in range((fin - inicio).days + 1)]
    for f in lista_fechas:
        la_fecha = f
        # pregunto si el numero de dia de la semana corresponde al numero del dia por defecto
        if str(int(datetime.weekday(la_fecha)) + 1) in listado_dias_semana.split(':'):
            contador_fecha += 1
            lst_fechas.append({
                'contador_fecha': contador_fecha,
                'fecha': la_fecha
            })

    # *****************************************************************************

    datos = {
        'lst_fechas': lst_fechas,
        'ficha_id': ficha.ficha_id
    }

    file_template = 'academico/ajax/ajax_horario.html'
    template = get_template(file_template)
    el_html = template.render(datos)
    try:
        pass
    except:
        fecha_ini = ""
        fecha_fin = ""
        el_html = ""
        ac_diasdelasemana = ""
        profesor = ""
        valorhoraprofesor = ""
        tipomoneda = ""

    html = {
        'lst_ficha': lst_ficha,
        'fecha_ini': fecha_ini,
        'fecha_fin': fecha_fin,
        'ficha_estado': ficha.ficha_estado,
        'el_html':el_html,
        'ac_diasdelasemana':ac_diasdelasemana,
        'valorhoraprofesor':valorhoraprofesor,
        'profesor':profesor,
        'tipomoneda':tipomoneda,
    }
    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

@csrf_exempt
def ajax_listado_alumnos_matricula(request):

    lst_alumno_ficha = []
    alumno_ficha = AlumnoFicha.objects.filter(ficha_id=request.POST['ficha_id']).exclude(af_activo='N')
    ficha_id = request.POST['ficha_id']

    for af in alumno_ficha:
        tipo_rut = elige_choices(Alumno.TIPO_RUT, af.alumno.alu_tiporut)
        lst_alumno_ficha.append({
            'alu_id': af.alumno.alu_id,
            'tipo_rut': tipo_rut,
            'alu_tiporut': af.alumno.alu_tiporut,
            'alu_rut': af.alumno.alu_rut,
            'alu_nombre': af.alumno.alu_nombre,
            'alu_apellidopat': af.alumno.alu_apellidopat,
            'alu_apellidomat': af.alumno.alu_apellidomat,
            'alu_mail': af.alumno.alu_mail,
            'alu_sexo': af.alumno.alu_sexo,
            'alu_fono': af.alumno.alu_fono,
            'ficha_estado': af.ficha.ficha_estado,
            'af_essence': af.af_essence,
        })

    datos = {
        'lst_alumno_ficha': lst_alumno_ficha,
        'ficha_id': ficha_id,
    }

    file_template = 'integrado/ajax/ajax_listado_alumnos_matricula.html'
    template = get_template(file_template)
    html = template.render(datos)

    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

@csrf_exempt
def ajax_calendario_academico(request):

    num_inicio = HORA_INI
    num_termino = HORA_FINI
    lst_horas=[]
    lst_sala = []

    for i in range(num_inicio,num_termino):
        tiene_dato = False
        fecha = request.POST['fecha']
        hora_ini = "{h}:00:00".format(h=i)
        fecha_nueva = fecha.split("/")[2] + "-" + fecha.split("/")[1] + "-" + fecha.split("/")[0]
        fecha_hora_ini = fecha_nueva + " " + hora_ini
        fecha_ini = datetime.strptime(fecha_hora_ini, '%Y-%m-%d %H:%M:%S')

        try:

            el_horario = Horario.objects.get(hor_fecha=fecha_ini)
            tiene_dato = True
            sala=el_horario.sala.sala_nombre
            la_ficha = Ficha.objects.get(ficha_id=el_horario.ficha_id)

            # -------------------------------------------
            ficha_id = el_horario.ficha_id
            cur_nombre = la_ficha.curso.cur_nombre
            # -------------------------------------------

            if el_horario.hor_break == 'S':
                is_break = 'SI'
            else:
                is_break = 'NO'

        except:
            sala = ''
            is_break = ''
            ficha_id = ''
            cur_nombre = ''

        lst_horas.append({
            'id_hora':i,
            'hora':"{hora}:00".format(hora=i),
            'tiene_dato':tiene_dato,
            'sala':sala,
            'is_break':is_break,
            'cur_nombre':cur_nombre,
            'ficha_id':ficha_id,
        })

    las_salas = Sala.objects.all()
    for s in las_salas:
        lst_sala.append({
            'sala_id':s.sala_id,
            'sala_nombre':s.sala_nombre,
        })

    datos = {
        'fecha':request.POST['fecha'],
        'lst_horas':lst_horas,
        'las_salas':las_salas,
        'la_ficha':request.POST['ficha_id']
    }

    file_template = 'academico/ajax/ajax_calendario_academico.html'
    template = get_template(file_template)
    html = template.render(datos)

    response = {
        'html':html,
    }

    response = json.dumps(response)
    return HttpResponse(response, content_type='application/json')

# ------------------------------------
@csrf_exempt
def ajax_guardar_horario(request):

    lista_err = []
    lst_dato_response = []

    fecha = request.POST['fecha']
    hora_ini  = "{h}:00:00".format(h=request.POST['id_hora'])
    hora_fin  = "{h}:45:00".format(h=request.POST['id_hora'])

    fecha_nueva = fecha.split("/")[2]+"-"+fecha.split("/")[1]+"-"+fecha.split("/")[0]

    fecha_hora_ini = fecha_nueva + " " + hora_ini
    fecha_hora_fin = fecha_nueva + " " + hora_fin

    fecha_ini = datetime.strptime(fecha_hora_ini, '%Y-%m-%d %H:%M:%S')
    fecha_fin = datetime.strptime(fecha_hora_fin, '%Y-%m-%d %H:%M:%S')

    try:

        sala = Sala.objects.get(sala_id=request.POST['sala'])
        ficha = Ficha.objects.get(ficha_id=request.POST['ficha_id'])

        h = Horario()
        h.hor_fecha = fecha_ini
        h.sala = sala
        h.ficha = ficha
        h.hor_break = request.POST['break']
        h.save()

        if h.hor_break == 'S':
            el_break = 'SI'
        else:
            el_break = 'NO'

        lst_dato_response.append({
            'sala_nombre':sala.sala_nombre,
            'el_break':el_break,
            'fecha_ini':str(fecha_ini),
            'el_curso':ficha.curso.cur_nombre,
            'ficha_id':ficha.ficha_id,
        })
        is_save = True
    except Exception as e:
        # print("type error: " + str(e))
        lista_err.append({
            'the_error': str(e),
        })
        is_save = False

    html = {
        'is_save': is_save,
        'lista_err':lista_err,
        'lst_dato_response':lst_dato_response
    }
    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

@csrf_exempt
def obtener_listado_profesores(request):
    lst_profesores = []
    for p in Usuario.objects.filter(usu_tipousuario=5):
        lst_profesores.append({
            'key':p.usu_id,
            'value':p.to_string,
        })
    html = {
        'lst_elementos':lst_profesores
    }
    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')
# ------------------------------------
# ******* AREA ACADEMICA *********
# ------------------------------------

# ------------------------------------
# ******* AREA RELATORES *********
# ------------------------------------
def listado_relatores(request):

    lst_profesores = []

    query_set_usuario = Usuario.objects.filter(usu_tipousuario=5)
    if request.POST:
        filtro_usu_activo = request.POST['filtro_usu_activo']
        filtro_usu_extranjero = request.POST['filtro_usu_extranjero']
        if request.POST['filtro_usu_activo']:
            query_set_usuario = query_set_usuario.filter(usu_usuarioactivo = request.POST['filtro_usu_activo'])

        if request.POST['filtro_usu_extranjero']:
            query_set_usuario = query_set_usuario.filter(usu_extranjero = request.POST['filtro_usu_extranjero'])
    else:
        filtro_usu_activo = ""
        filtro_usu_extranjero = ""

    for p in query_set_usuario:
        lst_profesores.append({
            'usu_id':p.usu_id,
            'usu_tiporut_name':elige_choices(Usuario.TIPO_RUT, p.usu_tiporut),
            'usu_tiporut':p.usu_tiporut,
            'usu_rut': p.usu_generadorrut,
            'usu_nombre':p.usu_nombre,
            'usu_apepat':p.usu_apepat,
            'usu_apemat':p.usu_apemat,
            'usu_sexo_name':elige_choices(Usuario.SEXO, p.usu_sexo),
            'usu_sexo':p.usu_sexo,
            'usu_mail':p.usu_mail,
            'usu_fono':p.usu_fono,
            'usu_fechanacimiento':p.usu_fechanacimiento,
            'pais_name': p.pais.pa_nombre,
            'pais': p.pais_id,
            'region_name': p.region.re_nombre,
            'region': p.region_id,
            'comuna_name': p.comuna.com_nombre,
            'comuna': p.comuna_id,
            'usu_direccion': p.usu_direccion,
            'usu_estadocivil': p.usu_estadocivil,
            'usu_profesion': p.usu_profesion,
            'usu_extranjero_name': elige_choices(Usuario.OPCIONES, p.usu_extranjero),
            'usu_extranjero': p.usu_extranjero,
            'usu_usuarioactivo_name': elige_choices(Usuario.OPCIONES, p.usu_usuarioactivo),
            'usu_usuarioactivo': p.usu_usuarioactivo,
        })

    data = {
        'usuario': request.session["dicUsuario"],
        'lst_profesores': lst_profesores,
        'usuarioDocenteForm': UsuarioDocenteForm(),
        'filtro_usu_activo':filtro_usu_activo,
        'filtro_usu_extranjero':filtro_usu_extranjero,
    }
    return render(request, 'academico/listado_relatores.html', data)

# funcion para agregar relator
@csrf_exempt
def ajax_add_relator(request):
    lista_err = []

    form = UsuarioDocenteForm(request.POST or None)

    if form.is_valid():

        if Usuario.objects.filter(usu_mail=request.POST['usu_mail']).exists():
            is_save = 2
        else:

            a = form.save(commit=False)
            a.pais = Pais.objects.get(pa_id=request.POST['key_pais'])
            a.region = Region.objects.get(re_id=request.POST['key_region'])
            a.comuna = Comuna.objects.get(com_id=request.POST['key_comuna'])
            a.usu_tipousuario = 5
            a.save()
            is_save = 1
    else:
        # listado de errores, esto funciona como una especie de debug
        for field in form:
            for error in field.errors:
                lista_err.append({
                    'label': field.label,
                    'error': error,
                })
        is_save = 0

    html = {
        'lista_err': lista_err,
        'is_save': is_save
    }
    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

# funcion para editar un relator
@csrf_exempt
def ajax_edit_relator(request):
    lista_err = []

    usu_instance = Usuario.objects.get(usu_id=request.POST['hdn_usu_id'])
    form = UsuarioDocenteForm(request.POST or None, instance=usu_instance)

    if form.is_valid():

        a = form.save(commit=False)
        a.pais = Pais.objects.get(pa_id=request.POST['key_pais'])
        a.region = Region.objects.get(re_id=request.POST['key_region'])
        a.comuna = Comuna.objects.get(com_id=request.POST['key_comuna'])
        a.save()
        is_save = 1
    else:
        # listado de errores, esto funciona como una especie de debug
        for field in form:
            for error in field.errors:
                lista_err.append({
                    'label': field.label,
                    'error': error,
                })
        is_save = 0

    html = {
        'lista_err': lista_err,
        'is_save': is_save
    }
    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

# funcion para anular relator
@csrf_exempt
def ajax_anular_relator(request):

    try:
        del_relator = Usuario.objects.get(usu_tipousuario=5, usu_id=request.POST['usu_id'])
        del_relator.usu_usuarioactivo = 'N'
        del_relator.save()

        is_save=True
        the_error = ''
    except Exception as e:
        is_save = False
        the_error = str(e)

    html = {
        'is_save': is_save,
        'the_error':the_error
    }
    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

# ------------------------------------
# ******* AREA RELATORES *********
# ------------------------------------

# ------------------------------------
# ******* AREA CURSOS *********
# ------------------------------------
# CREANDO CURSO PARTE 0
def listado_cursos(request, accion):

    """
    Funcion que se encarga de mostrar el views que lista los cursos, crea cursos y configura un curso
    :param request: None
    :param accion: si el dato es add, quiere decir que mostrar el template con el listado de los cursos
                   y dando la opción de crear un curso. De lo contrario lo que trae es el id del curso, dando laopcopn de editar
                   o configurar.
    :return  academico/listado_cursos.html
    """

    if accion == 'add':

        # inicializa formularios
        frmCurso = CursoForm()
        frmFiltroCursosForm = FiltroCursosForm()
        lst_cursos = []

        # -------------------------------------------------
        # bloque hecho para los filtros de las paginas
        query_set = Curso.objects.all()

        if request.POST:
            lst_query = filtros_cabecera(request)
            for l in lst_query:
                frmFiltroCursosForm.fields[l['key']].initial = l['value']
                if l['key'] == 'cur_nivel':
                    query_set = query_set.filter(cur_nivel=l['value'])
                if l['key'] == 'cur_tipobanner':
                    query_set = query_set.filter(cur_tipobanner=l['value'])
                if l['key'] == 'cur_cursoactivo':
                    query_set = query_set.filter(cur_cursoactivo=l['value'])
        # -------------------------------------------------

        # listado de cursos
        for c in query_set:
            lst_cursos.append({
                'cur_id':c.cur_id,
                'cur_nombre':c.cur_nombre,
                'cur_nivel':elige_choices(Curso.NIVEL_CURSO, c.cur_nivel),
                'cur_codigocurso':c.cur_codigocurso,
                'cur_valor':c.cur_valor,
                'cur_duracionhoras':c.cur_duracionhoras,
                'producto':c.producto.prod_nombre.title(),
                'cur_codigosence':c.cur_codigosence,
                'ruta':reverse("integrado_namespace:listado_cursos", args=[c.cur_id])
            })

        data = {
            'usuario': request.session["dicUsuario"],
            'frmFiltroCursosForm': frmFiltroCursosForm,
            'frmCurso': frmCurso,
            'lst_cursos': lst_cursos,
            'accion': accion,
            'ruta_modal': reverse('integrado_namespace:ajax_add_primera_parte_curso', args=[]),
        }
    else:

        # dato del curso
        query_set = Curso.objects.get(cur_id=accion)

        # inicializa el formulario de los datos que faltan del curso
        frmCursoParteDosForm = CursoParteDosForm()
        frmModuloForm = ModuloForm()
        frmClaseForm = ClaseForm()

        # variables de configuracion en la pagina
        tabs_avalible=False
        multimedia = ""
        lst_modulos = []

        if query_set.cur_tabs == '2':
            pass
        if query_set.cur_tabs == '3':
            tabs_avalible = True
            multimedia = query_set.cur_imagen
        if query_set.cur_tabs == '4':
            tabs_avalible = True
            multimedia = query_set.cur_imagen
            los_modulos = Modulo.objects.filter(curso=query_set).order_by('-mod_numero')
            for m in los_modulos:
                lst_modulos.append({
                    'mod_id':m.mod_id,
                    'mod_titulo':m.mod_titulo,
                    'mod_numero':m.mod_numero,
                    'cur_nombre':m.curso.cur_nombre,
                    'mod_activar':m.mod_activar,
                    'ruta_modal_clases': reverse('integrado_namespace:add_clase', args=[accion, m.mod_id]),
                })

        frmCursoParteDosForm.fields['cur_descripcionpequenia'].initial = query_set.cur_descripcionpequenia
        frmCursoParteDosForm.fields['cur_introduccion'].initial = query_set.cur_introduccion
        frmCursoParteDosForm.fields['cur_requerimientos'].initial = query_set.cur_requerimientos
        frmCursoParteDosForm.fields['cur_tipobanner'].initial = query_set.cur_tipobanner
        frmCursoParteDosForm.fields['multimedia'].required = False

        dic_miga_pan = {
            'uno': '<a href="{r}" class="btn btn-default">{n}</a>'.format(r=reverse("integrado_namespace:listado_cursos", args=[query_set.cur_id]), n=query_set.cur_nombre),
            'dos': '<a href="javascript:void();" class="btn btn-dark">{}</a>'.format('módulo'),
        }

        data = {
            'usuario': request.session["dicUsuario"],
            'frmCursoParteDosForm':frmCursoParteDosForm,
            'frmModuloForm':frmModuloForm,
            'frmClaseForm':frmClaseForm,
            'accion':accion,
            'cur_nombre':query_set.cur_nombre,
            'tabs_avalible':tabs_avalible,
            'multimedia':multimedia,
            'ruta_modal': reverse('integrado_namespace:ajax_add_modulo_curso', args=[accion]),
            'lst_modulos':lst_modulos,
            'dic_miga_pan':dic_miga_pan,
        }
    return render(request, 'academico/listado_cursos.html', data)

# CREANDO CURSO PARTE 1
@csrf_exempt
def ajax_add_primera_parte_curso(request):

    """
    Funcion que se encarga de crear una primera instancea en la creación de un curso
    en donde se guardan algunos datos
    :param request: None
    :return response
    """
    lista_err = []
    is_save = False
    cur_id  = ""

    if request.POST:
        form = CursoForm(request.POST)
        if form.is_valid():
            a = form.save(commit=False)

            # crea instancea
            a.producto = Producto.objects.get(prod_id=request.POST['id_producto'])
            a.cur_tabs = 2
            a.save()

            # guarda el codigo y el nombre de la carpeta en
            # donde se guardara el curso
            a.cur_codigocurso = a.creaCodigoCurso.upper()
            a.cur_namefolder  = a.creaCodigoCurso.lower()
            a.save()

            # crea ruta de la carpeta en donde se guardara
            # el curso
            path = UPDATE_CURSOS_PATH + a.cur_namefolder

            # crea carpeta en donde se guardara el curso
            if not os.path.exists(path):
                os.makedirs(path)
                # aquí se crea el archivo
                archi = open(path + '/index.php', 'a')
                # aquí se le crea una linea de texto
                archi.write('<?php header("Location: /"); ?>')
                # se cierra el archivo
                archi.close()

            # os.chmod(path, 777)
            # ruta del directorio del curso
            a.cur_rutaimagenvideo = path
            a.save()

            is_save = True
            cur_id = a.cur_id
            request.session['cur_id'] = a.cur_id

        else:
            # listado de errores, esto funciona como una especie de debug
            for field in form:
                for error in field.errors:
                    lista_err.append({
                        'label': field.label,
                        'error': error,
                    })
            is_save = False

    html = {
        'is_save': is_save,
        'cur_id':cur_id,
        'the_error':lista_err,
    }
    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

# CREANDO CURSO PARTE 2
@csrf_exempt
def ajax_add_segunda_parte_curso(request, cur_id):

    """
    Funcion que se encarga de subir la imagen principal del curso
    :param request: None
    :param cur_id: id del curso
    :return response
    """

    lista_err = []
    is_save = False

    curso = Curso.objects.get(cur_id=cur_id)

    fileName, fileExtension = os.path.splitext(request.FILES['multimedia'].name)
    fileName = str(fileName).replace('-', '').split(' ')
    fileName = 'banner_{hora}{extension}'.format(na=curso.cur_codigocurso.lower(), hora=time.strftime("%H%M%S"),
                                                      extension=fileExtension)
    contenido_file = curso.cur_rutaimagenvideo+ "/" + fileName

    with open(contenido_file, 'wb+') as destination:
        for chunk in request.FILES['multimedia'].chunks():
            destination.write(chunk)
    destination.close()

    is_save = True
    curso.cur_tipobanner = request.POST['cur_tipobanner']
    curso.cur_imagen = fileName
    curso.cur_descripcionpequenia = request.POST['cur_descripcionpequenia']
    curso.cur_introduccion = request.POST['cur_introduccion']
    curso.cur_requerimientos = request.POST['cur_requerimientos']
    curso.cur_tabs = '3'
    curso.save()

    html = {
        'is_save': is_save,
        'the_error':lista_err
    }
    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

# CREANDO CURSO PARTE 3
@csrf_exempt
def ajax_add_modulo_curso(request, cur_id):
    """
    Funcion que crea una instancea de un modulo en la base de datos
    :param request: None
    :param
    :return response
    """
    lista_err = []

    form = ModuloForm(request.POST)

    if form.is_valid():

        query_set = Curso.objects.get(cur_id=cur_id)

        a = form.save(commit=False)
        a.curso = query_set
        a.save()

        query_set.cur_tabs = '4'
        query_set.save()
        is_save = True

    else:
        # listado de errores, esto funciona como una especie de debug
        for field in form:
            for error in field.errors:
                lista_err.append({
                    'label': field.label,
                    'error': error,
                })
        is_save = False

    html = {
        'lista_err': lista_err,
        'is_save': is_save
    }

    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

# CREANDO CURSO PARTE 4
def add_clase(request, cur_id, mod_id):
    """
    Funcion que se encarga de armar el template html de las clases
    :param request: None
    :param cur_id: id del curso para asociar la clase
    :return render(request, 'academico/cerrar_ficha.html', data)
    """
    frmClaseForm = ClaseForm()
    lst_clases = []
    query_set = Curso.objects.get(cur_id=cur_id)
    #query_clases = Clase.objects.filter(modulo__curso = query_set).exclude(cl_activarclase = 'N')
    query_clases = Clase.objects.filter(modulo_id = mod_id).exclude(cl_activarclase = 'N')

    for c in query_clases:
        lst_clases.append({
            'cl_id':c.cl_id,
            'modulo':c.modulo_id,
            'cl_titulo':c.cl_titulo,
            'cl_numero':c.cl_numero,
            'cl_introduccion':c.cl_introduccion,
            'cl_tipoclase': c.cl_tipoclase,
            'cl_activarclase': c.cl_activarclase,

            'cl_tipoclase_name':elige_choices(Clase.TIPO_CLASE, c.cl_tipoclase),
            'cl_activarclase_name':elige_choices(Clase.OPCIONES, c.cl_activarclase),
            'ruta_material': reverse('integrado_namespace:add_material_clase', args=[cur_id, mod_id, c.cl_id]),
            'cl_modulo_name': c.modulo.mod_titulo.lower(),
        })

    dic_miga_pan = {
        'uno': '<a href="{r}" class="btn btn-default">{n}</a>'.format(r=reverse("integrado_namespace:listado_cursos", args=[query_set.cur_id]), n=query_set.cur_nombre),
        'dos': '<a href="{r}" class="btn btn-default">módulo</a>'.format( r=reverse("integrado_namespace:listado_cursos", args=[query_set.cur_id])),
        'tres':'<a href="javascript:void();" class="btn btn-dark">{}</a>'.format('clases'),
    }

    data = {
        'usuario': request.session["dicUsuario"],
        'frmClaseForm': frmClaseForm,
        'ruta_modal': reverse('integrado_namespace:ajax_add_clase', args=[mod_id]),
        'miga_pan': "{} / modulo / clases".format(query_set.cur_nombre),
        'lst_clases': lst_clases,
        'dic_miga_pan': dic_miga_pan,
    }
    return render(request, 'academico/add_clases.html', data)

# CREANDO CURSO PARTE 5
@csrf_exempt
def ajax_add_clase(request, mod_id):
    """
    Funcion que crea una instancea de una clase en la base de datos
    :param request: None
    :param cur_id : id del curso para asociar la clase
    :return response
    """

    lista_err = []

    try:
        la_clase = Clase.objects.get(cl_id=request.POST['flag_extra'])
        form = ClaseForm(request.POST, instance=la_clase)
    except:
        form = ClaseForm(request.POST or None)

    if form.is_valid():

        query_set_modulo = Modulo.objects.get(mod_id=mod_id)

        a = form.save(commit=False)
        a.modulo = query_set_modulo
        a.save()

        is_save = True

    else:
        # listado de errores, esto funciona como una especie de debug
        for field in form:
            for error in field.errors:
                lista_err.append({
                    'label': field.label,
                    'error': error,
                })
        is_save = False

        print(lista_err)

    html = {
        'lista_err': lista_err,
        'is_save': is_save
    }

    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

# CREANDO CURSO PARTE 6.1
@csrf_exempt
def delete_clase_curso(request, cl_id):
    """
    Funcion que crea una instancea de materiales en la base de datos
    :param request: None
    :param cur_id : id del curso para asociar el material a la clase
    :return response
    """
    lista_err = []
    is_save = False
    la_clase = Clase.objects.get(cl_id = cl_id)
    la_clase.cl_activarclase = 'N'
    la_clase.save()

    return redirect('integrado_namespace:add_clase', la_clase.modulo.curso_id, la_clase.modulo_id)

# CREANDO CURSO PARTE 6
def add_material_clase(request, cur_id, mod_id, cl_id):
    """
    Funcion que se encarga de armar el template html de las clases
    :param request: None
    :param cur_id: id del curso para asociar la clase
    :return render(request, 'academico/cerrar_ficha.html', data)
    """
    frmMaterialForm = MaterialForm()
    lst_materiales = []
    query_set = Curso.objects.get(cur_id=cur_id)
    query_set_materiales = Material.objects.filter(clase_id = cl_id).exclude(mat_materialactivo = 'N')

    for m in query_set_materiales:

        lst_materiales.append({
            'mat_id':m.mat_id,
            'mat_titulo':m.mat_titulo,
            'mat_numero':m.mat_numero,
            'mat_numero':m.mat_numero,
            'mat_tipomaterial':elige_choices(Material.TIPO_MATERIAL, m.mat_tipomaterial),
            'cl_id':m.clase.cl_id,
            'mat_texto':m.mat_texto,
            'mat_rutayoutube':m.mat_rutayoutube,
            'id_mat_tipomaterial': m.mat_tipomaterial,
            'mat_nombrearchivo': m.mat_nombrearchivo,
        })

    dic_miga_pan = {
        'uno'   : '<a href="{r}" class="btn btn-default">{n}</a>'.format(r=reverse("integrado_namespace:listado_cursos", args=[query_set.cur_id]), n=query_set.cur_nombre),
        'dos'   : '<a href="{r}" class="btn btn-default">{n}</a>'.format(r=reverse("integrado_namespace:listado_cursos", args=[query_set.cur_id]), n='módulo'),
        'tres'  : '<a href="{r}" class="btn btn-default">{n}</a>'.format(r=reverse("integrado_namespace:add_clase", args=[cur_id, mod_id]), n='clases'),
        'cuatro': '<a href="javascript:void();" class="btn btn-dark">{}</a>'.format('materiales'),
    }

    data = {
        'usuario': request.session["dicUsuario"],
        'frmMaterialForm': frmMaterialForm,
        'ruta_modal': reverse('integrado_namespace:ajax_add_material_clase_curso', args=[cur_id, cl_id]),
        'dic_miga_pan': dic_miga_pan,
        'lst_materiales': lst_materiales,
    }
    return render(request, 'academico/add_material.html', data)

# CREANDO CURSO PARTE 6.1
@csrf_exempt
def ajax_add_material_clase_curso(request, cur_id, cl_id):
    """
    Funcion que crea una instancea de materiales en la base de datos
    :param request: None
    :param cur_id : id del curso para asociar el material a la clase
    :return response
    """
    lista_err = []

    try:
        el_material = Material.objects.get(mat_id=request.POST['flag_extra'])
        form = MaterialForm(request.POST, instance=el_material)
    except:
        form = MaterialForm(request.POST or None)

    if form.is_valid():

        query_set = Curso.objects.get(cur_id=cur_id)

        if request.FILES:

            fileName, fileExtension = os.path.splitext(request.FILES['multimedia'].name)
            fileName = str(fileName).replace('-', '').split(' ')
            fileName = 'archivo_{hora}{extension}'.format(na=query_set.cur_codigocurso.lower(),
                                                          hora=time.strftime("%H%M%S"),
                                                          extension=fileExtension)
            contenido_file = query_set.cur_rutaimagenvideo + "/" + fileName

        query_set_clase = Clase.objects.get(cl_id = cl_id)
        a = form.save(commit=False)
        a.clase = query_set_clase

        if request.FILES:
            a.mat_rutaarchivo = query_set.cur_rutaimagenvideo
            a.mat_nombrearchivo = fileName

        a.save()

        is_save = True

        if request.FILES:
            with open(contenido_file, 'wb+') as destination:
                for chunk in request.FILES['multimedia'].chunks():
                    destination.write(chunk)
            destination.close()

    else:
        # listado de errores, esto funciona como una especie de debug
        for field in form:
            for error in field.errors:
                lista_err.append({
                    'label': field.label,
                    'error': error,
                })
        is_save = False

    html = {
        'lista_err': lista_err,
        'is_save': is_save
    }

    response = json.dumps(html)
    return HttpResponse(response, content_type='application/json')

# CREANDO CURSO PARTE 6.2
@csrf_exempt
def delete_material_clase_curso(request, mat_id):
    """
    Funcion que crea una instancea de materiales en la base de datos
    :param request: None
    :param cur_id : id del curso para asociar el material a la clase
    :return response
    """
    lista_err = []
    is_save = False
    el_material = Material.objects.get(mat_id = mat_id)
    el_material.mat_materialactivo = 'N'
    el_material.save()

    return redirect('integrado_namespace:add_material_clase', el_material.clase.modulo.curso_id)

# ------------------------------------
# ******* AREA CURSOS *********
# ------------------------------------

# ------------------------------------
# ******* AREA FICHAS/ALUMNO/CURSO *********
# ------------------------------------

def cerrar_ficha(request):

    data = {
        'usuario': request.session["dicUsuario"],
    }
    return render(request, 'academico/cerrar_ficha.html', data)

def dar_plazo_ficha(request):

    data = {
        'usuario': request.session["dicUsuario"],
    }
    return render(request, 'academico/dar_plazo_ficha.html', data)

def dar_plazo_alumno(request):

    data = {
        'usuario': request.session["dicUsuario"],
    }
    return render(request, 'academico/dar_plazo_alumno.html', data)

# ------------------------------------
# ******* AREA CURSOS *********
# ------------------------------------


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

from __future__ import unicode_literals, absolute_import, division, print_function, absolute_import

import gevent.monkey
import gevent.pool
gevent.monkey.patch_all()

from collections import OrderedDict
from datetime import datetime
from importlib import import_module
from requests.models import Response
import click
import copy
import glob
import html2text
import itertools
import json
import logging
import math
import os
import random
import re
import requests
import signal
import sys
import time
#import urllib.request

# for debug to disable insecureWarning
requests.packages.urllib3.disable_warnings()
requests.adapters.DEFAULT_RETRIES = 3

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

class WiktionaryCrawler:

    def __init__(self, input_proxies_file, json_output_path, process_seq=0, process_count=1):
        self.pool = gevent.pool.Pool(400)
        self.word_list = []
        self.proxy_anchor = 0
        self.proxy_list = []
        self.downloaded = 0
        self.word_retry = {}
        self.proxies_hash = {}
        self.json_output_path = json_output_path

        # 多进程模式
        self.process_seq   = process_seq
        self.process_count = process_count

        self.load_proxies(input_proxies_file)
        random.shuffle(self.proxy_list) # 打乱列表, 避免不同scheme, 相同IP/Port的相连.

        self.load_word_list()

    def get_wiki_json(self, word):
        proxy = self.next_proxy()
        try:
            self.process_proxies_info(proxy, { 'request': '1' })
            # 代理的请求+1
            proxy['request'] += 1
            # 请求URL, 获得json返回
            host = proxy.get('host')
            port = proxy.get('port')
            scheme = proxy['type']

            request_proxies = { scheme: "%s:%s" % (host, port) }
            if scheme == 'socks4' or scheme == 'socks5':
                request_proxies = { 'http': "http://%s:%s" % (host, port), 'https': "https://%s:%s" % (host, port) }
                scheme = 'https'
            # https://www.wordnik.com/words/hello
            url = "%s://www.wordnik.com/words/"  % (scheme)
            r = requests.get( url + word,proxies=request_proxies, timeout=20, verify=False)
            # 成功
            if r.status_code is 200:
                self.process_proxies_info(proxy, { 'success': '1' })
                return (word, r.content, None)
            else:
                return (word, None, r.status_code)
            #return (word, None, 500)
        except requests.exceptions.ProxyError:
            self.process_proxies_info(proxy, { 'failure': '1' })
            return (word, None, "ProxyError")
        except Exception:
            self.process_proxies_info(proxy, { 'refused': '1' })
            return (word, None, "Error")

    def batch_download_words(self, words):
        word_success = []

        # 将结果保存到列表中
        def save_result(p):
            try:
                word, entry, err = p
                if err:
                    # 超过5次
                    if self.word_retry[word] > 1:
                        words.remove(word)
                        self.word_list.append(word) # 加到尾部, 再次尝试.
                    self.word_retry[word] += 1
                    pass
                else:
                    self.downloaded += 1

                    words.remove(word)
                    word_success.append([word, entry])
            except Exception as e:
                pass

        for word in words:
            self.pool.apply_async(self.get_wiki_json, args=(word,), callback=save_result)

        request_begin = time.time()
        # 等待结束
        timeout=60*(len(words)/200)
        if timeout < 60 : timeout = 60
        timeout =60

        is_empty = self.pool.join(timeout=timeout)
        self.pool.kill()

        request_end = time.time()
        request_time_spent = round(request_end - request_begin, 2)

        # 保存文件
        for word_info in word_success:
            word = word_info[0]
            entry = word_info[1]
            if word == 'aux':
                continue
            fw = open("d:\\enwiktionary\\"+self.json_output_path+"\\"+word+".html", 'w', -1, encoding="utf_8_sig")
            fw.write(entry.decode("utf-8"))
            fw.close()

        io_end = time.time()
        io_time_spent = round( io_end - request_end , 2)

        timeout_str = "\ttime out..." if not is_empty else ""

        print(str(self.process_seq)+\
              "\tdownloaded: "+str(self.downloaded) +\
              "\tproxy: "+ str(len(self.proxy_list)) +\
              "\trequest time: "+ str(request_time_spent) +\
              "\tio time: "+ str(io_time_spent) +\
              timeout_str )

    #==-------------------------------------------------------
    # 获取一个成功过的代理
    def next_proxy(self):
        # 因为删除的原因
        if self.proxy_anchor >= len(self.proxy_list):
            self.proxy_anchor = 0

        proxy = self.proxy_list[self.proxy_anchor]
        self.proxy_anchor += 1

        return proxy

    def load_proxies(self, input_proxies_file):
        for proxy_file in input_proxies_file:
            if proxy_file and os.path.exists(proxy_file):
                with open(proxy_file) as fd:
                    for line in fd:
                        try:
                            j = json.loads(line)
                            j['failure'] = 0
                            j['success'] = 0
                            j['request'] = 0
                            j['refused'] = 0

                            type_= j['type']
                            host = j['host']
                            port = j['port']
                            proxy_hash = '%s:%s:%s' % (type_, host, port)
                            if proxy_hash in self.proxies_hash:
                                continue

                            if type_ == 'http':
                                continue

                            self.proxy_list.append(j)
                        except Exception as e:
                            logger.info("[-] error: %s" % ( str(e)))

    def dump_proxies_info(self):
        for proxy in self.proxy_list:
            pass

    def process_proxies_info(self, proxy, test_info):

        if test_info.get('request'):
            # 代理的请求+1
            proxy['request'] += 1
        elif test_info.get('success'):
            proxy['success'] += 1
            proxy['failure'] = 0 # 成功过一次, 将其归零. 说明这个代理是正常的.
            proxy['refused'] = 0
        elif test_info.get('failure'):
            proxy['failure'] += 1
            # 如果代理连续5次连接不上, 将其删除掉
            if proxy['failure'] > 1:
                self.proxy_list.remove(proxy)
        elif test_info.get('refused'):
            proxy['refused'] += 1
            if proxy['refused'] > 10:
                self.proxy_list.remove(proxy)

    #==-------------------------------------------------------

    def load_downloaded_word_list(self):
        self.downloaded_word_list = {}

        downloaded_file = "d:\\enwiktionary\\downloaded.txt"
        #if not os.path.exists(downloaded_file):
        #    f = open(downloaded_file, 'w', -1, encoding="utf_8_sig")
        #    for fpath in glob.glob("d:\\enwiktionary\\*\\*.json"):
        #        m = re.search("([^\\\\]+)[.]json", fpath)
        #        fn = m.group(1)
        #        fn=fn.strip()
        #        f.write(fn+"\n")
        #    f.close()
        #    raise Exception("create download list.................")
        #else:
        if True:
            # 加载已经的下载单词列表
            f = open(downloaded_file, 'r', -1, encoding="utf_8_sig")
            for word in f:
                word = word.replace("\n","")
                word = word.strip()
                self.downloaded_word_list[word] = True
            f.close()

    def load_word_list(self):
        # 先加载已经下载的单词列表
        self.load_downloaded_word_list()

        fd_word_list    = open("d:\\enwiktionary-latest-all-titles-in-ns0-filted.txt", 'r', -1, encoding="utf_8_sig")
        for line in fd_word_list:
            line = line.replace("\n", "")
            word = line.strip()

            # 该单词还没有下载
            if len(word) > 0 and self.downloaded_word_list.get(word) is None:
                self.word_list.append(word)

        fd_word_list.close()

        print("undownloaded: "+str(len(self.word_list)) +"\tdownloaded: "+ str(len(self.downloaded_word_list)) )

        # 在多进程下面, 只检测部分
        if self.process_count > 1:
            num_per_part = int((len(self.word_list)+self.process_count-1)/self.process_count)
            start = self.process_seq*num_per_part
            end   = (self.process_seq+1)*num_per_part
            self.word_list = self.word_list[start:end]

            print("downloading range( {0}, {1} ), total number: {2}".format(start, end, len(self.word_list)))

    def start(self):
        cur_word_list = []

        while len(self.word_list) and len(self.proxy_list):

            # 每次1000个批量查询
            num = min(400-len(cur_word_list), len(self.word_list))
            for i in range(0, num):
                word = self.word_list.pop()
                cur_word_list.append(word)
                self.word_retry[word] = 0

            # 去下载这批数据
            self.batch_download_words(cur_word_list)

@click.command()
@click.argument('process_seq')
def main(process_seq):

    time.sleep(int(process_seq)*30)

    process_count = 6

    fs = []
    fs += glob.glob("x_valid_proxies*.txt")
    v = WiktionaryCrawler(fs, "20200210-00-"+str(process_seq), int(process_seq), process_count)
    v.start()

if __name__ == '__main__':
    main()


#!/usr/bin/env python
from __future__ import print_function

import sys, gym, time

import numpy as np
from PIL import Image
#from matplotlib import pyplot as plt
import cv2

from PyV4L2Camera.camera import Camera
from PyV4L2Camera.controls import ControlIDs

from keras.models import load_model
from statistics import mode
from utils.datasets import get_labels
from utils.inference import detect_faces
from utils.inference import draw_text
from utils.inference import draw_bounding_box
from utils.inference import apply_offsets
from utils.inference import load_detection_model
from utils.preprocessor import preprocess_input

from PIL import Image

USE_WEBCAM = True # If false, loads video file source

# parameters for loading data and images
emotion_model_path = './models/emotion_model.hdf5'
emotion_labels = get_labels('fer2013')

# hyper-parameters for bounding boxes shape
frame_window = 10
emotion_offsets = (20, 40)

# loading models
face_cascade = cv2.CascadeClassifier('./models/haarcascade_frontalface_default.xml')
emotion_classifier = load_model(emotion_model_path)

# getting input model shapes for inference
emotion_target_size = emotion_classifier.input_shape[1:3]

# starting lists for calculating modes
emotion_window = []

# starting video streaming

cv2.namedWindow('window_frame')
#video_capture = cv2.VideoCapture(0)
# Select video or webcam feed
cap = None

# from PyV4L2Camera.camera import Camera
# from PyV4L2Camera.controls import ControlIDs

camera = Camera('/dev/video0', 1920, 1080)
controls = camera.get_controls()

#
# Test yourself as a learning agent! Pass environment name as a command-line argument, for example:
#
# python keyboard_agent.py SpaceInvadersNoFrameskip-v4
#

env = gym.make('LunarLander-v2' if len(sys.argv)<2 else sys.argv[1])

if not hasattr(env.action_space, 'n'):
    raise Exception('Keyboard agent only supports discrete action spaces')
ACTIONS = env.action_space.n
SKIP_CONTROL = 0    # Use previous control decision SKIP_CONTROL times, that's how you
                    # can test what skip is still usable.

human_agent_action = 0
human_wants_restart = False
human_sets_pause = False

### LEO WEBCAM SECTION

for control in controls:
    print(control.name)

camera.set_control_value(ControlIDs.BRIGHTNESS, 48)

### LEO END SECTION

def key_press(key, mod):
    global human_agent_action, human_wants_restart, human_sets_pause
    if key==0xff0d: human_wants_restart = True
    if key==32: human_sets_pause = not human_sets_pause
    a = int( key - ord('0') )
    if a <= 0 or a >= ACTIONS: return
    human_agent_action = a

def key_release(key, mod):
    global human_agent_action
    a = int( key - ord('0') )
    if a <= 0 or a >= ACTIONS: return
    if human_agent_action == a:
        human_agent_action = 0

env.render()
env.unwrapped.viewer.window.on_key_press = key_press
env.unwrapped.viewer.window.on_key_release = key_release

#myfig = plt.figure(figsize=(5,5))

def rollout(env):
    global human_agent_action, human_wants_restart, human_sets_pause
    human_wants_restart = False
    obser = env.reset()
    skip = 0
    total_reward = 0
    total_timesteps = 0
    while 1:
        if not skip:
##### LEO SECTION
            frame = camera.get_frame()

            # Decode the image
            im = Image.frombytes('RGB', (camera.width, camera.height), frame, 'raw',
                                 'RGB')

            # Convert the image to a numpy array and back to the pillow image
            arr = np.asarray(im)
        #    im = Image.fromarray(np.uint8(arr))

            bgr_image = arr

            #bgr_image = video_capture.read()[1]

            gray_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2GRAY)
            rgb_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)

            faces = face_cascade.detectMultiScale(gray_image, scaleFactor=1.1, minNeighbors=5,
        			minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE)

            for face_coordinates in faces:

                x1, x2, y1, y2 = apply_offsets(face_coordinates, emotion_offsets)
                gray_face = gray_image[y1:y2, x1:x2]
                try:
                    gray_face = cv2.resize(gray_face, (emotion_target_size))
                except:
                    continue

                gray_face = preprocess_input(gray_face, True)
                gray_face = np.expand_dims(gray_face, 0)
                gray_face = np.expand_dims(gray_face, -1)
                emotion_prediction = emotion_classifier.predict(gray_face)
                emotion_probability = np.max(emotion_prediction)
                emotion_label_arg = np.argmax(emotion_prediction)
                emotion_text = emotion_labels[emotion_label_arg]
                emotion_window.append(emotion_text)

                if len(emotion_window) > frame_window:
                    emotion_window.pop(0)
                try:
                    emotion_mode = mode(emotion_window)
                except:
                    continue

                if emotion_text == 'angry':
                    color = emotion_probability * np.asarray((255, 0, 0))
                elif emotion_text == 'sad':
                    color = emotion_probability * np.asarray((0, 0, 255))

                    human_agent_action = 3

                elif emotion_text == 'happy':
                    color = emotion_probability * np.asarray((255, 255, 0))

                    human_agent_action = 2

                elif emotion_text == 'surprise':
                    color = emotion_probability * np.asarray((0, 255, 255))
                elif emotion_text == 'fear':
                    print('YOU SCARED - YOU DIE')
                    break

                else:
                    color = emotion_probability * np.asarray((0, 255, 0))

                    human_agent_action = 1

                color = color.astype(int)
                color = color.tolist()

                draw_bounding_box(face_coordinates, rgb_image, color)
                draw_text(face_coordinates, rgb_image, emotion_mode,
                          color, 0, -45, 1, 1)

            bgr_image = cv2.cvtColor(rgb_image, cv2.COLOR_RGB2BGR)
            cv2.imshow('window_frame', bgr_image)
            cv2.waitKey(10)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break

#### END LEO SECTION

#            human_agent_action = np.random.randint(1,high=4)
            print("taking action {}".format(human_agent_action))

            a = human_agent_action
            total_timesteps += 1
            skip = SKIP_CONTROL
        else:
            skip -= 1

        obser, r, done, info = env.step(a)
        if r != 0:
            print("reward %0.3f" % r)
        total_reward += r
        window_still_open = env.render()
        if window_still_open==False: return False
        if done: break
        if human_wants_restart: break
        while human_sets_pause:
            env.render()
            time.sleep(0.1)
        time.sleep(0.1)
    print("timesteps %i reward %0.2f" % (total_timesteps, total_reward))

print("ACTIONS={}".format(ACTIONS))
print("Press keys 1 2 3 ... to take actions 1 2 3 ...")
print("No keys pressed is taking action 0")

while 1:
    window_still_open = rollout(env)
    if window_still_open==False: break


# -*- coding: utf-8 -*-

'''This module contains recurrent network structures.'''

import collections
import numpy as np
import re
import sys
import theano.tensor as TT

from . import feedforward

def batches(arrays, steps=100, batch_size=64, rng=None):
    '''Return a callable that generates samples from a dataset.

    Parameters
    ----------
    arrays : list of ndarray (time-steps, data-dimensions)
        Arrays of data. Rows in these arrays are assumed to correspond to time
        steps, and columns to variables. Multiple arrays can be given; in such
        a case, these arrays usually correspond to [input, output]---for
        example, for a recurrent regression problem---or [input, output,
        weights]---for a weighted regression or classification problem.
    steps : int, optional
        Generate samples of this many time steps. Defaults to 100.
    batch_size : int, optional
        Generate this many samples per call. Defaults to 64. This must match the
        batch_size parameter that was used when creating the recurrent network
        that will process the data.
    rng : :class:`numpy.random.RandomState` or int, optional
        A random number generator, or an integer seed for a random number
        generator. If not provided, the random number generator will be created
        with an automatically chosen seed.

    Returns
    -------
    callable :
        A callable that can be used inside a dataset for training a recurrent
        network.
    '''
    assert batch_size >= 2, 'batch_size must be at least 2!'
    assert isinstance(arrays, (tuple, list)), 'arrays must be a tuple or list!'

    if rng is None or isinstance(rng, int):
        rng = np.random.RandomState(rng)

    def sample():
        xs = [np.zeros((batch_size, steps, a.shape[1]), a.dtype) for a in arrays]
        for i in range(batch_size):
            j = rng.randint(len(arrays[0]) - steps)
            for x, a in zip(xs, arrays):
                x[i] = a[j:j+steps]
        return xs

    return sample

class Text(object):
    '''A class for handling sequential text data.

    Parameters
    ----------
    text : str
        A blob of text.
    alpha : str, optional
        An alphabet to use for representing characters in the text. If not
        provided, all characters from the text occurring at least ``min_count``
        times will be used.
    min_count : int, optional
        If the alphabet is to be computed from the text, discard characters that
        occur fewer than this number of times. Defaults to 2.
    unknown : str, optional
        A character to use to represent "out-of-alphabet" characters in the
        text. This must not be in the alphabet. Defaults to '\0'.

    Attributes
    ----------
    text : str
        A blob of text, with all non-alphabet characters replaced by the
        "unknown" character.
    alpha : str
        A string containing each character in the alphabet.
    '''

    def __init__(self, text, alpha=None, min_count=2, unknown='\0'):
        self.alpha = alpha
        if self.alpha is None:
            self.alpha = ''.join(sorted(set(
                char for char, count in
                collections.Counter(text).items()
                if char != unknown and count >= min_count)))
        self.text = re.sub(r'[^{}]'.format(re.escape(self.alpha)), unknown, text)
        assert unknown not in self.alpha
        self._rev_index = unknown + self.alpha
        self._fwd_index = dict(zip(self._rev_index, range(1 + len(self.alpha))))

    def encode(self, txt):
        '''Encode a text string by replacing characters with alphabet index.

        Parameters
        ----------
        txt : str
            A string to encode.

        Returns
        -------
        classes : list of int
            A sequence of alphabet index values corresponding to the given text.
        '''
        return list(self._fwd_index.get(c, 0) for c in txt)

    def decode(self, enc):
        '''Encode a text string by replacing characters with alphabet index.

        Parameters
        ----------
        classes : list of int
            A sequence of alphabet index values to convert to text.

        Returns
        -------
        txt : str
            A string containing corresponding characters from the alphabet.
        '''
        return ''.join(self._rev_index[c] for c in enc)

    def classifier_batches(self, steps, batch_size, rng=None):
        '''Create a callable that returns a batch of training data.

        Parameters
        ----------
        steps : int
            Number of time steps in each batch.
        batch_size : int
            Number of training examples per batch.
        rng : :class:`numpy.random.RandomState` or int, optional
            A random number generator, or an integer seed for a random number
            generator. If not provided, the random number generator will be
            created with an automatically chosen seed.

        Returns
        -------
        batch : callable
            A callable that, when called, returns a batch of data that can be
            used to train a classifier model.
        '''
        assert batch_size >= 2, 'batch_size must be at least 2!'

        if rng is None or isinstance(rng, int):
            rng = np.random.RandomState(rng)

        T = np.arange(steps)

        def batch():
            inputs = np.zeros((batch_size, steps, 1 + len(self.alpha)), 'f')
            outputs = np.zeros((batch_size, steps), 'i')
            for b in range(batch_size):
                offset = rng.randint(len(self.text) - steps - 1)
                enc = self.encode(self.text[offset:offset + steps + 1])
                inputs[b, T, enc[:-1]] = 1
                outputs[b, T] = enc[1:]
            return [inputs, outputs]

        return batch

class Autoencoder(feedforward.Autoencoder):
    '''An autoencoder network attempts to reproduce its input.

    Notes
    =====

    A recurrent autoencoder model requires the following inputs during training:

    - ``x``: A three-dimensional array of input data. Each element of axis 0 of
      ``x`` is expected to be one sample in a minibatch. Each element of axis 1
      of ``x`` represents a moment of time. Each element of axis 2 of ``x``
      represents the measurements of a particular input variable across all
      times and all data items.
    '''

    def __init__(self, layers=(), loss='mse', weighted=False):
        super(feedforward.Autoencoder, self).__init__(
            layers=layers, loss=loss, in_dim=3, weighted=weighted)

class Regressor(feedforward.Regressor):
    '''A regressor attempts to produce a target output.

    Notes
    =====

    A recurrent regression model takes the following inputs:

    - ``x``: A three-dimensional array of input data. Each element of axis 0 of
      ``x`` is expected to be one moment in time. Each element of axis 1 of
      ``x`` holds a single sample from a batch of data. Each element of axis 2
      of ``x`` represents the measurements of a particular input variable across
      all times and all data items.

    - ``targets``: A three-dimensional array of target output data. Each element
      of axis 0 of ``targets`` is expected to be one moment in time. Each
      element of axis 1 of ``targets`` holds a single sample from a batch of
      data. Each element of axis 2 of ``targets`` represents the measurements of
      a particular output variable across all times and all data items.
    '''

    def __init__(self, layers=(), loss='mse', weighted=False):
        super(feedforward.Regressor, self).__init__(
            layers=layers, loss=loss, in_dim=3, out_dim=3, weighted=weighted)

class Classifier(feedforward.Classifier):
    '''A classifier attempts to match a 1-hot target output.

    Notes
    =====

    Unlike a feedforward classifier, where the target labels are provided as a
    single vector, a recurrent classifier requires a vector of target labels for
    each time step in the input data. So a recurrent classifier model requires
    the following inputs for training:

    - ``x``: A three-dimensional array of input data. Each element of axis 0 of
      ``x`` is expected to be one moment in time. Each element of axis 1 of
      ``x`` holds a single sample in a batch of data. Each element of axis 2 of
      ``x`` represents the measurements of a particular input variable across
      all times and all data items in a batch.

    - ``labels``: A two-dimensional array of integer target labels. Each element
      of ``labels`` is expected to be the class index for a single batch item.
      Axis 0 of this array represents time, and axis 1 represents data samples
      in a batch.
    '''

    def __init__(self, layers=(), loss='xe', weighted=False):
        super(feedforward.Classifier, self).__init__(
            layers=layers, loss=loss, in_dim=3, out_dim=2, weighted=weighted)

    def predict_sequence(self, labels, steps, streams=1, rng=None):
        '''Draw a sequential sample of class labels from this network.

        Parameters
        ----------
        labels : list of int
            A list of integer class labels to get the classifier started.
        steps : int
            The number of time steps to sample.
        streams : int, optional
            Number of parallel streams to sample from the model. Defaults to 1.
        rng : :class:`numpy.random.RandomState` or int, optional
            A random number generator, or an integer seed for a random number
            generator. If not provided, the random number generator will be
            created with an automatically chosen seed.

        Yields
        ------
        label(s) : int or list of int
            Yields at each time step an integer class label sampled sequentially
            from the model. If the number of requested streams is greater than
            1, this will be a list containing the corresponding number of class
            labels.
        '''
        if rng is None or isinstance(rng, int):
            rng = np.random.RandomState(rng)
        offset = len(labels)
        batch = max(2, streams)
        inputs = np.zeros((batch, offset + steps, self.layers[0].size), 'f')
        inputs[:, np.arange(offset), labels] = 1
        for i in range(offset, offset + steps):
            chars = []
            for pdf in self.predict_proba(inputs[:i])[:, -1]:
                try:
                    c = rng.multinomial(1, pdf).argmax(axis=-1)
                except ValueError:
                    # sometimes the pdf triggers a normalization error. just
                    # choose greedily in this case.
                    c = pdf.argmax(axis=-1)
                chars.append(int(c))
            inputs[np.arange(batch), i, chars] = 1
            yield chars[0] if streams == 1 else chars


import pytest

import pandas as pd
import numpy as np

from pyrle import Rle
from io import StringIO

from pyrle import PyRles
from pyrle.methods import coverage, _to_ranges, to_ranges

import pyranges as pr

@pytest.fixture
def chipseq_dataset():

    gr = pr.load_dataset("chipseq")

    return gr

@pytest.fixture
def teensy():

    c = """Chromosome Start End Name Score Strand
chr2 13611 13636 U0 2 -
chr2 32620 32645 U0 4 -
chr2 33241 33266 U0 1 +
chr2 788268 788293 U0 0 -
chr2 1150665 1150690 U0 -1 -"""

    return pr.PyRanges(pd.read_table(StringIO(c), sep="\s+"))

# def test_roundtrip_to_ranges_grles(chipseq_dataset):

#     cv = PyRles(chipseq_dataset)

#     chipseq_dataset.df.to_csv("input_dataset.txt", sep=" ")
#     gr = to_ranges(cv)

#     print(chipseq_dataset)
#     print(gr)
#     gr.df.to_csv("result.txt", sep=" ")

#     assert list(gr.df.Start) == list(chipseq_dataset.df.Start)
#     assert list(gr.df.Chromosome) == list(chipseq_dataset.df.Chromosome)
#     assert list(gr.df.End) == list(chipseq_dataset.df.End)

def test_roundtrip_to_ranges_single_rle_teensy(teensy):

    cv = coverage(teensy, value_col="Score")

    starts, ends, values = _to_ranges(cv)

    df = pd.concat([pd.Series(a) for a in [starts, ends, values]], axis=1)
    df.columns = "Start End Score".split()
    df.insert(0, "Chromosome", "chr2")

    gr = pr.PyRanges(df)

    # print(", ".join(str(i) for i in starts))
    # print(", ".join(str(f) for f in ends))
    assert list(starts) == [0, 13611, 13636, 32620, 32645, 33241, 33266, 1150665]
    assert list(ends) == [13611, 13636, 32620, 32645, 33241, 33266, 1150665, 1150690]

@pytest.fixture
def teensy_duplicated():

    "Contains duplicates"

    c = """Chromosome     Start       End Name  Score Strand
180       chr2  42058716  42058741   U0      0      +
181       chr2  42130511  42130536   U0      0      +
182       chr2  42593165  42593190   U0      0      -
183       chr2  42593165  42593190   U0      0      -
184       chr2  42635413  42635438   U0      0      -
185       chr2  43357333  43357358   U0      0      +
186       chr2  43854685  43854710   U0      0      +"""

    df = pd.read_table(StringIO(c), sep="\s+")

    return pr.PyRanges(df)

@pytest.fixture
def expected_result_teensy_duplicated():

    "Contains duplicates"

    c = """Chromosome     Start       End Name  Score Strand
180       chr2  42058716  42058741   U0      1      +
181       chr2  42130511  42130536   U0      1      +
183       chr2  42593165  42593190   U0      2      -
184       chr2  42635413  42635438   U0      1      -
185       chr2  43357333  43357358   U0      1      +
186       chr2  43854685  43854710   U0      1      +"""

    df = pd.read_table(StringIO(c), sep="\s+")

    return pr.PyRanges(df)

def test_roundtrip_to_ranges_single_rle_teensy_duplicated(teensy_duplicated, expected_result_teensy_duplicated):

    gr = teensy_duplicated
    cv = coverage(teensy_duplicated)
    # print(cv.values)

    starts, ends, values = _to_ranges(cv)

    # print(gr)
    # print(pr.PyRanges(gr.df.drop_duplicates()))
    # print("len(starts)", len(starts))
    # print("starts")
    # print(starts[:5])
    # print(starts[-5:])
    # print("ends")
    # print(ends[:5])
    # print(ends[-5:])

    assert list(starts) == [0, 42058716, 42058741, 42130511, 42130536, 42593165, 42593190, 42635413, 42635438, 43357333, 43357358, 43854685]
    assert list(ends) == [42058716, 42058741, 42130511, 42130536, 42593165, 42593190, 42635413, 42635438, 43357333, 43357358, 43854685, 43854710]

@pytest.fixture
def expected_result_single_chromosome(chipseq_dataset):

    return pr.PyRanges(chipseq_dataset["chr2"].df.drop_duplicates())

@pytest.fixture
def overlapping_gr():

    c = """Chromosome      Start        End Name  Score Strand
chr2  1  7   U0      0      -
chr2  4  10   U0      0      +"""

    df = pd.read_table(StringIO(c), sep="\s+")

    return pr.PyRanges(df)

def test_roundtrip_to_ranges_single_rle_overlapping(overlapping_gr):

    gr = overlapping_gr
    print(gr)
    cv = coverage(gr)
    print(cv)

    starts, ends, values = _to_ranges(cv)

    df = pd.concat([pd.Series(a) for a in [starts, ends, cv.values]], axis=1)
    df.columns = "Start End Score".split()
    df.insert(0, "Chromosome", "chr2")
    print(pr.PyRanges(df))
    # print(gr)
    # print(pr.PyRanges(gr.df.drop_duplicates()))
    # print("len(starts)", len(starts))
    # print("starts")
    # print(starts[:5])
    # print("ends")
    # print(ends[:5])

    assert list(starts) == [0, 1, 4, 7]
    assert list(ends) == [1, 4, 7, 10]

    # assert (starts == expected_result_problematic_gr.df.Start).all()
    # assert (ends == expected_result_problematic_gr.df.End).all()
    # assert (scores == expected_result_problematic_gr.df.Score).all()

# gr = PyRanges('toyChr',IRanges(cumsum(c(0,runLength(toyData)[-nrun(toyData)])),
#                               width=runLength(toyData)),
#              toyData = runValue(toyData))

# def test_roundtrip_to_ranges_single_chromosome(chipseq_dataset, expected_result_single_chromosome):

#     chr2 = chipseq_dataset["chr2"]
#     print(chr2)
#     print(expected_result_single_chromosome)

#     cv = coverage(chr2)
#     print(cv.values)

#     starts, ends, scores = _to_ranges(cv)

#     print(chr2.df.loc[list(range(600, 640))])
#     print(starts[183])
#     print(ends[183])

#     # gr = pr.PyRanges(pd.concat([chr2.df.head(5), chr2.df.tail(5)]))
#     # print(gr)
#     print("len(starts)", len(starts))
#     print("starts")
#     print(starts[:5])
#     print(starts[-5:])
#     print("ends")
#     print(ends[:5])
#     print(ends[-5:])
#     print("scores")
#     print(scores[:5])
#     print(scores[-5:])

#     assert list(starts) == list(expected_result_single_chromosome.df.Start)
#     assert (ends == expected_result_single_chromosome.df.End).all()
#     assert (scores == expected_result_single_chromosome.df.Score).all()

def test_create_grles(chipseq_dataset):

    grles = PyRles(chipseq_dataset)

    print(grles)

def test_create_stranded_grles(chipseq_dataset):

    grles = PyRles(chipseq_dataset, stranded=True)

    print(grles)

def test_create_stranded_grles_multicpu(chipseq_dataset):

    grles = PyRles(chipseq_dataset, stranded=True, nb_cpu=5)

    print(grles)

# def test_grange_to_grle_and_back():

@pytest.fixture
def expected_result_coverage():

    runs = """9739215      25  463205      25 3069430      25    9143      25  993038 25  142071      25  260968      25   71512      25   18072      25 103292 25""".split()
    runs = [int(s) for s in runs]
    values = [0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1.]

    return runs, values

@pytest.fixture
def df():

    c = """chr21	9739215	9739240	U0	0	-
chr21	10202445	10202470	U0	0	+
chr21	13271900	13271925	U0	0	-
chr21	13281068	13281093	U0	0	-
chr21	14274131	14274156	U0	0	-
chr21	14416227	14416252	U0	0	+
chr21	14677220	14677245	U0	0	-
chr21	14748757	14748782	U0	0	+
chr21	14766854	14766879	U0	0	+
chr21	14870171	14870196	U0	0	-"""

    return pd.read_table(StringIO(c), header=None, names="Chromosome Start End Name Score Strand".split())

@pytest.fixture
def grle1():

    r1 = Rle([1, 5, 10], [1, 2, 3])
    r2 = Rle([4, 7, 9], [0.01, 0.02, 0.03])

    d = {"chr1": r1, "chr2": r2}
    print(d)
    g = PyRles(d)
    print(g)
    return g

@pytest.fixture
def grle1_stranded(grle1):

    return PyRles({("chr1", "+"): grle1["chr1"], ("chr1", "-"): grle1["chr1"], ("chr2", "-"): grle1["chr2"]})

@pytest.fixture()
def grle2():

    r1 = Rle([1, 2, 3], [1, 2, 3])
    r2 = Rle([5, 4, 2], [-0.1, -0.2, -0.3])

    d = {"chr1": r1, "chr2": r2}

    return PyRles(d)

@pytest.fixture
def expected_result():

    r1 = Rle([1, 2, 3, 10], [2, 4, 5, 3])
    r2 = Rle([4, 1, 4, 2, 9], [-0.09, -0.08, -0.18, -0.28, 0.03])

    d = {"chr1": r1, "chr2": r2}

    return PyRles(d)

@pytest.fixture
def d1():

    r1 = Rle([1, 5, 10], [1, 2, 3])
    r2 = Rle([4, 7, 9], [0.01, 0.02, 0.03])
    print(r1)

    d = {"chr1": r1, "chr2": r2}

    return d

def test_create_PyRles(d1):

    # No errors
    result = PyRles(d1)

def test_add_PyRles(grle1, grle2, expected_result):

    result = grle1.add(grle2)
    print(result)
    print("----")
    print(expected_result)

    assert result == expected_result

def test_create_stranded_grles(grle1_stranded):

    result = to_ranges(grle1_stranded)

# df = read.table(text="Chromosome Start End Name Score Strand
# 625 chr2 175474407 175474432 U0 0 -
# 626 chr2 175474427 175474452 U0 0 +", sep=" ")

# ir = IRanges(start=df$Start, end=df$End)

# cv = coverage(ir)

# ir2 = IRanges(cumsum(c(0,runLength(cv)[-nrun(cv)])),
#               width=runLength(cv))