# %%
from functools import partial
import tensorflow as tf

# %%
def phase(
    dim_sizes,
    pos_low: float = -1,
    pos_high: float = 1,
    centered: bool = True,
    dtype=tf.float32,
):
    """
    Basically a clone of `encode_positions`,
    but reimplemented here to keep the other thing unpickling correctly.
    """

    def generate_grid(size):
        width = (pos_high - pos_low) / size
        if centered:
            left = pos_low + width / 2
            right = pos_high - width / 2
        else:
            left = pos_low
            right = pos_high - width
        return tf.linspace(left, right, num=size)

    grid_list = list(map(generate_grid, dim_sizes))
    grid = tf.stack(tf.meshgrid(*grid_list, indexing="ij"), axis=-1)
    return grid

@tf.function
def manual_rfft2d(x):
    x = tf.signal.rfft(x)
    x = tf.cast(x, tf.complex64)
    x = tf.signal.fft(tf.transpose(x, perm=[0, 1, 3, 2]))
    x = tf.transpose(x, perm=[0, 1, 3, 2])
    return x

@tf.function
def manual_irfft2d(x):
    x = tf.signal.ifft(tf.transpose(x, perm=[0, 1, 3, 2]))
    x = tf.transpose(x, perm=[0, 1, 3, 2])
    x = tf.signal.irfft(x)
    return x

# %%
class FourierLayer2dLite(tf.keras.layers.Layer):
    def __init__(self, in_dim, out_dim, n_modes):
        super(FourierLayer2dLite, self).__init__()
        self.in_dim = in_dim
        self.out_dim = out_dim
        self.n_modes = n_modes
        self.linear = tf.keras.layers.Dense(out_dim)
        self.act = tf.keras.layers.ReLU()

        # fourier_weight = [
        #     tf.Variable(
        #         tf.random.normal(
        #             shape=(in_dim, out_dim, n_modes, n_modes, 2),
        #             stddev=1 / (in_dim * out_dim),
        #         )
        #     )
        #     for _ in range(2)
        # ]
        # self.fourier_weight = fourier_weight

        # Create the first variable using add_weight
        self.fourier_weight_1 = self.add_weight(
            shape=(in_dim, out_dim, n_modes, n_modes, 2),
            initializer="glorot_uniform",
            trainable=True,
            name="FourierLayer2dLite/fourier_weight_1",
        )

        # Create the second variable using add_weight
        self.fourier_weight_2 = self.add_weight(
            shape=(in_dim, out_dim, n_modes, n_modes, 2),
            initializer="glorot_uniform",
            trainable=True,
            name="FourierLayer2dLite/fourier_weight_2",
        )

    @staticmethod
    def complex_matmul_2d(a, b):
        # (batch, in_channel, x, y), (in_channel, out_channel, x, y) -> (batch, out_channel, x, y)
        # assert they have the dimension
        op = partial(tf.einsum, "bixy,ioxy->boxy")
        return tf.stack(
            [
                op(a[..., 0], b[..., 0]) - op(a[..., 1], b[..., 1]),
                op(a[..., 1], b[..., 0]) + op(a[..., 0], b[..., 1]),
            ],
            axis=-1,
        )

    def call(self, inputs):
        # x.shape == [batch_size, grid_size, grid_size, in_dim]
        _, M, N, I = inputs.shape
        dynamic_shape = tf.shape(inputs)

        inputs = tf.transpose(inputs, perm=[0, 3, 1, 2])
        # x.shape == [batch_size, in_dim, grid_size, grid_size]

        # x_ft = tf.signal.rfft2d(inputs, fft_length=[M, N])
        x_ft = manual_rfft2d(inputs)

        scale = tf.sqrt(tf.cast(tf.reduce_prod(dynamic_shape[-2:]), tf.complex64))
        x_ft /= scale

        x_ft = tf.stack([tf.math.real(x_ft), tf.math.imag(x_ft)], axis=4)
        # x_ft.shape == [batch_size, in_dim, grid_size, grid_size // 2 + 1, 2]

        out_ft = self.complex_matmul_2d(
            x_ft[:, :, : self.n_modes, : self.n_modes], self.fourier_weight_1
        )
        # out_ft.shape = (batch_size, in_dim, n_modes, n_modes, 2)
        # dynamic shape
        out_ft_zero = tf.zeros(
            [dynamic_shape[0], I, N - self.n_modes * 2, self.n_modes, 2],
            dtype=tf.float32,
        )
        # out_ft_zero.shape = (batch_size, in_dim, grid_size - n_modes * 2, n_modes, 2)
        out_ft = tf.concat([out_ft, out_ft_zero], axis=-3)
        # out_ft.shape = (batch_size, in_dim, grid_size - n_modes, n_modes, 2)
        out_ft = tf.concat(
            [
                out_ft,
                self.complex_matmul_2d(
                    x_ft[:, :, -self.n_modes :, : self.n_modes], self.fourier_weight_2
                ),
            ],
            axis=-3,
        )
        # out_ft.shape = (batch_size, in_dim, grid_size , n_modes, 2)
        out_ft = tf.concat(
            [out_ft, tf.zeros([dynamic_shape[0], I, N, M // 2 + 1 - self.n_modes, 2])],
            axis=-2,
        )
        # out_ft.shape = (batch_size, in_dim, grid_size , grid_size // 2 + 1, 2)
        out_ft = tf.complex(out_ft[..., 0], out_ft[..., 1])

        # inputs = tf.signal.irfft2d(out_ft, fft_length=[N, M])
        inputs = manual_irfft2d(out_ft)
        scale = tf.sqrt(tf.cast(tf.reduce_prod(tf.shape(out_ft)[-2:]), tf.float32))
        inputs *= scale

        inputs = tf.transpose(inputs, perm=[0, 2, 3, 1])

        res = self.linear(inputs)
        res = self.act(res + inputs)
        return res

# %%
class FourierLayer2d(tf.keras.layers.Layer):
    def __init__(self, in_dim, out_dim, n_modes, residual=True):
        super(FourierLayer2d, self).__init__()
        self.in_dim = in_dim
        self.out_dim = out_dim
        self.n_modes = n_modes
        self.linear = tf.keras.layers.Dense(out_dim)
        self.residual = residual
        self.act = tf.keras.layers.ReLU()

        fourier_weight = [
            tf.Variable(
                tf.random.normal(
                    shape=(in_dim, out_dim, n_modes, n_modes, 2),
                    stddev=1 / (in_dim * out_dim),
                )
            )
            for _ in range(2)
        ]
        self.fourier_weight = fourier_weight

    @staticmethod
    def complex_matmul_2d(a, b):
        # (batch, in_channel, x, y), (in_channel, out_channel, x, y) -> (batch, out_channel, x, y)
        # assert they have the dimension
        op = partial(tf.einsum, "bixy,ioxy->boxy")
        return tf.stack(
            [
                op(a[..., 0], b[..., 0]) - op(a[..., 1], b[..., 1]),
                op(a[..., 1], b[..., 0]) + op(a[..., 0], b[..., 1]),
            ],
            axis=-1,
        )

    def call(self, x):
        # x.shape == [batch_size, grid_size, grid_size, in_dim]
        _, M, N, I = x.shape
        dynamic_shape = tf.shape(x)

        x = tf.transpose(x, perm=[0, 3, 1, 2])
        assert x.dtype == tf.float32
        # x.shape == [batch_size, in_dim, grid_size, grid_size]

        # x_ft = tf.signal.rfft2d(x, fft_length=[M, N])
        x_ft = manual_rfft2d(x)

        x_ft = tf.stack([tf.math.real(x_ft), tf.math.imag(x_ft)], axis=-1)

        # downcast to float32, see https://www.tensorflow.org/api_docs/python/tf/signal/rfft2d, since tf.signal.rfft2d returns complex64 and input is float32
        x_ft = tf.cast(x_ft, tf.float32)

        # x_ft.shape == [batch_size, in_dim, grid_size, grid_size // 2 + 1, 2]

        # x_ft_stacked = tf.stack([x_ft_real, x_ft_imag], axis=-1)
        # x_ft_stacked.shape == [batch_size, in_dim, grid_size, grid_size // 2 + 1, 2]
        out_ft = self.complex_matmul_2d(
            x_ft[:, :, : self.n_modes, : self.n_modes], self.fourier_weight[0]
        )
        # out_ft.shape=(batch_size, in_dim, n_modes, n_modes, 2)
        out_ft_zero = tf.zeros(
            [dynamic_shape[0], I, N - self.n_modes * 2, self.n_modes, 2],
            dtype=tf.float32,
        )
        out_ft = tf.concat([out_ft, out_ft_zero], axis=-3)
        out_ft = tf.concat(
            [
                out_ft,
                self.complex_matmul_2d(
                    x_ft[:, :, -self.n_modes :, : self.n_modes], self.fourier_weight[1]
                ),
            ],
            axis=-3,
        )
        out_ft = tf.concat(
            [out_ft, tf.zeros([dynamic_shape[0], I, N, M // 2 + 1 - self.n_modes, 2])],
            axis=-2,
        )

        out_ft = tf.complex(out_ft[..., 0], out_ft[..., 1])

        # x = tf.signal.irfft2d(out_ft, fft_length=[N, M])
        x = manual_irfft2d(out_ft)

        # x_ift_real = tf.signal.irfft(out_ft, fft_length=M, name="irfft_real")
        # x_ift_real.shape == [batch_size, grid_size, grid_size, in_dim]

        x = tf.transpose(x, perm=[0, 2, 3, 1])

        if self.residual:
            res = self.linear(x)
            x = self.act(x + res)
        else:
            x = self.act(x)
        return x


import json

# Demander le nom du fichier JSON à modifier
filename = input("Entrez le nom du fichier JSON à modifier : ")

# Vérifier si le fichier existe ou pas
try:
    with open(filename, "r") as f:
        data = json.load(f)
except FileNotFoundError:
    data = {}

# Afficher le contenu actuel du fichier JSON
print("Contenu actuel :")
print(json.dumps(data, indent=4))

# Demander à l'utilisateur les modifications à apporter
while True:
    print("Que voulez-vous faire ?")
    print("1. Ajouter ou modifier une entrée")
    print("2. Supprimer une entrée")
    print("3. Quitter")

    choice = input("Entrez votre choix (1-3) : ")

    if choice == "1":
        key = input("Entrez le nom de la clé : ")
        value = input("Entrez la valeur de la clé : ")
        data[key] = value
    elif choice == "2":
        key = input("Entrez le nom de la clé à supprimer : ")
        if key in data:
            del data[key]
        else:
            print(f"La clé '{key}' n'existe pas dans le fichier.")
    elif choice == "3":
        break
    else:
        print("Choix invalide. Veuillez entrer 1, 2 ou 3.")

# Enregistrer les modifications dans le fichier JSON
with open(filename, "w") as f:
    json.dump(data, f, indent=4)

print("Modifications enregistrées dans le fichier.")


import asyncio

import pytest

from telegram import Contact, Voice
from telegram.error import BadRequest
from telegram.request import RequestData
from tests.auxil.slots import mro_slots

@pytest.fixture(scope="module")
def contact():
    return Contact(
        TestContactBase.phone_number,
        TestContactBase.first_name,
        TestContactBase.last_name,
        TestContactBase.user_id,
    )

class TestContactBase:
    phone_number = "+11234567890"
    first_name = "Leandro"
    last_name = "Toledo"
    user_id = 23

class TestContactWithoutRequest(TestContactBase):
    def test_slot_behaviour(self, contact):
        for attr in contact.__slots__:
            assert getattr(contact, attr, "err") != "err", f"got extra slot '{attr}'"
        assert len(mro_slots(contact)) == len(set(mro_slots(contact))), "duplicate slot"

    def test_de_json_required(self, bot):
        json_dict = {"phone_number": self.phone_number, "first_name": self.first_name}
        contact = Contact.de_json(json_dict, bot)
        assert contact.api_kwargs == {}

        assert contact.phone_number == self.phone_number
        assert contact.first_name == self.first_name

    def test_de_json_all(self, bot):
        json_dict = {
            "phone_number": self.phone_number,
            "first_name": self.first_name,
            "last_name": self.last_name,
            "user_id": self.user_id,
        }
        contact = Contact.de_json(json_dict, bot)
        assert contact.api_kwargs == {}

        assert contact.phone_number == self.phone_number
        assert contact.first_name == self.first_name
        assert contact.last_name == self.last_name
        assert contact.user_id == self.user_id

    def test_to_dict(self, contact):
        contact_dict = contact.to_dict()

        assert isinstance(contact_dict, dict)
        assert contact_dict["phone_number"] == contact.phone_number
        assert contact_dict["first_name"] == contact.first_name
        assert contact_dict["last_name"] == contact.last_name
        assert contact_dict["user_id"] == contact.user_id

    def test_equality(self):
        a = Contact(self.phone_number, self.first_name)
        b = Contact(self.phone_number, self.first_name)
        c = Contact(self.phone_number, "")
        d = Contact("", self.first_name)
        e = Voice("", "unique_id", 0)

        assert a == b
        assert hash(a) == hash(b)
        assert a is not b

        assert a == c
        assert hash(a) == hash(c)

        assert a != d
        assert hash(a) != hash(d)

        assert a != e
        assert hash(a) != hash(e)

    async def test_send_contact_without_required(self, bot, chat_id):
        with pytest.raises(ValueError, match="Either contact or phone_number and first_name"):
            await bot.send_contact(chat_id=chat_id)

    async def test_send_mutually_exclusive(self, bot, chat_id, contact):
        with pytest.raises(ValueError, match="Not both"):
            await bot.send_contact(
                chat_id=chat_id,
                contact=contact,
                phone_number=contact.phone_number,
                first_name=contact.first_name,
            )

    async def test_send_with_contact(self, monkeypatch, bot, chat_id, contact):
        async def make_assertion(url, request_data: RequestData, *args, **kwargs):
            data = request_data.json_parameters
            phone = data["phone_number"] == contact.phone_number
            first = data["first_name"] == contact.first_name
            last = data["last_name"] == contact.last_name
            return phone and first and last

        monkeypatch.setattr(bot.request, "post", make_assertion)
        assert await bot.send_contact(contact=contact, chat_id=chat_id)

class TestContactWithRequest(TestContactBase):
    @pytest.mark.parametrize(
        ("default_bot", "custom"),
        [
            ({"allow_sending_without_reply": True}, None),
            ({"allow_sending_without_reply": False}, None),
            ({"allow_sending_without_reply": False}, True),
        ],
        indirect=["default_bot"],
    )
    async def test_send_contact_default_allow_sending_without_reply(
        self, default_bot, chat_id, contact, custom
    ):
        reply_to_message = await default_bot.send_message(chat_id, "test")
        await reply_to_message.delete()
        if custom is not None:
            message = await default_bot.send_contact(
                chat_id,
                contact=contact,
                allow_sending_without_reply=custom,
                reply_to_message_id=reply_to_message.message_id,
            )
            assert message.reply_to_message is None
        elif default_bot.defaults.allow_sending_without_reply:
            message = await default_bot.send_contact(
                chat_id, contact=contact, reply_to_message_id=reply_to_message.message_id
            )
            assert message.reply_to_message is None
        else:
            with pytest.raises(BadRequest, match="message not found"):
                await default_bot.send_contact(
                    chat_id, contact=contact, reply_to_message_id=reply_to_message.message_id
                )

    @pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
    async def test_send_contact_default_protect_content(self, chat_id, default_bot, contact):
        tasks = asyncio.gather(
            default_bot.send_contact(chat_id, contact=contact),
            default_bot.send_contact(chat_id, contact=contact, protect_content=False),
        )
        protected, unprotected = await tasks
        assert protected.has_protected_content
        assert not unprotected.has_protected_content


##### Importar bibliotecas.
import sqlite3
from sqlite3 import Error

##### Criar função de CONEXÃO com o database.
def ConexaoBanco():
    caminho = "C:\\Users\\joaod\\projetos\\cadastroMedCompany\\medCompany.db"
    conex = None
    try:
        conex = sqlite3.connect(caminho)
    except Error as ex:
        print(ex)
    return conex

##### Variável para inicializar a função.
conect_db = ConexaoBanco()

##### Criar TABELA
newTabSql = """CREATE TABLE IF NOT EXISTS Clientes(
                NOME VARCHAR (100),
                DATA NASCIMENTO DATE,
                IDADE INTEGER,
                SEXO VARCHAR (20),
                ESTADO CIVIL VARCHAR (20),
                CPF INTEGER,
                RG INTEGER,
                MAE VARCHAR (100),
                ENDERECO VARCHAR (150),
                NUMERO INTEGER,
                MUNICIPIO VARCHAR (100),
                BAIRRO VARCHAR (100),
                CEP NUMBER,
                FONE NUMBER,
                CELULAR NUMBER,
                EMAIL VARCHAR (100)
                )"""

##### Criar função CURSOR
def createTab (conexao, sql):
    try:
        cursor=conexao.cursor()
        cursor.execute(sql)
        print('Tabela Criada')
    except Error as ex:
        print(ex)

##### Chamando as funções e criando tabela no database.
createTab(conect_db, newTabSql)


import time
from pathlib import Path
from shlex import split
from subprocess import run
from sys import exit

from requests import get
from RPi import GPIO as GPIO

NUM_PIN = 37

GPIO.setmode(GPIO.BOARD)
GPIO.setup(NUM_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

DEFAULT_PORT = 5000
TICKER_HOST = "ticker"
SCREEN1_HOST = "mbp-van-lotte"
SCREEN2_HOST = "ilinx"

counter = 0
COUNTER_GOAL = 30

SLEEP_FOR_SCREEN_ANIM = 320

TIP_TINY = Path("assets/sounds/tip_tiny.mp3").absolute()
TIP_GOAL = Path("assets/sounds/tip_medium.mp3").absolute()

def play_sound(fpath):
    run(split(f"play {fpath}"))

def fire_request(url):
    try:
        get(url)
        return True
    except Exception:
        print(f"Failed to request {url}...")
        return False

def go_test():
    for host in [TICKER_HOST, SCREEN1_HOST, SCREEN2_HOST]:
        success = fire_request(f"http://{host}:{DEFAULT_PORT}/ok")
        if not success:
            print(f"Unable to contact the '{host}' host, bailing out...")
            exit(1)

print("Running start-up tests...")
go_test()
print("Success!")

try:
    print("Running GPIO read loop...")
    while 1:
        if GPIO.input(NUM_PIN) == 1:
            print("Coin detected...")

            play_sound(TIP_TINY)
            fire_request(f"http://{SCREEN1_HOST}:{DEFAULT_PORT}")
            fire_request(f"http://{SCREEN2_HOST}:{DEFAULT_PORT}")

            counter += 1
            if counter == COUNTER_GOAL:
                print("Goal reached...")
                play_sound(TIP_GOAL)
                fire_request(f"http://{TICKER_HOST}:{DEFAULT_PORT}")
                counter = 0

                print(f"Going to sleep for {SLEEP_FOR_SCREEN_ANIM} seconds...")
                time.sleep(SLEEP_FOR_SCREEN_ANIM)
                print("Waking up again...")

        time.sleep(0.1)
except KeyboardInterrupt:
    GPIO.cleanup()


##################################################################################
# Author:   Adam Tipton
# Title:    Project Bogie Trainer
# Version:  1
#
# Company:  Brigham Young University - Idaho
# Course:   CSE 499 Senior Project
# Semester: Fall 2020
#
# Description:
#   Project Bogie is a Convolutional Neural Network written in the Python language.
#   This CNN will train a model from a dataset the ability to identify military
#   aircraft/jets.
#
#   Once trained, the program will create save the model for use in an application
#   that allows a user to input an image to test if it contains a military aircraft.
#
#   This version will use a 3 block CNN that draws from the VGG16 model. This gives
#   our model a headstart in training as many of the weights are preset, giving
#   us the advantage of a shortened training time.
#
#   Project Bogie uses TensorFlow as a backend and Keras as a driving force for training.
#
#   The TRAINER will be the primary workshorse for this project, which is divided into
#   two parts. Part I is this program, the trainer. Part II is the Predictor program.
#   The trainer is responsible for taking a data set of Modern Military Aircraft Images
#   and training a CNN to output a model. Therefore, the deliverable of this program
#   is to create h5 and hdf5 models.
#
#
# Sources:
#   Much inspiration and reusable code base is taken from the following tutorial -
#   URL: https://machinelearningmastery.com/how-to-develop-a-convolutional-neural-network-to-classify-photos-of-dogs-and-cats/
#   create_model(), diagnostics(), and train_and_evaluate_model() rely heavily on this
#   tutorial and code.
###################################################################################
###################################################################################

# System
import sys

# This important import gives us a headstart on training our model.
from keras.applications.vgg16 import VGG16

# Other important keras imports
from keras.models import Model
from keras.layers import Dense
from keras.layers import Flatten
from keras.optimizers import SGD
from keras.optimizers import Adam
from keras.optimizers import Adadelta
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import ModelCheckpoint

# This import is for playing a sound
import winsound

# These imports are important for file handling
import os
from os import listdir

# This allows us to easily adjust epochs
epoch_rate = 200

# The learning rate is an important factor in training neural networks
learning_rate = 0.0016
# Import for pyplot
from matplotlib import pyplot

# The momentum variable can be adjusted to tweak learning
momentum_rate = 0.9

# The batch variable allows us to adjust batch sizes
batch = 42

# The model names
default_model_name = "Project_Bogie_Model.h5"
checkpoint_model_name = "weights.best"

# The location of our dataset for training
dataSet_dir = 'C:/Users/Adam/source/repos/military_aircraft/aircraft_folders_unsorted'

# The location of an alert sound we'd like to play once it finishes - replace with your own
sound_url = 'C:/Users/Adam/Music/Sounds/Soothing_Alert'

# The location of the model saves
model_save_path = ''

##########################################################
################         FUNCTIONS        ################
##########################################################

# This function welcomes the user with a informative message
def welcome():
    print(" ############################################################################################")
    print(" ############################################################################################")
    print(" ##################################                         #################################")
    print(" ##################################      PROJECT BOGIE      #################################")
    print(" ##################################         TRAINER         #################################")
    print(" ##################################                         #################################")
    print(" ##################################  Copyright BYU-I 2020   #################################")
    print(" ##################################  Author: Adam Tipton    #################################")
    print(" ##################################                         #################################")
    print(" ##################################  CS499 Final Project    #################################")
    print(" ##################################                         #################################")
    print(" ############################################################################################")
    print(" ############################################################################################")
    print(" #                                                                                #")
    print(" #                                 Welcome to Project Bogie.                                #")
    print(" #                                                                                #")
    print(" #                                        *  *  *  *                                        #")
    print(" #                                                                                #")
    print(" #  Project Bogie Trainer uses a Convolutional Neural Network trained against image data    #")
    print(" #  for the purpose of creating a predictive model that can determine the likelihood of an  #")
    print(" #  input image containing a modern military aircraft or something else. For the purposes   #")
    print(" #  of clarification, a modern military aircraft will generally be post-Vietnam War era     #")
    print(" #  vintage.                                                                                #")
    print(" #                                                                                #")
    print(" #  Generally, two classes of data are provided. The first class, which would produce near  #")
    print(" #  0 values, would contain modern military aircraft. The second class, which would produce #")
    print(" #  near 1 values, would contain other non-military aircraft and other unclassified random  #")
    print(" #  images. This is done in order to train the model to detect the difference between what  #")
    print(" #  we want to identify, modern military aircraft, and what we don't, everything else.      #")
    print(" #                                                                                #")
    print(" #  The trainer will produce a hierarchical model with the .h5 and/or the .hdf5 extension.  #")
    print(" #  As the training progresses, checkpoints will periodically save the current best model   #")
    print(" #  which will be saved in a directory in the .hdf5 extension. Several 'best' models may be #")
    print(" #  produced. Additionally, a final model witht he .hdf5 extension will be produced at the  #")
    print(" #  conclusion of the epoch cycle. This model may differ significantly from the 'best'      #")
    print(" #  checkpointed model in its accuracy, which is why both model types are provided. Testing #")
    print(" #  has shown that the checkpoing 'best' model will generally contain a higher accuracy     #")
    print(" #  than the end of epoch model, which has the possibility to degrade slightly. if you are  #")
    print(" #  unsure which model to chose, chose the checkpointed 'best' model, as it will guaruntee  #")
    print(" #  you the highest accuracy.                                                               #")
    print(" #                                                                                #")
    print(" ############################################################################################")
    print(" ############################################################################################")
    print("\n")
    print("\n")

# This function allows the user to initialize important variable if they choose and enter a save filename
def setup():

    # ask the user if they want to set up the variables
    response = input("\n Press 'Y' if you would like to setup the training variables or any other key to skip: ")
    if response == 'Y' or response == 'y':
        print("\n Sure . . .")
    else:
        print("\n Skipping setup . . . \n")
        return

    #epoch_rate
    global epoch_rate
    epoch_rate = input("\n Please enter the epoch rate (e.g. 50): ")
    #handle unexpected string input
    if not epoch_rate.isnumeric():
        epoch_rate = int(1);
    else:
        int(float(epoch_rate))
    if int(float(epoch_rate)) < int(1):
        print(" Epoch rate too low. Adjusting epoch rate to 1 . . .")
        epoch_rate = int(1)
    else:
        print(" Epoch rate is set to: ", str(epoch_rate))

    #learning_rate
    global learning_rate
    learning_rate = input("\n Please enter the learning rate (e.g. 0.001): ")
    #handle unexpected string input
    if not learning_rate.isnumeric():
        learning_rate = float(0.1)
    else:
        float(learning_rate)
    if float(learning_rate) >= float(1.0):
        print(" Learning rate too high. Adjusting learning rate to 0.1")
        learning_rate = float(0.1)
    else:
        print(" Learning rate is set to: ", str(learning_rate))

    #momentum_rate
    global momentum_rate
    momentum_rate = input("\n Please enter the momentum rate (e.g. 0.9): ")
    #handle unexpected string input
    if not momentum_rate.isnumeric():
        momentum_rate = float(0.1)
    else:
        float(momentum_rate)
    if float(momentum_rate) >= float(1):
        print(" Momentum rate too high. Adjusting momentum rate to 0.1")
        momentum_rate = float(0.1)
    else:
        print(" Momentum rate is set to: ", str(momentum_rate))

    #batch
    global batch
    batch = input("\n Please enter the batch count (e.g. 25): ")
    #handle unexpected string input
    if not batch.isnumeric():
        batch = int(10)
    else:
        int(batch)
    if int(batch) <= int(0):
        print(" Batch ammount is set too low. Adjusting batch to 10 . . .\n")
        batch = int(10)
    else:
        print(" Batch is set to: ", str(batch))
        print("\n")

# This function allows the user to set the model name
def set_model_name():
    #access global variables
    global default_model_name
    global checkpoint_model_name

    #save default model filename
    response = input("\n Press 'Y' if you would like to set the filenames for the models," +
                     " or press any other key to skip: ")
    if response == 'Y' or response == 'y':
        print("\n Sure . . .")
        #Default Model Name
        name = input("\n Please enter the new default model name: ")
        model_name = name + ".h5"
        print(" The Default model name is now set to: " , model_name)
        default_model_name = model_name

        #Checkpoint Model Name
        print("\n The current filename for the checkpoint model name is 'weights.best'"
              + "\n appended by the accuracy rate and the .hdf5 extension.")
        print(" Changing the filename will not remove the accuracy rate or the .hdf5 extension.")
        name2 = input("\n Please enter the new checkpoint model name (default: weights.best): ")
        print("\n The checkpoint model name is now set to: " , name2 + "-{the accuracy value}.hdf5")
        checkpoint_model_name = name2
    else:
        print("\n Skipping model name change.")
        # Ensuring default model name stays default
        default_model_name = "Project_Bogie_Model.h5"
        print("\n Default model name is: ", default_model_name)
        print("\n Checkpoint model name is: weights.best-{the accuracy value}.hdf5")

# This function allows the user to set the save path for the model
def set_model_save_path():
    # Ask the user if they want to set the save filepath for the model
    response = input("\n Enter 'Y' if you would like to set the save filepath for the default and checkpoint models: ")
    if response == 'y' or response == 'Y':
        print("\n Of course . . .\n")
    else:
        print(" Default save path selected.\n ")
        return

    # filepath
    filepath_found = False
    save_dir = ''

    # Prompt and check for valid path
    while not filepath_found:
        save_dir = input(" Please enter the filepath to the new save directory: ")

        # check if directory exists
        if not os.path.exists(save_dir):
            print("\n Filepath error: " + save_dir + " not found.\n")
        else:
            filepath_found = True
            print("\n Dataset directory found.\n")
            # Set the dataset directory to the new path
            global model_save_path
            model_save_path = save_dir

# This function allows the user to select a dataset directory path
def set_dataset_path():
    # Ask the user if they want to provide a different dataset dir
    print(" Project Bogie Trainer has a dataset directory that is hard coded.")
    response = input(" Enter 'Y' if you would like to set another dataset directory: ")
    if response == 'y' or response == 'Y':
        print("\n Of course . . .\n")
    else:
        print(" Default path selected.\n ")
        return

    # dataset
    datasetFound = False
    datasetDir = ''

    # Explain what is expected
    print(" For success, please ensure the directory path to the dataset contains classes (folders with images).")
    print(" You will need at least two classes (folders with images) to successfully train a model.\n")

    print(" The neural net will sort the classes inside the directory alphabetically, assigning them the value of 0-n.")
    print(" For example, class A (folder named A), will be assigned 0, class B will be assigned 1, etc,.\n")

    # Prompt and check for valid path
    while not datasetFound:
        datasetDir = input(" Please insert the directory location of the dataset: ")

        # check if directory exists
        if not os.path.exists(datasetDir):
            print("\n Filepath error: " + datasetDir + " not found.\n")
        else:
            datasetFound = True
            print("\n Dataset directory found.\n")
            # Set the dataset directory to the new path
            global dataSet_dir
            dataSet_dir = datasetDir

# This function will create our model. Here we call VGG16.
def create_model():
    # define a model variable and load the VGG16 information inside it.
    # it defines the expected shape of our files.
    model = VGG16(include_top=False, input_shape=(244, 244, 3))

    # Loop through and set the trainable flags to false for now.
    # We don't need to train them as VGG16 comes pretrained.
    for pre_existing_layer in model.layers:
        pre_existing_layer.trainable = False

    # Here we add new classifier layers to the model, see how they stack?
    # We'll use relu in class1 Dense(), and sigmoid in the output Dense()
    flat1 = Flatten()(model.layers[-1].output)
    class1 = Dense(128, activation='relu', kernel_initializer='he_uniform')(flat1)
    output = Dense(1, activation='sigmoid')(class1)

    # New model definition created
    # The inputs are taken from the VGG16 model, the outputs are define above.
    model = Model(inputs=model.inputs, outputs=output)

    # Compile and then return the model
    #opt = Adadelta(lr = float(learning_rate), rho = 0.95, epsilon = 1e-06)
    opt = Adam(lr=float(learning_rate), beta_1=0.9, beta_2=0.999, epsilon=0.1)
    #opt = SGD(lr=float(learning_rate), momentum=float(momentum_rate))
    model.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy'])

    return model

# Lets plot diagnostic data to see what is going on
def diagnostics(history):
    # plot loss
	pyplot.subplot(211)
	pyplot.title('Cross Entropy Loss')
	pyplot.plot(history.history['loss'], color='blue', label='train')
	pyplot.plot(history.history['val_loss'], color='red', label='test')
	# plot accuracy
	pyplot.subplot(212)
	pyplot.title('Classification Accuracy')
	pyplot.plot(history.history['accuracy'], color='blue', label='train')
	pyplot.plot(history.history['val_accuracy'], color='red', label='test')
	# save plot to file
	filename = sys.argv[0].split('/')[-1]
	pyplot.savefig(filename + '_plot.png')
	pyplot.close()

# This function will test and evaluate the model
# It takes a directory string for the sound we'd like to play
def train_and_evaluate_model(sound_url):
    # create the model
    model = create_model()

    # Here we create the data generator
    data_gen = ImageDataGenerator(featurewise_center=True,
                                  width_shift_range=0.25,
                                  height_shift_range=0.25,
                                  horizontal_flip=True,
                                  vertical_flip=False,
                                  rotation_range=45,
                                  brightness_range=[0.8, 1.2],
                                  zoom_range=[0.4, 1.4])

    # Here we specify the image mean values for centering purposes
    data_gen.mean = [123.68, 116.779, 103.939]

    # Create the iterator for file handling
    training_iterator = data_gen.flow_from_directory(dataSet_dir,
        class_mode='binary', batch_size=int(batch), target_size=(244, 244))

    #callback
    global model_save_path
    #filepath = "C:/Users/Adam/source/repos/ProjectBogieTrainer/" + str(checkpoint_model_name) + "-{val_accuracy:.2f}.hdf5"
    filepath = str(checkpoint_model_name) + "-{val_accuracy:.2f}.hdf5"
    checkpoint = ModelCheckpoint(filepath=model_save_path + filepath, monitor='val_accuracy', verbose=1, save_best_only=True, mode='max')
    callbacks_list = [checkpoint]

    # Fit the model
    history = model.fit_generator(training_iterator, steps_per_epoch=len(training_iterator),
        validation_data = training_iterator, epochs=int(epoch_rate), callbacks=callbacks_list, verbose=0)

    # Here we evalute the model
    _, acc = model.evaluate_generator(training_iterator, steps=len(training_iterator), verbose=0)
    print('Accuracy:')
    print('>%.3f' % (acc * 100.0))
    print("Learning Rate:")
    print(float(learning_rate))
    print('\n')

    # This will produce the learning curve graphs
    print('Summarizing daignostic curves...\n')
    diagnostics(history)

    # Now we save the model
    print("Saving Model...")
    model.save(model_save_path + default_model_name)

    # Play an alert when the model is done training. Replace the file with your own.
    winsound.PlaySound(sound_url, winsound.SND_FILENAME)
    print("Model now complete...\n")

def test():
    print("Epoch: ", epoch_rate)
    print("Learning rate: ", learning_rate)
    print("Momentum: ", momentum_rate)
    print("Batch: ", batch)
    print("Default Model Name: ", default_model_name)
    print("Checkpoint Model Name: ", checkpoint_model_name)
    print("Model save path: ", model_save_path)

# Welcome the user
welcome()

# Set the dataset dir
set_dataset_path()

# Setup important variables and model name
setup()
set_model_name()
set_model_save_path()
test()
# This is the entry point to our program. From here, all of the training begins.
train_and_evaluate_model(sound_url)


from utils import PAD_TOK, START_TOK, END_TOK
import sys
import os

sys.path.append(os.path.join(os.getcwd(), 'tgen'))
from tgen.data import DAI

class TokEmbeddingSeq2SeqExtractor(object):
    def __init__(self, tokenised_texts, max_length=None):
        self.vocab = set()
        for tokens in tokenised_texts:
            self.vocab.update(tokens)
        self.vocab.add(PAD_TOK)
        self.tok_to_embed = {tok: i for i, tok in enumerate(sorted(list(self.vocab)))}
        self.embed_to_tok = {i: tok for i, tok in enumerate(sorted(list(self.vocab)))}
        self.vocab_length = len(self.vocab)
        self.length = max([len(x) for x in tokenised_texts])
        if max_length is not None:
            self.length = min(self.length, max_length)
        self.start_emb = [self.tok_to_embed[START_TOK]]
        self.end_embs = [self.tok_to_embed[END_TOK], self.tok_to_embed[PAD_TOK]]
        self.empty_embedding = [self.tok_to_embed[PAD_TOK] for _ in range(self.length)]

    def get_embeddings(self, tokenised_texts, pad_from_end=True):
        embs = []
        for toks in tokenised_texts:
            emb = [self.tok_to_embed.get(x, self.tok_to_embed[PAD_TOK]) for x in toks]
            pad = [self.tok_to_embed[PAD_TOK] for _ in range(self.length - len(toks))]
            if pad_from_end:
                embs.append(emb + pad)
            else:
                embs.append(pad + emb)
        return [e[:self.length] for e in embs]

    def add_pad_to_embed(self, emb, to_start=False):
        pad = [self.tok_to_embed[PAD_TOK] for _ in range(self.length - len(emb))]
        if to_start:
            return pad + list(emb)
        else:
            return list(emb) + pad

    def reverse_embedding(self, embedding):
        return [self.embed_to_tok[e] for e in embedding]

    def remove_pad_from_embed(self, emb):
        return [x for x in emb if x != self.tok_to_embed[PAD_TOK]]

    def pad_to_length(self, text_emb, to_start=True):
        pads = [self.tok_to_embed[PAD_TOK]] * (self.length - len(text_emb))
        if to_start:
            return pads + text_emb
        else:
            return text_emb + pads

class DAEmbeddingSeq2SeqExtractor(object):
    UNK_ACT = 'UNK_ACT'
    UNK_VALUE = 'UNK_VALUE'
    UNK_SLOT = 'UNK_SLOT'

    def __init__(self, das):
        self.acts = {self.UNK_ACT}
        self.slots = {self.UNK_SLOT}
        self.values = {self.UNK_VALUE}
        self.coocurrence = set()
        for da in das:
            for dai in da:
                self.acts.add(dai.da_type)
                self.slots.add(dai.slot)
                self.values.add(dai.value)
                self.coocurrence.add((dai.da_type, dai.slot, dai.value))
        taken_emb = 0
        self.act_emb = {act: taken_emb + i for i, act in enumerate(sorted(list(self.acts)))}
        self.rev_act_emb = {taken_emb + i: act for i, act in enumerate(sorted(list(self.acts)))}
        taken_emb += len(self.acts)
        self.slot_emb = {slo: taken_emb + i for i, slo in enumerate(sorted(list(self.slots)))}
        self.rev_slot_emb = {taken_emb + i: slo for i, slo in enumerate(sorted(list(self.slots)))}
        taken_emb += len(self.slots)
        self.val_emb = {val: taken_emb + i for i, val in enumerate(sorted(list(self.values)))}
        self.rev_val_emb = {taken_emb + i: val for i, val in enumerate(sorted(list(self.values)))}
        taken_emb += len(self.values)
        self.vocab_length = taken_emb
        self.inclusion_map = {val: i for i, val in enumerate(sorted(list(self.coocurrence)))}
        self.inclusion_rev = {i: val for i, val in enumerate(sorted(list(self.coocurrence)))}
        self.length = max([len(x) for x in das]) * 3
        self.inclusion_length = len(self.inclusion_map)
        self.empty_inclusion = [0 for _ in range(self.inclusion_length)]
        self.empty_embedding = self.get_embeddings([[]])[0]

    def get_embeddings(self, das):
        embs = []
        unk_ack_emb = self.act_emb[self.UNK_ACT]
        unk_slot_emb = self.slot_emb[self.UNK_SLOT]
        unk_val_emb = self.val_emb[self.UNK_VALUE]
        for da in das:
            emb = []
            for dai in sorted(da):
                emb.append(self.act_emb.get(dai.da_type, unk_ack_emb))
                emb.append(self.slot_emb.get(dai.slot, unk_slot_emb))
                emb.append(self.val_emb.get(dai.value, unk_val_emb))

            pad = [unk_ack_emb, unk_slot_emb, unk_val_emb] * (self.length // 3 - len(da))
            embs.append(pad + emb)
        return [e[-self.length:] for e in embs]

    def reverse_embedding(self, da_emb):
        i = 0
        das = []
        while i < len(da_emb):
            act = self.rev_act_emb[da_emb[i]]
            slot = self.rev_slot_emb[da_emb[i + 1]]
            val = self.rev_val_emb[da_emb[i + 2]]
            i += 3
            if act != self.UNK_ACT:
                das.append(DAI(act, slot, val))
        return das

    def remove_pad_from_embed(self, emb):
        results = []
        for i in range(0, len(emb), 3):
            if emb[i] == self.act_emb[self.UNK_ACT] and emb[i + 1] == self.slot_emb[self.UNK_SLOT] and emb[i + 2] == \
                    self.val_emb[self.UNK_VALUE]:
                continue
            results.append(emb[i])
            results.append(emb[i + 1])
            results.append(emb[i + 2])
        return results

    def add_pad_to_embed(self, emb, to_start=True):
        pad = [self.act_emb[self.UNK_ACT], self.slot_emb[self.UNK_SLOT], self.val_emb[self.UNK_VALUE]] \
              * ((self.length - len(emb))//3)
        if to_start:
            return pad+list(emb)
        else:
            return list(emb)+pad

    def get_inclusion(self, das):
        included = set()
        for dai in das:
            if (dai.da_type, dai.slot, dai.value) in self.inclusion_map:
                included.add(self.inclusion_map[(dai.da_type, dai.slot, dai.value)])
        return [(1 if x in included else 0) for x in range(self.inclusion_length)]

    def reverse_inclusion(self, inclusion):
        included = set()
        for i, x in enumerate(inclusion):
            if x == 1:
                included.add(i)
        return [self.inclusion_rev[x] for x in included]

if __name__ == "__main__":
    texts = ["<S> My name is James . <E>", "<S> My favourite food is steak . <E>", "<S> A <E>"]
    tokenized = [x.split(" ") for x in texts]
    print(tokenized)
    emb = TokEmbeddingSeq2SeqExtractor(tokenized)
    print(emb.get_embeddings(tokenized))
    print(emb.get_embeddings([['<S>']]))


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.shortcuts import render
from django.http import HttpResponseRedirect
from .forms import ChristmasTree, BlindMaster, BlindTeam

import time

from .utils import is_raspberry_pi

if is_raspberry_pi:
    # noinspection PyUnresolvedReferences
    import RPi.GPIO as GPIO
    import Adafruit_WS2801
    import Adafruit_GPIO.SPI as SPI
    from ledstrip.led_light import wheel

import threading
import pygame  # Need also to do: sudo apt-get install git curl libsdl2-mixer-2.0-0 libsdl2-image-2.0-0 libsdl2-2.0-0

# Configure the count of pixels:
PIXEL_COUNT = 6

# Alternatively specify a hardware SPI connection on /dev/spidev0.0:
SPI_PORT   = 0
SPI_DEVICE = 0

thread_running = False
thread_stop = False

if is_raspberry_pi:
    blue  = Adafruit_WS2801.RGB_to_color(0, 0, 255)
    white = Adafruit_WS2801.RGB_to_color(100, 100, 100)
    red   = Adafruit_WS2801.RGB_to_color(255, 0, 0)
else:
    blue = white = red = None

def unicolor(color):
    if is_raspberry_pi:
        LED_power_off()

        for i in range(96):
            pixels.set_pixel(i, color)
            pixels.show()

def rainbow_cycle(pixels, wait=0.005):
    global thread_stop

    for j in range(256): # one cycle of all 256 colors in the wheel
        for i in range(pixels.count()):
            pixels.set_pixel(i, wheel(((i * 256 // pixels.count()) + j) % 256))
        pixels.show()
        if wait > 0:
            time.sleep(wait)
        if thread_stop == True:
            break

def blink_color(pixels, blink_times=5, wait=0.5, color=(255, 0, 0)):
    global thread_stop

    for i in range(blink_times):
        # blink two times, then wait
        pixels.clear()
        for j in range(2):
            for k in range(pixels.count()):
                pixels.set_pixel(k, Adafruit_WS2801.RGB_to_color( color[0], color[1], color[2]))
            pixels.show()
            time.sleep(0.08)
            pixels.clear()
            pixels.show()
            time.sleep(0.08)
        time.sleep(wait)
        if thread_stop == True:
            break

def moving(pixels):
    nb_pixels = 5
    list_led = list(range(96 - nb_pixels)) + list(range(96 - nb_pixels, 0, -1))
    for i2 in list_led:
        pixels.clear()

        for i in range(96 - nb_pixels):
            if i == i2:
                pixels.set_pixel(i + 0, blue)
                pixels.set_pixel(i + 1, blue)
                pixels.set_pixel(i + 2, white)
                pixels.set_pixel(i + 3, white)
                pixels.set_pixel(i + 4, red)
                pixels.set_pixel(i + 5, red) # update nb_pixels accordingly
        pixels.show()
        time.sleep(0.02)
        if thread_stop == True:
            break

def doCycle(effect):
    global pixels, thread_running, thread_stop

    thread_running = True
    thread_stop = False
    print("thread started")

    while thread_stop == False:
        if effect == 'rainbow':
            rainbow_cycle(pixels, wait=0.01)
        elif effect == 'blink':
            blink_color(pixels, color=(255, 255, 255))
        elif effect == 'moving':
            moving(pixels)

    thread_running = False
    print("thread stopped")

def LED_power_on(effect):
    if is_raspberry_pi:
        if thread_running == False:
            t = threading.Thread(target=doCycle, daemon=True, args=(effect,))
            t.start()

def LED_power_off():
    if is_raspberry_pi:
        global pixels, thread_stop, thread_running

        thread_stop = True
        while thread_running == True:
            time.sleep(0.1)

        pixels.clear()
        pixels.show()

############################################################################
# Create your views here

def home_page(request):

    # If this is a POST request then process the Form data
    if request.method == 'POST':

        # Create a form instance and populate it with data from the request (binding)
        form = ChristmasTree(request.POST)

        # Check if the form is valid
        if form.is_valid():
            print(form.cleaned_data['LED_effect'])
            if form.cleaned_data['LED_effect'] == 'off':
                LED_power_off()
            elif form.cleaned_data['LED_effect'] != '':
                LED_power_off()
                LED_power_on(form.cleaned_data['LED_effect'])

            if form.cleaned_data['music'] == 'off':
                stop_music()
            elif form.cleaned_data['music'] != '':
                play_music(form.cleaned_data['music'])

            if form.cleaned_data['volume'] != None:
                set_volume(form.cleaned_data['volume'])

            # redirect to a new URL:
            return HttpResponseRedirect('/')

    # If this is a GET (or any other method) create the default form
    else:
        form = ChristmasTree()

    return render(request, 'ledstrip/home_page.html', context={'form': form})

############################################################################
faster_team_to_answer = None
blue_score = 0
red_score = 0

def master(request):
    global faster_team_to_answer, blue_score, red_score

    # If this is a POST request then process the Form data
    if request.method == 'POST':

        # Create a form instance and populate it with data from the request (binding)
        form = BlindMaster(request.POST)

        # Check if the form is valid:
        if form.is_valid():
            if form.cleaned_data['blind_music'] == 'off':
                stop_music()
            elif form.cleaned_data['blind_music'] != '':
                faster_team_to_answer = None
                unicolor(white)
                play_music(form.cleaned_data['blind_music'])

            if form.cleaned_data['volume'] != None:
                set_volume(form.cleaned_data['volume'])

            if form.cleaned_data['add_point'] == 'blue':
                blue_score += 1
                unicolor(white)
            elif form.cleaned_data['add_point'] == 'red':
                red_score += 1
                unicolor(white)
            if form.cleaned_data['remove_point'] == 'blue':
                blue_score -= 1
            elif form.cleaned_data['remove_point'] == 'red':
                red_score -= 1
            elif form.cleaned_data['remove_point'] == 'reset':
                blue_score = 0
                red_score = 0

            if form.cleaned_data['bad_answer_continue'] == True:
                faster_team_to_answer = None
                unicolor(white)
                continue_music()

            # redirect to a new URL
            return HttpResponseRedirect('/master')

    # If this is a GET (or any other method) create the default form
    else:
        form = BlindMaster()

    context = {'form': form, 'faster_team_to_answer': faster_team_to_answer,
               'blue_score': blue_score, 'red_score': red_score}
    return render(request, 'ledstrip/master.html', context=context)

def blueteam(request):
    return team(request, 'blue', blue)

def redteam(request):
    return team(request, 'red', red)

def team(request, color, rgb_color):
    global faster_team_to_answer, blue_score, red_score

    # If this is a POST request then process the Form data
    if request.method == 'POST':

        # Create a form instance and populate it with data from the request (binding)
        form = BlindTeam(request.POST)

        # Check if the form is valid:
        if form.is_valid():
            if faster_team_to_answer == None:
                faster_team_to_answer = color
                stop_music()
                unicolor(rgb_color)

            # redirect to a new URL
            return HttpResponseRedirect(f'/{color}team')

    # If this is a GET (or any other method) create the default form
    else:
        form = BlindTeam()

    context = {'form': form, 'team': color, 'faster_team_to_answer': faster_team_to_answer,
               'blue_score': blue_score, 'red_score': red_score}
    return render(request, 'ledstrip/team.html', context=context)

############################################################################

def play_music(filepath):
    pygame.mixer.music.load(filepath)
    print("playing", filepath)
    pygame.mixer.music.play()

def stop_music():
    print("stop music")
    pygame.mixer.music.pause()

def continue_music():
    print("continue music")
    pygame.mixer.music.unpause()

def set_volume(volume):
    ''' Set the volume of the music playback.
    The volume argument is a float between 0.0 and 1.0 that sets volume.
    '''
    print(f"set volume {volume}%")
    pygame.mixer.music.set_volume(volume / 100)

#if __name__ == '__main__':
# Executed when app is loaded
print("init pygame mixer")
pygame.mixer.init()

if is_raspberry_pi:
    print("init Adafruit_WS2801")
    pixels = Adafruit_WS2801.WS2801Pixels(96, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE), gpio=GPIO)

    LED_power_on('rainbow')


from string import Template
from math import log2
import argparse
import hjson
import os.path
import sys

from jsonref import JsonRef

parser = argparse.ArgumentParser(description="Convert binary file to verilog rom")
parser.add_argument(
    "filename", metavar="filename.bin", nargs=1, help="filename of input binary"
)
parser.add_argument(
    "--output", nargs="?", metavar="file", help="Name of output file", default=None
)
parser.add_argument(
    "--clustercfg",
    "-c",
    metavar="file",
    type=argparse.FileType("r"),
    required=True,
    help="A cluster configuration file",
)

args = parser.parse_args()
file = args.filename[0]

# check that file exists
if not os.path.isfile(file):
    sys.exit("File {} does not exist.".format(file))
filename = os.path.splitext(file)[0]

output = args.output
if output is None:
    output = filename

# Read HJSON description
cfg = []
with args.clustercfg as file:
    try:
        srcfull = file.read()
        cfg = hjson.loads(srcfull, use_decimal=True)
        cfg = JsonRef.replace_refs(cfg)
    except ValueError:
        raise SystemExit(sys.exc_info()[1])

DataWidth = int(cfg["cluster"]["dma_data_width"])
DataOffset = int(log2(DataWidth / 8))
if DataWidth < 32:
    sys.exit("DataWidth must be larger than 32")
if DataWidth % 32:
    sys.exit("DataWidth must be multiple of 32")

AddrWidth = int(cfg["cluster"]["addr_width"])

license = """\
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
//
// Description: Automatically generated bootrom
//
// Generated by hardware/scripts/generate_bootrom.py

"""

module = """\
module $filename #(
  /* Automatically generated. DO NOT CHANGE! */
  parameter int unsigned DataWidth = $DataWidth,
  parameter int unsigned AddrWidth = $AddrWidth
) (
  input  logic                 clk_i,
  input  logic                 req_i,
  input  logic [AddrWidth-1:0] addr_i,
  output logic [DataWidth-1:0] rdata_o
);
  localparam int RomSize = $size;
  localparam int AddrBits = RomSize > 1 ? $$clog2(RomSize) : 1;

  const logic [RomSize-1:0][DataWidth-1:0] mem = {
$content
  };

  logic [AddrBits-1:0] addr_q;

  always_ff @(posedge clk_i) begin
    if (req_i) begin
      addr_q <= addr_i[AddrBits-1+$DataOffset:$DataOffset];
    end
  end

  // this prevents spurious Xes from propagating into
  // the speculative fetch stage of the core
  assign rdata_o = (addr_q < RomSize) ? mem[addr_q] : '0;
endmodule
"""

c_var = """\
// Auto-generated code

const int reset_vec_size = $size;

uint32_t reset_vec[reset_vec_size] = {
$content
};
"""

def read_bin():
    with open(filename + ".bin", "rb") as f:
        rom = bytes.hex(f.read())
        rom = list(map("".join, zip(rom[::2], rom[1::2])))
    # align to 64 bit
    align = (int((len(rom) + 7) / 8)) * 8
    for i in range(len(rom), align):
        rom.append("00")
    return rom

rom = read_bin()

""" Generate SystemVerilog bootcode for FPGA and ASIC
"""
with open(output, "w") as f:
    rom_str = ""
    ByteWidth = int(DataWidth / 8)
    # process in junks of DataWidth bit (DataWidth/8 byte)
    for i in reversed(range(int(len(rom) / ByteWidth))):
        rom_str += "    {}'h".format(DataWidth)
        for b in reversed(range(int(ByteWidth / 4))):
            rom_str += (
                "".join(rom[i * ByteWidth + b * 4 : i * ByteWidth + b * 4 + 4][::-1])
                + "_"
            )
            rom_str = rom_str[:-1]
        rom_str += ",\n"
    # remove the trailing comma
    rom_str = rom_str[:-2]
    f.write(license)
    s = Template(module)
    f.write(
        s.substitute(
            filename=os.path.basename(filename),
            size=int(len(rom) / ByteWidth),
            content=rom_str,
            DataWidth=DataWidth,
            DataOffset=DataOffset,
            AddrWidth=AddrWidth,
        )
    )


import torch as T
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import numpy as np
import os
from lbfgsnew import LBFGSNew # custom optimizer
import pickle # for saving replaymemory
from enet_sac import ReplayBuffer,PER

# (try to) use a GPU for computation?
use_cuda=True
if use_cuda and T.cuda.is_available():
  mydevice=T.device('cuda')
else:
  mydevice=T.device('cpu')

# initialize all layer weights, based on the fan in
def init_layer(layer,sc=None):
  sc = sc or 1./np.sqrt(layer.weight.data.size()[0])
  T.nn.init.uniform_(layer.weight.data, -sc, sc)
  T.nn.init.uniform_(layer.bias.data, -sc, sc)

# input: state,action output: q-value
class CriticNetwork(nn.Module):
    def __init__(self, lr, input_dims, n_actions, name):
        super(CriticNetwork, self).__init__()
        self.input_dims = input_dims # state dims
        self.n_actions = n_actions # action dims
        self.fc11 = nn.Linear(*self.input_dims, 512)
        self.fc12 = nn.Linear(512, 256) # before concat with actions

        self.fc21 = nn.Linear(n_actions, 128)
        self.fc22 = nn.Linear(128, 64) # before concat with state

        self.fc3 = nn.Linear(256+64, 1)

        self.bn11 = nn.LayerNorm(512)
        self.bn12 = nn.LayerNorm(256)
        self.bn21 = nn.LayerNorm(128)
        self.bn22 = nn.LayerNorm(64)

        init_layer(self.fc11)
        init_layer(self.fc12)
        init_layer(self.fc21)
        init_layer(self.fc21)
        init_layer(self.fc3,0.003) # last layer

        self.optimizer = optim.Adam(self.parameters(), lr=lr)
        #self.optimizer = LBFGSNew(self.parameters(), history_size=7, max_iter=1, line_search_fn=True,batch_mode=True)
        self.device = mydevice
        self.checkpoint_file = os.path.join('./', name+'_td3_critic.model')

        self.to(self.device)

    def forward(self, state, action):
        x=F.elu(self.bn11(self.fc11(state)))
        x=F.elu(self.bn12(self.fc12(x)))

        y=F.elu(self.bn21(self.fc21(action)))
        y=F.elu(self.bn22(self.fc22(y)))

        # concat state,actions
        z = T.cat((x,y),dim=1)

        qval=self.fc3(z)

        return qval

    def save_checkpoint(self):
        T.save(self.state_dict(), self.checkpoint_file)

    def load_checkpoint(self):
        self.load_state_dict(T.load(self.checkpoint_file))

    def load_checkpoint_for_eval(self):
        self.load_state_dict(T.load(self.checkpoint_file,map_location=T.device('cpu')))

# input: state output: action
class ActorNetwork(nn.Module):
    def __init__(self, lr, input_dims, n_actions, name):
        super(ActorNetwork, self).__init__()
        self.input_dims = input_dims
        self.n_actions = n_actions
        self.fc1 = nn.Linear(*self.input_dims, 512)
        self.fc2 = nn.Linear(512, 256)
        self.fc3 = nn.Linear(256, 128)
        self.fc4 = nn.Linear(128, self.n_actions)
        self.bn1 = nn.LayerNorm(512)
        self.bn2 = nn.LayerNorm(256)
        self.bn3 = nn.LayerNorm(128)

        init_layer(self.fc1)
        init_layer(self.fc2)
        init_layer(self.fc3)
        init_layer(self.fc4,0.003) # last layer

        self.optimizer = optim.Adam(self.parameters(), lr=lr)
        #self.optimizer = LBFGSNew(self.parameters(), history_size=7, max_iter=1, line_search_fn=True,batch_mode=True)
        self.device = mydevice
        self.checkpoint_file = os.path.join('./', name+'_td3_actor.model')

        self.to(self.device)

    def forward(self, x):
        x=F.elu(self.bn1(self.fc1(x)))
        x=F.elu(self.bn2(self.fc2(x)))
        x=F.elu(self.bn3(self.fc3(x)))
        actions=T.tanh(self.fc4(x)) # in [-1,1], scale up and shift as needed (in the environment)

        return actions

    def save_checkpoint(self):
        T.save(self.state_dict(), self.checkpoint_file)

    def load_checkpoint(self):
        self.load_state_dict(T.load(self.checkpoint_file))

    def load_checkpoint_for_eval(self):
        self.load_state_dict(T.load(self.checkpoint_file,map_location=T.device('cpu')))

class Agent():
    def __init__(self, gamma, lr_a, lr_c, input_dims, batch_size, n_actions,
            max_mem_size=100, tau=0.001, update_actor_interval=2, warmup=1000, noise=0.1, prioritized=False, use_hint=False):
        self.gamma = gamma
        self.tau=tau
        self.batch_size = batch_size
        self.n_actions=n_actions
        # actions are always in [-1,1]
        self.max_action=1
        self.min_action=-1
        self.learn_step_cntr=0
        self.time_step=0
        self.warmup=warmup
        self.update_actor_interval=update_actor_interval
        self.prioritized=prioritized
        self.use_hint=use_hint
        self.admm_rho=0.1 # nominal value, will be updated in adaptive ADMM
        self.Nadmm=5
        self.adaptive_admm=True
        self.corr_min=0.5 # minimum correlation for accepting an update

        if not self.prioritized:
         self.replaymem=ReplayBuffer(max_mem_size, input_dims, n_actions)
        else:
         self.replaymem=PER(max_mem_size, input_dims, n_actions)

        # online nets
        self.actor=ActorNetwork(lr_a, input_dims=input_dims, n_actions=n_actions,
                name='a_eval')
        self.critic_1=CriticNetwork(lr_c, input_dims=input_dims, n_actions=n_actions, name='q_eval_1')
        self.critic_2=CriticNetwork(lr_c, input_dims=input_dims, n_actions=n_actions, name='q_eval_2')
        # target nets
        self.target_actor=ActorNetwork(lr_a, input_dims=input_dims, n_actions=n_actions,
                name='a_target')
        self.target_critic_1=CriticNetwork(lr_c, input_dims=input_dims, n_actions=n_actions, name='q_target_1')
        self.target_critic_2=CriticNetwork(lr_c, input_dims=input_dims, n_actions=n_actions, name='q_target_2')
        # noise fraction
        self.noise = noise

        # initialize targets (hard copy)
        self.update_network_parameters(tau=1.)

    def update_network_parameters(self, tau=None):
        if tau is None:
            tau = self.tau

        actor_params = self.actor.named_parameters()
        actor_state_dict = dict(actor_params)
        target_actor_params = self.target_actor.named_parameters()
        target_actor_dict = dict(target_actor_params)
        for name in actor_state_dict:
            actor_state_dict[name] = tau*actor_state_dict[name].clone() + \
                                      (1-tau)*target_actor_dict[name].clone()
        self.target_actor.load_state_dict(actor_state_dict)

        critic_params = self.critic_1.named_parameters()
        target_critic_params = self.target_critic_1.named_parameters()
        critic_state_dict = dict(critic_params)
        target_critic_dict = dict(target_critic_params)
        for name in critic_state_dict:
            critic_state_dict[name] = tau*critic_state_dict[name].clone() + \
                                      (1-tau)*target_critic_dict[name].clone()
        self.target_critic_1.load_state_dict(critic_state_dict)

        critic_params = self.critic_2.named_parameters()
        target_critic_params = self.target_critic_2.named_parameters()
        critic_state_dict = dict(critic_params)
        target_critic_dict = dict(target_critic_params)
        for name in critic_state_dict:
            critic_state_dict[name] = tau*critic_state_dict[name].clone() + \
                                      (1-tau)*target_critic_dict[name].clone()
        self.target_critic_2.load_state_dict(critic_state_dict)

    def store_transition(self, state, action, reward, state_, terminal, hint):
        if not self.prioritized:
          self.replaymem.store_transition(state,action,reward,state_,terminal,hint)
        else:
          # Set reward as initial priority (assuming positive reward), see:
          #   https://jaromiru.com/2016/11/07/lets-make-a-dqn-double-learning-and-prioritized-experience-replay/
          self.replaymem.store_transition(state,action,reward,state_,terminal,hint,reward)

    def choose_action(self, observation):
        if self.time_step<self.warmup:
          mu=T.tensor(np.random.normal(scale=self.noise, size=(self.n_actions,))).to(mydevice)
        else:
          self.actor.eval() # to disable batchnorm
          state = T.cat((observation['eig'],observation['A'])).to(mydevice)
          mu = self.actor.forward(state).to(mydevice)

        mu_prime = mu + T.tensor(np.random.normal(scale=self.noise,size=(self.n_actions,)),
                                 dtype=T.float).to(mydevice)
        mu_prime = T.clamp(mu_prime,self.min_action,self.max_action)

        self.time_step +=1
        return mu_prime.cpu().detach().numpy()

    def learn(self):
        if self.replaymem.mem_cntr < self.batch_size:
            return

        if not self.prioritized:
          state, action, reward, new_state, done, hint = \
                                 self.replaymem.sample_buffer(self.batch_size)
        else:
          state, action, reward, new_state, done, hint, idxs, is_weights = \
                                self.replaymem.sample_buffer(self.batch_size)

        state_batch = T.tensor(state).to(mydevice)
        new_state_batch = T.tensor(new_state).to(mydevice)
        action_batch = T.tensor(action).to(mydevice)
        reward_batch = T.tensor(reward).to(mydevice)
        terminal_batch = T.tensor(done).to(mydevice)
        hint_batch = T.tensor(hint).to(mydevice)

        if self.prioritized:
            is_weight=T.tensor(is_weights).to(mydevice)

        self.target_actor.eval()
        self.target_critic_1.eval()
        self.target_critic_2.eval()
        target_actions = self.target_actor.forward(new_state_batch)
        target_actions = target_actions + \
                T.clamp(T.tensor(np.random.normal(scale=0.2)), -0.5, 0.5)
        target_actions = T.clamp(target_actions, self.min_action,
                                self.max_action)

        q1_ = self.target_critic_1.forward(new_state_batch, target_actions)
        q2_ = self.target_critic_2.forward(new_state_batch, target_actions)
        q1_[terminal_batch] = 0.0
        q2_[terminal_batch] = 0.0
        q1_ = q1_.view(-1)
        q2_ = q2_.view(-1)
        critic_value_ = T.min(q1_, q2_)
        target = reward_batch + self.gamma*critic_value_
        target = target.view(self.batch_size, 1)

        # update priorities in the replay buffer
        if self.prioritized:
          q1=self.critic_1.forward(state_batch,action_batch)
          errors1=T.abs(q1-target).detach().cpu().numpy()
          q2=self.critic_2.forward(state_batch,action_batch)
          errors2=T.abs(q2-target).detach().cpu().numpy()
          self.replaymem.batch_update(idxs,0.5*(errors1+errors2))

        self.critic_1.train()
        self.critic_2.train()

        def closure():
          if T.is_grad_enabled():
            self.critic_1.optimizer.zero_grad()
            self.critic_2.optimizer.zero_grad()
          q1 = self.critic_1.forward(state_batch, action_batch)
          q2 = self.critic_2.forward(state_batch, action_batch)
          if self.prioritized:
            q1_loss = self.replaymem.mse(target, q1, is_weight)
            q2_loss = self.replaymem.mse(target, q2, is_weight)
          else:
            q1_loss = F.mse_loss(target, q1)
            q2_loss = F.mse_loss(target, q2)
          critic_loss = q1_loss + q2_loss
          if critic_loss.requires_grad:
            critic_loss.backward(retain_graph=True)

          return critic_loss

        self.critic_1.optimizer.step(closure)
        self.critic_2.optimizer.step(closure)

        self.learn_step_cntr += 1

        if self.learn_step_cntr % self.update_actor_interval == 0:
          self.actor.train()

          if not self.use_hint:
            self.actor.optimizer.zero_grad()
            actor_q1_loss = self.critic_1.forward(state_batch, self.actor.forward(state_batch))
            if not self.prioritized:
               actor_loss = -T.mean(actor_q1_loss)
            else:
               actor_loss = -T.mean(actor_q1_loss*is_weight)
            actor_loss.backward()
            self.actor.optimizer.step()
          else:
            lagrange_y=T.zeros(hint_batch.numel(),requires_grad=False).to(mydevice)
            lagrange_y0=None
            actions0=None
            admm_rho=self.admm_rho
            for admm in range(self.Nadmm):
               self.actor.optimizer.zero_grad()
               actions=self.actor.forward(state_batch)
               actor_q1_loss = self.critic_1.forward(state_batch, actions)
               if not self.prioritized:
                 actor_loss = -T.mean(actor_q1_loss)
               else:
                 actor_loss = -T.mean(actor_q1_loss*is_weight)
               diff1=(actions-hint_batch).view(-1)
               if not self.prioritized:
                 loss1=(T.dot(lagrange_y,diff1)+admm_rho/2*F.mse_loss(actions,hint_batch)).mean()/actions.numel()
               else:
                 loss1=((T.dot(lagrange_y,diff1)+admm_rho/2*F.mse_loss(actions,hint_batch))*is_weight).mean()/actions.numel()
               actor_loss=actor_loss+loss1
               actor_loss.backward()
               self.actor.optimizer.step()
               with T.no_grad():
                  lagrange_y=lagrange_y+admm_rho*(actions-hint_batch).view(-1)
                  # adaptive ADMM
                  if self.adaptive_admm:
                    # initialize y0 and action0
                    if admm==0:
                      lagrange_y0=actions.view(-1).detach().clone()
                      actions0=actions.view(-1).detach().clone()
                    elif admm%3==0 and admm<self.Nadmm-1:
                      lagrange_y1=lagrange_y+admm_rho*(actions-hint_batch).view(-1)
                      deltay=lagrange_y1 - lagrange_y0
                      deltau=actions.view(-1).detach() - actions0
                      delta11=T.dot(deltay,deltay)
                      delta12=T.dot(deltay,deltau)
                      delta22=T.dot(deltau,deltau)
                      lagrange_y0=lagrange_y1
                      actions0=actions.view(-1).detach().clone()
                      if delta11>0 and delta12>0 and delta22>0:
                        alpha=delta12/T.sqrt(delta11*delta22)
                        alpha_sd=delta11/delta12
                        alpha_mg=delta12/delta22

                        if 2*alpha_mg > alpha_sd:
                          alpha_hat=alpha_mg
                        else:
                          alpha_hat=alpha_sd-0.5*alpha_mg

                        if alpha>self.corr_min and alpha_hat<10*self.admm_rho and alpha_hat>0.1*self.admm_rho:
                          admm_rho=alpha_hat

                        #print(f'{admm} d11={delta11} d12={delta12} d22={delta22} alpha={alpha} sd={alpha_sd} mg={alpha_mg} ahat={alpha_hat} rho={admm_rho}')

          self.update_network_parameters()

    def save_models(self):
        self.actor.save_checkpoint()
        self.target_actor.save_checkpoint()
        self.critic_1.save_checkpoint()
        self.critic_2.save_checkpoint()
        self.target_critic_1.save_checkpoint()
        self.target_critic_2.save_checkpoint()
        self.replaymem.save_checkpoint()

    def load_models(self):
        self.actor.load_checkpoint()
        self.target_actor.load_checkpoint()
        self.critic_1.load_checkpoint()
        self.critic_2.load_checkpoint()
        self.target_critic_1.load_checkpoint()
        self.target_critic_2.load_checkpoint()
        self.replaymem.load_checkpoint()
        self.actor.train()
        self.target_actor.eval()
        self.critic_1.train()
        self.critic_2.train()
        self.target_critic_1.eval()
        self.target_critic_2.eval()
        self.update_network_parameters(tau=1.)

    def load_models_for_eval(self):
        self.actor.load_checkpoint_for_eval()
        self.critic_1.load_checkpoint_for_eval()
        self.critic_2.load_checkpoint_for_eval()
        self.actor.eval()
        self.critic_1.eval()
        self.critic_2.eval()

    def print(self):
        print(self.actor)
        print(self.critic_1)

#a=Agent(gamma=0.99, batch_size=32, n_actions=2, max_mem_size=1024, input_dims=[11],
#        lr_a=0.001, lr_c=0.001, prioritized=True, use_hint=True)


#coding:utf-8

"""
ID:          gtcs.isql-show-command-ambiguity
TITLE:       SHOW TABLE / VIEW: ambiguity between tables and views
DESCRIPTION:
  ::: NB :::
  ### Name of original test has no any relation with actual task of this test: ###
  https://github.com/FirebirdSQL/fbtcs/blob/master/GTCS/tests/CF_ISQL_22.script

  bug #223513 ambiguity between tables and views
FBTEST:      functional.gtcs.isql_show_command_ambiguity
"""

import pytest
from firebird.qa import *

db = db_factory()

test_script = """
    create table t(a int);
    create view v as select a from t;
    show tables;
    show views;
    show table v;
    show table t;
    show view v;
    show view t;
"""

act = isql_act('db', test_script, substitutions=[('=', ''), ('[ \t]+', ' ')])

expected_stdout = """
    T
    V
    A INTEGER Nullable
    A INTEGER Nullable

    View Source:
    select a from t
"""

expected_stderr = """
    There is no table V in this database
    There is no view T in this database
"""

@pytest.mark.version('>=3')
def test_1(act: Action):
    act.expected_stdout = expected_stdout
    act.expected_stderr = expected_stderr
    act.execute()
    assert (act.clean_stderr == act.clean_expected_stderr and
            act.clean_stdout == act.clean_expected_stdout)


# -*- encoding: utf-8 -*-

"""Handle trading-orders endpoints."""

from .base import Trading
from ..decorators import dyndoc_insert, endpoint
from .responses.orders import responses

@endpoint("openapi/trade/v2/orders", "POST")
class Order(Trading):
    """Place a new order."""

    @dyndoc_insert(responses)
    def __init__(self, data):
        """Instantiate an Order request.

        Parameters
        ----------
        data : dict (required)
            dict representing the data body, in this case an order spec.

        OrderBody example::

            data = {_v3_Order_body}

        >>> import saxo_openapi
        >>> import saxo_openapi.endpoints.trading as tr
        >>> import json
        >>> client = saxo_openapi.API(access_token=...)
        >>> data = ...
        >>> r = tr.orders.Order(data=data)
        >>> client.request(r)
        >>> print(json.dumps(r.response, indent=4))

        Output::

            {_v3_Order_resp}

        """
        super(Order, self).__init__()
        self.data = data

@endpoint("openapi/trade/v2/orders", "PATCH")
class ChangeOrder(Trading):
    """Change one or more existing orders."""

    @dyndoc_insert(responses)
    def __init__(self, data):
        """Instantiate a ChangeOrder request.

        Parameters
        ----------
        data : dict (required)
            dict representing the data body, in this case an order change spec.

        OrderBody example::

            data = {_v3_ChangeOrder_body}

        >>> import saxo_openapi
        >>> import saxo_openapi.endpoints.trading as tr
        >>> import json
        >>> client = saxo_openapi.API(access_token=...)
        >>> data = ...
        >>> r = tr.orders.ChangeOrder(data=data)
        >>> client.request(r)
        >>> print(json.dumps(r.response, indent=4))

        Output::

           {_v3_ChangeOrder_resp}

        """
        super(ChangeOrder, self).__init__()
        self.data = data

@endpoint("openapi/trade/v2/orders/{OrderIds}", "DELETE")
class CancelOrders(Trading):
    """Cancel one or more orders."""

    @dyndoc_insert(responses)
    def __init__(self, OrderIds, params):
        """Instantiate a CancelOrders request.

        Parameters
        ----------
        OrderIds: string (required)
            ',' delimited string with one or more orderId's

        params : dict (required)
            dict representing the querystring parameters.

        params example::

            params = {_v3_CancelOrders_params}

        >>> import saxo_openapi
        >>> import saxo_openapi.endpoints.trading as tr
        >>> import json
        >>> client = saxo_openapi.API(access_token=...)
        >>> OrderIds="76289286"
        >>> params = ...
        >>> r = tr.orders.CancelOrders(OrderIds=OrderIds, params=params)
        >>> client.request(r)
        >>> print(json.dumps(r.response, indent=4))

        Output::

            {_v3_CancelOrders_resp}

        """
        super(CancelOrders, self).__init__(OrderIds=OrderIds)
        self.params = params

@endpoint("openapi/trade/v2/orders/precheck", "POST")
class PrecheckOrder(Trading):
    """Precheck an order."""

    @dyndoc_insert(responses)
    def __init__(self, data):
        """Instantiate a PrecheckOrder request.

        Parameters
        ----------

        data : dict (required)
            dict representing the data body, in this case an order change spec.

        data example::

            data = {_v3_PrecheckOrder_body}

        >>> import saxo_openapi
        >>> import saxo_openapi.endpoints.trading as tr
        >>> import json
        >>> client = saxo_openapi.API(access_token=...)
        >>> data = ...
        >>> r = tr.orders.PrecheckOrder(data=data)
        >>> client.request(r)
        >>> print(json.dumps(r.response, indent=4))

        Output::

            {_v3_PrecheckOrder_resp}

        """
        super(PrecheckOrder, self).__init__()
        self.data = data


#!/usr/bin/python3
"""
    Define 'BaseModel' class
"""
import models
from uuid import uuid4
from datetime import datetime

class BaseModel:
    """
        Represent 'BaseModel' class
    """

    def __init__(self, *args, **kwargs):
        """
            Initialize new 'BaseModel' instance
            Arguments:
                *args (any): this will not be used.
                **kwargs (dict): this will be used to get keyword arguments passed.
        """
        time_format = "%Y-%m-%dT%H:%M:%S.%f"
        self.id = str(uuid4())
        self.created_at = datetime.today()
        self.updated_at = datetime.today()
        if len(kwargs) != 0:
            for key, value in kwargs.items():
                if key == "created_at" or key == "updated_at":
                    self.__dict__[key] = datetime.strptime(value, time_format)
                else:
                    self.__dict__[key] = value
        else:
            models.storage.new(self)

    def save(self):
        """
            Save the model instance to the storage engine
        """
        self.updated_at = datetime.now()
        models.storage.save()

    def to_dict(self):
        """
            Return dictionary representation of the model instance
        """
        new_dict = self.__dict__.copy()
        new_dict["created_at"] = self.created_at.isoformat()
        new_dict["updated_at"] = self.updated_at.isoformat()
        new_dict["__class__"] = self.__class__.__name__
        return new_dict

    def __str__(self):
        """
            Return string representation of the model instance
        """
        class_name = self.__class__.__name__
        return "[{}] ({}) {}".format(class_name, self.id, self.__dict__)


# -*- encoding: utf-8 -*-

from apps.home import blueprint
from apps.home.function import *
from flask_login import login_required
from jinja2 import TemplateNotFound

from flask import render_template, request

# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #

@blueprint.route('/index')
@login_required
def index():
    return render_template('home/index.html', segment='index')

# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #

@blueprint.route('/home')
def home():
    return render_template('home.html')

# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #

# Special route to pages with jinja template

@blueprint.route('/project', methods=["POST", "GET"])
def project():
    projects = projectall()
    keyvalue = keyvalueall()
    return render_template('project.html', pas=projects, pas2=keyvalue)

@blueprint.route('/upload_bucket', methods=["POST", "GET"])
def upload_bucket():
    return render_template('project.html')

@blueprint.route('/create_key_value', methods=["POST", "GET"])
def key_value():
    projects = projectall()
    keyvalue = keyvalueall()
    if request.method == 'POST':
        key = request.form.get('key')
        value = request.form.get('value')
        createkeyvalue(key, value)

        projects = projectall()
        return render_template('project.html', pas=projects, pas2=keyvalue)
    return render_template('project.html', pas=projects, pas2=keyvalue)

@blueprint.route('/document', methods=["POST", "GET"])
def document_save():
    projects = projectall()
    keyvalue = keyvalueall()
    if request.method == 'POST':
        key = request.form.get('key')
        value = request.form.get('value')
        return render_template('project.html', pas=projects, pas2=keyvalue)
    return render_template('project.html', pas=projects, pas2=keyvalue)

@blueprint.route('/logstore', methods=["POST", "GET"])
def logstore():
    return render_template('project.html')

# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #

# Basics route to all pages

@blueprint.route('/<template>')
@login_required
def route_template(template):
    pas = "hi"

    try:

        if not template.endswith('.html'):
            pass

        # Detect the current page
        segment = get_segment(request)

        # Serve the file (if exists) from app/templates/home/FILE.html
        return render_template("home/" + template, segment=segment)

    except TemplateNotFound:
        return render_template('home/page-404.html'), 404

    except:
        return render_template('home/page-500.html'), 500

# Helper - Extract current page name from request
def get_segment(request):
    try:

        segment = request.path.split('/')[-1]

        if segment == '':
            segment = 'index'

        return segment

    except:
        return None


"""
You are given an m x n grid where each cell can have one of three values:

0 representing an empty cell,
1 representing a fresh orange, or
2 representing a rotten orange.
Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten.

Return the minimum number of minutes that must elapse until no cell has a fresh
orange. If this is impossible, return -1.

Example:
Input: grid = [[2,1,1],[1,1,0],[0,1,1]]
Output: 4

Solution: Breadth First Search
Time Complexity: O(m*n)
"""

from collections import deque

def orangesRotting(grid):
    """
    :type grid: List[List[int]]
    :rtype: int
    """

    if not grid:
        return 0

    # time and number of fresh oranges
    rows, cols = len(grid), len(grid[0])
    visited = set()
    time = 0
    fresh = 0

    def bfs(r, c, cur_fresh, cur_time):
        """Helper function for BFS"""
        queue = deque()
        visited.add((r, c))
        queue.append((r, c))

        dirs = [(0, 1), (0, -1), (1, 0), (-1, 0)]
        while queue and cur_fresh > 0:
            row, col = queue.popleft()
            for dr, dc in dirs:
                r, c = row + dr, col + dc
                if (
                    r in range(r)
                    and c in range(r)
                    and grid[r][c] != 1
                    and (r, c) not in visited
                ):
                    grid[r][c] = 2
                    queue.append((r, c))
                    visited.add((r, c))
                    cur_fresh -= cur_fresh  # decrement fresh oranges
            cur_time = time + 1

    for r in range(rows):
        for c in range(cols):
            # if cell equals fresh orange, add to queue
            if grid[r][c] == 1 and (r, c, time) not in visited:
                fresh += 1
            if grid[r][c] == 2:
                bfs(r, c)
    return time if fresh == 0 else -1

if __name__ == "__main__":
    grid = [[2, 1, 1], [1, 1, 0], [0, 1, 1]]
    print(orangesRotting(grid))


import time
from select import select
from selenium.common import NoSuchElementException, ElementNotInteractableException, TimeoutException, \
    StaleElementReferenceException
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome import webdriver
from selenium.webdriver.firefox import webdriver

from selenium.webdriver.firefox.service import Service as FirefoxService
from selenium.webdriver.firefox.options import Options as FireFoxOptions
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
# from selenium.webdriver.firefox.service import Service as firefoxService
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait

import pytest

@pytest.fixture()
def driver():
    firefox_driver_binary = "./geckodriver"
    ser_firefox = FirefoxService(firefox_driver_binary)

    browser_name = "chrome"

    # if isinstance(browserName,list):
    #     for browser_name in browserName:
    if browser_name == "firefox-webdriver":
        driver = webdriver.Firefox(service=ser_firefox)
    elif browser_name == "firefox":
        dc = {
            "browserName": "firefox",
            # "browserVersion": "101.0.1(x64)",
            "platformName": "MAC"
        }
        driver = webdriver.Remote("http://localhost:4444",desired_capabilities=dc)

    elif browser_name == "safari":
        dc = {
            "browserName": "safari",
            "platformName": "MAC"
        }
        driver = webdriver.Remote("http://localhost:4444",desired_capabilities=dc)

    elif browser_name == "chrome":
        dc = {
            "browserName": "chrome",
            "platformName": "MAC"
        }
        driver = webdriver.Remote("http://localhost:4444", desired_capabilities=dc)

    elif browser_name == "firefox-mobile":
        firefox_options = FireFoxOptions()
        firefox_options.add_argument("--width=375")
        firefox_options.add_argument("--height=812")
        firefox_options.set_preference("general.useragent.override", "userAgent=Mozilla/5.0 "
                                                                     "(iPhone; CPU iPhone OS 15_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like "
                                                                     "Gecko) CriOS/101.0.4951.44 Mobile/15E148 Safari/604.1")
        # firefox_options.set_preference("general.useragent.override", "Nexus 7")

        driver = webdriver.Firefox(service=ser_firefox, options=firefox_options)

    yield driver
    driver.close()

# Positive Scenario
def test_Registration(driver):
    driver.get('http://localhost:8000/')
    driver.maximize_window()
    driver.find_element(By.CSS_SELECTOR, "#navbarScroll > div > a:nth-child(2)").click()
    driver.find_element(By.CSS_SELECTOR, "#root > div > main > div > div > div > div > div > a").click()
    driver.find_element(By.CSS_SELECTOR, "#name").send_keys("amaroo")
    Actual = driver.find_element(By.CSS_SELECTOR, "#name").text
    driver.find_element(By.CSS_SELECTOR, "#email").send_keys("amar.barake.1910sadas@gmail.com")
    driver.find_element(By.CSS_SELECTOR, "#password").send_keys("Amar1234&*")
    driver.find_element(By.CSS_SELECTOR, "#passwordConfirm").send_keys("Amar1234&*")
    driver.find_element(By.CSS_SELECTOR, "#root > div > main > div > div > div > form > button").click()
    time.sleep(3)
    driver.find_element(By.CSS_SELECTOR, "#username").click()
    driver.find_element(By.LINK_TEXT, "Profile").click()
    name = driver.find_element(By.CSS_SELECTOR, "#name")
    Expected = name.text
    assert Expected == Actual

# def test_login(driver):
#     driver.get('http://localhost:8000/')
#     driver.maximize_window()
#     driver.find_element(By.CSS_SELECTOR, "#navbarScroll > div > a:nth-child(2)").click()
#     driver.find_element(By.CSS_SELECTOR, "#email").send_keys("amar.abs3dsfsf5@gmail.com")
#     driver.find_element(By.CSS_SELECTOR, "#password").send_keys("Amar1234&*")
#     driver.find_element(By.CSS_SELECTOR, "#root > div > main > div > div > div > form > button").click()
#     time.sleep(2)
#     # driver.find_element(By.CSS_SELECTOR, "#username").click()
#     # driver.find_element(By.LINK_TEXT, "Profile").click()
#     # result = driver.find_element(By.CSS_SELECTOR, "#name").text
#     # assert result == 'amaroo'

def test_Invalid_username(driver):
    driver.get('http://localhost:8000/')
    driver.maximize_window()
    driver.find_element(By.CSS_SELECTOR,"#navbarScroll > div > a:nth-child(2)").click()
    driver.find_element(By.CSS_SELECTOR,"#email").send_keys("asasdasd@gmail.com")
    driver.find_element(By.CSS_SELECTOR,"#password").send_keys("as13213")
    driver.find_element(By.CSS_SELECTOR,"#root > div > main > div > div > div > form > button").click()
    time.sleep(3)
    message = driver.find_element(By.CSS_SELECTOR,"#root > div > main > div > div > div > div.fade.alert.alert-danger.show").text
    assert message == "No active account found with the given credentials"


from .cm import ConnectionManager
from .msg import Message
from .config import MASTER_URL, MYID_PATH
import threading

class Agent:
    # 管理连接，开启连接，负责重连，关闭连接。

    def __init__(self):
        self.msg = Message(MYID_PATH)
        self.cm = ConnectionManager(MASTER_URL, self.msg)
        self.event = threading.Event()

    def start(self):
        # 负责重连
        while not self.event.is_set():
            print('reset')
            try:
                self.cm.start()  # 正常时会阻塞在这
            except:
                print('error !!!!')
                self.cm.shutdown()

            self.event.wait(3)

    def shutdown(self):
        self.event.set()
        self.cm.shutdown()


#!/usr/bin/env python

import json

# Open ai gpt-3.5-turbo-16k-0613 -  poor results - I think missed point although had more description

def find_common_parents(page_structure, units_data):
    common_parent_attrs = {
        "attributes": {
            "data unit item including title link text": [],
            "title": [],
            "link": [],
            "text": []
        }
    }
    stack = []

    def iterate_page_structure(structure, unit_data):
        nonlocal stack
        tag = structure.get("tag")
        attributes = structure.get("attributes", {})
        text = structure.get("text")
        link = structure.get("link")

        if tag == "a" and link == unit_data["link"]:
            stack.append(attributes.get("class", []))
            if text:
                stack.append(attributes.get("class", []))
                common_parent_attrs["attributes"]["link"] = stack.copy()
                stack.pop()

        if text == unit_data["text"]:
            stack.append(attributes.get("class", []))
            common_parent_attrs["attributes"]["text"] = stack.copy()
            stack.pop()

        if tag in ["h1", "h2", "h3", "h4", "h5", "h6"] and text == unit_data["title"]:
            title = text if text else ""
            stack.append(attributes.get("class", []))
            common_parent_attrs["attributes"]["title"] = stack.copy()
            stack.pop()

        stack.append(attributes.get("class", []))

        for child_structure in structure.get("children", []):
            iterate_page_structure(child_structure, unit_data)

        stack.pop()

    for unit_data in units_data:
        stack = []
        iterate_page_structure(page_structure, unit_data)
        common_parent_attrs["attributes"]["data unit item including title link text"] = stack.copy()

    return common_parent_attrs

if __name__ == "__main__":
    with open("../banana/webpage_content.json", "r") as file:
        page_structure = json.load(file)

    with open("../units_data.json", "r") as file:
        units_data = json.load(file)

    common_parent_attributes = find_common_parents(page_structure, units_data)

    with open("../output.json", "w") as file:
        json.dump(common_parent_attributes, file)


import toga
import requests
import threading

from consts import *
from toga.style.pack import *

class PokeDex(toga.App):
    def __init__(self, title, id):
        toga.App.__init__(self, title, id)

        self.title = title
        self.size = (WIDTH, HEIGTH)

        self.heading = ['Name']
        self.data = list()

        self.offset = 0  # Inicio de pokemonos en pokeapi

        self.response_name = ''
        self.response_description = ''
        self.response_sprite = ''

        self.create_elements()
        self.load_async_data()
        self.validate_previous_command()

    def startup(self):
        self.main_window = toga.MainWindow("main", title=self.title,
                                            size=self.size)

        information_area = toga.Box(
            children=[self.image_view, self.pokemon_name, self.pokemon_description],
            style=Pack(
                direction=COLUMN,
                alignment=CENTER,
            )
        )

        # information_area.add(self.image_view)
        # information_area.add(self.pokemon_name)
        # information_area.add(self.pokemon_description)

        self.main_window.toolbar.add(self.previous_command, self.next_command)

        split = toga.SplitContainer()
        split.content = [self.table, information_area]

        #self.main_window.content = split
        self.main_window.content = split

        self.main_window.show()

    def create_elements(self):
        self.create_table()
        self.create_toolbar()
        self.create_image(PIKACHU_ICON)
        self.create_labels()

    def create_toolbar(self):
        self.create_next_command()
        self.create_previous_command()

    def create_next_command(self):
        self.next_command = toga.Command(self.next, label='Next', tooltip='Siguientes',
                                        icon=NEXT_ICON)

    def create_previous_command(self):
        self.previous_command = toga.Command(self.previous, label='Previous', tooltip='Anteriores',
                                        icon=BACK_ICON)

    def create_table(self):
        self.table = toga.Table(self.heading, data = self.data,
                                on_select = self.select_element)

    def create_image(self, path, width=150, height=150):
        image = toga.Image(path)
        style = Pack(width=width, height=height)

        self.image_view = toga.ImageView(image,style=style)

    def create_labels(self):
        styleName = Pack(text_align=CENTER, font_size=20, padding_bottom = 30)
        styleDescription = Pack(text_align=CENTER, padding_bottom = 30)
        self.pokemon_name = toga.Label('Name', style=styleName)
        self.pokemon_description = toga.Label('Description', style=styleDescription)

        # self.pokemon_name.style.font_size = 20
        # self.pokemon_name.style.padding_bottom = 10

    def load_async_data(self):
        self.data.clear()  # Limpiar lista de Pokemons
        self.table.data = self.data  # Limpiar elemento seleccionado en la tabla
        thread = threading.Thread(target=self.load_data)
        thread.start()
        thread.join()

        self.table.data = self.data  # Pasar infomacion de la peticion a PokerApi

    def load_data(self):
        path = 'https://pokeapi.co/api/v2/pokemon-form?offset={}&limit=20'.format(self.offset)

        #response = requests.get(path,verify=False)   #En caso de error con la peticion
        response = requests.get(path)

        if response:
            result = response.json()

            for pokemon in result['results']:
                name = pokemon['name']
                self.data.append(name)

    def load_async_pokemon(self, pokemon):
        thread = threading.Thread(target=self.load_pokemon, args=[pokemon])
        thread.start()
        thread.join()

        self.image_view.image = toga.Image(self.response_sprite)
        self.pokemon_name.text = self.response_name
        self.pokemon_description.text = self.response_description

    def load_pokemon(self, pokemon):

        path = 'https://pokeapi.co/api/v2/pokemon/{}/'.format(pokemon)

        response = requests.get(path)
        if response:
            result = response.json()

            self.response_name = result['forms'][0]['name']
            abilities = list()

            for ability in result['abilities']:
                name_ability = ability['ability']['name']
                abilities.append(name_ability)

            self.response_sprite = result['sprites']['front_default']

            self.response_description = ''.join(abilities)

            # print(name)
            # print(abilities)
            # print(sprite)
#CALLBACKS

    def next(self, widget):
        self.offset +=1

        self.handler_command(widget)

        #print("Next  "+ str(self.offset))

    def previous(self, widget):
        self.offset -=1

        self.handler_command(widget)

        #print("Previous " + str(self.offset))

    def handler_command(self, widget):
        widget.enable = False

        self.load_async_data()

        widget.enable = True

        self.validate_previous_command()

    def validate_previous_command(self):
        self.previous_command.enabled = not self.offset == 0

    def select_element(self, widget, row):
        if row:
            self.load_async_pokemon(row.name)

if __name__ == "__main__":
    pokedex = PokeDex('PokeDex', 'dev.johamSMC')
    pokedex.main_loop()