from math import sqrt
from typing import Tuple
import numpy as np

import library.constants

def distance(
    point_1: Tuple[float, float],
    point_2: Tuple[float, float]
) -> float:
    return sqrt(
        (point_1[0] - point_2[0])**2
        + (point_1[1] - point_2[1])**2)

def line_eqtn_from_2_points(
    line_start: Tuple[float, float],
    line_end: Tuple[float, float]
) -> Tuple[float, float, float]:
    """
    Finds equation of the line (in form Ax + By = C) from 2 given
    points.  If the points coincide, returns an equation of a line
    parallel to y axis.  Result is a tuple (A, B, C)
    """
    (x1, y1) = line_start
    (x2, y2) = line_end
    a, b = 0, 0
    if x1 != x2:
        b = 1
        a = -b*(y1-y2)/(x1-x2)
    elif y1 != y2:
        a = 1
        b = -a*(x1-x2)/(y1-y2)
    else:
        # The points coincide; Let's assume the line is vertical
        a = 1
        b = 0
    c = a*x1 + b*y1
    return (a, b, c)

def point_in_box(
    point: Tuple[float, float],
    box_corner_1: Tuple[float, float],
    box_corner_2: Tuple[float, float]
) -> bool:
    # As was found in debugging, sometimes computing error cause
    # the algorithm believe that the collision point is located outside
    # the segments (usually at scale of 1e-15).  It seems to happen only
    # on horizontal lines.  Thus, let's add some margin of error.
    min_corner = (
        min(box_corner_1[0], box_corner_2[0]) - library.constants.ERROR_MARGIN,
        min(box_corner_1[1], box_corner_2[1])
    )
    max_corner = (
        max(box_corner_1[0], box_corner_2[0]) + library.constants.ERROR_MARGIN,
        max(box_corner_1[1], box_corner_2[1])
    )

    return (min_corner[0] <= point[0] <= max_corner[0]
            and min_corner[1] <= point[1] <= max_corner[1])

def rotation_matrix_2d(
    alpha: float
) -> np.ndarray:
    """
    Calculates 2D rotational matrix for given angle alpha (counter
    clockwise for xy-plane with inverted y, like in our case)
    """
    cosine = np.math.cos(alpha)
    sine = np.math.sin(alpha)
    return np.array([
        [cosine, -sine],
        [sine, cosine]
    ])

def mirror_vector_2d(
    input_vector: Tuple[float, float],
    mirror_normal: Tuple[float, float]
) -> Tuple[float, float]:
    vec: np.ndarray = np.array(input_vector)
    normal: np.ndarray = np.array(mirror_normal)
    # Normalize the normal (lol)
    normal /= np.linalg.norm(normal)
    # Substract 2 projections of movement onto normal to get the
    # reflected vector
    vec -= 2*(normal.dot(vec))*normal
    return (vec[0], vec[1])

def change_origin(
    point: Tuple[float, float],
    old_origin: Tuple[float, float],
    new_origin: Tuple[float, float]
) -> Tuple[float, float]:
    """
    Shifts origin of `point`'s coordinates from `oldOrigin` to
    `newOrigin`
    """
    new_point = []
    for old_coord, old_offset, new_offset in zip(point, old_origin, new_origin):
        new_point.append(old_coord + old_offset - new_offset)
    # If the specified origin had less dimensions, leave remaining
    # ones unchanged
    if len(new_point) < len(point):
        new_point.extend(point[len(new_point):])
    return new_point


import clr
clr.AddReferenceToFileAndPath(r"D:\Lib\dll\NSoup.dll")
import NSoup

import System
from System.IO import *
from System.Net import *

debug = True
image_ext = None

def Download(url,filename,referer):
    wc = WebClient()
    wc.Headers.Add ("User-Agent", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36')
    wc.Headers.Add ("Referer", referer)
    try:
        wc.DownloadFile(url, filename)
    except:
        print( url, "except" )

def DebugSave(filename,data):
    if debug:
        with open(filename, "w") as f:
            f.write(data.encode("utf-8"))

def DebugPrint(data):
    if debug:
        print(data);

def getWolfCom(comicsUrl,baseUrl,baseDir):
    doc = NSoup.NSoupClient.Connect(comicsUrl).Get()
    #DebugSave("a.html",doc.Html())
    elems = doc.Select("div.box > div.group.left-box > div.webtoon-bbs-list.bbs-list > ul > li");
    #DebugPrint(elems.Html());

    print(doc.Title)
    new_dir = Path.Combine(baseDir, doc.Title.replace(":","_"))
    if not Directory.Exists(new_dir): Directory.CreateDirectory(new_dir)
    print(new_dir)

    for e in elems:
        url = e.Select("a").First.Attr("href")
        if not url: continue
        url = baseUrl + url

        doc = NSoup.NSoupClient.Connect(url).Get()
        imgs = doc.Select("section.webtoon-body > div.group.image-view > img")
        print(doc.Title)

        sub_dir = Path.Combine(new_dir, doc.Title.replace(":","_"))
        if not Directory.Exists(sub_dir): Directory.CreateDirectory(sub_dir)

        k = 1;
        for img in imgs:
            img_url = img.Attr('src')
            if not img_url: continue
            if image_ext == None or img_url.endswith(image_ext):
                if( not img_url.startswith("http") ):
                    img_url = baseUrl + img_url
                file_name = "img_%04d.jpg" % k
                Download( img_url, Path.Combine(sub_dir, file_name), comicsUrl)
                print( img_url + " -> " + file_name )
                k = k + 1

def getFoxComics(comicsUrl,baseUrl,baseDir):
    getWolfCom(comicsUrl,baseUrl,baseDir)

def getWToon(comicsUrl,baseUrl,baseDir):
    doc = NSoup.NSoupClient.Connect(comicsUrl).Get()
    #DebugSave("a.html",doc.Html())
    elems = doc.Select("div.row > div.wvlist > div.left-box > div.wbody > ul > li");
    #DebugPrint(elems.Html());

    print(doc.Title)
    new_dir = Path.Combine(baseDir, doc.Title.replace(":","_"))
    if not Directory.Exists(new_dir): Directory.CreateDirectory(new_dir)
    print(new_dir)

    for e in elems:
        url = e.Select("a").First.Attr("href")
        if not url: continue
        url = baseUrl + url

        doc = NSoup.NSoupClient.Connect(url).Get()
        imgs = doc.Select("section.body > div.container > div.row > div.wview > div.wbody > img")
        print(doc.Title)
        #DebugSave("b.html",doc.Html())

        sub_dir = Path.Combine(new_dir, doc.Title.replace(":","_"))
        if not Directory.Exists(sub_dir): Directory.CreateDirectory(sub_dir)

        k = 1;
        for img in imgs:
            img_url = img.Attr('src')
            if not img_url: continue
            if image_ext == None or img_url.endswith(image_ext):
                if( not img_url.startswith("http") ):
                    img_url = baseUrl + img_url
                file_name = "img_%04d.jpg" % k
                Download( img_url, Path.Combine(sub_dir, file_name), comicsUrl)
                print( img_url + " -> " + file_name )
                k = k + 1

if __name__ == "__main__":
    url = [
        "https://wtwt30.com/v1?toon=1079",
    ]
    baseDir = r"D:\Temp2"
    for u in url:
        pos = u.find("/",len('https://')) #"https://wfwf105.com",https://fxfx21.com
        baseUrl = u[:pos+1]
        #getFoxComics(u,baseUrl,baseDir)
        getWToon(u,baseUrl,baseDir)


import gym
import tensorflow as tf
import numpy as np
import matplotlib as mlp
import matplotlib.pyplot as plt
from tensorflow import keras
from ns3gym import ns3env

def epsilon_greedy(epsilon):

    rand = np.random.random()
    if rand < epsilon:
        action = env.action_space.sample()
    else:
        action = np.argmax(Q)

    return action

port = 5556
env = ns3env.Ns3Env(port=port,startSim=False)
env.reset()
env.render()

ob_space = env.observation_space
ac_space = env.action_space

print("Observation space: ",ob_space,ob_space.dtype)
print("Action space: ", ac_space,ac_space.n)

s_size = ob_space.shape[0]
a_size = ac_space.n
print(s_size)

num_rounds = 200000
count = np.zeros(10)
sum_rewards = np.zeros(10)
Q= np.zeros(10)
stepIdx = 0
time_history =[]
rew_history=[]
state = env.reset()
state=np.reshape(state,[1,s_size])
rewardsum = 0

try:

    while True:

        arm = epsilon_greedy(0.95)

        #get the reward
        observation,reward, done, info = env.step(arm)
        print(reward)

        count[arm] +=1

        sum_rewards[arm]+=reward

        Q[arm] = sum_rewards[arm]/count[arm]

        stepIdx +=1

        time_history.append(stepIdx)
        rew_history.append(arm)
        rewardsum = rewardsum + reward

except KeyboardInterrupt:
    print("Ctrl-C -> Exit")
finally:

    print(rew_history)
    print("Done")
    mlp.rcdefaults()
    mlp.rcParams.update({'font.size': 16})
    plt.grid(True,linestyle='--')
    plt.title('Learning Performance')
    #plt.plot(range(len(time_history)), time_history,label='Step',marker="^",linestyle=":")
    plt.plot(rew_history,label="Channel Selection",marker="",linestyle="-")
    plt.xlabel('Episode')
    plt.ylabel('Channel')
    plt.legend(prop={'size':12})

    plt.savefig('learning.pdf',bbox_inches='tight')
    print(rewardsum)
    plt.show()
    env.close()


"""
URL configuration for youseai project.

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.urls import path
from rest_framework_simplejwt.views import TokenObtainPairView,TokenRefreshView
from api.views import UserLoginView,UserRegisterView,UserLogoutView,UserProfileView,flushexpired

urlpatterns = [
    path('user/register/',UserRegisterView.as_view(), name='register_new_user'),
    path('user/login/',UserLoginView.as_view(), name='login_user'),
    path('user/logout/',UserLogoutView.as_view(), name='logout_user'),
    path('user/refresh/',TokenRefreshView.as_view(), name='obtain_access_token'),
    path('user/profile/',UserProfileView.as_view(), name='user_profile_get_post_put'),
    path('manage/flush/tokens/',flushexpired, name='flush_expired_tokens'),
]


import pandas as pd
import openpyxl
from simplificador_nome import simplificador_nome
from pathlib import Path
from datetime import datetime

# Verifica a empresa que será feito os crachás, com base nela faz a pesquisa no sistema dos dados de cada pessoa, trata eles e salva na planilha para ser realiza a impressão.
# Check the company where the badges will be made, based on it, search the system for each person's data, process them and save them in the spreadsheet for printing.

dia_atual = f"{datetime.today().day}/{datetime.today().month}/{datetime.today().year}"
caminho_planilha_crachas = None
empresa = input("Digite a empresa onde será salvo os dados dos colaboradores: ").upper()

caminho_planilha_dados_colaboradores = Path(r"caminho arquivo")
if empresa == "Empresa1":
        nome_planilha = "Empresa1"
elif empresa == "Empresa2" or empresa == "Empresa2":
        nome_planilha = "Empresa2"

data = pd.read_excel(caminho_planilha_dados_colaboradores, sheet_name=nome_planilha)
pd.set_option('display.max_colwidth', None)

while True:
    matricula_bruta = input("Digite a matricula a ser pegue os dados: ")
    if matricula_bruta.lower() == "s":
        break

    matricula = matricula_bruta.lstrip("0")

    pessoa = data.query(f"chapa == {matricula}")
    fator_rh = pessoa["tipo_sanguineo"].to_string(index=False)
    nome_bruto = pessoa["nome"].to_string(index=False)
    funcao = pessoa["funcao"].to_string(index=False)
    dt_admissao = pessoa["data_admissao"].to_string(index=False)
    chapa = pessoa["chapa"].to_string(index=False)
    registro_pf = pessoa["registro_pf"].to_string(index=False)
    cpf = pessoa["cpf"].to_string(index=False)
    reciclagem = pessoa["data_reciclagem"].to_string(index=False)

    nome = simplificador_nome(nome_bruto)

    if empresa == "Empresa1":
        caminho_planilha_crachas = Path(r"caminho arquivo")
    elif empresa == "Empresa2" or empresa == "Empresa2":
        caminho_planilha_crachas = Path(r"caminho arquivo")
    planilha = openpyxl.load_workbook(caminho_planilha_crachas)
    sheet = planilha["Planilha1"]

    proxima_linha = sheet.max_row + 1

    sheet.cell(row=proxima_linha, column=1, value=fator_rh)
    sheet.cell(row=proxima_linha, column=2, value=nome)
    sheet.cell(row=proxima_linha, column=3, value=funcao)
    sheet.cell(row=proxima_linha, column=4, value=dt_admissao)
    sheet.cell(row=proxima_linha, column=5, value=chapa)
    sheet.cell(row=proxima_linha, column=6, value=dia_atual)
    sheet.cell(row=proxima_linha, column=8, value=registro_pf)
    sheet.cell(row=proxima_linha, column=9, value=cpf)
    sheet.cell(row=proxima_linha, column=10, value=reciclagem)

    # Salvar o arquivo

    if empresa == "Empresa1":
        caminho_planilha_crachas = Path(r"caminho arquivo")
        planilha.save(caminho_planilha_crachas)
    elif empresa == "Empresa2" or empresa == "Empresa2":
        caminho_planilha_crachas = Path(r"caminho arquivo")
        planilha.save(caminho_planilha_crachas)

print("Dados salvos com sucesso!")


from django import forms
from mobile.models import Limit
from django.contrib.admin import widgets

class LimitForm(forms.ModelForm):

    class Meta:
        model = Limit
        fields = [
            'sim',
            'amount',
            'infinite',
            'date'
        ]
        widgets = {
            'date': widgets.AdminDateWidget()
        }


from datetime import datetime, timedelta
from typing import List, Union, Tuple

from tasks.model.model import Task
from tasks.model.util import table_for_not_found_data
from sqlalchemy.exc import DataError
from tasks import session, validator

class DataManager:

    def get_task_values(self, option: str) -> List[List]:
        """
        A function that uses the get_tasks function with an option selected, and returns the data prepared for printing

        :param option: a filtering option to be used in the get_tasks function. Possible: --all, --today, --missed,
                        --week, --done
        :return: list containing lists of tasks values with an option selected, prepared to be printed for the user.
        """
        return [[task.name,
                 task.deadline.strftime("%Y-%m-%d") if task.deadline else "No hurry",
                 task.description if task.description else "---",
                 task.creation_date.strftime("%Y-%m-%d %H:%M"),
                 task.task_hash]
                for task in self.get_tasks(option)]

    def get_table(self, option: str) -> List[List]:
        """
        A function that prepares tables for printing, database column names with the values of the selected option
        (option passed to the get_task_values function).

        :param option: a filtering option to be used in the get_task_values function. Possible: --all, --today,
                     --missed, --week, --done
        :return: list containing lists of column names and tasks values with an option selected, prepared to be printed
                    for the user.
        """
        return [self.get_column_names(), *self.get_task_values(option)] if self.get_task_values(option) \
            else [self.get_column_names(), table_for_not_found_data]

    def get_table_with_one_record(self, task: Task):
        """
        A function that prepares table for printing, database column names with the values of provided Task object.

        :param task: Task class object
        :return: list containing lists of column names and task values, prepared to be printed for the user.
        """
        return [self.get_column_names(),
                [task.name,
                 task.deadline.strftime("%Y-%m-%d") if task.deadline else "No hurry",
                 task.description if task.description else "---",
                 task.creation_date.strftime("%Y-%m-%d %h:%M"),
                 task.task_hash]]

    @staticmethod
    def add_task(name: str, deadline: datetime.date = None, description: str = None) -> Task:
        """
        Function that adds a new task to database.

        :param name: Task.name, string(max 30 chars long). Required
        :param deadline: Task.deadline, datetime.date object. Optional.
        :param description: Task.description, string(max 150 chars long). Optional
        :return: Task object with given properties.
        """
        new_task = Task(name=name, deadline=deadline, description=description)
        session.add(new_task)
        session.commit()
        return new_task

    @staticmethod
    def get_column_names() -> List[str]:
        """
        :return: list of database column names declared in Task object, without 'ID' and 'Done' columns.
        """
        id_index = 1
        done_index = -1
        return Task.__table__.columns.keys()[id_index:done_index]

    @staticmethod
    def get_tasks(option: str) -> List[Task]:
        """
        Function which returns a list of Task objects with given criteria.

        :param option: filtering option for dictionary declared in a function. Possible: --all, --today, --missed,
                        --week, --done
        :return: list of Task objects filtered by given option. If wrong option provided, return all objects.
        """
        today = datetime.date(datetime.utcnow()).strftime("%Y-%m-%d")
        week_later = (datetime.date(datetime.utcnow()) + timedelta(days=7)).strftime("%Y-%m-%d")
        option_dict = {
            "--all": session.query(Task)
                            .filter(Task.done.is_(False))
                            .order_by(Task.deadline.is_(None), Task.deadline)
                            .all(),
            "--today": session.query(Task)
                              .filter(Task.deadline == today, Task.done.is_(False))
                              .all(),
            "--missed": session.query(Task)
                              .filter(Task.deadline < today, Task.done.is_(False))
                              .all(),
            "--week": session.query(Task)
                             .filter(Task.deadline < week_later, Task.done.is_(False))
                             .all(),
            "--done": session.query(Task)
                             .filter(Task.done)
                             .all()
        }
        if option not in option_dict.keys():
            return option_dict.get("--all")
        return option_dict.get(option)

class CMDDataManager(DataManager):

    def validate_and_add_task(self, sys_args: List[str]) -> Tuple[bool, int]:
        """
        Validate and, if validated, fires add_task function, which creates a new Task class object

        :param sys_args: List of arguments received from the command line
        :return: Tuple of boolean and integer. Boolean means if it was successful, integer is index of message from
                 messages.py
        """
        try:
            task_validation = validator.validate_add_task_arguments(sys_args)
            if type(task_validation) == dict:
                new_task = self.add_task(name=task_validation.get("name"),
                                         deadline=task_validation.get("deadline"),
                                         description=task_validation.get("description"))
                return True, 1 if new_task else (False, 6)
            else:
                return False, task_validation
        except DataError:
            return False, 7

    def find_task_for_table(self, sys_args: List[str]) -> Union[Tuple[bool, List], Tuple[bool, int]]:
        """
        Function returning True or False value, indicating its success and tables with the found "Task" object or
        message index in case of its failure. Used for listing one record for rind or remove tasks functionality.

        :param sys_args: list of arguments provided in command line
        :return: tuple of boolean value meaning if it was successful and table with found Task or error message
        """
        hash_index = 2
        task = self.find_task_by_hash(sys_args, hash_index)
        if task:
            return True, self.get_table_with_one_record(task)
        return False, 5

    def finish_task(self, sys_args: List[str]) -> Tuple[bool, int]:
        """
        Function that changes Task's "Done" column value to True, if it is not.

        :param sys_args: list of arguments provided in command line
        :return: tuple with boolean value meaning if it was successful and index of message
        """
        hash_index = 2
        task = self.find_task_by_hash(sys_args, hash_index)
        if task and task.done:
            return False, 9
        elif task:
            task.done = True
            session.commit()
            return True, 3
        return False, 5

    def undo_task(self, sys_args: List[str]) -> Tuple[bool, int]:
        """
        Function that changes Task's "Done" column value to False, if it is not.

        :param sys_args: list of arguments provided in command line
        :return: tuple with boolean value meaning if it was successful and index of message
        """
        task = self.find_task_by_hash(sys_args, 2)
        if task and not task.done:
            return False, 10
        elif task:
            task.done = False
            session.commit()
            return True, 5
        return False, 5

    def validate_and_update_task(self, sys_args: List[str]) -> Tuple[bool, int]:
        """
        A function that validates entered system arguments for updating a job and changes or removes them depending
        on the options selected.

        :param sys_args: list of arguments provided in command line
        :return: tuple with boolean value meaning if it was successful and index of message
        """

        try:
            task = self.find_task_by_hash(sys_args, -1)
            if not task:
                return False, 5
            new_values_validator = validator.validate_update_args(sys_args)
            if type(new_values_validator) != dict:
                return False, new_values_validator

            new_name = new_values_validator.get("name")
            new_deadline = new_values_validator.get("deadline")
            new_description = new_values_validator.get("description")

            task.name = new_name \
                if new_name else task.name

            task.deadline = None if new_deadline == "remove" \
                else (new_deadline if new_deadline
                      else task.deadline)

            task.description = None if new_description == "remove" \
                else (new_description if new_description
                      else task.description)

            session.commit()
            return True, 2
        except DataError:
            return False, 7

    def validate_and_get_list(self, sys_args: List[str]) -> Union[Tuple[bool, List], Tuple[bool, int]]:
        """
        Function that validates arguments provided in command line for listing records a and return table with chosen
        option, if was valid.

        :param sys_args: list of arguments provided in command line
        :return: tuple with boolean value meaning if it was successful and table, if validated, or wrong option which
                was provided
        """
        list_option = validator.validate_listing_arguments(sys_args)
        if type(list_option) != str:
            return False, list_option
        return True, self.get_table(list_option)

    def validate_and_remove_task(self, sys_args: List[str]) -> Tuple[bool, int]:
        """
        A function that validates whether a task with a hash specified on the command line exists and, if it exists,
        removes it.

        :param sys_args: list of arguments provided in command line
        :return: tuple with boolean value meaning if it was successful and index of message
        """
        hash_index = 2
        task = self.find_task_by_hash(sys_args, hash_index)
        if not task:
            return False, 5
        session.delete(task)
        session.commit()
        return True, 4

    @staticmethod
    def find_task_by_hash(sys_args: List[str], hash_index: int) -> Task:
        """
        Finds the "Task" object by passing it a list of system arguments and indicating on which index the hash value of
        the searched element is located.

        :param sys_args: list of arguments provided in command line
        :param hash_index: integer which points to the index under which the hash value is
        :return: Task class object
        """
        task = session.query(Task).filter(Task.task_hash == sys_args[hash_index]).first()
        return task

class LazyDataManager(DataManager):

    def lazy_find_and_remove_task(self, task_hash: str) -> Tuple[bool, int]:
        """
        Function for lazy mode. Look for task with provided task_hash and removes it from database

        :param task_hash: hash value for the object to be deleted
        :return: tuple with boolean value meaning if it was successful and index of message
        """
        task = self.lazy_find_task(task_hash)
        if task:
            session.delete(task)
            session.commit()
            return True, 4
        return False, 5

    def lazy_update_task(self, task: Task, name: str, deadline: datetime.date, description: str) -> Tuple[bool, int]:
        """
        Lazy mode function. Updates, removes or keep current values of provided task.

        :param task: Task class object to be updated
        :param name: New name for provided task. Keeps current if None.
        :param deadline: New deadline for provided task. Keeps current if None or removes it if "remove" provided.
        :param description: New description for provided task. Keeps current if None or removes it if "remove" provided.
        :return: tuple with boolean value meaning if it was successful and index of message
        """
        if not self.lazy_find_task(task.task_hash):
            return False, 5
        task.name = name if name else task.name

        task.deadline = deadline if deadline == "remove" else \
            (deadline if deadline else task.deadline)

        task.description = description if description == "remove" else \
            (description if description else task.description)

        session.commit()
        return True, 2

    def lazy_finish_or_undo_task(self, task: Task) -> Tuple[bool, int]:
        """
        Lazy mode function. Changes task's "Done" column to opposite value.

        :param task: Task class object.
        :return: tuple with boolean value meaning if it was successful and index of message
        """
        if not self.lazy_find_task(task.task_hash):
            return False, 5
        if task.done:
            ret_val = (True, 5)
        else:
            ret_val = (True, 3)
        task.done = not task.done
        return ret_val

    @staticmethod
    def lazy_find_task(task_hash: Union[str, int]) -> Task:
        """
        Function for lazy mode. Look for task with provided task_hash and returns it.

        :param task_hash: hash value of the searched object
        :return: Task class object with provided hash value, if found
        """
        return session.query(Task).filter(Task.task_hash == task_hash).first()


import cv2
import registers.points_detection as ptd

MARGIN = 30
LIMIT = 20

def isCenter(center: int, top: int, bottom: int, middlex: int = 0, middley: int = 0) -> bool:
    if center is None or top is None or bottom is None:
        return False

    if center[0] < middley-MARGIN or center[0] > middley+MARGIN or center[1] < middlex-MARGIN or center[1] > middlex+MARGIN:
        return False

    if top[0] < center[0]-MARGIN or top[0] > center[0]+MARGIN:
        return False

    if bottom[0] < center[0]-MARGIN or bottom[0] > center[0]+MARGIN:
        return False

    return True

def getFaces(frame: cv2.Mat):
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    coordinates = ptd.face_detector(gray, 1)
    faces = []
    for c in coordinates:
        face = gray[c.top():c.bottom(), c.left():c.right()]
        face = cv2.resize(face, (160, 160), interpolation=cv2.INTER_CUBIC)
        cv2.rectangle(
            frame, (c.left(), c.top()), (c.right(), c.bottom()), (0, 255, 0), 2)
        faces.append({"face": face, "rectangle": c})

    return faces

def capture(frame: cv2.Mat, faceSamples: list):

    faces = getFaces(frame)

    width, heigth, _ = frame.shape
    middlex, middley = width//2, heigth//2

    for face in faces:
        rectangle = face["rectangle"]

        cv2.circle(frame, (middley, middlex), 2, (100, 200, 0), 5)

        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        center, top, bottom = ptd.get_points(gray, rectangle, [
            30, 27, 8])

        cv2.circle(frame, center, 2, (100, 200, 0), 5)
        cv2.circle(frame, top, 2, (255, 0, 0), 5)
        cv2.circle(frame, bottom, 2, (255, 0, 0), 5)

        cv2.putText(frame, str(len(faceSamples)), (80, 80),
                    cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 2)

        if not isCenter(center, top, bottom, middlex, middley):
            continue

        faceSamples.append(face["face"])


from __future__ import print_function

import os
import sys
from subprocess import check_output

from setuptools import find_packages, setup

def get_version():
    """Determine the most appropriate version number for this package."""

    # try to use git tag if building python package
    try:
        # append git sha to version
        return '0.0.' + check_output("git rev-list --count HEAD",shell=True).decode('utf8')
    except Exception as e:
        print("Failed to acquire version info from git: {e}".format(e=e), file=sys.stderr)
        return '0.0.0'

def requirements(extra=None):
    """Return list of required package names from requirements.txt."""
    # strip trailing comments, and extract package name from git urls.
    if extra:
        filename = 'requirements-' + extra + '.txt'
    else:
        filename = 'requirements.txt'
    requirements = [r.strip().split(';',1)[0].split(' ', 1)[0].split('egg=', 1)[-1]
                    for r in open(filename) if r.strip() and not r.strip().startswith('#')]
    return requirements

for x in requirements(extra='deploy'):
    print(x)

setup(
    name='dashboard',
    version=get_version(),
    packages=find_packages(),
    install_requires=requirements(),
    extras_require={
        'deploy': requirements(extra='deploy'),
    },
    entry_points={
        'console_scripts': [
            'dashboard = dashboard.manage:main',
        ],
    },
    include_package_data=True,
)


from flask import Flask, render_template, jsonify, request
import json
import pandas as pd

app = Flask(__name__)

with open('./config/config.txt', 'r') as configfile:
    datafolder = configfile.readline()

@app.route('/')
def index():
    return render_template('anki.html')

@app.route('/deck', methods=['POST'])
def writeDeck():
    deck = request.json
    deckName = deck['deck']
    cards = deck['cards']

    df = pd.DataFrame(cards)
    df['questionHTML'] = df['questionHTML'].str.replace(r'\n', '')
    df['answerHTML'] = df['answerHTML'].str.replace(r'\n', '')
    df['finalQuestion'] = df.apply(lambda x: x['question'] if x['isSimpleMode'] else x['questionHTML'], axis=1)
    df['finalAnswer'] = df.apply(lambda x: x['answer'] if x['isSimpleMode'] else x['answerHTML'], axis=1)
    df = df[['finalQuestion', 'finalAnswer']]
    df.to_csv(datafolder + '/' + deckName + '.csv', index=False, header=False, encoding='utf-8')
    return 'ok'

if __name__ == '__main__':
    app.run(debug=True, port=80)


"""initial table

Revision ID: 3726039df26e
Revises:
Create Date: 2021-11-01 16:26:47.165308

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '3726039df26e'
down_revision = None
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('sections',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=64), nullable=True),
    sa.Column('moderated', sa.Boolean(), nullable=True),
    sa.Column('premium', sa.Boolean(), nullable=True),
    sa.Column('parent_section_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['parent_section_id'], ['sections.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_sections_id'), 'sections', ['id'], unique=False)
    op.create_table('tags',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=64), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_tags_id'), 'tags', ['id'], unique=False)
    op.create_table('users',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('username', sa.String(), nullable=True),
    sa.Column('password', sa.String(), nullable=True),
    sa.Column('premium', sa.Boolean(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
    op.create_table('articles',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('content', sa.String(length=2048), nullable=True),
    sa.Column('author_id', sa.Integer(), nullable=True),
    sa.Column('section_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['author_id'], ['users.id'], ),
    sa.ForeignKeyConstraint(['section_id'], ['sections.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_articles_id'), 'articles', ['id'], unique=False)
    op.create_table('userModerators',
    sa.Column('user_id', sa.Integer(), nullable=True),
    sa.Column('section_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['section_id'], ['sections.id'], ),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], )
    )
    op.create_table('comments',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('content', sa.String(length=256), nullable=True),
    sa.Column('response_id', sa.Integer(), nullable=True),
    sa.Column('article_id', sa.Integer(), nullable=True),
    sa.Column('author_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['article_id'], ['articles.id'], ),
    sa.ForeignKeyConstraint(['author_id'], ['users.id'], ),
    sa.ForeignKeyConstraint(['response_id'], ['comments.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_comments_id'), 'comments', ['id'], unique=False)
    op.create_table('tagArticles',
    sa.Column('tag_id', sa.Integer(), nullable=True),
    sa.Column('article_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['article_id'], ['articles.id'], ),
    sa.ForeignKeyConstraint(['tag_id'], ['tags.id'], )
    )
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('tagArticles')
    op.drop_index(op.f('ix_comments_id'), table_name='comments')
    op.drop_table('comments')
    op.drop_table('userModerators')
    op.drop_index(op.f('ix_articles_id'), table_name='articles')
    op.drop_table('articles')
    op.drop_index(op.f('ix_users_id'), table_name='users')
    op.drop_table('users')
    op.drop_index(op.f('ix_tags_id'), table_name='tags')
    op.drop_table('tags')
    op.drop_index(op.f('ix_sections_id'), table_name='sections')
    op.drop_table('sections')
    # ### end Alembic commands ###


# Poly Report, by Joshua Buck, (c)2019, all rights reserved

"""
import jbPolyReport
jbPolyReport.jbPolyReportGo()
"""

import maya.cmds as cmds

defColor = 0.3, 0.3, 0.3
passColor = (0.17, 1.0, 0.17)
warningColor = (1.0, 1.0, 0.17)
errorColor = (1.0, 0.17, 0.17)

def jbPolyReportGo():
    jbPolyReportWIN()
    jbPolyReportUI()

def jbPolyReportWIN():
    version = 1.01
    if cmds.window("jbPrWin", exists=True):
        cmds.deleteUI("jbPrWin", window=True)

    # if cmds.windowPref("jbPrWin", exists=True):
    #     cmds.windowPref('jbPrWin', remove=True)

    cmds.window("jbPrWin", title="Poly Report  v" + str(version), mnb=False, mxb=False, s=True)

    cmds.columnLayout("mainUI_C", p="jbPrWin")

    cmds.showWindow("jbPrWin")
    cmds.window("jbPrWin", e=True, h=1)

def jbPolyReportUI():
    mainCW = 300
    warningTitleCW = 100
    warningContentCW = 280

    # General Mesh Info
    cmds.rowColumnLayout(nc=1, cw=[(1, mainCW)], p="mainUI_C")
    cmds.separator(h=4, style='none')

    cmds.rowColumnLayout(nc=2, cw=[(1, 100), (2,180)], p="mainUI_C")
    cmds.text(l="Mesh Name:   ", align="right", font="boldLabelFont")
    cmds.text("meshName_TX", l='No Mesh', align="left", font="boldLabelFont")

    cmds.rowColumnLayout(nc=1, cw=[(1, 280)], cs=[(1,10)], p="mainUI_C")
    cmds.separator(h=8)

    cmds.rowColumnLayout(nc=4, cw=[(1, 80),(2,60),(3,75),(4,50)], p="mainUI_C")

    cmds.text(l='Triangles:  ', align="right")
    cmds.text("numTriangles_TX", l='', align="left")

    cmds.text(l='Edges:  ', align="right")
    cmds.text("numEdges_TX", l='', align="left")

    cmds.text(l='Faces:  ', align="right")
    cmds.text("numFaces_TX", l='', align="left")

    cmds.text(l='Vertices:  ', align="right")
    cmds.text("numVerts_TX", l='', align="left")

    cmds.text(l='Mesh Shells:  ', align="right")
    cmds.text("numMeshShells_TX", l='', align="left")

    cmds.text(l='Shapes:  ', align="right")
    cmds.text("numShapes_TX", l='', align="left")

    cmds.text(l='Parents:  ', align="right")
    cmds.text("parents_TX", l='', align="left")

    cmds.text(l='Children:  ', align="right")
    cmds.text("children_TX", l='', align="left")

    cmds.rowColumnLayout(nc=1, cw=[(1, mainCW)], p="mainUI_C")
    cmds.separator(h=4, style='none')

    cmds.rowColumnLayout(nc=4, cw=[(1, 80), (2, 60), (3, 75), (4, 50)], p="mainUI_C")

    cmds.text(l='Materials:  ', align="right")
    cmds.text("materials_TX", l='', align="left")

    cmds.text(l='UV Sets:  ', align="right")
    cmds.text("uvSets_TX", l='', align="left")

    cmds.text(l='UV Points:  ', align="right")
    cmds.text("uvPoints_TX", l='', align="left")

    cmds.text(l='UV Shells:  ', align="right")
    cmds.text("uvShells_TX", l='', align="left")

    cmds.rowColumnLayout(nc=1, cw=[(1, mainCW)], p="mainUI_C")
    cmds.separator(h=4, style='none')

    cmds.rowColumnLayout(nc=4, cw=[(1, 80), (2, 40), (3, 95), (4, 74)], p="mainUI_C")

    cmds.text(l='Has History:  ', align="right")
    cmds.text("hasHistory_TX", l='', align="left")

    cmds.text(l='Transformations:  ', align="right")
    cmds.text("transformations_TX", l='', align="left")

    cmds.rowColumnLayout(nc=1, cw=[(1, 280)], cs=[(1,10)], p="mainUI_C")
    cmds.separator(h=8)

    # Face Info
    cmds.rowColumnLayout(nc=3, cw=[(1, 130), (2, 14), (3, 75)], cs=[(1,0),(2,6),(3,6)], p="mainUI_C")

    cmds.text(l='Quadrilaterals:', align="right")
    cmds.button("quadFaces_BUT", l='', h=14, bgc=defColor)
    cmds.text("quadFaces_TX", l='', align="left")

    cmds.text(l='Defined Triangles:', align="right")
    cmds.button("triangleFaces_BUT", l='', h=14, bgc=defColor)
    cmds.text("triangleFaces_TX", l='', align="left")

    cmds.text(l='N-Gons:', align="right")
    cmds.button("faceFivePlusEdgesSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("faceFivePlusEdges_TX", l='', align="left")

    cmds.rowColumnLayout(nc=1, cw=[(1, mainCW)], p="mainUI_C")
    cmds.separator(h=8, style='none')

    # Vertex Info
    cmds.rowColumnLayout(nc=3, cw=[(1, 130), (2, 14), (3, 75)], cs=[(1,0),(2,6),(3,6)], p="mainUI_C")

    cmds.text(l='Vertices w/2 Edges:', align="right")
    cmds.button("vtxTwoEdgesSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("vtxTwoEdges_TX", l='', align="left")

    cmds.text(l='Vertices w/3 Edges:', align="right")
    cmds.button("vtxThreeEdgesSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("vtxThreeEdges_TX", l='', align="left")

    cmds.text(l='Vertices w/4 Edges:', align="right")
    cmds.button("vtxFourEdgesSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("vtxFourEdges_TX", l='', align="left")

    cmds.text(l='Vertices w/5+ Edges:', align="right")
    cmds.button("vtxFivePlusEdgesSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("vtxFivePlusEdges_TX", l='', align="left")

    # General Cleanup Info
    cmds.rowColumnLayout(nc=1, cw=[(1, 280)], cs=[(1,10)], p="mainUI_C")
    cmds.separator(h=8)
    # cmds.text(l='Examine / Cleanup', font="boldLabelFont")
    # cmds.separator(h=5, style='none')

    cmds.rowColumnLayout(nc=3, cw=[(1, 130), (2, 14), (3, 75)], cs=[(1, 0), (2, 6), (3, 6)], p="mainUI_C")

    cmds.text(l='Faces w/Zero Area:', align="right")
    cmds.button("faceZeroAreaSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("faceZeroArea_TX", l='', align="left")

    cmds.text(l='Faces w/2 Edges:', align="right")
    cmds.button("faceTwoEdgesSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("faceTwoEdges_TX", l='', align="left")

    cmds.text(l='Vertices Sharing Space:', align="right")
    cmds.button("vtxSharingSpaceSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("vtxSharingSpace_TX", l='', align="left")

    cmds.text(l='Nonmanifold Edges:', align="right")
    cmds.button("nonmanifoldEdgesSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("nonmanifoldEdges_TX", l='', align="left")

    cmds.text(l='Nonmanifold UV Edges:', align="right")
    cmds.button("nonmanifoldUvEdgesSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("nonmanifoldUvEdges_TX", l='', align="left")

    cmds.text(l='Nonmanifold UVs:', align="right")
    cmds.button("nonmanifoldUvsSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("nonmanifoldUvs_TX", l='', align="left")

    cmds.text(l='UV Faces w/Zero Area:', align="right")
    cmds.button("uvZeroAreaFaces_BUT", l='', h=14, bgc=defColor)
    cmds.text("uvZeroAreaFaces_TX", l='', align="left")

    cmds.text(l='Lamina Faces:', align="right")
    cmds.button("laminaFacesSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("laminaFaces_TX", l='', align="left")

    cmds.text(l='Invalid Edges:', align="right")
    cmds.button("invalidEdgesSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("invalidEdges_TX", l='', align="left")

    cmds.text(l='Invalid Vertices:', align="right")
    cmds.button("invalidVerticesSelect_BUT", l='', h=14, bgc=defColor)
    cmds.text("invalidVertices_TX", l='', align="left")

    cmds.rowColumnLayout(nc=1, cw=[(1, 280)], cs=[(1,10)], p="mainUI_C")
    cmds.separator(h=8)

    # Notice
    cmds.rowColumnLayout(nc=1, cw=[(1, warningTitleCW)], cs=[(1,10)],  p="mainUI_C")
    cmds.text(l="Notice:", font="boldLabelFont", align="left")

    cmds.rowColumnLayout("meshNotice_RCL", nc=1, cw=[(1, warningContentCW)], cs=[(1,20)], p="mainUI_C")

    # Warnings
    cmds.rowColumnLayout(nc=1, cw=[(1, warningTitleCW)], cs=[(1,10)], p="mainUI_C")
    cmds.separator(h=8, style='none')
    cmds.text(l="Warnings:", font="boldLabelFont", align="left")

    cmds.rowColumnLayout("meshWarnings_RCL", nc=1, cw=[(1, warningContentCW)], cs=[(1,15)], p="mainUI_C")

    # Errors
    cmds.rowColumnLayout(nc=1, cw=[(1, warningTitleCW)],cs=[(1,10)], p="mainUI_C")
    cmds.separator(h=8, style='none')
    cmds.text(l="Errors:", font="boldLabelFont", align="left")

    cmds.rowColumnLayout("meshErrors_RCL", nc=1, cw=[(1, warningContentCW)], cs=[(1,15)], p="mainUI_C")

    # Action Button
    cmds.rowColumnLayout(nc=1, cw=[(1, 280)], cs=[(1,10)], p="mainUI_C")
    cmds.separator(h=10, style='none')
    cmds.button(l='Report', h=30, c=lambda args: generatePolyReport())
    cmds.separator(h=8, style='none')

def generatePolyReport():
    sel = cmds.ls(sl=True, o=True)
    if len(sel) != 1:
        cmds.warning("Please select a single object of type: mesh")
        return None
    sel = sel[0]
    shapes = cmds.listRelatives(sel, shapes=True, fullPath=True)
    if shapes is not None:
        for each in shapes:
            if cmds.nodeType(each) != "mesh":
                cmds.warning("Selection type must be of type: mesh")
                return None
    else:
        cmds.warning("Selection has no mesh components. Make sure you are in object mode.")
        return None

    # report list [0] = info, [1] = warnings, [2] = errors
    printReport = [[], [], []]

    # MESH
    meshReport = processMesh(sel)
    if meshReport[0]:
        printReport[0].extend(meshReport[0])
    if meshReport[1]:
        printReport[1].extend(meshReport[1])
    if meshReport[2]:
        printReport[2].extend(meshReport[2])

    # FACES
    facesReport = processFaces(sel)
    if facesReport[0]:
        printReport[0].extend(facesReport[0])
    if facesReport[1]:
        printReport[1].extend(facesReport[1])
    if facesReport[2]:
        printReport[2].extend(facesReport[2])

    # VERTICES
    vertexReport = processVertices(sel)
    if vertexReport[0]:
        printReport[0].extend(vertexReport[0])
    if vertexReport[1]:
        printReport[1].extend(vertexReport[1])
    if vertexReport[2]:
        printReport[2].extend(vertexReport[2])

    # processEdges(sel)

    # CLEANUP / EXAMINE
    cleanupReport = processCleanup(sel)
    if cleanupReport[0]:
        printReport[0].extend(cleanupReport[0])
    if cleanupReport[1]:
        printReport[1].extend(cleanupReport[1])
    if cleanupReport[2]:
        printReport[2].extend(cleanupReport[2])

    # UVs
    uvReport = processUvs(sel)
    if uvReport[0]:
        printReport[0].extend(uvReport[0])
    if uvReport[1]:
        printReport[1].extend(uvReport[1])
    if uvReport[2]:
        printReport[2].extend(uvReport[2])

    # Materials
    materialReport = processMaterials(sel)
    if materialReport[0]:
        printReport[0].extend(materialReport[0])
    if materialReport[1]:
        printReport[1].extend(materialReport[1])
    if materialReport[2]:
        printReport[2].extend(materialReport[2])

    # Write report to UI fields
    clearLayoutChildren("meshNotice_RCL")
    for info in printReport[0]:
        cmds.text(l=info, p="meshNotice_RCL", align="left")
    clearLayoutChildren("meshWarnings_RCL")
    for warning in printReport[1]:
        cmds.text(l=warning, p="meshWarnings_RCL", align="left")
    clearLayoutChildren("meshErrors_RCL")
    for error in printReport[2]:
        cmds.text(l=error, p="meshErrors_RCL", align="left")

def processMesh(sel):

    returnReport = [[], [], []]

    parents = cmds.listRelatives(sel, allParents=True)
    children = cmds.listRelatives(sel, ad=True, type="transform")
    shapes = cmds.listRelatives(sel, shapes=True)
    numVerts = cmds.polyEvaluate(sel, vertex=True)
    numEdges = cmds.polyEvaluate(sel, edge=True)
    numTriangles = cmds.polyEvaluate(sel, triangle=True)
    numFaces = cmds.polyEvaluate(sel, face=True)
    numMeshShells = cmds.polyEvaluate(sel, shell=True)
    cmds.text("meshName_TX", e=True, l=sel)
    cmds.text("numVerts_TX", e=True, l=numVerts)
    cmds.text("numEdges_TX", e=True, l=numEdges)
    cmds.text("numTriangles_TX", e=True, l=numTriangles)
    cmds.text("numFaces_TX", e=True, l=numFaces)
    cmds.text("numMeshShells_TX", e=True, l=numMeshShells)
    cmds.text("numShapes_TX", e=True, l=len(shapes))
    if parents is not None:
        for each in parents:
            s = cmds.listRelatives(each, shapes=True)
            if s is None:
                warning = "Mesh's parent has no shape node"
                if warning not in returnReport[0]:
                    returnReport[0].append(warning)
        cmds.text("parents_TX", e=True, l=len(parents))
    else:
        cmds.text("parents_TX", e=True, l="None")
    if children is not None:
        cmds.text("children_TX", e=True, l=len(children))
    else:
        cmds.text("children_TX", e=True, l="None")

    # Mesh history
    ch = cmds.listHistory(sel, pdo=True, il=2)
    if ch is None or not ch or ch[0] == "initialShadingGroup":
        cmds.text("hasHistory_TX", edit=True, l="No")
    else:
        cmds.text("hasHistory_TX", edit=True, l="Yes")
        warning = "Mesh has input/construction history"
        if warning not in returnReport[0]:
            returnReport[0].append(warning)

    # Multiple Shapes Warning
    if len(shapes) > 1:
        returnReport[1].append("Mesh has more than one shape")

    # Default mesh names warning
    defPolyMeshNames = ["Sphere", "Cube", "Cylinder", "Cone", "Torus", "Plane", "Disc", "Pipe", "Pyramid",
                        "Helix", "Surface"]
    for name in defPolyMeshNames:
        if name in sel and sel.startswith("p"):
            warning="Mesh name may be default"
            if warning not in returnReport[0]:
                returnReport[0].append(warning)

    # Transformations
    transAttrX = cmds.getAttr(sel + ".translateX")
    transAttrY = cmds.getAttr(sel + ".translateY")
    transAttrZ = cmds.getAttr(sel + ".translateZ")
    rotateAttrX = cmds.getAttr(sel + ".rotateX")
    rotateAttrY = cmds.getAttr(sel + ".rotateY")
    rotateAttrZ = cmds.getAttr(sel + ".rotateZ")
    scaleAttrX = cmds.getAttr(sel + ".scaleX")
    scaleAttrY = cmds.getAttr(sel + ".scaleY")
    scaleAttrZ = cmds.getAttr(sel + ".scaleZ")

    transformText = "No"

    if transAttrX != 0.0 or transAttrY != 0.0 or transAttrZ != 0.0:
        transformText = "Trns "
        warning = "Translate attributes are not (0, 0, 0)"
        if warning not in returnReport[0]:
            returnReport[0].append(warning)
    if rotateAttrX != 0.0 or rotateAttrY != 0.0 or rotateAttrZ != 0.0:
        transformText = transformText + "Rot "
        warning = "Rotate attributes are not (0, 0, 0)"
        if warning not in returnReport[0]:
            returnReport[0].append(warning)
    if scaleAttrX != 1.0 or scaleAttrY != 1.0 or scaleAttrZ != 1.0:
        transformText = transformText + "Scl"
        warning = "Scale attributes are not (1, 1, 1)"
        if warning not in returnReport[0]:
            returnReport[0].append(warning)

    cmds.text("transformations_TX", e=True, l=transformText)

    return returnReport

def processVertices(sel):
    returnReport = [[], [], []]
    numVerts = cmds.polyEvaluate(sel, vertex=True)

    twoEdgeVerts = []
    threeEdgeVerts = []
    fourEdgeVerts = []
    moreThanFourEdgeVerts = []
    for x in range(numVerts):
        vtxName = sel + ".vtx[" + str(x) + "]"
        vtxEdges = cmds.polyListComponentConversion(vtxName, te=True)
        vtxEdges = cmds.ls(vtxEdges, flatten=True)
        if len(vtxEdges) == 2:
            twoEdgeVerts.append(vtxName)
            warning = "One or more vertices only has two edges"
            if warning not in returnReport[1]:
                returnReport[1].append(warning)
        elif len(vtxEdges) == 3:
            threeEdgeVerts.append(vtxName)
        elif len(vtxEdges) == 4:
            fourEdgeVerts.append(vtxName)
        elif len(vtxEdges) > 4:
            moreThanFourEdgeVerts.append(vtxName)
            warning = "One or more vertices has more than 4 edges (poles)"
            if warning not in returnReport[0]:
                returnReport[0].append(warning)
        else:
            cmds.warning("Unknown number of edges per vertex")

    # Vertex Positions - Test for vertices in common location
    vertexPositions = []
    sharedVertexSpace = []
    for x in range(numVerts):
        vtxName = sel + ".vtx[" + str(x) + "]"
        vtxPos = cmds.xform(vtxName, q=True, t=True, ws=True)
        pos = [round(vtxPos[0], 5), round(vtxPos[1], 5), round(vtxPos[2], 5)]
        if pos in vertexPositions:
            if pos not in sharedVertexSpace:
                sharedVertexSpace.append(pos)
        vertexPositions.append(pos)
    verticesInSharedSpace = []
    if sharedVertexSpace:
        warning = "One or more vertices share space"
        if warning not in returnReport[1]:
            returnReport[1].append(warning)

        for x in range(numVerts):
            vtxName = sel + ".vtx[" + str(x) + "]"
            vtxPos = cmds.xform(vtxName, q=True, t=True, ws=True)
            pos = [round(vtxPos[0], 5), round(vtxPos[1], 5), round(vtxPos[2], 5)]
            for each in sharedVertexSpace:
                if pos == each:
                    verticesInSharedSpace.append(vtxName)

    if twoEdgeVerts:
        cmds.button("vtxTwoEdgesSelect_BUT", e=True, bgc=warningColor, c=lambda args: selectComponent(sel, twoEdgeVerts, "none"))
    else:
        cmds.button("vtxTwoEdgesSelect_BUT", e=True, bgc=defColor)
    cmds.text("vtxTwoEdges_TX", e=True, l=len(twoEdgeVerts))

    cmds.text("vtxThreeEdges_TX", e=True, l=len(threeEdgeVerts))
    cmds.button("vtxThreeEdgesSelect_BUT", e=True, c=lambda args: selectComponent(sel, threeEdgeVerts, "none"))

    cmds.text("vtxFourEdges_TX", e=True, l=len(fourEdgeVerts))
    cmds.button("vtxFourEdgesSelect_BUT", e=True, c=lambda args: selectComponent(sel, fourEdgeVerts, "none"))

    if moreThanFourEdgeVerts:
        cmds.button("vtxFivePlusEdgesSelect_BUT", e=True, bgc=warningColor,
                    c=lambda args: selectComponent(sel, moreThanFourEdgeVerts, "none"))
    else:
        cmds.button("vtxFivePlusEdgesSelect_BUT", e=True, bgc=defColor)
    cmds.text("vtxFivePlusEdges_TX", e=True, l=len(moreThanFourEdgeVerts))

    if verticesInSharedSpace:
        cmds.text("vtxSharingSpace_TX", e=True, l=len(verticesInSharedSpace))
        cmds.button("vtxSharingSpaceSelect_BUT", e=True, bgc=errorColor, c=lambda args: selectComponent(sel, verticesInSharedSpace, "none"))
    else:
        cmds.button("vtxSharingSpaceSelect_BUT", e=True, bgc=passColor)
        cmds.text("vtxSharingSpace_TX", e=True, l="None")

    return returnReport

def processFaces(sel):
    returnReport = [[], [], []]

    numFaces = cmds.polyEvaluate(sel, face=True)

    faceAreas = []
    zeroFaceArea = []
    for x in range(numFaces):
        faceName = sel + ".f[" + str(x) + "]"
        faceArea = cmds.polyEvaluate(faceName, faceArea=True)
        faceArea = float("%.5f" % faceArea[0])
        faceAreas.append(faceArea)
        if faceArea == 0.0:
            warning = "One or more faces has zero area"
            if warning not in returnReport[2]:
                returnReport[2].append(warning)
            zeroFaceArea.append(faceName)

    # Face Connections
    twoEdgeFaces = []
    threeEdgeFaces = []
    fourEdgeFaces = []
    moreThanFourEdgeFaces = []
    for x in range(numFaces):
        faceName = sel + ".f[" + str(x) + "]"
        faceEdges = cmds.polyListComponentConversion(faceName, te=True)
        faceEdges = cmds.ls(faceEdges, flatten=True)
        if len(faceEdges) == 2:
            warning = "One or more faces has only two edges"
            if warning not in returnReport[2]:
                returnReport[2].append(warning)
            twoEdgeFaces.append(faceName)
        elif len(faceEdges) == 3:
            threeEdgeFaces.append(faceName)
        elif len(faceEdges) == 4:
            fourEdgeFaces.append(faceName)
        elif len(faceEdges) > 4:
            warning = "One or more faces has more than 4 edges (n-gons)"
            if warning not in returnReport[0]:
                returnReport[0].append(warning)
            moreThanFourEdgeFaces.append(faceName)
        else:
            cmds.warning("Unknown number of edges per face")

    if twoEdgeFaces:
        cmds.text("faceTwoEdges_TX", e=True, l=len(twoEdgeFaces))
        cmds.button("faceTwoEdgesSelect_BUT", e=True, bgc=errorColor, c=lambda args: selectComponent(sel, twoEdgeFaces, "none"))
    else:
        cmds.button("faceTwoEdgesSelect_BUT", e=True, bgc=passColor)
        cmds.text("faceTwoEdges_TX", e=True,  l="None")

    cmds.text("triangleFaces_TX", e=True, l=len(threeEdgeFaces))
    cmds.button("triangleFaces_BUT", e=True, c=lambda args: selectComponent(sel, threeEdgeFaces, "none"))

    cmds.text("quadFaces_TX", e=True, l=len(fourEdgeFaces))
    cmds.button("quadFaces_BUT", e=True, c=lambda args: selectComponent(sel, fourEdgeFaces, "none"))

    if moreThanFourEdgeFaces:
        cmds.button("faceFivePlusEdgesSelect_BUT", e=True, bgc=warningColor,
                    c=lambda args: selectComponent(sel, moreThanFourEdgeFaces, "none"))
    else:
        cmds.button("faceFivePlusEdgesSelect_BUT", e=True, bgc=defColor)
    cmds.text("faceFivePlusEdges_TX", e=True, l=len(moreThanFourEdgeFaces))

    if zeroFaceArea:
        cmds.text("faceZeroArea_TX", e=True, l=len(zeroFaceArea))
        cmds.button("faceZeroAreaSelect_BUT", e=True, bgc=errorColor, c=lambda args: selectComponent(sel, zeroFaceArea, "faceToVertex"))
    else:
        cmds.button("faceZeroAreaSelect_BUT", e=True, bgc=passColor)
        cmds.text("faceZeroArea_TX", e=True, l="None")

    return returnReport

def processUvs(sel):
    returnReport = [[], [], []]

    uvPoints = cmds.polyEvaluate(sel, uv=True)
    uvShells = cmds.polyEvaluate(sel, us=True)
    uvSets = cmds.polyUVSet(sel, q=True, allUVSets=True)

    numFaces = cmds.polyEvaluate(sel, face=True)
    uvZeroAreaFaces = []
    for x in range(numFaces):
        faceName = sel + ".f[" + str(x) + "]"
        uvFaceArea = cmds.polyEvaluate(faceName, ufa=True)

        if uvFaceArea:
            if uvFaceArea[0] == 0.0:
                uvZeroAreaFaces.append(faceName)
        else:
            warning = "Mesh has no UVs"
            if warning not in returnReport[0]:
                returnReport[0].append(warning)

    cmds.text("uvPoints_TX", e=True, l=uvPoints)
    cmds.text("uvShells_TX", e=True, l=uvShells)
    cmds.text("uvSets_TX", e=True, l=len(uvSets))

    if uvZeroAreaFaces:
        cmds.text("uvZeroAreaFaces_TX", e=True, l=len(uvZeroAreaFaces))
        cmds.button("uvZeroAreaFaces_BUT", e=True, bgc=errorColor, c=lambda args: selectComponent(sel, uvZeroAreaFaces, "none"))
        warning = "One or more UV faces has zero area"
        if warning not in returnReport[2]:
            returnReport[2].append(warning)
    else:
        cmds.button("uvZeroAreaFaces_BUT", e=True, bgc=passColor)
        cmds.text("uvZeroAreaFaces_TX", e=True, l="None")

    return returnReport

def processMaterials(sel):
    returnReport = [[], [], []]

    shape = cmds.listRelatives(sel, shapes=True, fullPath=True)
    shadingEngine = cmds.listConnections(shape, type="shadingEngine")
    materials = []
    for each in shadingEngine:
        conn = cmds.listConnections(each)
        mat = cmds.ls(conn, materials=True)
        mat = mat[0]
        if mat not in materials:
            materials.append(mat)

    if materials:
        cmds.text("materials_TX", e=True, l=len(materials))
        if len(materials) > 1:
            warning = "Mesh has more than one material"
            if warning not in returnReport[0]:
                returnReport[0].append(warning)
        defMatNames = ["lambert", "phong", "blinn", "Stingray"]
        for each in materials:
            for name in defMatNames:
                if name in each:
                    warning = "One or more materials may have a default name"
                    if warning not in returnReport[0]:
                        returnReport[0].append(warning)

    return returnReport

def processCleanup(sel):
    returnReport = [[], [], []]

    nonManifoldEdges = cmds.polyInfo(sel, nme=True)
    nonManifoldUvEdges = cmds.polyInfo(sel, nue=True)
    nonManifoldUvs = cmds.polyInfo(sel, nuv=True)
    laminaFaces = cmds.polyInfo(sel, lf=True)
    invalidEdges = cmds.polyInfo(sel, ie=True)
    invalidVertices = cmds.polyInfo(sel, iv=True)

    if nonManifoldEdges is not None:
        warning = "One or more edges are nonmanifold"
        if warning not in returnReport[2]:
            returnReport[2].append(warning)
        cmds.text("nonmanifoldEdges_TX", e=True, l=len(nonManifoldEdges))
        cmds.button("nonmanifoldEdgesSelect_BUT", e=True, bgc=errorColor, c=lambda args: selectComponent(sel, nonManifoldEdges, "none"))
    else:
        cmds.button("nonmanifoldEdgesSelect_BUT", e=True, bgc=passColor)
        cmds.text("nonmanifoldEdges_TX", e=True, l="None")

    if nonManifoldUvEdges is not None:
        warning = "One or more UV edges are nonmanifold"
        if warning not in returnReport[2]:
            returnReport[2].append(warning)
        cmds.text("nonmanifoldUvEdges_TX", e=True, l=len(nonManifoldUvEdges))
        cmds.button("nonmanifoldUvEdgesSelect_BUT", e=True, bgc=errorColor, c=lambda args: selectComponent(sel, nonManifoldUvEdges, "none"))
    else:
        cmds.button("nonmanifoldUvEdgesSelect_BUT", e=True, bgc=passColor)
        cmds.text("nonmanifoldUvEdges_TX", e=True, l="None")

    if nonManifoldUvs is not None:
        warning = "One of more UV points are nonmanifold"
        if warning not in returnReport[2]:
            returnReport[2].append(warning)
        cmds.text("nonmanifoldUvs_TX", e=True, l=len(nonManifoldUvs))
        cmds.button("nonmanifoldUvsSelect_BUT", e=True, bgc=errorColor, c=lambda args: selectComponent(sel, nonManifoldUvs, "none"))
    else:
        cmds.button("nonmanifoldUvsSelect_BUT", e=True, bgc=passColor)
        cmds.text("nonmanifoldUvs_TX", e=True, l="None")

    if laminaFaces is not None:
        warning = "One or more faces share all edges (lamina)"
        if warning not in returnReport[2]:
            returnReport[2].append(warning)
        cmds.text("laminaFaces_TX", e=True, l=len(laminaFaces))
        cmds.button("laminaFacesSelect_BUT", e=True, bgc=errorColor, c=lambda args: selectComponent(sel, laminaFaces, "none"))
    else:
        cmds.button("laminaFacesSelect_BUT", e=True, bgc=passColor)
        cmds.text("laminaFaces_TX", e=True, l="None")

    if invalidEdges is not None:
        warning = "One or more edges have no face association (invalid)"
        if warning not in returnReport[2]:
            returnReport[2].append(warning)
        cmds.text("invalidEdges_TX", e=True, l=len(invalidEdges))
        cmds.button("invalidEdgesSelect_BUT", e=True, bgc=errorColor, c=lambda args: selectComponent(sel, invalidEdges, "none"))
    else:
        cmds.button("invalidEdgesSelect_BUT", e=True, bgc=passColor)
        cmds.text("invalidEdges_TX", e=True, l="None")

    if invalidVertices is not None:
        warning = "One or more vertices have no face association (invalid)"
        if warning not in returnReport[2]:
            returnReport[2].append(warning)
        cmds.text("invalidVertices_TX", e=True, l=len(invalidVertices))
        cmds.button("invalidVerticesSelect_BUT", e=True, bgc=errorColor, c=lambda args: selectComponent(sel, invalidVertices, "none"))
    else:
        cmds.button("invalidVerticesSelect_BUT", e=True, bgc=passColor)
        cmds.text("invalidVertices_TX", e=True, l="None")

    return returnReport

def clearLayoutChildren(layout):
    children = cmds.layout(layout, q=True, ca=True)
    if children is not None:
        for each in children:
            cmds.deleteUI(each)
        cmds.window("jbPrWin", e=True, h=1)

def selectComponent(sel, comp, convert):
    cmds.select(clear=True)
    if len(comp) == 0:
        cmds.select(sel, r=True)
        return None

    if convert == "none":
        comp = comp
    elif convert == "faceToVertex":
        for each in comp:
            vtxFaces = cmds.polyListComponentConversion(each, tv=True)
            vtxFaces = cmds.ls(vtxFaces, flatten=True)
        comp = vtxFaces

    cmds.select(clear=True)
    cmds.hilite(sel, r=True)
    for each in comp:
        cmds.select(each, add=True)


import json
from django.urls import reverse

def test_account_create(client):
    url = reverse("accounts:create")
    response = client.get(url)
    content = json.loads(response.content)
    assert response.status_code == 200
    assert content["create"] == "created"

def test_account_retrieve(client):
    url = reverse("accounts:retrieve")
    response = client.get(url)
    content = json.loads(response.content)
    assert response.status_code == 200
    assert content["retrieve"] == "retrieved"

def test_account_update(client):
    url = reverse("accounts:update")
    response = client.get(url)
    content = json.loads(response.content)
    assert response.status_code == 200
    assert content["update"] == "updated"

def test_account_delete(client):
    url = reverse("accounts:delete")
    response = client.get(url)
    content = json.loads(response.content)
    assert response.status_code == 200
    assert content["delete"] == "deleted"


# uncompyle6 version 3.8.0
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.15 (default, Dec 21 2021, 12:03:22)
# [GCC 10.2.1 20210110]
# Embedded file name: /home/cagatay/PycharmProjects/Expiry/FCOS/fcos_core/layers/misc.py
# Compiled at: 2021-12-16 06:36:40
# Size of source mod 2**32: 3984 bytes
"""
helper class that supports empty tensors on some nn functions.

Ideally, add support directly in PyTorch to empty tensors in
those functions.

This can be removed once https://github.com/pytorch/pytorch/issues/12013
is implemented
"""
import math, torch
from torch.nn.modules.utils import _ntuple

class _NewEmptyTensorOp(torch.autograd.Function):

    @staticmethod
    def forward(ctx, x, new_shape):
        ctx.shape = x.shape
        return x.new_empty(new_shape)

    @staticmethod
    def backward(ctx, grad):
        shape = ctx.shape
        return (_NewEmptyTensorOp.apply(grad, shape), None)

class Conv2d(torch.nn.Conv2d):

    def forward(self, x):
        if x.numel() > 0:
            return super(Conv2d, self).forward(x)
        else:
            output_shape = [(i + 2 * p - (di * (k - 1) + 1)) // d + 1 for i, p, di, k, d in zip(x.shape[-2:], self.padding, self.dilation, self.kernel_size, self.stride)]
            output_shape = [
             x.shape[0], self.weight.shape[0]] + output_shape
            return _NewEmptyTensorOp.apply(x, output_shape)

class BatchNorm2d(torch.nn.BatchNorm2d):

    def forward(self, x):
        if x.numel() > 0:
            return super(BatchNorm2d, self).forward(x)
        else:
            output_shape = x.shape
            return _NewEmptyTensorOp.apply(x, output_shape)

class DFConv2d(torch.nn.Module):
    __doc__ = 'Deformable convolutional layer'

    def __init__(self, in_channels, out_channels, with_modulated_dcn=True, kernel_size=3, stride=1, groups=1, padding=1, dilation=1, deformable_groups=1, bias=False):
        super(DFConv2d, self).__init__()
        if isinstance(kernel_size, (list, tuple)):
            assert len(kernel_size) == 2
            offset_base_channels = kernel_size[0] * kernel_size[1]
        else:
            offset_base_channels = kernel_size * kernel_size
        if with_modulated_dcn:
            from fcos_core.layers import ModulatedDeformConv
            offset_channels = offset_base_channels * 3
            conv_block = ModulatedDeformConv
        else:
            from fcos_core.layers import DeformConv
            offset_channels = offset_base_channels * 2
            conv_block = DeformConv
        self.offset = Conv2d(in_channels,
          (deformable_groups * offset_channels),
          kernel_size=kernel_size,
          stride=stride,
          padding=padding,
          groups=1,
          dilation=dilation)
        for l in [self.offset]:
            torch.nn.init.kaiming_uniform_((l.weight), a=1)
            torch.nn.init.constant_(l.bias, 0.0)

        self.conv = conv_block(in_channels,
          out_channels,
          kernel_size=kernel_size,
          stride=stride,
          padding=padding,
          dilation=dilation,
          groups=groups,
          deformable_groups=deformable_groups,
          bias=bias)
        self.with_modulated_dcn = with_modulated_dcn
        self.kernel_size = kernel_size
        self.stride = stride
        self.padding = padding
        self.dilation = dilation
        self.offset_base_channels = offset_base_channels

    def forward(self, x):
        assert x.numel() > 0, 'only non-empty tensors are supported'
        if x.numel() > 0:
            if not self.with_modulated_dcn:
                offset = self.offset(x)
                x = self.conv(x, offset)
            else:
                offset_mask = self.offset(x)
                split_point = self.offset_base_channels * 2
                offset = offset_mask[:, :split_point, :, :]
                mask = offset_mask[:, split_point:, :, :].sigmoid()
                x = self.conv(x, offset, mask)
            return x
# okay decompiling ./fcos_core/layers/misc.pyc


import enum, urwid, argparse
import configparser, io
import pyftdi.ftdi
import pyftdi.usbtools
import pyftdi.eeprom
import pyftdi

# enum of usb module hex dumps to base configs on
# this is a temporary solution until pyftdi supports more properties
class ModuleBaseRAW(enum.Enum):
    FACTORY = (enum.auto(), "0808030410600007a04b080011119a0aa426ca1200000000560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0346005400440049002603460054003200320033003200480020004d0069006e0069004d006f00640075006c0065001203460054003400510046004e0044004e00020300000000000000000000000000000000000000000000000000000000000000003961")
    TUNER = (enum.auto(), "0001030410600007c000080033339a0aa424c81200000000560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a034600540044004900240355005300420020003c002d003e0020004e0049004d002000740075006e006500720012034400410034004d00470045005a004b000000000000000000000000000000000000000000000000000000000000000000000000004a42")

    def __init__(self, enum, raw):
        self._raw = raw

    @property
    def baselineIni(self):
        config = configparser.ConfigParser()
        config.add_section('raw')
        config.set('raw','@00',self._raw)
        configIO = io.StringIO()
        config.write(configIO)
        configIO.seek(0)
        return configIO

# enum of known module configs
class ModuleConfigs(enum.Enum):
    UNKNOWN = (enum.auto(), frozenset(), False, False, False, None)
    GENERAL = (enum.auto(), frozenset([
        ('channel_a_type', 'UART'),
        ('group_0_drive', 16),
        ('group_0_schmitt', False),
        ('group_0_slow_slew', False),
        ('group_1_drive', 16),
        ('group_1_schmitt', False),
        ('group_1_slow_slew', False),
        ('group_2_drive', 16),
        ('group_2_schmitt', False),
        ('group_2_slow_slew', False),
        ('group_3_drive', 16),
        ('group_3_schmitt', False),
        ('group_3_slow_slew', False),
        ('has_serial', True),
        ('in_isochronous', False),
        ('out_isochronous', False),
        ('product_id', 24592),
        ('suspend_dbus7', pyftdi.eeprom.FtdiEeprom.CFG1(0)),
        ('suspend_pull_down', False),
        ('type', 1792),
        ('vendor_id', 1027)
    ]), False, False, False, None)
    TUNER = (enum.auto(), GENERAL[1] | frozenset([
        ('channel_a_driver', 'D2XX'),
        ('channel_b_driver', 'D2XX'),
        ('channel_b_type', 'FIFO'),
        ('remote_wakeup', False),
        ('self_powered', True),
        ('power_max', 0)
    ]), False, False, False, ModuleBaseRAW.TUNER)
    TUNER256 = (enum.auto(), TUNER[1] | frozenset([
        ('chip', 86),
    ]), False, False, False, ModuleBaseRAW.TUNER)
    FACTORY = (enum.auto(), GENERAL[1] | frozenset([
        ('channel_a_driver', 'VCP'),
        ('channel_b_driver', 'VCP'),
        ('channel_b_type', 'UART'),
        ('chip', 86),
        ('power_max', 150),
        ('product', 'FT2232H MiniModule'),
        ('remote_wakeup', True),
        ('self_powered', False),
    ]), True, False, True, ModuleBaseRAW.FACTORY)
    MINITIOUNER = (enum.auto(), TUNER256[1] | frozenset([
        ('product', 'USB <-> NIM tuner'),
    ]), True, True, True, ModuleBaseRAW.TUNER)
    MINITIOUNEREXPRESS = (enum.auto(), TUNER[1] | frozenset([
        ('chip', 70),
        ('product', 'MiniTiouner-Express'),
    ]), True, False, False, ModuleBaseRAW.TUNER)
    MINITIOUNER_S = (enum.auto(), TUNER256[1] | frozenset([
        ('product', 'MiniTiouner'),
    ]), True, False, False, ModuleBaseRAW.TUNER)
    MINITIOUNER_PRO_TS1 = (enum.auto(), TUNER256[1] | frozenset([
        ('product', 'MiniTiouner_Pro_TS1'),
    ]), True, False, False, ModuleBaseRAW.TUNER)
    MINITIOUNER_PRO_TS2 = (enum.auto(), TUNER256[1] | frozenset([
        ('product', 'MiniTiouner_Pro_TS2'),
    ]), True, False, False, ModuleBaseRAW.TUNER)
    KNUCKER = (enum.auto(), TUNER256[1] | frozenset([
        ('product', 'CombiTuner-Express'),
    ]), True, True, True, ModuleBaseRAW.TUNER)

    def __init__(self, enum, configset, canIdentify, flashableConfig, flashableDevice, rawBaseline):
        self._configset = configset
        self._canIdentify = canIdentify
        self._flashableConfig = flashableConfig
        self._flashableDevice = flashableDevice
        self._rawBaseline = rawBaseline

    @property
    def configSet(self):
        return self._configset

    @property
    def canIdentify(self):
        return self._canIdentify

    @property
    def flashableConfig(self):
        return self._flashableConfig

    @property
    def flashableDevice(self):
        return self._flashableDevice

    @property
    def rawBaseline(self):
        return self._rawBaseline

# urwid check box but also holds a device id and a current config
class ModuleCheckBox(urwid.CheckBox):
    def __init__(self, label, device, config):
        self._device = device
        self._config = config
        super().__init__(label)

    @property
    def device(self):
        return self._device

    @property
    def config(self):
        return self._config

# urwid text box for displaying unidentified modules but is compatible with the check box
class ModuleTextBox(urwid.Text):
    def __init__(self, label, device, config):
        self._device = device
        self._config = config
        super().__init__(label)

    @property
    def device(self):
        return self._device

    @property
    def config(self):
        return self._config

# urwid module that pops up a simple message box over the top level widget
class MessagePopUp(urwid.Overlay):
    def __init__(self, loop, messageText = "", messageTitle = None):
        self.loop = loop
        okButton = urwid.Button('OK', self.handleOk)

        self.contentlist = urwid.Pile([])
        if messageTitle is not None:
            self.contentlist.contents.append((urwid.Text(('heading', messageTitle), align="center"), self.contentlist.options('pack')))
        self.contentlist.contents.append((urwid.Text(messageText), self.contentlist.options('pack')))
        self.buttonBox = urwid.GridFlow([okButton], 7, 1, 1, 'center')
        self.contentlist.contents.append((self.buttonBox, self.contentlist.options('pack')))
        self.contentlist.set_focus(len(self.contentlist.contents)-1)
        self.mainBox = urwid.LineBox(self.contentlist)

    def open(self, button=None):
        self.buttonBox.focus_position = 0
        urwid.Overlay.__init__(self, self.mainBox, self.loop.widget,
				align='center', width=('relative', 50),
				valign='middle', height='pack',
				min_width=20, min_height=5)
        self.loop.widget = self

    def close(self, button=None):
        self.loop.widget = self.bottom_w

    def handleOk(self, button):
        self.close(None)

# urwid module that pops up a simple confirmation box over the top level widget
class ConfirmPopUp(urwid.Overlay):
    def __init__(self, loop, messageText = "", messageTitle = None, callback = None, userData = None):
        self.loop = loop
        self.callback = callback
        self.userData = userData
        yesButton = urwid.Button('Yes', self.handleYes)
        noButton = urwid.Button('No', self.handleNo)

        self.contentlist = urwid.Pile([])
        if messageTitle is not None:
            self.contentlist.contents.append((urwid.Text(('heading', messageTitle), align="center"), self.contentlist.options('pack')))
        self.contentlist.contents.append((urwid.Text(messageText), self.contentlist.options('pack')))
        self.buttonBox = urwid.GridFlow([noButton, yesButton], 7, 10, 1, 'center')
        self.contentlist.contents.append((self.buttonBox, self.contentlist.options('pack')))
        self.contentlist.set_focus(len(self.contentlist.contents)-1)
        self.mainBox = urwid.LineBox(self.contentlist)

    def open(self, button=None):
        self.buttonBox.focus_position = 0
        urwid.Overlay.__init__(self, self.mainBox, self.loop.widget,
				align='center', width=('relative', 50),
				valign='middle', height='pack',
				min_width=20, min_height=5)
        self.loop.widget = self

    def close(self, button=None):
        self.loop.widget = self.bottom_w

    def handleYes(self, button):
        self.close(None)
        if self.callback is not None:
            self.callback(self.loop, self.userData)

    def handleNo(self, button):
        self.close(None)

# urwid module that pops up a progress bar box over the top level widget
class ProgressPopUp(urwid.Overlay):
    def __init__(self, loop, callback = None, userData = None):
        self.loop = loop
        self.callback = callback
        self.userData = userData
        self.progressBar = urwid.ProgressBar('ProgressBack', 'ProgressFore')
        self.contentlist = urwid.Pile([])
        self.mainBox = urwid.LineBox(self.contentlist)

    def open(self, title, doneValue):
        self.progressBar.done = doneValue
        self.contentlist.contents[:] = [(urwid.Text(('heading',title), align='center'), self.contentlist.options('pack')), (self.progressBar, self.contentlist.options('pack'))]
        urwid.Overlay.__init__(self, self.mainBox, self.loop.widget,
				align='center', width=('relative', 50),
				valign='middle', height='pack',
				min_width=20, min_height=5)
        self.loop.widget = self

    def close(self, button):
        self.loop.widget = self.bottom_w
        if self.callback is not None:
            self.callback(button, self.userData)

    def setProgress(self, newProgress):
        self.progressBar.set_completion(newProgress)

    def setDone(self):
        self.progressBar.set_completion(self.progressBar.done)
        closeButton = urwid.Button('Close', self.close)
        buttonBox = urwid.GridFlow([closeButton], 9, 1, 1, 'center')
        self.contentlist.contents.append((buttonBox, self.contentlist.options('pack')))
        self.contentlist.set_focus(len(self.contentlist.contents)-1)

    def draw(self):
        self.loop.draw_screen()

# medium level interface to pyftdi
class ModulesInterface(object):
    def __init__(self, dryRun=True):
        self.dryRun = dryRun

    # returns dict of mapping between all found device identifiers and sets of tuples of config name/values pairs
    def fetchDevices(self):
        pyftdi.usbtools.UsbTools.flush_cache()
        foundDevices = pyftdi.ftdi.Ftdi.list_devices("ftdi://ftdi:2232h/1")
        devices = {}
        for deviceDesc in foundDevices:
            device = pyftdi.usbtools.UsbTools.get_device(deviceDesc[0])
            eeprom = pyftdi.eeprom.FtdiEeprom()
            eeprom.open(device)
            signature = []
            for prop in sorted(list(eeprom.properties)+['product']):
                signature.append((prop,getattr(eeprom, prop)))
            devices[deviceDesc]=frozenset(signature)
            eeprom.close()
            pyftdi.usbtools.UsbTools.release_device(device)
        return devices

    # programs a device with a config out of the config enum
    def programModule(self, deviceDesc, config):
        device = pyftdi.usbtools.UsbTools.get_device(deviceDesc[0])
        eeprom = pyftdi.eeprom.FtdiEeprom()
        eeprom.open(device)
        # hack #1 load closeish eeprom image as not all properties are configrarable
        configIO = config.rawBaseline.baselineIni
        eeprom.load_config(configIO, 'raw')
        eeprom.sync()
        varStringMap ={
                'manufacturer': eeprom.set_manufacturer_name,
                'product': eeprom.set_product_name,
                'serial': eeprom.set_serial_number
                }
        toRetry = {}
        for (prop, value) in config.configSet:
            if getattr(eeprom, prop) != value:
                if prop in varStringMap:
                    varStringMap[prop](value)
                else:
                    try:
                        eeprom.set_property(prop, value)
                    except NotImplementedError:
                        toRetry[prop]= value
        # hack #2 the data doesn't get reparsed when loaded as a raw, retry failed properties at the end as something is likely to have caused it to get repared in the meantime
        eeprom.sync()
        for prop, value in toRetry.items():
            if getattr(eeprom, prop) != value:
                eeprom.set_property(prop, value)
        result = eeprom.commit(self.dryRun)
        eeprom.close()
        return result

# UI widget that displays a list of found modules and manages their selection
class ModuleListWidget(urwid.WidgetWrap):
    def __init__(self, ftdiInterface, attemptIdentUnknown):
        self.ftdiInterface = ftdiInterface
        self.attemptIdentUnknown = attemptIdentUnknown
        self.walker = urwid.SimpleListWalker([])
        self.updateModuleCheckBoxes(None)
        urwid.WidgetWrap.__init__(self, urwid.ListBox(self.walker))

    def updateModuleCheckBoxes(self, button, userData = None):
        devices = self.ftdiInterface.fetchDevices()
        buttons = []
        for device in devices:
            nameStr = str(device[0].sn)+"("+str(device[0].bus)+":"+str(device[0].address)+")"
            configType = ModuleConfigs.UNKNOWN
            for config in ModuleConfigs:
                if devices[device] == config.configSet and config.canIdentify:
                    configType = config
            checkBox = None
            for oldCheckBox in self.walker.contents:
                if oldCheckBox.device == device and oldCheckBox.config == configType:
                    checkBox = oldCheckBox
                    break
            if checkBox is None:
                if configType is not ModuleConfigs.UNKNOWN and configType.flashableDevice:
                    checkBox = ModuleCheckBox(nameStr+":"+configType.name, device, configType)
                else:
                    if self.attemptIdentUnknown and configType is ModuleConfigs.UNKNOWN:
                        # attempt partial idenfication of module
                        # find stored config with largest intersection with this module
                        identString = "partial("
                        closestModule = ModuleConfigs.UNKNOWN
                        closestInCommon = set()
                        for config in ModuleConfigs:
                            inCommon = devices[device] & config.configSet
                            if len(inCommon) > len(closestInCommon):
                                closestInCommon = inCommon
                                closestModule = config
                        identString += closestModule.name+"+{"
                        settingPairStrings = []
                        # print difference between module and closest config
                        for settingPair in (devices[device]-closestInCommon):
                            settingPairStrings.append(settingPair[0]+":"+repr(settingPair[1]))
                        identString += ','.join(settingPairStrings)
                        identString += "})"
                    else:
                        identString = configType.name
                    checkBox = ModuleTextBox(nameStr+":"+identString, device, configType)

            buttons.append(checkBox)
        self.walker[:]=buttons

    def selectAll(self, button, config=None):
        for checkBox in self.walker.contents:
            if isinstance(checkBox, urwid.CheckBox):
                if config is None or checkBox.config is config:
                    checkBox.set_state(True)

    def deSelectAll(self, button):
        for checkBox in self.walker.contents:
            if isinstance(checkBox, urwid.CheckBox):
                checkBox.set_state(False)

    def invertSelection(self, button):
        for checkBox in self.walker.contents:
            if isinstance(checkBox, urwid.CheckBox):
                checkBox.toggle_state()

    def getSelectedDevices(self, config):
        devices = []
        for checkBox in self.walker.contents:
            if isinstance(checkBox, urwid.CheckBox) and checkBox.get_state() and checkBox.config != config:
                devices.append(checkBox.device)
        return devices

# UI widget that displays the command menu
class CommandListWidget(urwid.WidgetWrap):
    def __init__(self, ftdiInterface, moduleList, loop, allowAllConfigs, dryRun):
        self.ftdiInterface = ftdiInterface
        self.moduleList = moduleList
        self.loop = loop
        self.dryRun = dryRun
        if dryRun:
            dryRunText = "Dry Run "
        else:
            dryRunText = ""
        walker = urwid.SimpleListWalker([])
        for config in ModuleConfigs:
            if config.canIdentify and config.flashableDevice:
                walker.contents.append(urwid.Button('Select all '+config.name, on_press=self.moduleList.selectAll, user_data=config))

        selectGroupButtons = [
                urwid.Button('Select all', on_press=self.moduleList.selectAll),
                urwid.Button('Deselect all', on_press=self.moduleList.deSelectAll),
                urwid.Button('Invert selection', on_press=self.moduleList.invertSelection),
                urwid.Divider('-'),
                ]
        walker.contents.extend(selectGroupButtons)

        for config in ModuleConfigs:
            if (config.flashableConfig or (allowAllConfigs and config.canIdentify)) and config.flashableDevice:
                walker.contents.append(urwid.Button(dryRunText+'Program as '+config.name, on_press=self.scanAndConfirmProgram, user_data=config ))

        genButtons = [
                urwid.Divider('-'),
                urwid.Button('Rescan', on_press=self.moduleList.updateModuleCheckBoxes),
                urwid.Button('Quit', on_press=self.quitApp)
            ]
        walker.contents.extend(genButtons)
        urwid.WidgetWrap.__init__(self, urwid.ListBox(walker))

    def quitApp(self, button=None):
        raise urwid.ExitMainLoop()

    def programSelected(self, loop, userData):
        (config, devices) = userData
        programmingPopup = ProgressPopUp(self.loop, self.moduleList.updateModuleCheckBoxes)
        boxTitle = ""
        if self.dryRun:
            boxTitle += "DRY RUN: NOT "
        boxTitle += "Programming {0} modules with {1}".format(len(devices),config.name)
        programmingPopup.open(boxTitle, len(devices))
        programmingPopup.draw()
        programmingPopup.setProgress(0)
        for device in devices:
            self.ftdiInterface.programModule(device, config)
            programmingPopup.setProgress(devices.index(device)+1)
            programmingPopup.draw()
        programmingPopup.setDone()

    def scanAndConfirmProgram(self, button, config):
        devices = self.moduleList.getSelectedDevices(config)
        if len(devices)<1:
            errorPopup = MessagePopUp(self.loop, "Either no modules are selected or they are all already programmed as {0}.\nPlease select some modules and try again.".format(config.name), "None Selected?")
            errorPopup.open()
        else:
            confirmPopup = ConfirmPopUp(self.loop, "Are you sure you want to flash {0} modules with {1}?".format(len(devices), config.name), "Program Module?", self.programSelected, (config, devices))
            confirmPopup.open()

class TunerFTDIConfigUtil(object):
    def __init__(self, dryRun=True, allowAllConfigs=False, attemptIdentUnknown=False):
        colsBox = urwid.Columns([], 1)
        titlebox = urwid.AttrMap(urwid.Text('Tuner FTDI module configuration utility', align='center'), 'title')
        footerbox = urwid.AttrMap(urwid.Text(["To navigate use the keyboard or the mouse on compatible consoles"]), 'footer')

        main = urwid.Frame(colsBox, titlebox, footerbox)
        background = urwid.AttrMap(urwid.SolidFill(), 'bg')
        top = urwid.Overlay(main, background,
            align='center', width=('relative', 80),
            valign='middle', height=('relative', 80),
            min_width=20, min_height=5)

        # urwid colour pallette
        pallette = [
            ('ProgressBack', 'white', 'dark blue'),
            ('ProgressFore', 'white', 'dark red'),
            ('bg', 'black', 'dark blue'),
            ('title', 'white, bold', 'dark red'),
            ('heading', 'white, bold', 'default'),
            ('footer', 'black', 'light gray'),
            ('highlight', 'light blue', 'light gray'),
            ('reversed', 'standout', '')
        ]

        self.loop = urwid.MainLoop(top, palette=pallette)

        ftdiInterface = ModulesInterface(dryRun)
        moduleList = ModuleListWidget(ftdiInterface, attemptIdentUnknown)

        commandList = CommandListWidget(ftdiInterface, moduleList, self.loop, allowAllConfigs, dryRun)

        colsBox.contents.append((urwid.LineBox(commandList, title="Commands"), colsBox.options()))
        colsBox.contents.append((urwid.LineBox(moduleList, title="Modules"), colsBox.options()))

    def run(self):
        self.loop.run()

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Tuner FTDI module configuration utility")
    parser.add_argument("-u", "--update", action="store_true", help="Enable actual updates")
    parser.add_argument("-x", "--extra-configs", action="store_true", help="Allow flashing of all identifyable configs")
    parser.add_argument("-i", "--attempt-ident-unknown", action="store_true", help="Attempt to partially identify unknown modules")
    args = parser.parse_args()
    ftdiUI = TunerFTDIConfigUtil(not args.update, args.extra_configs, args.attempt_ident_unknown)
    ftdiUI.run()


import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--attn_type', type=str, default='channelwise')
args = parser.parse_args()

import pandas as pd
import numpy as np
from utils.paths import path_expt, path_expt_raw

n_all = len(list(path_expt_raw.glob('results_task*.txt')))
n_spatial = len(list(path_expt_raw.glob('results_task*_spatial.txt')))
n_tasks = n_spatial if args.attn_type == 'spatial' else n_all - n_spatial
training, weights, results = [], [], []

for i in range(n_tasks):
    task_id = f'task{i:04}_spatial' if args.attn_type == 'spatial' else f'task{i:04}'
    training_i = pd.read_csv(path_expt_raw/f'training_{task_id}.csv')
    weights_i = np.loadtxt(path_expt_raw/f'weights_{task_id}.txt')
    results_i = np.loadtxt(path_expt_raw/f'results_{task_id}.txt')
    training_i.insert(0, 'task', i)
    training.append(training_i)
    weights.append(np.reshape(weights_i, (1, -1)))
    results.append(np.reshape(results_i, (1, -1)))

training = pd.concat(training)
weights = np.vstack(weights)
results = np.vstack(results)
extension = '_spatial' if args.attn_type == 'spatial' else ''
training.to_csv(path_expt/f'training{extension}.csv', index=False)
np.savetxt(path_expt/f'weights{extension}.txt', weights, fmt='%.18f')
np.savetxt(path_expt/f'results{extension}.txt', results, fmt='%.18f')


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft, fftfreq,fftshift,ifft,ifftshift
from scipy.signal import blackman
from scipy.signal import kaiserord, lfilter, firwin, freqz
from scipy import signal
from scipy.signal import butter, lfilter
from scipy.signal import fftconvolve
from sklearn.linear_model import LinearRegression
#
plt.rc('font', family='serif')
plt.rc('font', serif='Arial')
plt.rcParams['axes.linewidth'] = 2
fonts                          = 16

# Data from t1. F21 attenuation plots.
noF21           = [369,399,396,457]
thicknesses     = [0,1.3,7,11.3] # mm
thin            = [295,313,300,380]
# thin_control    = [369,399,396,457 ]
thin_control = noF21
medium          = [270, 294, 277, 307]
# medium_control  = [427, 423, 412, 421]
medium_control  = thin_control
thick           = [237, 243, 245, 231]
# thick_control   = [338, 350, 360, 346]
thick_control = thin_control

thin_attenuation   = np.array(thin)/np.array(thin_control)
medium_attenuation = np.array(medium)/np.array(medium_control)
thick_attenuation  = np.array(thick)/np.array(thick_control)
nothing_attenuation = np.array(noF21)/np.mean(noF21)
# np.array([1,1,1,1])

F21 = np.array([noF21,thin,medium,thick])
F21_attenuation = np.array([nothing_attenuation,thin_attenuation,medium_attenuation,thick_attenuation])
print (F21)
F21_mean = np.mean(F21,1)
F21_attenuation_mean = 1 - np.median(F21_attenuation,1)
F21_std = np.mean(F21,1)

x = np.array(thicknesses)
y = F21_mean
# find line of best fit
a, b = np.polyfit(x, y, 1)
print ('pressure line:gradient and add',a,b)

fig = plt.figure()
ax = fig.add_subplot(111)
plt.plot(thicknesses,F21,'.k')
plt.plot(x, a*x+b,'k')
# ax.set_ylabel('Pressure(Pa)')
# ax.set_xlabel('Thickness(mm)')
plt.yticks(fontsize=16)
plt.xticks(fontsize=16)
plt.tight_layout()
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
plt.savefig("F21_pressure_vs_thickness.png", bbox_inches="tight")
plt.show()

x = np.array(thicknesses)
y = 100*F21_attenuation_mean
#find line of best fit
a, b = np.polyfit(x, y, 1)
print ('attenuation line:gradient and add',a,b)

fig = plt.figure()
ax = fig.add_subplot(111)
plt.plot(thicknesses,100*(1 - F21_attenuation),'.k')
plt.plot(x, a*x+b,'k')
# ax.set_ylabel('Pressure(Pa)')
# ax.set_xlabel('Thickness(mm)')
plt.yticks(fontsize=16)
plt.xticks(fontsize=16)
ax.set_ylim([0,1.1*100*np.max(1 - F21_attenuation)])
plt.tight_layout()
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
plt.savefig("F21_attenuation_vs_thickness.png", bbox_inches="tight")
plt.show()


import gym
from Model import *
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

class Agent:
    def __init__(self,env_name='CartPole-v0'):
        self.env = gym.make(env_name)
        self.batch_size = 32
        num_states = self.env.observation_space.shape[0]
        num_actions = self.env.action_space.n

        self.agent = DQNAgent(num_states, num_actions)

    def train(self):
        # initialize gym environment and the agent
        episodes = 1000
        # Iterate the game
        for e in range(episodes):
            # reset state in the beginning of each game
            state = self.env.reset()
            state = np.reshape(state, [1, 4])
            rewards = 0
            # time_t represents each frame of the game
            # Our goal is to keep the pole upright as long as possible until score of 500
            # the more time_t the more score
            for time_t in range(500):

                # turn this on if you want to render
                # env.render()
                # Decide action
                action = self.agent.choose(state)
                # Advance the game to the next frame based on the action.
                # Reward is 1 for every frame the pole survived
                next_state, reward, done, _ = self.env.step(action)
                rewards += reward

                next_state = np.reshape(next_state, [1, 4])
                # Remember the previous state, action, reward, and done
                self.agent.transition(state, action, rewards, next_state, done)
                # make next_state the new current state for the next frame.
                state = next_state

                # done becomes True when the game ends
                # ex) The agent drops the pole

                if done:
                    # print the score and break out of the loop
                    print("episode: {}/{}, score: {}"
                          .format(e, episodes, time_t))
                    break
                if len(self.agent.memory) > self.batch_size:
                    self.agent.replay(self.batch_size)

            # train the agent with the experience of the episode
            print("episode: {}/{}, score: {}"
                  .format(e, episodes, rewards))

        self.final_position(self.agent.actions)
        self.agent.save()
    def test(self):
        # initialize gym environment and the agent
        self.agent.load()
        for i in range(10):
            state = self.env.reset()

            state = np.reshape(state, [1, 4])
            for j in range(500):
                self.env.render()
                action = self.agent.choose(state)
                # Advance the game to the next frame based on the action.
                # Reward is 1 for every frame the pole survived
                next_state, reward, done, _ = self.env.step(action)
                next_state = np.reshape(next_state, [1, 4])

                state = next_state

                if done:
                    # print the score and break out of the loop
                    print("game over")
                    break

    def final_position(self,positions):
        import time
        localtime = time.localtime()
        timeString = time.strftime("%m%d%H", localtime)
        timeString = str(timeString) + '.jpg'

        plt.figure(3, figsize=[10, 5])
        p = pd.Series(positions)
        ma = p.rolling(10).mean()
        plt.plot(p, alpha=0.8)
        plt.plot(ma)
        plt.xlabel('Epsiode')
        plt.ylabel('Poistion')
        plt.title('Final Position - Modified.jpg')
        plt.savefig(timeString)
        plt.show()