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

import os
import sys
import time
import random
import argparse
import threading

erros = []

def addErros(classe,codigo,erro):
	erros.append(dict(tituloErro=classe,
    codigoProblematico=codigo,
    erroPython=erro))

try: from Tkinter import *
except ImportError: from tkinter import *
except Exception: addErros((str(sys.exc_info()[0])+': Tkinter está instalado?'),'from Tkinter import *',sys.exc_info()[1])

try: import ttk
except ImportError: import tkinter.ttk as ttk
except Exception: addErros((str(sys.exc_info()[0])+': ttk está instalado?'),'from Tkinter import *',sys.exc_info()[1])

try: import tkMessageBox
except ImportError: import tkinter.messagebox

try: from tkColorChooser import askcolor
except ImportError: from tkinter.colorchooser import askcolor

try: from effects import *
except ImportError: addErros('ImportError: O arquivo efeitos.py está na pasta do código?','from effects import *',sys.exc_info()[1])
except NotImplementedError:	addErros('NotImplementedError: Erro na importação da biblioteca effects.py','from effects import *',sys.exc_info()[1])

try: from effectsBeta import *
except ImportError: addErros('ImportError: O arquivo efeitosBeta.py está na pasta do código?','from effectsBeta import *',sys.exc_info()[1])
except NotImplementedError:	addErros('NotImplementedError: Erro no código da biblioteca effectsBeta.py','from effectsBeta import *',sys.exc_info()[1])

try: from led5050 import *
except NotImplementedError:	addErros('NotImplementedError: Erro no código da biblioteca led5050.py','try: from led5050 import *',sys.exc_info()[1])
except ModuleNotFoundError: addErros('ModuleNotFoundError: Um módulo não foi executado','from led5050 import *',sys.exc_info()[1])
except ImportError: addErros('ImportError: O arquivo led5050.py está na pasta do código?','try: from led5050 import *',sys.exc_info()[1])
except Exception: addErros(sys.exc_info()[0],'from Tkinter import *',sys.exc_info()[1])

from decimal import *
from itertools import product
from functools import partial

pontoA=pontoB=redLed=greenLed=blueLed=vel = 0
atalho = 0

window = Tk()
offInNew = 0

def validateFields():
	if txtPontoA.get() == '':
		tkMessageBox.showinfo("Entrada necessaria", "Qual LED eu ligo? Ponto A necessario")
		return False
	return True

def desligar():
	off()

def testLED(event=0):
	if not validateFields(): return

	pontoA = int(txtPontoA.get())
	if txtPontoB.get() == '': pontoB = pontoA
	else: pontoB = int(txtPontoB.get())
	if txtR.get() == '': redLed = 0
	else: redLed = int(txtR.get())
	if txtG.get() == '': greenLed = 0
	else: greenLed = int(txtG.get())
	if txtB.get() == '': blueLed = 0
	else: blueLed = int(txtB.get())
	if txtVel.get() == '': vel = 0.1
	else: vel = float(txtVel.get())
	if txtBPM.get() == '': bpm = 128
	else: bpm = int(txtBPM.get())
	bpm = Decimal(60)/Decimal(bpm)

	if txtFuncao1.get() == '': funcao1 = 3
	else: funcao1 = int(txtFuncao1.get())

	if cmbEffects.current() == 0:
		testThread = threading.Thread(target=acenderLEDEffect,args=(pontoA,pontoB,redLed,greenLed,blueLed,))
		testThread.start()
	if cmbEffects.current() == 1:
		testThread = threading.Thread(target=simpleBassEffect,args=(pontoA,pontoB,redLed,greenLed,blueLed,vel,))
		testThread.start()
	if cmbEffects.current() == 2:
		testThread = threading.Thread(target=bassBracoEffectBETA,args=(pontoB/10,redLed,greenLed,blueLed,vel,))
		testThread.start()
	if cmbEffects.current() == 3:
		testThread = threading.Thread(target=corteCobraEffectBETA,args=(pontoA,pontoB,redLed,greenLed,blueLed,vel,funcao1,))
		testThread.start()
	if cmbEffects.current() == 4:
		testThread = threading.Thread(target=laserLeftEffectBETA,args=(pontoA,pontoB,redLed,greenLed,blueLed,vel,funcao1,))
		testThread.start()
	if cmbEffects.current() == 5:
		testThread = threading.Thread(target=corteEffectBETA,args=(pontoA,pontoB,redLed,greenLed,blueLed,vel,))
		testThread.start()
	if cmbEffects.current() == 6:
		testThread = threading.Thread(target=teatroEffect,args=(pontoA,pontoB,redLed,greenLed,blueLed,vel,funcao1))
		testThread.start()
	if cmbEffects.current() == 7:
		testThread = threading.Thread(target=posAleatoriaFade,args=(pontoA,pontoB,redLed,greenLed,blueLed,vel))
		testThread.start()
	if cmbEffects.current() == 8:
		testThread = threading.Thread(target=bassBracoInvertEffect2,args=(pontoA,pontoB,redLed,greenLed,blueLed,vel))
		testThread.start()
	if cmbEffects.current() == 9:
		testThread = threading.Thread(target=megaman,args=(pontoA,pontoB,redLed,greenLed,blueLed,vel))
		testThread.start()

def autoscroll(sbar, first, last):
    """Hide and show scrollbar as needed."""
    first, last = float(first), float(last)
    if first <= 0 and last >= 1:
        sbar.grid_remove()
    else:
        sbar.grid()
    sbar.set(first, last)

def janelaErros():

    janelaParaErros = Toplevel(window)
    janelaParaErros.title("Erros Sway LED")

    #   Barras horizontais
    vsb = ttk.Scrollbar(orient="vertical")
    hsb = ttk.Scrollbar(orient="horizontal")

    #   Objeto Treeview
    tree = ttk.Treeview(janelaParaErros, columns=("codigoProblematico","erroPython"), yscrollcommand=lambda f, l: autoscroll(vsb, f, l),
    xscrollcommand=lambda f, l:autoscroll(hsb, f, l))

    #   Barras de scroll às vistas x e y do objeto Treeview
    vsb['command'] = tree.yview
    hsb['command'] = tree.xview

    #   Cabeçalhos das diferentes colunas
    tree.heading("#0", text="Erro", anchor='w')
    tree.heading("codigoProblematico", text="Parte do código", anchor='w')
    tree.heading("erroPython", text="Erro Python", anchor='w')
    tree.column("#0", stretch=0, width=500)
    tree.column("codigoProblematico", stretch=0, width=200)
    tree.column("erroPython", stretch=0, width=200)

    #   Inserção na treeview
    for i in range(len(erros)):
    	tree.insert('', 'end', text=erros[i]["tituloErro"], values=[erros[i]["codigoProblematico"], erros[i]["erroPython"]])

    # Arrange the tree and its scrollbars in the toplevel
    tree.grid(column=0, row=0, sticky='nswe')
    vsb.grid(column=1, row=0, sticky='ns')
    hsb.grid(column=0, row=1, sticky='ew')
    janelaParaErros.grid_columnconfigure(0, weight=1)
    janelaParaErros.grid_rowconfigure(0, weight=1)

def pegarCor():
    color = askcolor()
    txtR.delete(0,END)
    txtR.insert(0,int(color[0][0]))
    txtG.delete(0,END)
    txtG.insert(0,int(color[0][1]))
    txtB.delete(0,END)
    txtB.insert(0,int(color[0][2]))

def pegarCorLed5050():
	color = askcolor()
	fitaLed(color[0][0],color[0][1],color[0][2])

def mostrarTxtFuncao(x):
	if cmbEffects.current() == 3:
		lblFuncao1.config(text = "Tamanho:")
		lblFuncao1.grid()
		txtFuncao1.grid()

	elif cmbEffects.current() == 4:
		lblFuncao1.config(text = "Duracao:")
		lblFuncao1.grid()
		txtFuncao1.grid()

	elif cmbEffects.current() == 6:
		lblFuncao1.config(text = "Duracao:")
		lblFuncao1.grid()
		txtFuncao1.grid()

	else:
		lblFuncao1.grid_remove()
		txtFuncao1.grid_remove()

def apagarTxts():
	txtR.delete(0,END)
	txtG.delete(0,END)
	txtB.delete(0,END)
	txtPontoA.delete(0,END)
	txtPontoB.delete(0,END)
	txtVel.delete(0,END)
	txtFuncao1.delete(0,END)

def atualizarCampos(pontoA,pontoB, R,G,B, vel, efeito, funcao1):
	txtR.insert(0,R)
	txtG.insert(0,G)
	txtB.insert(0,B)
	txtPontoA.insert(0,pontoA)
	txtPontoB.insert(0,pontoB)
	txtVel.insert(0,vel)
	txtFuncao1.insert(0,funcao1)
	cmbEffects.current(efeito)

def atalhoQ(event=0):
	rdbTheSermon.select()
	global atalho
	if atalho == 0:
		apagarTxts()
		# Atualizar Campos: Ponto A, Ponto B, R, G, B, Velocidade, Efeito, Funcao 1
		atualizarCampos("80","95","50","0","5",".02",1,"0")
		testLED()
		rdbTunnel1.select()
	elif atalho == 1:
		apagarTxts()
		atualizarCampos("80","95","50","50","0",".01",1,"0")
		testLED()
		rdbTunnel2.select()
	elif atalho == 2:
		apagarTxts()
		atualizarCampos("20","100","50","0","50",".01",4,"2")
		testLED()
		rdbTunnel3.select()
	elif atalho == 3:
		apagarTxts()
		atualizarCampos("20","80","24","0","100",".06",6,"2")
		testLED()
		rdbTunnel4.select()
	else:
		apagarTxts()
		# Atualizar Campos: Ponto A, Ponto B, R, G, B, Velocidade, Efeito, Funcao 1
		atualizarCampos("79", "119", "100", "100", "100", ".028", 1, "2")
		testLED()
		rdbTunnel5.select()

def atalhoW(event=0):
	rdbTheSermon.select()
	global atalho
	if atalho == 0:
		apagarTxts()
		atualizarCampos("50", "65", "50", "0", "5", ".02", 1, "0")
		testLED()
	elif atalho == 1:
		apagarTxts()
		txtR.insert(0,"50")
		txtG.insert(0,"50")
		txtPontoA.insert(0,"50")
		txtPontoB.insert(0,"65")
		txtVel.insert(0,".01")
		testLED()
	elif atalho == 4:
		apagarTxts()
		cmbEffects.current(1)
		txtR.insert(0,"100")
		txtG.insert(0,"100")
		txtB.insert(0,"100")
		txtPontoA.insert(0,"39")
		txtPontoB.insert(0,"79")
		txtVel.insert(0,".026")
		txtFuncao1.insert(0,"2")
		testLED()

def atalhoE(event=0):
	rdbTheSermon.select()
	global atalho
	if atalho == 0:
		cmbEffects.current(1)
		apagarTxts()
		txtR.insert(0,"50")
		txtB.insert(0,"5")
		txtPontoA.insert(0,"10")
		txtPontoB.insert(0,"25")
		txtVel.insert(0,".02")
		testLED()
	elif atalho == 1:
		apagarTxts()
		txtR.insert(0,"50")
		txtG.insert(0,"50")
		txtPontoA.insert(0,"10")
		txtPontoB.insert(0,"25")
		txtVel.insert(0,".01")
		testLED()
	elif atalho == 4:
		apagarTxts()
		cmbEffects.current(1)
		txtR.insert(0,"100")
		txtG.insert(0,"100")
		txtB.insert(0,"100")
		txtPontoA.insert(0,"0")
		txtPontoB.insert(0,"39")
		txtVel.insert(0,".019")
		txtFuncao1.insert(0,"2")
		testLED()

def atalhoA(event=0):
	apagarTxts()
	rndR = random.randint(40,100)
	rndG = random.randint(0,100)
	rndB = random.randint(0,100)
	atualizarCampos("50", "65", rndR,rndG,rndB, ".005", 3, "10")
	testLED()
	rdbTunnel.select()
def atalhoS(event=0):
	global atalho
	apagarTxts()
	atualizarCampos(0,60, 80,80,80, .06, 2, 0)
	testLED()
	rdbTunnel.select()
def atalhoD(event=0):
	pass

def atalhosVolta(event=0):
	global atalho
	if atalho == 0:
		atalho = 4
		rdbTheSermon5.select()
		rdbTunnel5.select()
	elif atalho == 1:
		atalho -= 1
		rdbTheSermon1.select()
		rdbTunnel1.select()
	elif atalho == 2:
		atalho -= 1
		rdbTheSermon2.select()
		rdbTunnel2.select()
	elif atalho == 3:
		atalho -= 1
		rdbTheSermon3.select()
		rdbTunnel3.select()
	else:
		atalho -= 1
		rdbTheSermon4.select()
		rdbTunnel4.select()

def atalhos(event=0):
	global atalho
	if atalho == 0:
		atalho += 1
		rdbTheSermon2.select()
		rdbTunnel2.select()
	elif atalho == 1:
		atalho += 1
		rdbTheSermon3.select()
		rdbTunnel3.select()
	elif atalho == 2:
		atalho += 1
		rdbTheSermon4.select()
		rdbTunnel4.select()
	elif atalho == 3:
		atalho += 1
		rdbTheSermon5.select()
		rdbTunnel5.select()
	else:
		atalho = 0
		rdbTheSermon1.select()
		rdbTunnel1.select()

def restart_program(event=0):
    """Restarts the current program.
    Note: this function does not return. Any cleanup action (like
    saving data) must be done before calling this function."""
    python = sys.executable
    os.execl(python, python, * sys.argv)

def wheel(pos):
    """Generate rainbow colors across 0-255 positions."""
    if pos < 85:
        return Color(pos * 3, 255 - pos * 3, 0)
    elif pos < 170:
        pos -= 85
        return Color(255 - pos * 3, 0, pos * 3)
    else:
        pos -= 170
        return Color(0, pos * 3, 255 - pos * 3)

def rainbowCycle(strip, wait_ms=20, iterations=5):
    """Draw rainbow that uniformly distributes itself across all pixels."""
    for j in range(256*iterations):
        for i in range(strip.numPixels()):
            strip.setPixelColor(i, wheel((int(i * 256 / strip.numPixels()) + j) & 255))
        strip.show()
        time.sleep(wait_ms/1000.0)

menu = Menu(window)
new_item = Menu(menu,tearoff=False)
new_item.add_command(label='Desligar LEDs',command=desligar)
new_item.add_separator()
new_item.add_command(label='Resetar App',command=restart_program,accelerator="F9")
new_item.add_command(label='Sobre')
menu.add_cascade(label='Arquivo', menu=new_item)

sel_menu = Menu(menu,tearoff=False)
sel_menu.add_command(label='Pegar RGB',command=pegarCor)
sel_menu.add_command(label='Pegar RGB 5050',command=pegarCorLed5050)
menu.add_cascade(label='Selecionar', menu=sel_menu)

if len(erros) > 0: menu.add_cascade(label='Erros', command=janelaErros)

frameCor = Frame(window, relief=RAISED)
frameCor.pack(fill=BOTH, expand=True)

lblR = Label(frameCor, text="R:")
lblR.pack(side=LEFT)
txtR = Entry(frameCor,width=5)
txtR.pack(side=LEFT)

lblG = Label(frameCor, text="G:")
lblG.pack(side=LEFT)
txtG = Entry(frameCor,width=5)
txtG.pack(side=LEFT)

lblB = Label(frameCor, text="B:")
lblB.pack(side=LEFT)
txtB = Entry(frameCor,width=5)
txtB.pack(side=LEFT)

botaoColorPicker = Button(frameCor,text='Color Picker',command=pegarCor)
botaoColorPicker.pack(side=LEFT)

framePontos = Frame(window,relief=RAISED)
framePontos.pack(fill=BOTH, expand=True)

lblPontoA = Label(framePontos, text="Ponto A:")
lblPontoA.pack(side=LEFT)
txtPontoA = Entry(framePontos,width=5,justify='center')
txtPontoA.pack(side=LEFT)

lblPontoB = Label(framePontos, text="Ponto B:")
lblPontoB.pack(side=LEFT)
txtPontoB = Entry(framePontos,width=5,justify='center')
txtPontoB.pack(side=LEFT)

botaoMostrarSlider = Button(framePontos,text='>')
botaoMostrarSlider.pack(side=RIGHT)

frameSlider = Frame(window,relief=RAISED)
frameSlider.pack(fill=BOTH, expand=True)

def attPontos(event=0):
	txtPontoA.delete(0,END)
	txtPontoA.insert(0,sliderPontoA.get())
	txtPontoB.delete(0,END)
	txtPontoB.insert(0,sliderPontoB.get())

sliderPontoA = Scale(frameSlider,from_=0,to_=120,orient=HORIZONTAL,command=attPontos)
sliderPontoA.pack(side=LEFT,expand=True)

sliderPontoB = Scale(frameSlider,from_=0,to_=120,orient=HORIZONTAL,command=attPontos)
sliderPontoB.pack(side=LEFT,expand=True)

frameVel = Frame(window,relief=RAISED)
frameVel.pack(fill=BOTH, expand=True)

lblVel = Label(frameVel, text="Vel:")
lblVel.pack(side=LEFT)
txtVel = Entry(frameVel,width=10)
txtVel.pack(side=LEFT)

lblFuncao1 = Label(window, text="...:")
lblFuncao1.pack(side=LEFT)
lblFuncao1.pack_forget()
txtFuncao1 = Entry(window,width=10)
txtFuncao1.pack(side=LEFT)
txtFuncao1.pack_forget()

'''
lblPontoA = Label(window, text="Ponto A:")
lblPontoA.grid(column=0, row=0)
lblPontoB = Label(window, text="Ponto B:")
lblPontoB.grid(column=0, row=1)
lblBPM = Label(window, text="BPM:")
lblBPM.grid(column=0, row=2)
lblVel = Label(window, text="Vel:")
lblVel.grid(column=0, row=3)

txtPontoA = Entry(window,width=10,text="0")
txtPontoA.grid(column=1,row=0)
txtPontoB = Entry(window,width=10,text="85")
txtPontoB.grid(column=1,row=1)
txtBPM = Entry(window,width=10)
txtBPM.grid(column=1,row=2)
txtVel = Entry(window,width=10)
txtVel.grid(column=1, row=3)

lblR = Label(window, text="R:")
lblR.grid(column=2, row=0)
lblG = Label(window, text="G:")
lblG.grid(column=2, row=1)
lblB = Label(window, text="B:")
lblB.grid(column=2, row=2)
lblFuncao1 = Label(window, text="...:")
lblFuncao1.grid(column=2, row=3)
lblFuncao1.grid_remove()

txtR = Entry(window,width=10)
txtR.grid(column=3, row=0)
txtG = Entry(window,width=10)
txtG.grid(column=3, row=1)
txtB = Entry(window,width=10)
txtB.grid(column=3, row=2)
txtFuncao1 = Entry(window,width=10)
txtFuncao1.grid(column=3, row=3)
txtFuncao1.grid_remove()

lblEffects = Label(window, text="Efeitos:")
lblEffects.grid(column=0,row=4,pady=10)

cmbEffects = ttk.Combobox(window,width=20,state="readonly")
cmbEffects['values']= ("0 - Ligar",
	"1 - Grave",
	"2 - Braco",
	"3 - Corte Cobra",
	"4 - Laser Esq",
	"5 - Corte",
	"6 - Teatro",
	"7 - Aleatorio Fade",
	"8 - Bass Braco Invert",
	"9 - Teste")
cmbEffects.current(0) #set the selected item
cmbEffects.grid(column=1, row=4,columnspan=3)
cmbEffects.bind("<<ComboboxSelected>>", mostrarTxtFuncao)

btnTest = Button(window, text="Testar", command=testLED)
btnTest.grid(column=0, row=5)

offInNew = IntVar()
Checkbutton(window, text="Desligar em novo efeito",variable=offInNew).grid(column=3,row=5)

musicaFrame = LabelFrame(window,text='Musicas')
musicaFrame.grid(column=0,row=6,columnspan=6)

rdbTheSermon = Radiobutton(musicaFrame, text="The Sermon", value=1,variable='musica')
rdbTheSermon.grid(column=0, row=6)
rdbTheSermon.select()
rdbTheSermon1 = Radiobutton(musicaFrame, text="1- Mini Bass (Q,W,E)", value=1,variable='sermonEfeito')
rdbTheSermon1.grid(column=1,row=6,sticky=W)
rdbTheSermon1.select()
rdbTheSermon2 = Radiobutton(musicaFrame, text="2- Hats (Q,W,E)", value=2,variable='sermonEfeito')
rdbTheSermon2.grid(column=2,row=6,sticky=W)
rdbTheSermon3 = Radiobutton(musicaFrame, text="3- Laser (Q)", value=3,variable='sermonEfeito')
rdbTheSermon3.grid(column=1,row=7,sticky=W)
rdbTheSermon4 = Radiobutton(musicaFrame, text="4- Teatro (Q)", value=4,variable='sermonEfeito')
rdbTheSermon4.grid(column=2,row=7,sticky=W)
rdbTheSermon5 = Radiobutton(musicaFrame, text="5- Bass (Q,W,E)", value=5,variable='sermonEfeito')
rdbTheSermon5.grid(column=1,row=8,sticky=W)

rdbTunnel = Radiobutton(musicaFrame, text="Tunnel Vision", value=2,variable='musica')
rdbTunnel.grid(column=0, row=9)
rdbTunnel1 = Radiobutton(musicaFrame, text="Tunnel + Bass (A, S)", value=1,variable='tunnelEfeito')
rdbTunnel1.grid(column=1,row=9,sticky=W)
rdbTunnel1.select()
'''

window.title("Sway LED")
window.bind_all("<F9>",restart_program)
window.bind_all("<F2>",atalhosVolta)
window.bind_all("<F3>",atalhos)
window.bind_all("<Return>",testLED)
window.bind_all("q",atalhoQ)
window.bind_all("w",atalhoW)
window.bind_all("e",atalhoE)
window.bind_all("a",atalhoA)
window.bind_all("s",atalhoS)
window.bind_all("d",atalhoD)
window.config(menu=menu)

window.mainloop()


# Murali
from flask import Flask
from flask import render_template, jsonify, request
import requests
# from models import *
import random
from frontdesk_util import front_response

app = Flask(__name__)
app.secret_key = '12345'

@app.route('/')
def hello_world():
    return render_template('home-SELECT.html')

@app.route('/chat', methods=["POST"])
def chat():
    try:
        user_message = request.form["text"]
        # should return the response to be
        response = front_response(user_message)

        return jsonify({"status": "success", "response": response})
    except Exception as e:
        print(e)
        return jsonify({"status": "success", "response": "Apologies! I am yet to be trained to respond to this query..."})

app.config["DEBUG"] = True
if __name__ == "__main__":
    app.run(port=8081)


from typing import Optional, Tuple

import torch
import numpy as np
from torch import nn, Tensor
from torch.utils.data import DataLoader

from tqdm import tqdm
from .bayesian import bayes_eval

__all__ = [
    "compute_calibration_errors",
    "_get_calibration",
    "_compute_calibration_statistics",
]

def _compute_calibration_statistics(total_counts, total_corrects, confs):
    """
    Computes the ECE and MCE statistics given the buckets
    """
    num_buckets = len(total_counts)
    n = total_counts.sum()

    ECE = 0
    MCE = 0

    for i in range(num_buckets):
        conf = confs[i]
        acc = total_corrects[i] / total_counts[i]
        calibration_error = total_counts[i] * np.absolute(conf - acc)

        ECE += calibration_error / n
        MCE = max(calibration_error / n, MCE)

    return ECE, MCE

def _get_calibration(
    net: nn.Module,
    dataloader: DataLoader,
    k: int,
    mode: Optional[str] = "all",
    num_buckets: Optional[int] = 10,
    fill_pix: Optional[int] = None,
):
    """
    Returns array of buckets for the reliability plot

    Args
    -----------
    net: nn.Module
    dataloader: torch.Dataloader
    k: set to 0 for regular forward pass, or larger than 0 for bayes forward pass
    mode: sets the mode of which bayesian inference to perform
    num_buckets: number of buckets to separate between 0 to 100 probabiliy

    Returns
    -----------
    total_counts: (num_buckets, ) np array, number of instances with confidence in the bucket
    total_corrects: (num_buckets, ) np array, number of correct instances with confidence in the bucket
    total_confs: (num_buckets, ) np array, the confidence score for each bucket by the network
    """
    total_counts = np.array([1e-16] * num_buckets, dtype=np.float32)
    total_corrects = np.array([0] * num_buckets, dtype=np.float32)
    total_confs = np.array([0] * num_buckets, dtype=np.float32)

    # loop through images and make predictions
    net.eval()
    for X_batch, y_batch in tqdm(dataloader):
        # force loop to be over single images
        for x, y in zip(X_batch, y_batch):
            # bayes forward pass
            if k == 0:
                net.set_bayes_mode(False, "all")
            elif k > 0:
                net.set_bayes_mode(True, mode)

            y_logits, y_pred = bayes_eval(net, x, k)
            y_softmax = y_logits.softmax(dim=0)

            counts, corrects, conf = _get_calibration_per_prediction(
                y, y_softmax, num_buckets, fill_pix
            )
            total_counts += counts
            total_corrects += corrects
            total_confs += conf

    # buckets = total_corrects / total_counts

    return total_counts, total_corrects, total_confs / total_counts

def _get_calibration_per_prediction(
    y_gt: Tensor,
    y_softmax: Tensor,
    num_buckets: int = 10,
    fill_pix: Optional[int] = None,
) -> Tuple[np.array, np.array, np.array]:
    """
    Computes reliability values for each image

    Args
    -----------
    y_gt: torch.tensor (1 x W x H)
    y_softmax: torch.tensor (c x W x H)
    num_buckets: int
    fill_pix: int, if not None, will ignore pixels with this value

    Returns
    -----------
    counts: np.array (num_buckets, ) counts in each bin
    correct: np.array (num_buckets, ) number of corrects in each bin
    conf: np.array (num_buckets, ) the average confidence p
    """
    if fill_pix is not None:
        mask = (y_gt != fill_pix).to(float)
    else:
        mask = torch.ones_like(y_gt).to(float)
    out_count = np.array([0 for _ in range(num_buckets)], dtype=np.float32)
    out_correct = np.array([0 for _ in range(num_buckets)], dtype=np.float32)
    out_conf = np.array([0 for _ in range(num_buckets)], dtype=np.float32)
    step = 1 / num_buckets
    # get probability and prediction
    y_prob, y_pred = torch.max(y_softmax, dim=0)
    indices = y_prob // step

    for i in range(num_buckets):
        idx_mask = mask * (indices == i)
        out_count[i] = (idx_mask).sum()
        out_correct[i] = (idx_mask * (y_pred == y_gt)).sum()
        out_conf[i] = (idx_mask * y_prob).sum().item()
    return out_count, out_correct, out_conf

def compute_calibration_errors(
    net: nn.Module,
    dataloader: DataLoader,
    k: int,
    mode: Optional[str] = "all",
    num_buckets: Optional[int] = 10,
) -> Tuple[float, float]:
    """
    Computes the ECE and MCE statistics given the buckets
    Args
    -----------
    net: nn.Module
    dataloader: torch.Dataloader
    k: set to 0 for regular forward pass, or larger than 0 for bayes forward pass
    mode: sets the mode of which bayesian inference to perform
    num_buckets: number of buckets to separate between 0 to 100 probabiliy

    Returns
    -----------
    ECE: (float) Expected Calibration Error
    MCE: (float) Maximum Calibration Error

    """
    total_counts, total_corrects, confs = _get_calibration(
        net, dataloader, k, mode, num_buckets
    )
    ECE, MCE = _compute_calibration_statistics(total_counts, total_corrects, confs)

    return ECE, MCE


def get_userid(db, login):
    cur = db.cursor()
    userid = cur.execute("SELECT id from users WHERE login = ?", (login,)).fetchall()
    return userid[0][0]

def get_dict_clothes(db, userid: int):
    cur = db.cursor()
    dict_clothes = {}
    clothes = cur.execute("SELECT id, weather, type, mediumcolor, name from clothes WHERE userid = ?",
                          (userid,)).fetchall()
    for garment in clothes:
        dict_clothes[garment[1]] = dict_clothes.get(garment[1], {})
        dict_clothes[garment[1]][garment[2]] = dict_clothes[garment[1]].get(garment[2], []) + [(garment[0],
                                                                                garment[3],
                                                                                garment[4])]
    return dict_clothes


import datetime

import pandas as pd
import numpy as np
import bs4
import re
import requests
from helpers.date_time_handler import DateTimeHandler
from helpers.progress_handler import ProgressHandler
from models.club import Club
from models.league import League

class SportsScraper:
    """
    Static methods which perform the scraping functionality.

    Attributes
    ----------
        __leagues      Acts as a cache for storing league url/name
        __clubs        Acts as a cache for storing club   ids/names

    Methods
    -------
        __scrap_leagues():
            Scraps data containing a list of leagues.
        scrap_leagues():
            Calls __get_leagues if __leagues is None, otherwise, it retrieves __leagues immediately.

        __get_cached_clubs():
            Retrieves the club's snapshot.
        cache_clubs():
            Collects a snapshot of the clubs for faster fetch in the future.
        __get_clubs(leagues, tolerate_too_many_requests=False):
            Calls http://site.api.espn.com/apis/site/v2/sports/soccer/{league}/teams iteratively to fetch all clubs ids.
        get_clubs(leagues, tolerate_too_many_requests=False):
            Calls __get_clubs if __clubs is None, otherwise, it retrieves __clubs immediately.

        __get_cached_players():
            Retrieves the player's snapshot.
        cache_players():
            Collects a snapshot of the players for faster fetch in the future.
        scrap_players(season_years=None, leagues=None, clubs=None, fast_fetch_clubs=False, fast_fetch=False)
            Scraps data containing information about club's players.
        __scrap_players(season_years=None, leagues=None, clubs=None, fast_fetch_clubs=False):
            Scraps data containing information about club's players.

        __get_cached_matches():
            Retrieves the match's snapshot.
        cache_matches():
            Collects a snapshot of the matches for faster fetch in the future.
        scrap_matches(start_date=None, end_date=None, fast_fetch=False):
            Scraps data containing information about the results of the matches.
        __scrap_matches(start_date=datetime.date.today() - datetime.timedelta(days=7), end_date=datetime.date.today()):
            Scraps data containing information about the results of the matches.
    """

    __leagues = None
    __clubs = None

    @staticmethod
    def __scrap_leagues():
        """
        Scraps data containing a list of leagues.

        :return: A list of leagues object
        """

        leagues = []

        # Partially prevents scraping detection
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.0; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0'}
        res = requests.get('https://www.espn.com/soccer/teams', headers=headers)
        soup = bs4.BeautifulSoup(res.text, 'html.parser')
        ddl = soup.find('select', attrs={'class': 'dropdown__select'})

        if not ddl:
            return leagues

        options = ddl.find_all('option')
        for option in options:
            leagues.append(League(option['value'], option.text))

        return leagues

    @staticmethod
    def scrap_leagues():
        """
        Calls __get_leagues if __leagues is None, otherwise, it retrieves __leagues immediately.

        :return: A list of leagues object
        """

        if SportsScraper.__leagues is None:
            print('Fetching leagues, this is a one time process...')
            SportsScraper.__leagues = SportsScraper.__scrap_leagues()
            print('Received leagues\n')

        return SportsScraper.__leagues.copy()

    @staticmethod
    def __get_cached_clubs():
        """
        Retrieves the club's snapshot.
        """

        clubs = []
        df = pd.read_csv('cached_clubs.csv', index_col='club_id', skiprows=1)

        for index, row in df.iterrows():
            clubs.append(Club(index, row['club_name'], League(row['league_url'], row['league_name'])))

        return clubs

    @staticmethod
    def cache_clubs():
        """
        Collects a snapshot of the clubs for faster fetch in the future.
        """
        clubs = SportsScraper.get_clubs()

        data = {
            'club_id': [x.club_id for x in clubs],
            'club_name': [x.name for x in clubs],
            'league_url': [x.league.url for x in clubs],
            'league_name': [x.league.name for x in clubs],
        }

        df = pd.DataFrame(data)

        df.set_index('club_id', inplace=True)

        f = open('cached_clubs.csv', "w+")
        f.write(f'# Timestamp: {datetime.datetime.utcnow()}\n')
        f.close()

        # noinspection PyTypeChecker
        df.to_csv('cached_clubs.csv', mode='a')

    @staticmethod
    def __get_clubs(tolerate_too_many_requests=False, fast_fetch=False):
        """
        Calls http://site.api.espn.com/apis/site/v2/sports/soccer/{league}/teams iteratively to fetch all clubs ids.

        :param bool tolerate_too_many_requests: Specify to whether throw an exception if the status code is not 200
        :param bool fast_fetch: Retrieves clubs from a saved snapshot instantly
        :return: A list of clubs object
        """

        if fast_fetch:
            return SportsScraper.__get_cached_clubs()

        scraped_leagues = SportsScraper.scrap_leagues()
        clubs = []

        processed = 0
        for league in scraped_leagues:
            print(ProgressHandler.show_progress(processed, len(scraped_leagues)))
            processed += 1
            # Partially prevents scraping detection
            headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.0; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0'}
            response = requests.get(f'http://site.api.espn.com/apis/site/v2/sports/soccer/{league.url}/teams',
                                    headers=headers)
            if response.status_code != 200 and tolerate_too_many_requests:
                continue
            for club in response.json()['sports'][0]['leagues'][0]['teams']:
                clubs.append(Club(club['team']['id'], club['team']['name'], league))

        ProgressHandler.reset_progress()

        return clubs

    @staticmethod
    def get_clubs(fast_fetch=False):
        """
        Calls __get_clubs if __clubs is None, otherwise, it retrieves __clubs immediately.

        :param bool fast_fetch: Retrieves clubs from a saved snapshot instantly
        :return: A list of clubs object
        """

        if SportsScraper.__clubs is None:
            print('Fetching clubs, this is a one time process...')
            SportsScraper.__clubs = SportsScraper.__get_clubs(tolerate_too_many_requests=True, fast_fetch=fast_fetch)
            print('Received clubs\n')

        return SportsScraper.__clubs.copy()

    @staticmethod
    def __get_cached_players():
        """
        Retrieves the player's snapshot.
        """

        players = pd.read_csv('cached_players.csv', skiprows=1)

        return players

    @staticmethod
    def cache_players():
        """
        Collects a snapshot of the players for faster fetch in the future.
        """
        players = SportsScraper.scrap_players(fast_fetch_clubs=True)

        f = open('cached_clubs.csv', "w+")
        f.write(f'# Timestamp: {datetime.datetime.utcnow()}\n')
        f.close()

        # noinspection PyTypeChecker
        players.to_csv('cached_players.csv', mode='a')

    @staticmethod
    def scrap_players(season_years=None, leagues=None, clubs=None, fast_fetch_clubs=False, fast_fetch=False):
        """
        Scraps data containing information about club's players.

        :param list[int] season_years: Collect the data from the provided year(s)
        :param list[str] leagues: Specify the desired league(s)
        :param list[str] clubs: Specify the desired club(s)
        :param bool fast_fetch_clubs: Retrieves clubs from a saved snapshot instantly
        :param bool fast_fetch: Retrieves players from a saved snapshot instantly
        :return: A dataframe containing club players
        """

        if fast_fetch:
            df = SportsScraper.__get_cached_players()
            if season_years is not None:
                df = df[df.YEAR.isin(season_years)]
            if leagues is not None:
                df = df[df.LEAGUE.isin(leagues)]
            if clubs is not None:
                df = df[df.CLUB.isin(clubs)]
            return df

        else:
            return SportsScraper.__scrap_players(season_years, leagues, clubs, fast_fetch_clubs)

    @staticmethod
    def __scrap_players(season_years=None, leagues=None, clubs=None, fast_fetch_clubs=False):
        """
        Scraps data containing information about club's players.

        :param list[int] season_years: Collect the data from the provided year(s)
        :param list[str] leagues: Specify the desired league(s)
        :param list[str] clubs: Specify the desired club(s)
        :param bool fast_fetch_clubs: Retrieves clubs from a saved snapshot instantly
        :return: A dataframe containing club players
        """

        if (season_years is None) or (len(season_years) == 0):
            season_years = np.arange(2000, 2022)

        scraped_leagues = SportsScraper.scrap_leagues()
        if (leagues is not None) and (len(leagues) != 0):
            scraped_leagues = list(filter(lambda x: x.name in leagues, scraped_leagues))

        if fast_fetch_clubs:
            scraped_clubs = SportsScraper.__get_cached_clubs()
        else:
            scraped_clubs = SportsScraper.get_clubs()

        if (clubs is not None) and (len(clubs) != 0):
            scraped_clubs = list(
                filter(lambda x: x.name in clubs and x.league.name in [x.name for x in scraped_leagues], scraped_clubs))

        if not all(isinstance(x, np.int32) or isinstance(x, int) for x in season_years):
            raise ValueError('season_year must be a list of integer')
        if clubs is not None and not all(isinstance(x, str) for x in clubs):
            raise ValueError('clubs must be a list of string')
        if leagues is not None and not all(isinstance(x, str) for x in leagues):
            raise ValueError('leagues must be a list of string')

        players_df_goalkeeper = pd.DataFrame()

        players_df_player = pd.DataFrame()

        processed = 0
        for season_year in season_years:
            for club in scraped_clubs:
                print(ProgressHandler.show_progress(processed,
                                                    len(season_years) * len(scraped_clubs)))
                processed += 1
                # Partially prevents scraping detection
                headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.0; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0'}
                res = requests.get(
                    f'https://www.espn.com/soccer/team/squad/_/'
                    f'id/{club.club_id}/'
                    f'league/{club.league.url}/'
                    f'season/{season_year}',
                    headers=headers)

                soup = bs4.BeautifulSoup(res.text, 'html.parser')
                tables = soup.find_all('table', attrs={'class': 'Table'})

                if not tables or len(tables) != 2:
                    continue

                for x in np.arange(0, 2):
                    data = []
                    rows = tables[x].find_all('tr')
                    for row in rows:
                        cols = row.find_all('td')
                        cols = [ele.text.strip() for ele in cols]  # Strips elements
                        if cols:  # If column is not empty
                            buff = [club.league.name, club.name, str(season_year)] + \
                                   [ele for ele in cols if ele]  # If element is not empty
                            number_list = re.findall(r'\d+$', buff[3])
                            if number_list:  # Checks if there is a number for the player
                                buff.insert(4, number_list[0])  # Extract player's number
                                buff[3] = re.findall(r'^([^0-9]*)', buff[3])[0]  # Remove player's name number
                            else:
                                buff.insert(4, np.nan)
                            data.append(buff)

                    if x == 0:
                        players_df_goalkeeper = players_df_goalkeeper.append(data)
                    else:
                        players_df_player = players_df_player.append(data)

        if players_df_goalkeeper.empty:
            players_df_goalkeeper = pd.DataFrame(np.empty((0, 19)))

        if players_df_player.empty:
            players_df_player = pd.DataFrame(np.empty((0, 20)))

        players_df_goalkeeper.columns = ['YEAR', 'LEAGUE', 'CLUB', 'NAME', 'NUM', 'POS', 'AGE', 'HT', 'WT', 'NAT',
                                         'APP', 'SUB',
                                         'SV', 'GA', 'A',
                                         'FC', 'FA', 'YC', 'RC']
        players_df_player.columns = ['YEAR', 'LEAGUE', 'CLUB', 'NAME', 'NUM', 'POS', 'AGE', 'HT', 'WT', 'NAT', 'APP',
                                     'SUB',
                                     'G', 'A', 'SH', 'ST',
                                     'FC', 'FA', 'YC', 'RC']

        df = players_df_goalkeeper.append(players_df_player)
        df = df.replace(r'^\s*$', np.nan, regex=True) \
            .replace('--', np.nan) \
            .reset_index(drop=True)

        df['WT'] = df['WT'].apply(lambda x: x if (pd.isnull(x)) else int(x.split(" ")[0]) / 2.205)
        df['HT'] = df['HT'].apply(
            lambda x: x
            if (pd.isnull(x)) else
            int(x.split("'")[0]) * 30.48 +
            int(x.split("'")[1].split("\"")[0]) * 2.54
        )

        cols = df.columns.drop(['LEAGUE', 'CLUB', 'NAME', 'POS', 'NAT'])
        df[cols] = df[cols].apply(pd.to_numeric)

        df.set_index(['LEAGUE', 'CLUB', 'YEAR', 'NAME'], inplace=True)

        ProgressHandler.reset_progress()
        return df

    @staticmethod
    def __get_cached_matches():
        """
        Retrieves the match's snapshot.
        """

        matches = pd.read_csv('cached_matches.csv', skiprows=1, dtype={'date': object,
                                                                       'club1': str,
                                                                       'SCORE': str,
                                                                       'club2': str,
                                                                       'DURATION': str,
                                                                       'LOCATION': str,
                                                                       'ATTENDANCE': str,
                                                                       'TIME': str,
                                                                       'TV': np.float})

        matches['ATTENDANCE'] = pd.to_numeric(matches['ATTENDANCE'].str.replace(',', ''), downcast='integer')
        matches['date'] = pd.to_datetime(matches['date']).dt.date

        return matches

    @staticmethod
    def cache_matches():
        """
        Collects a snapshot of the matches for faster fetch in the future.
        """

        matches = SportsScraper.scrap_matches(start_date=datetime.date(2002, 10, 1),
                                              end_date=datetime.date(2022, 5, 29))

        f = open('cached_matches.csv', "w+")
        f.write(f'# Timestamp: {datetime.datetime.utcnow()}\n')
        f.close()

        # noinspection PyTypeChecker
        matches.to_csv('cached_matches.csv', index=False, mode='a')

    @staticmethod
    def scrap_matches(start_date=None, end_date=None, fast_fetch=False):
        """
        Scraps data containing information about the results of the matches.

        :param datetime.date start_date: Specify the start date of the search
        :param datetime.date end_date: Specify the end date of the search
        :param bool fast_fetch: Retrieves matches from a saved snapshot instantly
        :return: An array of two dataframe containing match results (0: Elapsed, 1: Fixtures)
        """

        if fast_fetch:
            df = SportsScraper.__get_cached_matches()
            if start_date is not None:
                df = df[df.date >= start_date]
            if end_date is not None:
                df = df[df.date <= end_date]
            return df

        else:
            return SportsScraper.__scrap_matches(start_date, end_date)

    @staticmethod
    def __scrap_matches(start_date=datetime.date.today() - datetime.timedelta(days=7),
                        end_date=datetime.date.today(),
                        request_tries=8):
        """
        Scraps data containing information about the results of the matches.

        :param datetime.date start_date: Specify the start date of the search
        :param datetime.date end_date: Specify the end date of the search
        :param int request_tries: Determine to number of tries for each webpage request whenever it fails
        :return: An array of two dataframe containing match results (0: Elapsed, 1: Fixtures)
        """

        if start_date > end_date:
            raise ValueError('start_date cannot be less than end_date')

        elapsed_matches_df = pd.DataFrame()
        fixtures_list_df = pd.DataFrame()

        processed = 0

        days_between = DateTimeHandler.get_dates_between(start_date, end_date)

        for singleDay in days_between:
            data = []
            print(ProgressHandler.show_progress(processed, len(days_between)))
            processed += 1
            # Partially prevents scraping detection
            headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.0; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0'}
            res = requests.get(
                f'https://www.espn.in/football/fixtures/_/'
                f'date/{singleDay}',
                headers=headers)
            soup = bs4.BeautifulSoup(res.text, 'html.parser')
            tables = soup.find_all('tbody')

            tries = 0
            print(singleDay)
            while soup.find('h1', {'class': 'Error404__Title'}) is not None and tries < request_tries:
                print(f'try {tries + 1}')
                tries += 1
                headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.0; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0'}
                res = requests.get(
                    f'https://www.espn.in/football/fixtures/_/'
                    f'date/{singleDay}',
                    headers=headers)
                soup = bs4.BeautifulSoup(res.text, 'html.parser')
                tables = soup.find_all('tbody')

            if tries == request_tries:
                print('giving up...')

            if not tables:
                continue

            for table in tables:
                rows = table.find_all('tr')
                for row in rows:
                    cols = row.find_all('td')
                    if cols:  # If column is not empty
                        arr = [DateTimeHandler.year_month_day_to_date(singleDay)]
                        for col in np.arange(0, len(cols)):
                            if cols[col].find('small'):
                                continue
                            if col == 0:
                                club1 = cols[col].find('span').text
                                result = cols[col].find_all('a')[-1].text
                                arr.append(club1)
                                arr.append(result)
                            elif col == 1:
                                club2 = cols[col].find_all('span')[-1].text
                                arr.append(club2)
                            elif col == 2:
                                if cols[col].get('data-date'):
                                    date = datetime.datetime.strptime(cols[col].get('data-date'), '%Y-%m-%dT%H:%MZ')
                                    arr.append('{:d}:{:02d}'.format(date.hour, date.minute))
                                else:
                                    arr.append(cols[col].find('a').text)
                            else:
                                arr.append(cols[col].text)
                        data.append(arr)

            data = list(filter(lambda x: len(x) != 1, data))

            elapsed_matches_list = list(filter(lambda x: x[4] != 'LIVE' or ':' not in x[4], data))
            fixtures_list = list(filter(lambda x: x[4] == 'LIVE' or ':' in x[4], data))

            elapsed_matches_df = elapsed_matches_df.append(elapsed_matches_list)
            fixtures_list_df = fixtures_list_df.append(fixtures_list)

        if elapsed_matches_df.empty:
            elapsed_matches_df = pd.DataFrame(np.empty((0, 7)))
        if fixtures_list_df.empty:
            fixtures_list_df = pd.DataFrame(np.empty((0, 6)))

        elapsed_matches_df.columns = ['date', 'club1', 'SCORE', 'club2', 'DURATION', 'LOCATION', 'ATTENDANCE']
        fixtures_list_df.columns = ['date', 'club1', 'SCORE', 'club2', 'TIME', 'TV']

        df = elapsed_matches_df.append(fixtures_list_df)

        df = df.replace(r'^\s*$', np.nan, regex=True) \
            .replace('--', np.nan) \
            .fillna(value=np.nan) \
            .dropna(thresh=3) \
            .reset_index(drop=True)

        df = df.replace(r'^\s*$', np.nan, regex=True) \
            .replace('--', np.nan) \
            .fillna(value=np.nan) \
            .dropna(thresh=3) \
            .reset_index(drop=True)

        ProgressHandler.reset_progress()
        return df


import datetime
import zipfile
from urllib import quote, urlencode, unquote_plus
from StringIO import StringIO

import z3c.form
import zope.contentprovider.interfaces
import zope.event
from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile
from zope.component import adapts, queryMultiAdapter
from zope.i18n import translate
from zope.i18n.interfaces.locales import ICollator
from zope.interface import Interface
from zope.publisher.browser import BrowserView
from zope.publisher.browser import BrowserPage
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.publisher.interfaces import NotFound
from zope.publisher.interfaces import IPublishTraverse
from zope.traversing.browser.absoluteurl import AbsoluteURL
from zope.traversing.browser.interfaces import IAbsoluteURL
from zope.traversing.browser.absoluteurl import absoluteURL
from zope.interface import implements
from zope.cachedescriptors.property import Lazy
from zope.proxy import getProxiedObject
from z3c.form import button

import schooltool.traverser.traverser
from schooltool.common import format_message
from schooltool.person.interfaces import IPerson
from schooltool.report.interfaces import IReportLinksURL
from schooltool.report.interfaces import IReportLinkViewlet
from schooltool.report.interfaces import IReportFile
from schooltool.report.interfaces import IArchivePage
from schooltool.report.report import IFlourishReportLinkViewletManager
from schooltool.report.report import getReportRegistrationUtility
from schooltool.report.report import ReportTask
from schooltool.report.report import ArchiveReportTask
from schooltool.skin import flourish
from schooltool.skin.flourish.page import WideContainerPage
from schooltool.skin.flourish.page import RefineLinksViewlet
from schooltool.skin.flourish import IFlourishLayer
from schooltool.skin.flourish.form import DialogForm
from schooltool.task.tasks import query_message
from schooltool.task.interfaces import IRemoteTask
from schooltool.task.progress import TaskProgress
from schooltool.task.progress import normalized_progress
from schooltool.task.browser.task import MessageDialog

from schooltool.common import SchoolToolMessage as _

class ReportsView(BrowserView):

    template = ViewPageTemplateFile('templates/report_links.pt')

    def __call__(self):
        return self.template()

class StudentReportsView(ReportsView):

    title = _('Student Reports')

class GroupReportsView(ReportsView):

    title = _('Group Reports')

class SchoolYearReportsView(ReportsView):

    title = _('School Year Reports')

class TermReportsView(ReportsView):

    title = _('Term Reports')

class SectionReportsView(ReportsView):

    title = _('Section Reports')

class FlourishReportsView(flourish.page.Page):
    """Report request view base class."""
    content_template = ViewPageTemplateFile('templates/f_report_links.pt')
    subtitle = _('Reports')

def reportLinksURL(ob, request, name=''):
    """Helper method to obtain the report links URL"""
    url = queryMultiAdapter((ob, request), IReportLinksURL, name=name)
    if url is None:
       return ''
    return url

class ReportReferenceView(BrowserView):

    template = ViewPageTemplateFile('templates/report_reference.pt')

    def __call__(self):
        return self.template()

    def rows(self):
        collator = ICollator(self.request.locale)
        utility = getReportRegistrationUtility()
        app = self.context

        rows = []
        for group_key, group_reports in utility.reports_by_group.items():
            reference_url = reportLinksURL(app, self.request, name=group_key)
            for report in group_reports:
                row = {
                    'url': reference_url,
                    'group': report['group'],
                    'title': report['title'],
                    'description': report['description'],
                    }
                rows.append([collator.key(report['group']),
                             collator.key(report['title']),
                             row])

        return [row for group, title, row in sorted(rows)]

class FlourishReportReferenceView(WideContainerPage, ReportReferenceView):

    def done_link(self):
        return absoluteURL(self.context, self.request) + '/manage'

    def rows(self):
        self.collator = ICollator(self.request.locale)
        utility = getReportRegistrationUtility()
        app = self.context
        rows = {}
        for group_key, group_reports in utility.reports_by_group.items():
            reference_url = reportLinksURL(app, self.request, name=group_key)
            for report in group_reports:
                group = report['group']
                name = report['name']
                row = {
                    'url': reference_url,
                    'group': group,
                    'title': report['title'],
                    'file_type': report['file_type'].upper(),
                    'description': report['description'],
                    }
                # XXX: this check is needed to override old skin
                #      report links with flourish ones
                if (group, name) not in rows or \
                   report['layer'] is IFlourishLayer:
                    rows[group, name] = row
        return sorted(rows.values(), key=self.sortKey)

    def sortKey(self, row):
        return self.collator.key(row['group']), self.collator.key(row['title'])

class ReportsLinks(RefineLinksViewlet):
    """Reports links viewlet."""

    implements(IFlourishReportLinkViewletManager)

    body_template = ViewPageTemplateFile('templates/f_report_links_body.pt')

    @Lazy
    def items(self):
        items = super(ReportsLinks, self).items
        result = []
        for item in items:
            viewlet = item['viewlet']
            url = viewlet.link
            is_report_link = bool(getattr(viewlet, 'file_type', ''))
            if is_report_link:
                file_type = translate(viewlet.file_type,
                                      context=self.request)
                description = translate(viewlet.description,
                                        context=self.request)
                querystring = urlencode({
                        'file_type': file_type.encode('utf-8').upper(),
                        'description': description.encode('utf-8')})
                url = '%s?%s' % (viewlet.report_link, querystring)
            result.append({
                    'class': item['class'],
                    'viewlet': viewlet,
                    'content': item['content'],
                    'is_report_link': is_report_link,
                    'link_id': viewlet.link.replace('.', '_'),
                    'form_id': viewlet.link.replace('.', '_') + '_form',
                    'title': translate(viewlet.title, context=self.request),
                    'url': url,
                    })
        return result

class ReportLinkViewletProxy(flourish.viewlet.ViewletProxy):

    adapts(IReportLinkViewlet)

    def __init__(self, *args, **kw):
        super(ReportLinkViewletProxy, self).__init__(*args, **kw)
        self.restoreSortingAttrs()

    def restoreSortingAttrs(self):
        unproxied = getProxiedObject(self)
        self.before = getattr(unproxied, 'before', ())
        self.after = getattr(unproxied, 'after', ())

class RequestReportDownloadDialog(DialogForm, z3c.form.form.EditForm):

    template = ViewPageTemplateFile('templates/f_request_report_download.pt')

    dialog_submit_actions = ('download',)
    dialog_close_actions = ('cancel',)
    label = None

    @button.buttonAndHandler(_("Download"), name='download')
    def handleDownload(self, action):
        self.request.response.redirect(self.nextURL())
        # We never have errors, so just close the dialog.
        self.ajax_settings['dialog'] = 'close'

    @button.buttonAndHandler(_("Cancel"))
    def handle_cancel_action(self, action):
        pass

    def updateActions(self):
        super(RequestReportDownloadDialog, self).updateActions()
        self.actions['download'].addClass('button-ok')
        self.actions['cancel'].addClass('button-cancel')

    def nextURL(self):
        raise NotImplementedError("nextURL must redirect to a 'downloadable' view")

    @property
    def file_type(self):
        if 'file_type' in self.request:
            return unquote_plus(self.request['file_type'])

    @property
    def description(self):
        if 'description' in self.request:
            return unquote_plus(self.request['description'])

class RequestRemoteReportDialog(RequestReportDownloadDialog):

    template = ViewPageTemplateFile('templates/f_request_report_download.pt')

    dialog_submit_actions = ('download',)
    dialog_close_actions = ('cancel',)
    label = None

    report_builder = None # report generating class or view name

    task_factory = ReportTask

    report_task = None
    replace_dialog = None

    fields = z3c.form.field.Fields(Interface)
    form_params = None

    def resetForm(self):
        self.form_params = {}

    def update(self):
        self.resetForm()
        RequestReportDownloadDialog.update(self)

    def getContent(self):
        return self.form_params

    @button.buttonAndHandler(_("Generate"), name='download')
    def handleDownload(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return
        changes = self.applyChanges(data)
        if changes:
            self.status = self.successMessage
        else:
            self.status = self.noChangesMessage
        task = self.task_factory(self.report_builder, self.target)
        self.schedule(task)
        self.report_task = task
        message = query_message(task)
        if message is None:
            self.ajax_settings['dialog'] = 'close'
            return
        content = queryMultiAdapter(
            (message, self.request), name='dialog')
        if content is None:
            self.ajax_settings['dialog'] = 'close'
            return
        self.replace_dialog = content

    @button.buttonAndHandler(_("Cancel"))
    def handle_cancel_action(self, action):
        pass

    def updateActions(self):
        super(RequestReportDownloadDialog, self).updateActions()
        self.actions['download'].addClass('button-ok')
        self.actions['cancel'].addClass('button-cancel')

    @property
    def target(self):
        return self.context

    def updateTaskParams(self, task):
        """Subclasses should update task.request_params dict here."""
        task.request_params.update(self.form_params)

    def schedule(self, task):
        self.updateTaskParams(task)
        task.schedule(self.request)

    def render(self, *args, **kw):
        if self.replace_dialog is not None:
            return self.replace_dialog.render(*args, **kw)
        return super(RequestRemoteReportDialog, self).render(*args, **kw)

class ReportAbsoluteURLAdapter(AbsoluteURL):
    adapts(IReportFile, IBrowserRequest)
    implements(IAbsoluteURL)

    def __str__(self):
        base = absoluteURL(self.context.__parent__, self.request)
        filename = quote(unicode(self.context.__name__).encode('UTF-8'))
        url = base + '/download/' + filename
        return url

    __call__ = __str__

class ReportMessageDownloads(object):
    implements(IPublishTraverse)
    file_attrs = ('report',)
    files = None

    def __init__(self, context, request, name='download'):
        self.__name__ = name
        self.__parent__ = context
        self.context = context
        self.request = request
        self.collectFiles()

    def collectFiles(self):
        self.files = {}
        for attr in self.file_attrs:
            value = getattr(self.context, attr, None)
            if value is None:
                continue
            self.files[value.__name__] = value

    def publishTraverse(self, request, name):
        if name not in self.files:
            raise NotFound(self.context, name, self.request)
        return self.files[name]

class ReportMessageDownloadsPlugin(schooltool.traverser.traverser.TraverserPlugin):

    def traverse(self, name):
        return ReportMessageDownloads(self.context, self.request)

class DownloadReportDialog(MessageDialog):

    template = flourish.templates.File('templates/f_download_report_dialog.pt')
    show_cancel = True

    @property
    def report(self):
        return getattr(self.context, 'report', None)

    @property
    def report_generated(self):
        return bool(self.report)

    @property
    def main_recipient(self):
        person = IPerson(self.request, None)
        if self.context.recipients is None:
            return None
        recipients = sorted(self.context.recipients, key=lambda r: r.__name__)
        if person in recipients:
            return person
        for recipient in recipients:
            if flourish.canView(recipient):
                return recipient
        return None

    @Lazy
    def failure_ticket_id(self):
        sender = self.context.sender
        if (IRemoteTask.providedBy(sender) and
            sender.failed):
            return sender.__name__
        return None

    @Lazy
    def failed_task(self):
        sender = self.context.sender
        if not (IRemoteTask.providedBy(sender) and
                sender.failed):
            return None
        return sender

class ShortReportMessage(flourish.content.ContentProvider):

    @Lazy
    def failure_ticket_id(self):
        sender = self.context.sender
        if (IRemoteTask.providedBy(sender) and
            sender.failed):
            return sender.__name__
        return None

class ProgressReportPage(BrowserPage):

    render_invariant = False
    render_debug = False

    base_filename = 'export'

    message_title = _('export')

    task_progress = None
    overall_line_id = None

    def __init__(self, context, request):
        super(ProgressReportPage, self).__init__(context, request)
        self.makeProgress()

    def makeFileName(self, basename):
        if self.render_invariant:
            return '%s.xls' % basename
        timestamp = datetime.datetime.now().strftime('%y-%m-%d-%H-%M')
        return '%s_%s.xls' % (basename, timestamp)

    @property
    def filename(self):
        return self.makeFileName(self.base_filename)

    def makeProgress(self):
        task_id = getattr(self.request, 'task_id', None)
        self.task_progress = TaskProgress(task_id)

    def update(self):
        self.makeProgress()

    def updateOverall(self):
        if self.overall_line_id is None:
            return
        max_progress = 0.0
        overall = 0.0
        for lid, line in self.task_progress.lines.items():
            if (line.get('progress', None) is not None and
                lid != self.overall_line_id):
                overall += line.progress
                max_progress += 1.0
        if max_progress > 0.0:
            overall /= max_progress
        self.task_progress('overall', progress=overall)

    def progress(self, importer, value):
        self.task_progress(importer, progress=value, active=True)
        self.updateOverall()

    def finish(self, importer):
        self.task_progress.finish(importer)
        self.updateOverall()

class ArchiveFileManager(flourish.viewlet.ViewletManagerBase):

    collecting_title = _('Collecting (${name})')
    overall_line_id = 'overall'

    @property
    def title(self):
        return getattr(self.view, 'message_title', _("Archive"))

    @property
    def task_progress(self):
        return self.view.task_progress

    def addArchivers(self):
        progress = self.task_progress
        progress.add('active_file', title=None, active=False)
        if self.overall_line_id is not None:
            progress.add(self.overall_line_id,
                         title=self.title, progress=0.0)
            self.view.overall_line_id = self.overall_line_id

    def update(self):
        self.task_progress.add('collecting',
            title=u'', progress=0.0)
        event = zope.contentprovider.interfaces.BeforeUpdateEvent
        total = len(self.viewlets)
        for nv, viewlet in enumerate(self.viewlets):
            title = format_message(self.collecting_title,
                                   {'name': viewlet.title or _('files')})
            self.task_progress.force(
                'collecting',
                title=title,
                progress=normalized_progress(nv, total),
                active=True)
            zope.event.notify(event(viewlet, self.request))
            viewlet.update()
        title = format_message(self.collecting_title,
                               {'name': _('files')})
        self.task_progress.force('collecting', title=title, progress=None, active=False)
        self.addArchivers()

    def render(self, archive):
        for viewlet in self.viewlets:
            viewlet(archive)
        if self.overall_line_id is not None:
            self.task_progress.force(self.overall_line_id, progress=1.0)

    def updateOverall(self):
        self.view.updateOverall()

    def progress(self, archiver_name, filename, progress):
        self.task_progress(archiver_name, progress=progress, active=True)
        self.task_progress('active_file', title=filename, active=True, progress=None)
        self.updateOverall()

    def finish(self, archiver_name):
        self.task_progress.finish(archiver_name)
        self.task_progress('active_file', title=u'', active=True)
        self.updateOverall()

class FileArchiver(flourish.viewlet.Viewlet):

    title = None

    def addArchivers(self):
        progress = self.view.task_progress
        title = self.title or _('Reports')
        progress.add(self.__name__, active=False,
                     title=title, progress=0.0)

    def setTitle(self, title):
        self.title = title
        progress = self.view.task_progress
        progress(self.__name__, title=title)

    def progress(self, filename, file_n, total_files, file_progress=0.0):
        self.manager.progress(
            self.__name__,
            filename,
            normalized_progress(
                file_n, total_files,
                file_progress, 1.0,
                ))

    def finish(self):
        self.manager.finish(self.__name__)

    def update(self):
        flourish.viewlet.Viewlet.update(self)
        self.addArchivers()

    def render(self, archive):
        self.finish()

    def queryView(self, item, request, view_name):
        renderer = zope.component.queryMultiAdapter(
            (item, request), name=view_name)
        return renderer

class ReportArchivePage(ProgressReportPage):
    implements(IArchivePage)

    compression = zipfile.ZIP_STORED

    collecting_title = _("Preparing archive")
    archiving_title = _("Archiving")
    finished_title = _("Archive complete")

    def makeFileName(self, basename):
        if self.render_invariant:
            return '%s.zip' % basename
        timestamp = datetime.datetime.now().strftime('%y-%m-%d-%H-%M')
        return '%s_%s.zip' % (basename, timestamp)

    def render(self, stream, *args, **kw):
        """Render a zip to stream, return number of files written."""
        file_manager = flourish.content.queryContentProvider(
            self.context, self.request, self, 'file_manager')
        if file_manager is None:
            return 0

        self.task_progress.title = self.collecting_title
        file_manager.update()

        self.task_progress.title = self.archiving_title
        written = 0
        zip_file = zipfile.ZipFile(stream, 'w', compression=self.compression)
        try:
            file_manager.render(zip_file)
            written = len(zip_file.filelist)
        finally:
            zip_file.close()
        self.task_progress.title = self.finished_title
        return written

    def __call__(self, *args, **kw):
        self.update()
        if self.request.response.getStatus() in [300, 301, 302, 303,
                                                 304, 305, 307]:
            return u''
        stream = StringIO()
        self.render(stream, *args, **kw)
        data = stream.getvalue()
        if not data:
            return u''
        response = self.request.response
        response.setHeader('Content-Type', 'application/zip')
        response.setHeader('Content-Length', len(data))
        return data

class RequestReportArchiveDialog(RequestRemoteReportDialog):

    task_factory = ArchiveReportTask

class ReportReferenceLinkViewlet(flourish.page.LinkViewlet):
    pass


import subprocess
import os
import sys
import re
from workflow import Workflow

adb_path = os.getenv('adb_path')
serial = os.getenv('serial')
api = os.getenv('device_api')
ip = os.getenv("ip")

def wordMatch(arg, sentence):
    words = arg.lower().split(" ")
    sentenceComponents = sentence.lower().split(" ")
    for word in words:
        included = False
        for sentenceComponent in sentenceComponents:
            if word in sentenceComponent:
                included = True
                break
        if not included:
            return False
    return True

def main(wf):

    isWifiDevice = re.match(".*:5555$", serial)
    isEmulator = re.match("emulator-55[5-8][02468]", serial)
    arg = wf.args[0].strip()
    log.debug(arg)
    addAll = False
    if arg == '':
        addAll = True

    itemCount = 0

    # SHOW INSTALLED PACKAGES
    title = "Show apps list"

    if addAll or (wordMatch(arg, title + " start launch uninstall force stop clear info") and not arg.startswith("ins")):
        func = ""
        if "start" in arg.lower() or "launch" in arg.lower():
            title = "Select app to launch"
            func = "start_app"
        elif "uninstall" in arg.lower():
            title = "Select app to uninstall"
            func = "uninstall_app"
        elif "force" in arg.lower() or "stop" in arg.lower():
            title = "Select app to force stop"
            func = "force_stop"
        elif "clear" in arg.lower():
            title = "Select app to clear data"
            func = "clear_app_data"
        elif "info" in arg.lower():
            title = "Select app to show app info"
            func = "app_info"
        it = wf.add_item(title=title,
                         uid="list_app",
                         arg="list_app",
                         valid=True)
        it.setvar("func", func)
        if func == "":
            m = it.add_modifier('cmd', "Select app to launch")
            m.setvar("func", "start_app")

            m = it.add_modifier('alt', "Select app to uninstall")
            m.setvar("func", "uninstall_app")

            m = it.add_modifier('ctrl', "Select app to force stop")
            m.setvar("func", "force_stop")

            m = it.add_modifier('fn', "Select app to clear data")
            m.setvar("func", "clear_app_data")

            m = it.add_modifier('shift', "Select app to show app info")
            m.setvar("func", "app_info")
        itemCount += 1

    # INSTALL APK
    title = "Install apk"

    if addAll or wordMatch(arg, title):
        wf.add_item(title=title,
                    uid="install_apk",
                    arg="install_apk",
                    subtitle="install apk or apks",
                    valid=True)
        itemCount += 1

    # SCREENSHOT
    title = "Take screenshot"

    if addAll or wordMatch(arg, title):
        it = wf.add_item(title=title,
                         uid="screenshot",
                         arg="screenshot:to_clipboard",
                         subtitle="take a screenshot and copy to clipboard, `cmd` to copy to desktop",
                         valid=True)
        it.add_modifier(
            'cmd', 'take a screenshot and copy to desktop', arg="screenshot:to_desktop")
        itemCount += 1

    # OPEN SETTINGS
    title = "Open settings"

    if addAll or wordMatch(arg, title):
        it = wf.add_item(title=title,
                         uid="open_settings",
                         arg="open_settings",
                         subtitle="'cmd' - Dev Tool, 'alt' - WiFi, 'ctrl' - App, 'fn' - Date, 'shift' - Accessibility",
                         valid=True)
        it.add_modifier('cmd', 'Open Developer Settings',
                        arg="open_settings:developer_options")
        it.add_modifier('alt', 'Open WiFi Settings', arg="open_settings:wifi")
        it.add_modifier('fn', 'Open Date Settings', arg="open_settings:date")
        it.add_modifier('shift', 'Open Accessibility Settings',
                        arg="open_settings:accessibility")
        it.add_modifier('ctrl', 'Open Application Settings',
                        arg="open_settings:application")
        itemCount += 1

    # TOGGLE DEBUG LAYOUT
    title = "Toggle debug layout"

    if addAll or wordMatch(arg, title):
        it = wf.add_item(title=title,
                         uid="debug_layout",
                         arg="debug_layout",
                         valid=True)
        itemCount += 1
        it.add_modifier('cmd', 'Toggle pointer location',
                        arg="pointer_location")
        it.add_modifier('alt', 'Toggle show taps', arg="show_taps")
        it.add_modifier('ctrl', 'Toggle GPU profile', arg="gpu_profile")
        it.add_modifier('fn', 'Toggle GPU overdraw', arg="gpu_overdraw")
        it.add_modifier('shift', 'Turn off everything', arg="debug_off")

    # DEMO MODE
    if api and int(api) >= 23:
        title = "Toggle demo mode"

        if addAll or wordMatch(arg, title):
            wf.add_item(title=title,
                        uid="demo_mode",
                        arg="demo_mode",
                        valid=True)
            itemCount += 1

    # REBOOT SYSTEM
    title = "Reboot system"

    if addAll or wordMatch(arg, title) or wordMatch(arg, "restart"):
        it = wf.add_item(title=title,
                         uid="adb_reboot",
                         arg="adb_reboot",
                         subtitle="'cmd' - Bootloader, 'alt' - Recovery, 'ctrl' - Sideload",
                         valid=True)
        itemCount += 1
        it.add_modifier('cmd', 'Bootloader', arg="adb_reboot:bootloader")
        it.add_modifier('alt', 'Recovery', arg="adb_reboot:recovery")
        it.add_modifier('ctrl', 'Sideload', arg="adb_reboot:sideload")

    # CONNECT OVER WIFI
    if not isWifiDevice and not isEmulator and ip:
        title = "Connect over Wi-Fi"

        if addAll or wordMatch(arg, title):
            wf.add_item(title=title,
                        subtitle=ip,
                        uid="debug_wifi",
                        arg="debug_wifi",
                        valid=True)
            itemCount += 1

    # KEY INPUT
    title = "Keyevent input"

    if addAll or wordMatch(arg, title):
        it = wf.add_item(title=title,
                         uid="keyevent_input",
                         arg="keyevent_input",
                         valid=True)
        it.add_modifier("cmd", "Back", arg='keyevent_input_KEYCODE_BACK')
        it.add_modifier("alt", "Home", arg='keyevent_input_KEYCODE_HOME')
        it.add_modifier("ctrl", "App Switch",
                        arg='keyevent_input_KEYCODE_APP_SWITCH')
        it.add_modifier("fn", "Escape", arg='keyevent_input_KEYCODE_ESCAPE')
        it.add_modifier("shift", "Power", arg='keyevent_input_KEYCODE_POWER')
        itemCount += 1

    # DUMP STACK
    title = "Dump task stacks"

    if addAll or wordMatch(arg, title):
        it = wf.add_item(title=title,
                         uid="dump_stack",
                         arg="dump_stack",
                         valid=True)
        itemCount += 1
        it.add_modifier('cmd', 'Dump the first application',
                        arg="dump_stack:first_app")
        it.add_modifier('alt', 'Dump the first stack',
                        arg="dump_stack:first_stack")

    # DEMO MODE
    if api and int(api) >= 23:
        title = "Dump notifications"

        if addAll or wordMatch(arg, title):
            it = wf.add_item(title=title,
                        uid="dump_notification",
                        arg="dump_notification",
                        valid=True)
            itemCount += 1
            it.add_modifier('alt', 'Exclude empty notification', arg="dump_notification:exclude_empty")

    # Screen Copy with scrcpy
    title = "Screen Copy with scrcpy"
    log.debug("Screen Copy with scrcpy")
    try:
        rc = subprocess.check_output(['/usr/local/bin/scrcpy', '-v'])
    except OSError:
        log.error("OS error 1")
        try:
            rc = subprocess.check_output(['/opt/homebrew/bin/scrcpy', '-v'])
        except OSError:
            log.error("OS error 2")
            rc = -1
    if rc != -1 and (addAll or wordMatch(arg, title)) and not isEmulator:
        it = wf.add_item(title=title,
                         uid="scr_cpy",
                         arg="scr_cpy",
                         valid=True)

        it.setvar("dimension", 0)
        mod = it.add_modifier(
            "cmd", subtitle="Run with maximum resolution restriction 1024")
        mod.setvar("dimension", 1024)
        mod = it.add_modifier(
            "alt", subtitle="Record screen and save to User folder")
        mod.setvar("record", 1)
        log.debug("Manufacturer {0}".format(
            os.getenv("ro.product.manufacturer")))
        if os.getenv("ro.product.manufacturer") == "Oculus" and os.getenv("name") == "Quest 2":
            mod = it.add_modifier("ctrl", subtitle="Quest 2 - Mirror one eye")
            mod.setvar("crop", "1832:1920:0:0")
        itemCount += 1
    log.debug(rc)

    # COMMAND HISTORY
    title = "Command history"
    lastFuncs = wf.cached_data('last_func:' + os.getenv("his_tag"), max_age=0)

    if (addAll or wordMatch(arg, title)) and lastFuncs and len(lastFuncs) > 0:
        it = wf.add_item(title=title,
                         subtitle="show command history",
                         arg="cmd_history",
                         valid=True)
        itemCount += 1
        it.add_modifier('cmd', 'clear command history',
                        arg="cmd_history:clear")

    idx = 1
    while idx > 0:
        config = os.getenv('self_script_device_%d' % idx)
        if config:
            title = config.split("|")[0]
            if addAll or wordMatch(arg, title) or wordMatch(arg, "Self script"):
                path = config.split("|")[1]
                it = wf.add_item(title=title,
                                 subtitle="with script: %s" % path,
                                 arg="self_script_device_%d" % idx,
                                 valid=True)
                it.setvar("self_script_device", config)
                mod = it.add_modifier("cmd", subtitle="apply cmd modifier")
                mod.setvar("mod", "cmd")
                mod = it.add_modifier("alt", subtitle="apply alt modifier")
                mod.setvar("mod", "alt")
                mod = it.add_modifier("ctrl", subtitle="apply ctrl modifier")
                mod.setvar("mod", "ctrl")
                mod = it.add_modifier("fn", subtitle="apply fn modifier")
                mod.setvar("mod", "fn")
                mod = it.add_modifier("shift", subtitle="apply shift modifier")
                mod.setvar("mod", "shift")
            idx = idx + 1
        else:
            idx = -1

    # CUSTOM ACTION
    if itemCount == 0:
        it = wf.add_item(title="Execute custom command for " + serial,
                         subtitle="adb " + arg,
                         arg="adb_cmd:in_terminal:" + arg,
                         valid=True)
        m = it.add_modifier('cmd', 'Run without opening terminal',
                            arg="adb_cmd:in_background:" + arg)

    wf.send_feedback()

if __name__ == '__main__':
    wf = Workflow()
    log = wf.logger
    sys.exit(wf.run(main))


import inspect
import os.path
import sys
from datetime import date, datetime

from src.other.config import DEBUG, LOG_FILE_DIR_PATH, LOGGING

_PFX_DBG = ' * '
_PFX_ERR = ' ! '

class Log:
    @staticmethod
    def __log(msg):
        if LOGGING:
            today = date.today()
            datestamp = today.strftime("%m-%d-%y")
            filename = f'canary_{datestamp}.txt'
            filepath = f'{LOG_FILE_DIR_PATH}/{filename}'
            with open(filepath, 'a') as f:
                f.write(msg)
                f.write('\n')
        print(msg)

    @staticmethod
    def setup_logfiles():
        if not os.path.exists(LOG_FILE_DIR_PATH):
            os.mkdir(LOG_FILE_DIR_PATH)

    @staticmethod
    def dbg(msg):
        if DEBUG:
            caller = inspect.stack()[1].function
            timestamp = datetime.now()
            Log.__log(f'[{timestamp}] [{caller}] \033[92m{_PFX_DBG}{msg}\033[0m')

    @staticmethod
    def err(msg, bail=True):
        if DEBUG:
            caller = inspect.stack()[1].function
            timestamp = datetime.now()
            Log.__log(f'[{timestamp}] [{caller}] \033[91m{_PFX_ERR}{msg}\033[0m')
        else:
            Log.__log(f'\033[91m{_PFX_ERR}{msg}\033[0m')

        if bail:
            sys.exit(1)


# Je remplis une tirelire de la manière suivante, je commence par déposer 1 € dans la tirelire (un lundi),puis j'y dépose 2,01 euros le  jour suivant (un mardi),
#puis 3,02 E le jour suivant (un mercredi),  puis 4,03 euros le jour suivant (un jeudi), etc. (chaque jour j y dépose 1,01 euros de plus que la veille).

#Par exemple :
#le huitième jour (un lundi), j y dépose 8,07 euros et le contenu total de la tirelire est:
#1 +2,01+3,02 +4,03+5,04 +6,05+7,06+8,07 = 36,28 euros.
#Lorsque le contenu total de la tirelire dépasse 1 500 euros, je casse cette tirelire.
#Le but du sujet est de déterminer le contenu de la tirelire lorsque je la casserai,
#ainsi que le jour de la semaine correspondant (lundi, mardi, mercredi, jeudi, vendredi, samedi ou dimanche).

def depot(n):
        base = 1
        for i in range (1,n):
            base = base +1.01
            rounded_num = round(base,2)
        return base

s=0
for n in range (1,7):
    s = s+ depot(n)
    m  = s/7
print(m)

def contenu(n):
    somme=0
    for i in range (1,n):
        somme = somme + depot(n)
    return somme


# -*- coding: utf-8 -*-
"""
Created on Sun Aug  2 16:49:25 2020

@author: solis

module uses flopy extensively (https://modflowpy.github.io/flopydoc)

functionallity
Plot heads time series at the end of each stress period
    functions plot_heads_sg & plot_heads_ug
Gets heads time series at the end of each stress period
    functions get_heads_sg & get_heads_ug
"""
import numpy as np

def plot_heads_sg(org: str, rows_cols: tuple, dir_out: str, **kwargs):
    """
    It plots heads simulated in the last time step of each stress period.
        Heads are read from a bhd file in a structured grid (no DISV)
    args:
        org: bhd file
        rows_cols: list of pairs (row, column) to be plotted
        dir_out str: directory where png plots will be saved
    optional args
        layers tuple: list of layers to be plotted; is not present all layers
            will bel plotted
        ylabel str: label in y axis
        more optional args in get_heads_sg, get_requested_layers and xy_plot_1g
    """
    from os.path import join

    if 'ylabel' in kwargs:
        ylabel = kwargs['ylabel']
    else:
        ylabel = 'm asl'

    irows_cols = [(item[0] - 1, item[1] - 1) for item in rows_cols]
    hds, times = get_heads_sg(org, irows_cols, **kwargs)
    ilayers = get_requested_layers(hds.shape[1], **kwargs)

    for i, rc in enumerate(irows_cols):
        heads_ly = []
        leg_ly = []
        for j, layer in enumerate(ilayers):
            title = f'Head in row {rc[0]+1:n}, column {rc[1]+1:n}'
            dst = join(dir_out, f'hd_row_{rc[0]+1:n}_col_{rc[1]+1:n}.png')
            heads_ly.append(hds[i, j, :])
            leg_ly.append(f'ly {layer+1:n}')
        xy_plot_1g(title, times, heads_ly, leg_ly, ylabel, dst, **kwargs)

def plot_heads_ug(org: str, cells: tuple, dir_out: str, **kwargs):
    """
    It plots heads simulated in the last time step of each stress period.
        Heads are read from a bhd file in a unstructured grid (DISV)
    args:
        org: bhd file
        cells: list o cells to be plotted
        dir_out str: directory where png plots will be saved. If not present
            plot is shown in the screen
        xlabel str: label in x axis
    optional args
        layers tuple: list of layers to be plotted; is not present all layers
            will bel plotted
        ylabel str: label in y axis
        more optional args in get_heads_ug, get_requested_layers and xy_plot_1g
    """
    from os.path import join

    if 'ylabel' in kwargs:
        ylabel = kwargs['ylabel']
    else:
        ylabel = 'm asl'

    icells = [item-1 for item in cells]
    hds, times = get_heads_ug(org, icells, **kwargs)
    if 'layers' in kwargs:
        ilayers = [item-1 for item in kwargs['layers']]
    else:
        ilayers = [item for item in range(hds.shape[1])]
    for i, cell in enumerate(icells):
        heads_ly = []
        leg_ly = []
        for j, layer in enumerate(ilayers):
            title = f'Head in cell {cell+1:n}'
            dst = join(dir_out, f'hd_cell_{cell+1:n}.png')
            heads_ly.append(hds[i, j, :])
            leg_ly.append(f'ly {layer+1:n}')
        xy_plot_1g(title, times, heads_ly, leg_ly, ylabel, dst, **kwargs)

def get_disv_dimension(org):
    """
    it returns grid dimension as a tuple with 3 items by reading disv file:
        nlay: layers number
        ncpl: cell per layer number
        nvert: vertex number
    """
    import os.path

    ext = os.path.splitext(org)[1]
    if ext.lower() != '.disv':
        raise ValueError('org must have disv extension')

    i = 0
    kwords = ('NLAY', 'NCPL', 'NVERT')
    disvd = []
    fi = open(org)
    for line in fi:
        if kwords[i] in line:
            disvd.append(int(line.split()[1]))
            if len(disvd) == 3:
                return tuple(disvd)
            i += 1
    raise ValueError(f'missed key words in {org}')

def get_heads_sg(org: str, rows_cols: list, **kwargs):
    """
    it returns a np.array with the heads in the last time_step of each stress
        period for cells referendes as pairs (row, column). The heads are read
        from org, that is a file with .bhd extension.
    """
    import flopy.utils.binaryfile as bf

    fi = bf.HeadFile(org)
    _, ii = get_last_ts_sp(fi, **kwargs)
    times = fi.get_times()

    hds = fi.get_data(kstpkper=(0, 0))
    nlays = hds.shape[0]
    hd_ts = np.empty((len(rows_cols), nlays, len(ii)), np.float32)

    ilayers = get_requested_layers(nlays, **kwargs)

    # TODO:
    for i, rc in enumerate(rows_cols):
        for j, layer in enumerate(ilayers):
            hds = fi.get_ts((layer, rc[0], rc[1]))
            for k, i1 in enumerate(ii):
                hd_ts[i, j, k] = hds[i1][1]
    return hd_ts, times

def get_heads_ug(org: str, cells: list, **kwargs):
    """
    it returns a np.array with the heads in the last time_step of each stress
        period for cells in cells. The heads are read from org, that is a file
        with .bhd extension.
    """
    import flopy.utils.binaryfile as bf

    fi = bf.HeadFile(org)
    ts_sp, _ = get_last_ts_sp(fi, **kwargs)
    times = np.array(fi.get_times(), np.float32)

    for i, item in enumerate(ts_sp):
        hds = fi.get_data(kstpkper=item)
        if i == 0:
            nlays = hds.shape[0]
            hd_ts = np.empty((len(cells), nlays, len(ts_sp)), np.float32)

        for j, cell in enumerate(cells):
            for ilay in range(nlays):
                hd_ts[j, ilay, i] = hds[ilay, 0, cell]
    return hd_ts, times

def get_last_ts_sp(fi, **kwargs):
    """
    it returns 2 items:
        a list of pairs (time_step, stress_period) in witch time_step is the
            last time_step
        a list
    args
    fi: object heads binary file
    """
    import sqlite3

    create_db = \
    'create table t1 (time_step integer, stress_period integer, ' +\
    'primary key (time_step, stress_period) )'
    insert = \
    'insert into t1 (time_step, stress_period) values (?, ?)'
    select1 = 'select distinct stress_period from t1 order by stress_period'
    select2 = 'select max(time_step) from t1 where stress_period=?'

    con = sqlite3.connect(':memory:')
    cur = con.cursor()

    ts_sp = fi.get_kstpkper()  # time steps & stress periods

    if 'time_steps' not in kwargs or \
    'time_steps' in kwargs and not kwargs['time_steps']:
        cur.execute(create_db)

        for i, item in enumerate(ts_sp):
            cur.execute(insert, (int(item[0]), int(item[1])))
        cur.execute(select1)
        stress_periods = [ row[0] for row in cur.fetchall()]
        tsl_sp = []
        ii = []
        for i, stress_period in enumerate(stress_periods):
            cur.execute(select2, (stress_period,))
            row = cur.fetchone()
            tsl_sp.append((row[0], stress_period))
            ii.append(i)
        return tuple(tsl_sp), tuple(ii)
    else:
        return ts_sp, tuple([i for i in range(len(ts_sp))])

def get_requested_layers(nlays: int, **kwargs):
    """
    get the list of the index of layers to plott
   """
    if 'layers' in kwargs:
        return [item-1 for item in kwargs['layers']]
    else:
        return [item for item in range(nlays)]

def xy_plot_1g(title: str, x: list, ys: list, legs: list, ylabel: str,
               dst: str, **kwargs: dict):
    """
    plot a png file with head time series in a cell. As ys is a list
        of head time series in all or at least one layer, several series
        can be drawn in the same axis (the figure has only one axis in
        matplotlib terminlogy)
    args
        title: figure title
        x: list of stress periods
        ys: list of ts each element corresponds to one layer
        legs: list of legends, each one correspond to ys element
        ylabel: y axis label
        dst: output file name
    optional args
        xlabel: x axis label, default str 'stress periods'
        to_screen: figures are show in the screen, default bool False
    returns
        None
    """
    from datetime import date
    import warnings
    import matplotlib.pyplot as plt
    import matplotlib as mpl
    # parámetros específicos
    warnings.simplefilter(action='ignore', category=FutureWarning)
    mpl.rc('font', size=8)
    mpl.rc('axes', labelsize=8, titlesize= 10, grid=True)
    mpl.rc('axes.spines', right=False, top=False)
    mpl.rc('xtick', direction='out', top=False)
    mpl.rc('ytick', direction='out', right=False)
    mpl.rc('lines', linewidth=0.8, linestyle='-', marker='.', markersize=4)
    mpl.rc('legend', fontsize=8, framealpha=0.5, loc='best')

    fig, ax = plt.subplots()

    plt.suptitle(title)
    ax.set_ylabel(ylabel)
    if 'xlabel' in kwargs:
        ax.set_xlabel(kwargs['xlabel'])

    if isinstance(x[0], date):
        fig.autofmt_xdate()

    for y1, leg1 in zip(ys,legs):
        ax.plot(x, y1, label=leg1)
    plt.legend(framealpha=0.5, loc='best')

    if 'to_screen' in kwargs and  kwargs['to_screen']:
        plt.show()

    fig.savefig(dst)

    plt.close('all')
    plt.rcdefaults()

def plot_heads(org, lrc: list, ylabel: str, **kwargs):
    """
    Deprecated, use plot_head_sg instead

    it plots heads from bhd modflow 6 ourput files; heads can be plotted
        with stress periods or time steps in x axis
    now only structured grids can be used but unstructured grids will be
        addedd soon
    to read bhs fiel ir uses flopy

    https://modflowpy.github.io/flopydoc/binaryfile.html
    optional arguments
        date0 str: a str date representation as yyyy-mm-dd (it's not used yet)
        dir_out str: directory where png plots will be saved. If nor present
            plot is shown in the screen
        xlabel str: label in x axis -xlabel-
        time_step bool: if present and is True time steps are represented
    """
    import sqlite3
    import flopy.utils.binaryfile as bf
    create_db = \
    'create table t1 (fid integer, time_step integer, stress_period integer, ' +\
    'primary key (fid, time_step, stress_period) )'
    insert = \
    'insert into t1 (fid, time_step, stress_period) values (?, ?, ?)'
    select1 = 'select distinct stress_period from t1 order by stress_period'
    select2 = 'select max(fid) from t1 where stress_period=?'

    con = sqlite3.connect(':memory:')
    cur = con.cursor()

    fi = bf.HeadFile(org)
    if 'time_steps' not in kwargs or \
    'time_steps' in kwargs and not kwargs['time_steps']:
        cur.execute(create_db)
        # Get a list of unique stress periods and time steps in the file
        kstpkper = fi.get_kstpkper()
        for i, item in enumerate(kstpkper):
            cur.execute(insert, (int(i), int(item[0]), int(item[1])))
        cur.execute(select1)
        stress_periods = [ row[0] for row in cur.fetchall()]
        ii = []
        for stress_period in stress_periods:
            cur.execute(select2, (stress_period,))
            ii1 = cur.fetchone()
            ii.append(ii1[0])

    hds = fi.get_ts(lrc)
    fi.close()

    if 'time_steps' in kwargs and kwargs['time_steps']:
        x = hds[:, 0]
        suffix = '_ts'
    else:
        x = np.array([i for i in range(len(stress_periods))], np.float32)
        suffix = '_sp'
        y = np.empty((len(stress_periods)), np.float32)

    for i in range(len(lrc)):
        title = f'Head in layer {lrc[i][0]:n}, row {lrc[i][1]:n}, ' +\
        f'col {lrc[i][2]:n}'
        if 'dir_out' in kwargs:
            kwargs['name_file'] = \
            f'L{lrc[i][0]:n}_R{lrc[i][1]:n}_C{lrc[i][2]:n}' + suffix
        print(title)

        ycol = i + 1
        if 'time_steps' in kwargs and kwargs['time_steps']:
            y = hds[:, ycol]
        else:
            for j, ii1 in enumerate(ii):
                y[j] = hds[ii1, ycol]

        xy_plot_1g(title, x, y, ylabel, kwargs)
    con.close()