__author__ = 'wanyama'
from django.conf.urls import patterns,include,url
from rest_framework.urlpatterns import format_suffix_patterns
from api import views

urlpatterns = patterns('',
                       url(r'^donors/$',views.DonorList.as_view()),
                       url(r'^users/$',views.UserList.as_view()),
                       url(r'^donors/(?P<pk>[0-9]+)/$',views.DonorDetail.as_view()),
                       url(r'^users/(?P<pk>[0-9]+)/$',views.UserDetail.as_view()),

)


from .models import EdigeoCommune
from .models import EdigeoSection
from .models import EdigeoLieuDit
from .models import EdigeoParcelle
from .models import EdigeoSubdFisc
from .models import EdigeoBati
from .models import EdigeoBorneParcel
from django.contrib import admin

@admin.register(EdigeoCommune)
class EdigeoCommuneAdmin(admin.ModelAdmin):
    list_display = (
        'idu',
        'gb_ident'
    )

@admin.register(EdigeoSection)
class EdigeoSectionAdmin(admin.ModelAdmin):
    search_fields = ['idu', 'tex']
    list_display = (
        'idu',
        'tex'
    )

@admin.register(EdigeoLieuDit)
class EdigeoLieuDitAdmin(admin.ModelAdmin):
    search_fields = ['tex']
    search_fields = ['idu', 'tex']
    list_display = (
        'tex',
        'md5'
    )

@admin.register(EdigeoParcelle)
class EdigeoParcelleAdmin(admin.ModelAdmin):
    list_display = (
        'idu',
        'supf',
        'tex',
        'section_idu',
        'commune_idu'
    )

@admin.register(EdigeoSubdFisc)
class EdigeoSubdFiscAdmin(admin.ModelAdmin):
    list_display = (
        'tex',
        'md5'
    )


from flet import AlertDialog
from flet import colors
from flet import Column
from flet import Dropdown
from flet import dropdown
from flet import InputBorder
from flet import Page
from flet import Ref
from flet import Text
from flet import TextStyle
from flet import TextThemeStyle
from flet import UserControl

from utils import Preference

class MaterialYouCustomizationDialog(UserControl):
    def __init__(self, page: Page):
        super().__init__()
        self.pref = Preference(page)
        self.page = page
        self.dropdown = Ref[Dropdown]()
        self.COLORS = sorted(
            [
                "RED",
                "PINK",
                "PURPLE",
                "INDIGO",
                "BLUE",
                "CYAN",
                "TEAL",
                "GREEN",
                "LIME",
                "YELLOW",
                "AMBER",
                "ORANGE",
                "BROWN",
            ]
        )
        self.dialog = AlertDialog(
            title=Text(
                value="Material accent",
                font_family="sans",
                style=TextThemeStyle.TITLE_LARGE,
            ),
            content=Column(
                controls=[
                    Dropdown(
                        ref=self.dropdown,
                        options=[
                            *map(
                                lambda color: dropdown.Option(key=color),
                                self.COLORS,
                            )
                        ],
                        text_style=TextStyle(
                            font_family="sans", color=colors.SECONDARY
                        ),
                        dense=True,
                        filled=True,
                        border=InputBorder.NONE,
                        on_change=lambda event: self._theme(),
                    )
                ],
                tight=True
            ),
        )

    def _theme(self):
        self.pref.update(k="accent", v=self.dropdown.current.value.lower())

    def open(self):
        self.page.dialog = self.dialog
        self.dialog.open = True
        self.page.update()

    def build(self):
        return self.dialog


from django.forms import ModelForm
from user.models import User

# https://docs.djangoproject.com/en/3.2/topics/forms/modelforms/#overriding-the-default-fields
class UserForm(ModelForm):
    class Meta:
        model = User
        fields = ['username', 'email']
        labels = {
            'username': 'Username',
            'email': 'E-mail',
        }


from websocket import WebSocketApp
import ssl, json, concurrent.futures
import ssl, json
from threading import Thread

from time import sleep, time
from random import randrange

from .args import parse_args

PACKET = {
    "ts": 1501665144,
    "idx": 0,
    "data": "1" * 60
}

PACKET_SIZE = 100

def raw_data_json(idx):
    data = PACKET.copy()
    data["idx"] = idx
    data["ts"] = int(time() * 1000)
    return json.dumps(data)

def raw_data(idx):
    t = int(time() * 1000).to_bytes(8, 'big')
    return t + idx.to_bytes(1, 'big') * (PACKET_SIZE - len(t))

def on_open(ws):
    # Wait for a while to simulate box will not connect at same time.
    sleep(randrange(0, 1000) / 1000)
    count = 0
    # Endless
    while True:
        idx = ws.idx

        if ws.is_json:
            d = raw_data_json(idx)
            ws.send(d)
        else:
            d = raw_data(idx)
            ws.sock.send_binary(d)

        ws.idx = idx + 1 if (idx + 1) <= 0xFF else 0

        if ws.has_log:
            print(ws.display_name, "sent with idx ", idx)

        if ws.amount != 0:
            count += 1
            if count >= ws.amount:
                print(ws.display_name, "stopped, reach amount:", ws.amount)
                ws.close()
                break

        sleep(ws.inv)

def on_message(ws, message):
    print(ws.name, message)

def on_error(ws, error):
    print(error)

def run_boy(name, url, inv, is_json, has_log, amount):
    ws = WebSocketApp(url)
    ws.on_open = on_open
    ws.on_message = on_message
    ws.on_error = on_error
    ws.display_name = name
    ws.idx = 0
    ws.inv = inv
    ws.is_json = is_json
    ws.has_log = has_log
    ws.amount = amount

    ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})

def main():
    cmds = parse_args()
    num = cmds.clients
    url = "{0}://{1}".format("ws" if cmds.ws else 'wss', cmds.url)

    with concurrent.futures.ThreadPoolExecutor(max_workers=num) as executor:
        for idx in range(0, num):
            tt = executor.submit(run_boy, "Box{0}".format(idx), url, cmds.t, cmds.json, cmds.log, cmds.a)

if __name__ == '__main__':
    main()


from airflow.models import DAG
from airflow.utils.dates import days_ago
from airflow.operators.empty import EmptyOperator
from airflow.decorators import task

with DAG(
  'teste_python',
  start_date=days_ago(1),
  schedule_interval='@daily',
) as dag:

    tarefa_1 = EmptyOperator(task_id='tarefa_1')

    @task(task_id = 'task_cumprimentos')
    def cumprimentos(x):
        data_interval_end = '{{data_interval_end.strftime("%Y-%m-%d")}}'
        print(f'Boas-vindas ao Airflow!')
        print(f'{data_interval_end}')

    tarefa_2 = cumprimentos(5)

    tarefa_1 >> tarefa_2


import base64
import functools
from dataclasses import dataclass
from pathlib import Path

import cv2

@dataclass(frozen=True, slots=True)
class Video:
    path: Path

    def _capture(self) -> cv2.VideoCapture:
        capture = cv2.VideoCapture(str(self.path))

        if not capture.isOpened():
            raise

        return capture

    @property
    def frame_count(self) -> int:
        return int(self._capture().get(cv2.CAP_PROP_FRAME_COUNT))

    @property
    def fps(self) -> float:
        return float(self._capture().get(cv2.CAP_PROP_FPS))

    @functools.cache
    def frame_as_base64(self, frame_num: int) -> str:
        capture = self._capture()

        ret = capture.set(cv2.CAP_PROP_POS_FRAMES, frame_num)

        if not ret:
            raise

        ret, frame = capture.read()

        if not ret:
            raise

        ret, buffer = cv2.imencode(".jpg", frame)

        if not ret:
            raise

        return base64.b64encode(buffer.tobytes()).decode("utf-8")


import matplotlib
import matplotlib.style

def set_mpl_params():
    params = {
        'image.origin': 'lower',
        'image.interpolation': 'none',
        'image.cmap': 'viridis',
        'axes.grid': False,
        'savefig.dpi': 150,  # to adjust notebook inline plot size
        'axes.labelsize': 12,  # fontsize for x and y labels (was 10)
        'axes.titlesize': 12,
        'font.size': 12,  # was 10
        'legend.fontsize': 6,  # was 10
        'xtick.labelsize': 12,
        'ytick.labelsize': 12,
        'text.usetex': False,
        'figure.figsize': [5, 4],
        'font.family': 'serif',
    }
    matplotlib.rcParams.update(params)
    matplotlib.rcParams['pdf.fonttype'] = 42

def set_mpl_params_mod():
    params = {
        'image.origin': 'lower',
        'image.interpolation': 'none',
        'image.cmap': 'viridis',
        'axes.linewidth': 0.1,
        'axes.grid': False,
        'savefig.dpi': 300,  # to adjust notebook inline plot size
        'axes.labelsize': 5,  # fontsize for x and y labels (was 10)
        'axes.titlesize': 5,
        'font.size': 5,  # was 10
        'legend.fontsize': 6,  # was 10
        'xtick.labelsize': 5,
        'ytick.labelsize': 5,
        'text.usetex': False,
        'figure.figsize': [5, 4],
        'font.family': 'sans-serif',
        'font.sans-serif': 'Arial',
        'font.weight': 'bold',
        'xtick.major.width': 0.1,
        'xtick.minor.width': 0.1,
        'ytick.major.width': 0.1,
        'ytick.minor.width': 0.1
    }
    matplotlib.rcParams.update(params)
    matplotlib.rcParams['pdf.fonttype'] = 42

def set_mpl_defaults():
    matplotlib.style.use('default')


from socket_class import SOCKET_SIMPLE_TCP
from Crypto.Random import get_random_bytes
import funciones_aes
import json

def cifraPaso3(emisor, receptor, KET, E, t_ne):
    """ Cifra el mensaje del paso 1, y devuelve el cifrado """
    # Codifica el contenido (los campos binarios en una cadena) y contruyo el mensaje JSON
    mensaje_ET = [E, t_ne.hex()]
    json_ET = json.dumps(mensaje_ET)
    print(emisor + " -> " + receptor + " (descifrado): " + json_ET)

    # Cifra los datos con AES GCM
    motor_AES = funciones_aes.iniciarAES_GCM(KET)
    cifrado_T, mac_T, nonce_T = funciones_aes.cifrarAES_GCM(motor_AES, json_ET.encode("utf-8"))

    return cifrado_T, mac_T, nonce_T

# Lee clave KBT
KAT = open("KAT.bin", "rb").read()

# Crear el socket de conexion con T (5550)
print("Creando conexion con T...")

socket = SOCKET_SIMPLE_TCP('127.0.0.1', 5550)
socket.conectar()

# Paso 3) A->T: KAT(Alice, Na) en AES-GCM
num = get_random_bytes(16)

cifrado, cifrado_mac, cifrado_nonce = cifraPaso3("A", "T", KAT, "Alice", num)

socket.enviar(cifrado)
socket.enviar(cifrado_mac)
socket.enviar(cifrado_nonce)

# Paso 4
print("Comienza el paso 4 en A")

cifrado_Ta1 = socket.recibir()
mac_Ta1 = socket.recibir()
nonce_Ta1 = socket.recibir()

descifrado_ETa1 = funciones_aes.descifrarAES_GCM(KAT, nonce_Ta1, cifrado_Ta1, mac_Ta1)
json_ETa1 = descifrado_ETa1.decode("utf-8", "ignore")

print("T " + " -> A (descifrado): " + json_ETa1)
mensaje_ETa1 = json.loads(json_ETa1)

K1, K2, t_nA1 = mensaje_ETa1
K1 = bytearray.fromhex(K1)
K2 = bytearray.fromhex(K2)
t_nA1 = bytearray.fromhex(t_nA1)

Tema 3 - Esquemas, Protocolos y Mecanismos de Soporte/Prácticas/Práctica 1/Entrega/p-a.py


#!/usr/bin/env python3
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(os.path.dirname(__file__))))  # fix import directory

from app import app, db
from app.models import *
# from datetime import datetime

def parse_file(filename):
    data = []
    with open(filename) as f:
        # The first line
        for line in f:
            keys = [ col.strip() for col in line.split('\t') ]
            break

        for line in f:
            cols = [ col.strip() for col in line.split('\t') ]
            if len(keys) != len(cols):
                continue
            data.append(dict(zip(keys, cols)))

    return data

def parse_json(filename):
    with open(filename, encoding='utf-8') as f:
        import json
        return json.load(f)

depts_code_map = dict()
classes_map = dict()
majors_map = dict()
titles_map = dict()
teachers_map = dict()
# We should load all existing courses, because SQLAlchemy does not support .merge on non-primary-key,
# and we want to preserve course ID (primary key) for each (cno, term) pair.
course_classes_map = dict()
course_terms_map = dict()
courses_map = dict()

def load_courses(insert=True):

    existing_teachers = Teacher.query.all()
    for t in existing_teachers:
        teachers_map[t.name] = t
    print('%d existing teachers loaded' % len(teachers_map))

    existing_courses = Course.query.all()
    for c in existing_courses:
        courses_map[str(c)] = c
    print('%d existing courses loaded' % len(courses_map))

    existing_course_classes = CourseClass.query.all()
    for c in existing_course_classes:
        course_classes_map[str(c)] = c
    print('%d existing course classes loaded' % len(course_classes_map))

    existing_course_terms = CourseTerm.query.all()
    for c in existing_course_terms:
        course_terms_map[str(c)] = c
    print('%d existing course terms loaded' % len(course_terms_map))

    new_teacher_count = 0
    new_course_count = 0
    new_term_count = 0
    new_class_count = 0

    json = parse_json(sys.argv[1])
    if 'dict' in json:
        json = json['dict']
    print('Data loaded with %d courses' % len(json))
    for index, c in enumerate(json):
        print("\r" + str(index * 100 / len(json)) + "%", end="")
        course = c
        term = c['semester']
        course_kcbh = dict(
            kcbh = course['kcode'],
            name = course['name'],
            credit = course['credit'],
            course_type = c['course_type'],
        )

        teachers = []
        teacher_names = []
        if 'teacher' in c:
            teachers = c['teacher'].split(',')

        for teachername in teachers:
            teacher_names.append(teachername)
            if teachername in teachers_map:
                t = teachers_map[teachername]
            else:
                t = Teacher()
            t.name = teachername

            if not t.name in teachers_map:
                db.session.add(t)
                teachers_map[t.name] = t
                new_teacher_count += 1

        course_key = c['name'] + '(' + ','.join(sorted(teacher_names)) + ')'
        if course_key in courses_map:
            course = courses_map[course_key]
        else:
            course_name = c['name']
            course = Course()
            course.name = course_name
            course.teachers = []
            db.session.add(course)

            for t in teacher_names:
                course.teachers.append(teachers_map[t])
                # print(teachers_map[t])
                # exit(0)

            db.session.add(course)
            courses_map[course_key] = course

            # course rate
            course_rate = CourseRate()
            course_rate.course = course
            db.session.add(course_rate)
            new_course_count+=1

        # course term
        term_key = course_key + '@' + term
        if term_key in course_terms_map:
            course_term = course_terms_map[term_key]
        else:
            course_term = CourseTerm()
            db.session.add(course_term)
            course_terms_map[term_key] = course_term
            new_term_count+=1

        # update course term info
        course_term.course = course
        course_term.term = term
        # change 123456.01 format to the original 12345601 format
        class_code = c['kcode'].upper()
        course_term.courseries = class_code
        course_term.class_numbers = class_code

        for key in course_kcbh:
            setattr(course_term, key, course_kcbh[key])
        # with open("log", 'a', encoding=" utf-8") as f:
            # f.writelines(c['teacher'] + " " + c['name'] + " " + c['credit'] + "\n")

        # course class
        unique_key = class_code + '@' + term
        if unique_key in course_classes_map:
            course_class = course_classes_map[unique_key]
            course_class.course = course # update course mapping
        else:
            course_class = CourseClass()
            db.session.add(course_class)
            course_classes_map[unique_key] = course_class
            new_class_count+=1

        # update course class info
        course_class.course = course
        course_class.term = term
        course_class.cno = class_code

    print('load complete, committing changes to database')
    db.session.commit()
    print('%d new teachers loaded' % new_teacher_count)
    print('%d new courses loaded' % new_course_count)
    print('%d new terms loaded' % new_term_count)
    print('%d new classes loaded' % new_class_count)

# we have merge now, do not drop existing data
db.create_all()
load_courses()


import datetime
import time
import sys
import os
root_path = os.path.join(os.getenv("HOME"), "vqa2019")
sys.path.append(os.path.join(root_path, "utils"))
sys.path.append(os.path.join(root_path, "models"))
import numpy as np

import torch
import torch.nn.functional as F
import torch.nn as nn
from torch.autograd import Variable
import torch.utils.data as data
import torchvision

# from models.coatt_model import mfh_coatt_Med
from models.coatt_model_emb import mfh_coatt_Med_emb
import config
from utils.data_provider import VQADataProvider, VQADataset
from utils.validate import evaluate
from predict_test import make_prediction, save_prediction

# load hyperparameters
opt = config.parse_opt()

# set GPU device
torch.cuda.set_device(opt.TRAIN_GPU_ID)

# checkpoints to save model
model_checkpoints_folder = os.path.join(root_path, "models/checkpoints")

# provide data
train_val_data 	= VQADataset(mode="train_val", opt=opt)
train_val_loader = data.DataLoader(dataset=train_val_data, shuffle=True, batch_size=opt.BATCH_SIZE, num_workers=1) # set shuffle to True to see what will happen

# load the question word embedding matrix
embedding_matrix = np.load(os.path.join(root_path, "embedding/embedding_matrix.npy"))
embedding_matrix_tensor = torch.from_numpy(embedding_matrix).float()

# initialize model
model = mfh_coatt_Med_emb(opt, embedding_matrix_tensor)
# use GPU
model.cuda()
optimizer = torch.optim.Adam(model.parameters(), lr=opt.INIT_LEARNING_RATE)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=opt.DECAY_RATE)
# loss function for classification
# use weights only when the results is better than current one (training on training data and validate on val data)
weights = [1.0]*opt.ALL_CLASS_NUM
weights[-1] = 0.2
weights = torch.FloatTensor(weights).cuda()
loss_func1 = nn.CrossEntropyLoss(weight=weights, reduction="none")
# loss_func1 = nn.CrossEntropyLoss(reduction="none")
# loss function for producting words of answer
loss_func2 = nn.KLDivLoss(reduction="none")

print("Training starts ...")
train_losses = []
for epoch in range(1, opt.EPOCH+1):
	model.train()
	print("\n%d epoch begins..." % epoch)
	scheduler.step()
	epoch_classification_loss = 0
	epoch_producing_loss = 0
	for step, (img_matrix, q_idx_ls, q_raw_category, q_category_gt, q_etm_topic, ans_raw_category, ans_category_gt, ans_vec_gt) in enumerate(train_val_loader):
		batch_size = img_matrix.size()[0]
		# move datat to GPU
		img_matrix = img_matrix.cuda().float()
		q_idx_ls = q_idx_ls.cuda().long()
		q_category_gt = q_category_gt.cuda().float()
		q_etm_topic = q_etm_topic.cuda().float()
		ans_raw_category = ans_raw_category.cuda().long()
		# ans_category_gt = ans_category_gt.cuda().long()
		ans_vec_gt = ans_vec_gt.cuda().float()

		optimizer.zero_grad()
		classification, ans_producing = model(img_matrix, q_idx_ls, q_category_gt, q_etm_topic, mode="train_val")
		# compute the loss
		loss1 = torch.sum(torch.mul((1 - q_category_gt[:, -1]), loss_func1(classification, ans_raw_category)))
		# print("model prediction size, ", ans_producing.size())
		# print("gt size, ", ans_vec_gt.size())
		# print("loss2 size, ", loss_func2(ans_producing, ans_vec_gt).size())
		# print("sum size, ", torch.sum(loss_func2(ans_producing, ans_vec_gt), dim=1).size())
		loss2 = torch.sum(torch.mul(q_category_gt[:, -1], torch.sum(loss_func2(ans_producing, ans_vec_gt), dim=[1, 2])))
		loss = opt.CLASSIFICATION_LOSS_WEIGHT*loss1 + loss2
		loss.backward()
		optimizer.step()
		# store the 2 losses separately
		epoch_classification_loss += (loss1.item())/batch_size
		epoch_producing_loss += (loss2.item())/batch_size

	train_losses.append((epoch_classification_loss, epoch_producing_loss))
	if(epoch%20 == 0):
		now = str(datetime.datetime.now())
		print("{} Train Epoch{}: Classification Loss: {:.4f}; Producing Loss: {:.4f}".format(now, epoch, epoch_classification_loss, epoch_producing_loss))
		print("%d epoch ends \n" % epoch)
	if(epoch%50 == 0):
		print("\nSaving checkpoints")
		model_save_path = os.path.join(model_checkpoints_folder, "%s-%d-iteration-model.pt"%(time.strftime("%Y%m%d%H"), epoch))
		torch.save(model.state_dict(), model_save_path)

print("\nTraining Finished")
print("\nClassification Loss and answer loss for each epoch")
print(train_losses)

print("\nSaving the final model...")
model_save_path = os.path.join(root_path, "models/pretrained/%s_model_train_val.pt" % (time.strftime("%Y%m%d%H")))
torch.save(model.state_dict(), model_save_path)
print("Model saved at %s" % model_save_path)

print("\nPredicting starts ...")
test_img_ids, test_ans_predict = make_prediction(model, opt)
save_prediction(test_img_ids, test_ans_predict)


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

# # Initial Data Exploration
# $$$$
# The purpose of our initial data exploration is to:
#

#
Check the validity of the data and perform data cleaning methods if needed.

#
View the statistical details of the data

#
Perform data visualization to improve our understanding of the data

#
Feature engineer

#
Sort out the imbalanced dataset

#
Perform transformations (standardization, PCA)

#

#
#
# ## Importing required libraries
# Data processing

# In[1]:

import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import numpy as np
import copy
import random
import pickle
import json

# Data Visualization

# In[2]:

import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.patches as mpatches
import plotly.graph_objects as go
import squarify
plt.style.use('ggplot')

# Code Styling

# In[3]:

from typing import List, Dict

# Read in dataframe and bried inspection of the data.

# In[4]:

heart_disease: pd.DataFrame = pd.read_csv("data/heart_2020_cleaned.csv")

# ## Data Cleaning
#
# Check for missing values

# In[5]:

print(f'Does the heart disease dataset contain any null values? {heart_disease.isnull().any().any()}')

# Next, we calculate the basic statistics of each data set. This is a trivial step, and it is designed to increase understanding of the computational problem.

# In[6]:

print("Unique values in the heart disease dataset, stratified by column:", sep='')
print("-----------------------------------------------------------------\n")
for col in heart_disease.columns:
    if heart_disease[col].nunique() < 20:
        if heart_disease[col].nunique() < 3:
            heart_disease[col].value_counts(normalize=True).plot(kind='barh', title=f"Unique values of {col} column", )
            plt.show()

# From this we note the this dataset is imbalanced .i.e, data set with skewed class proportions. This is a common problem in classification problems. We will address this issue later in the notebook.

# In[7]:

heart_disease.describe()

# In[8]:

sns.pairplot(heart_disease[
    ["HeartDisease", "BMI", "PhysicalHealth", "MentalHealth", "SleepTime"]
    ], hue="HeartDisease", plot_kws=dict(s=80, edgecolor="white", linewidth=2.5))
plt.show()

# Surprisingly, the distribution of BMI, mental health and sleep time don't seem to be significantly different when comparing with people who **do or do not** have heart disease. However, the mental health do seem to have a strong correlation with heart disease.

# # Data Visualization
#
# To familiarize ourself with the data, we generate plots that seek to illuminate some research questions.
#
# 1. Are people from certain backgrounds more likely to have heart disease, given their ethnicity?
# 2. Is the percentage of females and males with heart disease similar?
# 3. Do peoples *reported* physical activity correlate with their physical health?
# 4. Which risk factors are most correlated with heart disease?
# 5. What is the flow of patients from  their age to substance misuse to if they have an aliment listed in the dataset to  their likelihood of contracting heart disease?

# In[9]:

def create_stacked_bar_hart(heart_disease: pd.DataFrame) -> plt.figure:
    """
    Args:
        heart_disease (pd.DataFrame): the original heart disease kaggle dataset

    Returns:
        plt.figure: a stacked bar chart of the percentage of people
            with heart disease and without heart disease, stratified by race
    """

    # top bar -> sum all values(HeartDisease=No and HeartDisease=Yes) to find y position of the bars
    total: pd.DataFrame = heart_disease.groupby('Race').count().reset_index()

    # bar chart 1 -> top bars (group of 'HeartDisease=No')
    bar1: sns.barplot = sns.barplot(x="Race",  y="HeartDisease", data=total, color='red')

    # bottom bar ->  take only HeartDisease=Yes values from the data
    HeartDisease: pd.DataFrame = heart_disease[heart_disease.HeartDisease=='Yes'].groupby('Race').count().reset_index()

    # bar chart 2 -> bottom bars (group of 'HeartDisease=Yes')
    bar2: sns.barplot = sns.barplot(x="Race", y="HeartDisease", data=HeartDisease, errorbar=None,  color='green')

    # add legend
    top_bar: mpatches.Patch = mpatches.Patch(color='red', label='HeartDisease = No')
    bottom_bar: mpatches.Patch = mpatches.Patch(color='green', label='HeartDisease = Yes')
    plt.legend(handles=[top_bar, bottom_bar])
    plt.ylabel('Number of Patients')
    plt.xticks(rotation=45,ha='right')
    # show the graph
    plt.show()

# In[10]:

def create_tree_diagram(heart_disease:pd.DataFrame) -> squarify.plot:
    """
    Args:
        heart_disease (pd.DataFrame): the original heart disease kaggle dataset

    Returns:
        squarify.plot: A tree diagram displaying the number of males and females
            that do and don't have heart disease
    """
    heart_disease = heart_disease.copy()
    tree_diagram_df: pd.DataFrame = heart_disease.groupby(['Sex', 'HeartDisease']).count().reset_index()[['Sex','HeartDisease','BMI']]
    tree_diagram_df['Gender_choice'] = tree_diagram_df[['Sex', 'HeartDisease']].agg(', '.join, axis=1)
    tree_diagram_df.rename(columns={'BMI': 'count'}, inplace=True)
    squarify.plot(sizes=tree_diagram_df['count'], label=tree_diagram_df['Gender_choice'], alpha=.4)
    plt.axis('off')
    plt.show()

def create_histplot(heart_disease:pd.DataFrame) -> plt.figure:
    """
    Args:
        heart_disease (pd.DataFrame): the original heart disease kaggle dataset

    Returns:
        plt.figure: Two histograms displaying the number of days in the past 30 days
                    in which patients were physically active.
                    Stratified by people's reported physical health
    """
    heart_disease = heart_disease.copy()
    sns.set(style="darkgrid")
    heart_disease = heart_disease[heart_disease.PhysicalHealth > 0]
    # plotting both distibutions on the same figure
    sns.histplot(heart_disease[heart_disease.PhysicalActivity == 'Yes']['PhysicalHealth'], color="skyblue", label="People who report doing physical activity")
    sns.histplot(heart_disease[heart_disease.PhysicalActivity == 'No']['PhysicalHealth'], color="red", label="People who do not physical activity")
    plt.legend()
    plt.xlabel("People who report that their physical health (physical illness and injury) was not good? (0-30 days).")
    plt.show()

# In[11]:

def create_corr_plot(heart_disease: pd.DataFrame):
    """
    Args:
        heart_disease (pd.DataFrame): the original heart disease kaggle dataset

    Returns:
        squarify.plot: A bar chart displaying the correlation of risk factors vs  heart disease
    """
    heart_disease = heart_disease.copy()
    binary_vars: List[str] = ["HeartDisease",
                        "Smoking",
                        "AlcoholDrinking",
                        "Stroke",
                        "DiffWalking",
                        "Diabetic",
                        "PhysicalActivity",
                        "Asthma",
                        "KidneyDisease",
                        "SkinCancer"]
    for binary_var in binary_vars:
        heart_disease[binary_var] = heart_disease[binary_var].replace({"Yes": 1, "No": 0})
    heart_disease.Diabetic = heart_disease.Diabetic.replace({'No, borderline diabetes': 1, 'Yes (during pregnancy)' : 1}).astype(int)
    heart_disease.corr()[['HeartDisease']].plot.bar(legend=False)
    plt.title("Correlations of HeartDisease variable vs risk factors")

# In[12]:

def create_sankey_chart(sankey_data:pd.DataFrame) -> go.Figure:
  """
  Args:
      heart_disease (pd.DataFrame): contains three columns; source target value.
                                    Indicates the amount of patients moving through each stage of the sankey chart

  Returns:
      go.Figure: A sankey chart indicating the flow of patients in the following pattern:
                    Age -> Substance Abuse -> Alinement -> Heart Disease outcome

  """
  sankey_data = sankey_data.copy()
  # Get unique values for both source and target columns
  unique_source_target_values  = list(pd.unique(sankey_data[['source', 'target']].values.ravel('K')))

  source_target_mapping = {k:v for v,k in enumerate(unique_source_target_values)}
  with open("data/color_sankey_dict.json") as f:
      colour_dict= json.loads(f.read())
  links = sankey_data[['source', 'target', 'value']].copy()

  links["source"] = links["source"].map(source_target_mapping)
  links["target"] = links["target"].map(source_target_mapping)
  links_dict = links.to_dict(orient="list")

  with open('data/link_colours.pkl', 'rb') as f:
      link_colours = pickle.load(f)

  fig = go.Figure(data = [go.Sankey(
      valueformat = ".0f",
      valuesuffix = "TWh",
      # Define nodes
      node = dict(
        pad = 15,
        thickness = 15,
        line = dict(color = "black", width = 0.2),
        label =  unique_source_target_values,
        color =  list(colour_dict.values())
      ),
      link = dict(
        source = links_dict["source"],
        target = links_dict["target"],
        value = links_dict["value"],
        color= link_colours,
      )

      )]
  )
  fig.update_layout(title_text="Patient Flow
 Age -> Substance Abuse -> Alinement -> Heart Disease outcome",
                    font_size=10)
  fig.show()

# In[13]:

create_stacked_bar_hart(heart_disease)

# White people appear to both appear most often in this dataset and have the highest prevalence of heart disease in the their sub-population. The race with the second most heart disease prevalance is heart disease.

# In[14]:

create_tree_diagram(heart_disease)

# It is evident that relataive rate of heart disease is higher in men than in women.

# In[15]:

create_histplot(heart_disease)

# Surprisingly, it seems that peoples reported physical health does not necessarily align with their physical activity levels.

# In[16]:

sankey_data: pd.DataFrame = pd.read_csv('data/full_sankey_data.csv')
create_sankey_chart(sankey_data)

# ## Feature Engineering
#
# A lot of our features contain categorical data. We will need to convert these to numerical values. Feature extraction projects the original high-dimensional features to a new feature space with low dimensionality, while feature selection directly selects a subset of relevant features. Both feature extraction
# and feature selection can improve learning performance, increase computational efficiency, decrease memory storage, and build better
# generalization models. However, as feature extraction creates a set of new features, further analysis is problematic as we cannot retain the
# physical meanings of these features. In contrast, by keeping some of the original features, feature selection maintains the physical
# meanings of the original features and gives models better readability and interpretability.
#
# The columns broadly fall into three categories:
#
#     - Binary Categorical variables
#       -  HeartDisease
#       -  Smoking
#       -  AlcoholDrinking
#       -  Stroke
#       -  DiffWalking
#       -  Diabet
#       -  Physical Activity
#       -  Asthma
#       -  Kidney Disease
#       -  Skin Cancer
#       -  Sex
#     - Continuous variables
#       - BMI
#     - Discrete variables
#       - Sleeptime
#       - PhsycialHealth
#       - MentalHealth
#     - Polytomous variables; these are variables with more than two categories
#       - AgeCategory
#       - Race
#       - GenHealth

# Binary variables with yes or no values are converted to 1 and 0 respectively. The diabetes column currently has four categories: 'Yes', 'No', 'No, borderline diabetes', 'Yes (during pregnancy)'. Our earlier analysis revealed that  the presence of heart dieasease and diabetes are weakly correlated. Therefore, we will combine the 'Yes (during pregnancy)' and 'No, borderline diabetes' categories into the 'Yes' category.
#

# In[17]:

heart_disease['Diabetic'].unique()

# In[18]:

binary_vars: List[str] = ["HeartDisease",
                        "Smoking",
                        "AlcoholDrinking",
                        "Stroke",
                        "DiffWalking",
                        "Diabetic",
                        "PhysicalActivity",
                        "Asthma",
                        "KidneyDisease",
                        "SkinCancer"]

# In[19]:

for binary_var in binary_vars:
    heart_disease[binary_var] = heart_disease[binary_var].replace({"Yes": 1, "No": 0})
heart_disease.Diabetic: pd.Series = heart_disease.Diabetic.replace({'No, borderline diabetes': 1, 'Yes (during pregnancy)' : 1}).astype(int)

# In[20]:

# One-hot encoding
sex_one_hot = pd.get_dummies(heart_disease['Sex'])
heart_disease = heart_disease.join(sex_one_hot)
heart_disease.drop(columns=['Sex'], inplace=True)

# In the dataset, ordinal attributes are present. There is a natural ordering to the categories (i.e. GenHealth, AgeCategory). Thus, we use the `replace()` function to label encode them.

# In[21]:

heart_disease["AgeCategory"].replace({
    '18-24':1,
    '25-29':2,
    '30-34':3,
    '35-39':4,
    '40-44':5,
    '45-49':6,
    '50-54':7,
    '55-59':8,
    '60-64':9,
    '65-69':10,
    '70-74':11,
    '75-79':12,
    '80 or older':13
}, inplace=True)

# In[22]:

heart_disease["GenHealth"].replace({
    'Poor': 1,
    'Fair':2,
    'Good':3,
    'Very good':4,
    'Excellent':5},
    inplace=True)

# Race has no order. Therefore, we decide to take two approaches.
#
# - One-hot-encoding:Increase the dimensions to facilite a better feature selection space step later which produces a final lower dimension feature vector (i.e. every race is a binary feature)
# - Frequency encoding:  Frequency Encoding is an encoding technique that encodes categorical feature values to their respected frequencies. This will preserve the information about the values of distributions. We normalize the frequencies that result in getting the sum of unique values as 1. This is done to avoid the curse of dimensionality and reduce the sparsity in the dataset.
#
# We will evaluate both approaches in our model evaluation.

# In[23]:

# One-hot encoding
race_one_hot: pd.DataFrame = pd.get_dummies(heart_disease['Race'])
heart_disease: pd.DataFrame = heart_disease.join(race_one_hot)

# Frequency encoding using value_counts function
race_freq: pd.Series = heart_disease['Race'].value_counts(normalize=True)

# Mapping the encoded values with original data
heart_disease['Race_freq']: pd.Series = heart_disease['Race'].apply(lambda x : race_freq[x])
heart_disease.drop(columns=['Race'], inplace=True)

# Outliers are extreme values. Outliers can skew the distribution but these are exceptional but genuine data points.  Such distributions can impact certain algorithms such as regression type models (Lasso), k Nearest neighbors and Naive Bayes. However, decision trees and its ensemble (random forest) are not impacted.

# In[24]:

heart_disease.hist(column=["BMI",
     "SleepTime",
     "PhysicalHealth",
     "MentalHealth",
     "AgeCategory",
     "Race_freq",
     "GenHealth"], layout=(4,2))
plt.tight_layout()

# In[25]:

green_diamond: Dict[str, str] = dict(markerfacecolor='g', marker='D')
fig1, ax1 = plt.subplots()
ax1.set_title('Changed Outlier Symbols')
ax1.boxplot(heart_disease["BMI"], flierprops=green_diamond)

# We note that many outliers exist in the BMI column. To rectify this, we take two approaches
#
# - **Turn it into a ordinal variable**: The WHO uses BMI as a convenient rule of thumb used to broadly categorize a person as underweight, normal weight, overweight, or obese based on tissue mass (muscle, fat, and bone) and height. Major adult BMI classifications are underweight (under 18.5 kg/m2), normal weight (18.5 to 24.9), overweight (25 to 29.9), and obese (30 or more). We will encode these describe caegories into ordinal values.
# - **Take log(x) of the BMI column**: Taking the log of a feature is a common trick to reduce the effect of outliers. This is because the log function is monotonically increasing. Therefore, the effect of outliers is reduced.
# Again, both aproaches will be evaluated in our model evaluation.

# In[26]:

bins: List[int] = [0, 18.5, 24.9, 29.9, np.inf]
names: List[int] = [1, 2, 3, 4]
heart_disease['BMI_Bin']: pd.Series = pd.cut(heart_disease['BMI'], bins, labels=names)

# In[27]:

heart_disease["LOG_BMI"]: pd.Series = np.log(heart_disease["BMI"])
heart_disease["LOG_BMI"].hist()

# ### PCA
#
# Standardization is typically used for features of incomparable units. E.g. someone reporting the number of times they were physical active in the last thirty days and BMI. Standardisation will be applied to all features (both orginal and engineered) except the binary variables. We will also standardize the features due to k-means “isotropic” nature. In this case, if we left our variances unequal; we would inversely be putting more weight on features with high variance. In addition, we will perform principal component analysis due to avoid the curse of dimensionality that k-means can suffer from. The function of PCA is to reduce the dimensionality of a data set consisting of many variables correlated with each other, either heavily or lightly, while retaining the variation present in the data set to the maximum extent.
#
# The same is done by transforming the variables (i.e. features) to a new set of variables, which are known as the principal components, ordered such that the retention of variation present decreases as we move down the order of components.
#
# In addition, we will perform principal component analysis due to avoid the curse of dimensionality that some algorithims can suffer from. Initally, PCA is only perfoormed on the *features present within the orginal dataset*. Later on,  we will perform A feature extraction method u by applying different subsets  of training  data to estimate the accuracy of these subsets for all used
# classifiers and measure the quality of the generated subsets  per classification algorithm, and the results of the classifier are shown. We plan to use PCA again for those best performing subets to see if any improvement is made.
#
#
# The function of PCA is to reduce the dimensionality of a data set consisting of many variables correlated with each other,
# either heavily or lightly, while retaining the variation present in the data set to the maximum extent.
#
# The same is done by transforming the variables (i.e. features) to a new set of variables, which are known as the principal components, ordered such that the retention of variation present decreases as we move down the order of components.
#

# The procedure of PCA involves five steps:

# 1) Standardize the data

# 2) Compute covariance matrix

# 3) Identify the eigenvalues and eigenvectors of the covariance matrix and order them according to the eigenvalues

# 4) Compute a feature vector

# 5) Recast the data

#

# #### Standardisation

# We now standardize the data using the following formulae:
#
# $$
# X_i = X_i - \bar{X}~~~~~~~~~~~~~~~~~~X_i = \frac{X_i}{\sigma}
# $$
#
# The standard deviation should equal 1 after standardization

# In[28]:

heart_disease[
    ['PhysicalHealth', 'MentalHealth', 'AgeCategory', 'GenHealth','SleepTime', 'BMI_Bin']
    ] = StandardScaler().fit_transform(heart_disease[['PhysicalHealth', 'MentalHealth', 'AgeCategory', 'GenHealth','SleepTime', 'BMI_Bin']])

# In[29]:

heart_disease.to_csv('data/standardised_heart_disease.csv', index=False)

# We will use the `PCA` function supplied by the `Scikit-learn` library for dimensionality reduction.  But how do we find the optimal number of components? Which eigenvalues are important?  The scree plot below describes the cumulative explained variance for each component. We reach 80% explained variance at the three component mark.

# In[30]:

orginal_features_df: List[str] = heart_disease[[ 'Smoking', 'AlcoholDrinking', 'Stroke',
       'PhysicalHealth', 'MentalHealth', 'DiffWalking', 'AgeCategory', 'PhysicalActivity', 'GenHealth', 'SleepTime', 'Asthma',
       'KidneyDisease', 'SkinCancer', 'Female', 'Male',
       'American Indian/Alaskan Native', 'Asian', 'Black', 'Hispanic', 'Other',
       'White']]

# In[31]:

pca = PCA().fit(orginal_features_df)
plt.plot(np.cumsum(pca.explained_variance_ratio_))
plt.xlabel('number of components')
plt.ylabel('cumulative explained variance')
plt.title('Scree plot')
plt.show()

# We note a slight indent at the the 5th principal compent mark. According to the average-eigenvalue test (Kaiser-Guttman test) we should retain only those eigenvalues that are above the average which is 1.0.

# Jolliffe relaxes this criterium and suggest to retain eigenvalues greater than 0.7.
#

# In[32]:

kasier_criterion: np.int64 = np.where(pca.explained_variance_ > 1)[-1][-1]
print(
        f'Kasier criterion optimal component number: {kasier_criterion}, explained variance: {np.cumsum(pca.explained_variance_ratio_)[kasier_criterion]}'
    )
jolliffe_criterion: np.int64  = np.where(pca.explained_variance_ > 0.7)[-1][-1]
print(
    f'Jolliffe criterion optimal component number: {jolliffe_criterion} , explained variance: {np.cumsum(pca.explained_variance_ratio_)[jolliffe_criterion]}'
    )

#  For the purpose of this investigation, we decide to go with **both** the Jolaliffe criterion, we will retain the first two  components.

# Finally, we fit the `pca` model with the dataframes containing top 2 components , apply the dimensionality reduction on those respective dataframe and save the resulting dataframes.

# In[33]:

pca_2d = PCA(n_components=2)
dim_reduced_2d: pd.DataFrame = pca_2d.fit_transform(orginal_features_df)
dim_reduced_2d = pd.DataFrame(data=dim_reduced_2d, columns=[f'component_{num}' for num in range(1,3)])
dim_reduced_2d = pd.merge(
        heart_disease[['HeartDisease']], dim_reduced_2d, left_index=True, right_index=True
        )
dim_reduced_2d.to_csv("data/dim_reduced_2d.csv", index=False)

# **Note**, later on we will use PCA to identify the variables that contribute most to the variation in the dataset.


from collections import OrderedDict
import os
from io import BytesIO

import numpy as np

import matplotlib.image as mimg

from astropy.io import fits
from astropy.visualization import simple_norm
from astropy.nddata import block_reduce

import ipywidgets as widgets
from ipywidgets import Accordion

import msumastro

from .notebook_dir import get_data_path

__all__ = [
    'ImageTree',
    'FitsViewer',
    'ImageBrowser',
    'ndarray_to_png',
]

class ImageTree(object):
    """
    Create a tree view of a collection of images.

    Parameters
    ----------

    tree : `msumastro.TableTree`
        Tree of images, arranged by metadata.
    """
    def __init__(self, tree):
        if not isinstance(tree, msumastro.TableTree):
            raise ValueError("argument must be a TableTree")
        self._tree = tree
        self._id_string = lambda l: os.path.join(*[str(s) for s in l]) if l else ''
        self._gui_objects = OrderedDict()
        self._top = None
        self._create_gui()
        self._set_titles()
        # Generate an array to improve initial display time
        ndarray_to_png(np.random.rand(1200, 1200))

    @property
    def top(self):
        """
        Widget at the top of the tree.
        """
        return self._top

    def _get_index_in_children(self, widget):
        parent = widget.parent
        for idx, wid in enumerate(parent.children):
            if widget is wid:
                return idx

    def _replace_child(self, parent, old=None, new=None):
        """
        Replace old child with new.

        Parameters
        ----------

        parent : IPython widget
            String that identifies parent in gui

        old : IPython widget
            Child to be replaced

        new : IPython widget or None
            Replacement child (or None)

        Notes
        -----

        Children are stored as a tuple so they are immutable.
        """
        current_children = list(parent.children)
        for idx, child in enumerate(current_children):
            if child is old:
                current_children[idx] = new
        parent.children = current_children

    def _create_gui(self):
        """
        Create the tree gui elements.

        Notes
        -----

        Each node of the tree is either an
        `IPython.html.widgets.Accordion`, if the node has child nodes,
        or a `IPython.html.widgets.Select`, if the node has a list.

        Note well this does **not** allow for the case of child nodes and
        a list, so this does not really suffice as a file browser.

        List nodes monkey with their parents by editing the description to
        include the number of list items in the node.
        """
        for parents, children, index in self._tree.walk():
            if children and index:
                # This should be impossible...
                raise RuntimeError("What the ???")
            parent_string = self._id_string(parents)
            depth = len(parents)
            try:
                key = self._tree.tree_keys[depth]
            except IndexError:
                key = ''
            if depth == 0:
                self._top = Accordion()
                self._top.description = key
                # self._top.selected_index = -1
                self._gui_objects[parent_string] = self._top

            parent = self._gui_objects[parent_string]

            # Do I have children? If so, add them as sub-accordions
            if children:
                child_objects = []
                for child in children:
                    desc = ": ".join([key, str(child)])
                    child_container = Accordion()
                    child_container.description = desc
                    # Make sure all panels start out closed.
                    # child_container.selected_index = -1
                    child_container.parent = self._gui_objects[parent_string]
                    child_string = os.path.join(parent_string, str(child))
                    self._gui_objects[child_string] = child_container
                    child_objects.append(child_container)
                parent.children = child_objects
            # Do I have only a list? Populate a select box with those...
            if index:
                new_text = widgets.Select(options=index)
                new_text.layout.width = '100%'
                index_string = self._id_string([parent_string, 'files'])
                self._gui_objects[index_string] = new_text

                # On the last pass an Accordion will have been created for
                # this item. We need to replace that Accordion with a Select.
                # The Select should be inside a box so that we can set a
                # description on the box that won't be displayed on the
                # Select. When titles are built for the image viewer tree
                # later on they are based on the description of the Accordions
                # and their immediate children.
                old_parent = parent
                grandparent = old_parent.parent
                desc = old_parent.description
                s_or_not = ['', 's']
                n_files = len(index)
                desc += " ({0} image{1})".format(n_files,
                                                 s_or_not[n_files > 1])

                # Place the box between the Select and the parent Accordion
                parent = widgets.Box()
                parent.description = desc
                parent.children = [new_text]
                parent.parent = grandparent
                self._replace_child(grandparent, old=old_parent, new=parent)

    def display(self):
        """
        Display and format this widget.
        """
        from IPython.display import display
        display(self._top)

    def _set_titles(self):
        """
        Set titles for accordions.

        This should apparently be done *before* the widget is displayed.
        """
        for name, obj in self._gui_objects.items():
            if isinstance(obj, Accordion):
                for idx, child in enumerate(obj.children):
                    if not isinstance(child, widgets.Select):
                        obj.set_title(idx, child.description)

    def format(self):
        """
        This gets called by the ImageBrowser so don't delete it.

        For now it also closes all of the tabs after the browser is created
        because doing it before (at least ipywidgets 5.1.5 and lower) causes
        a javascript error which prevents properly setting the titles.
        """
        for name, obj in self._gui_objects.items():
            if isinstance(obj, Accordion):
                obj.selected_index = None
                for idx, child in enumerate(obj.children):
                    if isinstance(child, Accordion):
                        child.selected_index = None
                    elif isinstance(child, widgets.Box):
                        child.children[0].width = "15em"

def ndarray_to_png(x, min_percent=20, max_percent=99.5):
    shape = np.array(x.shape)
    # Reverse order for reasons I do not understand...
    shape = shape[::-1]
    if len(shape) != 2:
        return

    width = 600  # pixels
    downsample = (shape[0] // width) + 1

    if downsample > 1:
        x = block_reduce(x,
                         block_size=(downsample, downsample))

    norm = simple_norm(x,
                       min_percent=min_percent,
                       max_percent=max_percent,
                       clip=True)

    x = norm(x)
    # Replace NaNs with black pixels
    x = np.nan_to_num(x)
    img_buffer = BytesIO()
    mimg.imsave(img_buffer, x, format='png', cmap='gray')
    return img_buffer.getvalue()

class FitsViewer(object):
    """
    Display the image and header from a single FITS file.

    """
    def __init__(self):
        self._top = widgets.Tab(visible=False)
        self._data = None  # hdu.data
        self._png_image = None  # ndarray_to_png(self._data)
        self._header = ''

        self._image_box = widgets.VBox()
        self._image = widgets.Image()
        # Do this so the initial display looks ok.
        self._image.layout.min_width = '400px'
        self._image_title = widgets.Label()
        self._image_box.children = [self._image, self._image_title]

        self._header_box = widgets.VBox()
        self._header_display = widgets.Textarea(disabled=True)
        self._header_display.layout.width = '50rem'
        self._header_display.layout.height = '20rem'
        self._header_box.children = [self._header_display]
        self._top.children = [self._image_box, self._header_box]

    @property
    def top(self):
        return self._top

    def display(self):
        """
        Display and format this widget.
        """
        from IPython.display import display
        display(self._top)
        self.format()

    def format(self):
        """
        Format widget.

        Must be called after the widget is displayed, and is automatically
        called by the `display` method.
        """
        self._top.set_title(0, 'Image')
        self._top.set_title(1, 'Header')
        self._header_display.height = '400px'
        self._header_display.width = '500px'

        # Let the bike shedding begin....
        self._image_box.align = "center"
        self._image.padding = "10px"
        self._image_box.border_style = 'solid'
        self._image_box.border_radius = "5px"
        self._image_box.border_color = "lightgray"
        self._header_box.align = "center"
        self._header_box.padding = "10px"

    def set_fits_file_callback(self, demo=True, image_dir=None):
        """
        Returns a callback function that sets the name of FITS file to
        display and updates the widget.

        The callback takes one argument, the name of the fits file, or 'demo'
        to enable the display of a couple of sample images.
        """
        def set_fits_file(name, fits_file):
            """
            Set image and header to a particular FITS file.

            Parameters
            ----------

            fits_file : str
                The name of the fits file, or 'demo' to enable the display of
                a couple of sample images.
            """
            if demo:
                import random
                place_holder_files = ['flood-flat-001R.fit',
                                      'SA112-SF1-001R1.fit']
                use_file = random.choice(place_holder_files)
                full_path = os.path.join(get_data_path(), use_file)
            else:
                if image_dir is not None:
                    full_path = os.path.join(image_dir, fits_file)
                else:
                    full_path = fits_file
            with fits.open(full_path) as hdulist:
                hdu = hdulist[0]
                self._data = hdu.data
                self._header = hdu.header
            self._header_display.value = repr(self._header)
            self._image.value = ndarray_to_png(self._data)
            self._image_title.value = os.path.basename(full_path)
            self.top.visible = True

        return set_fits_file

class ImageBrowser(widgets.Box):
    """
    Browse a tree of FITS images and view image/header.

    Parameters
    ----------

    collection : `ccdproc.ImageFileCollection`
        Directory of images.
    """
    def __init__(self, collection, allow_missing=True, *args, **kwd):
        self._directory = collection.location
        self._demo = kwd.pop('demo', False)
        self._tree_keys = kwd.pop('keys', [])
        missing = 'No value' if allow_missing else None
        tree = msumastro.TableTree(collection.summary, self._tree_keys, 'file',
                                   fill_missing=missing)
        kwd['orientation'] = 'horizontal'
        super(ImageBrowser, self).__init__(*args, **kwd)
        self._tree_widget = ImageTree(tree)
        self._fits_display = FitsViewer()
        self._fits_display.top.visible = False
        self.children = [self.tree_widget, self.fits_display]
        # Connect the select boxes to the image displayer
        self._add_handler(self.tree_widget)

    @property
    def tree_widget(self):
        """
        Widget that represents the image tree.
        """
        return self._tree_widget.top

    @property
    def fits_display(self):
        """
        Widget that displays FITS image/header.
        """
        return self._fits_display.top

    def display(self):
        """
        Display and format this widget.
        """
        from IPython.display import display
        display(self)
        self.format()

    def format(self):
        """
        Format widget.

        Must be called after the widget is displayed, and is automatically
        called by the `display` method.
        """
        # self.set_css('width', '100%')
        self.width = '100%'

        self._tree_widget.format()
        self._fits_display.format()

        # self.tree_widget.add_class('box-flex1')
        self.tree_widget.width = '25%'
        # self.fits_display.add_class('box-flex2')
        self.fits_display.width = '67%'
        for child in self.children:
            # child.set_css('margin', '10px')
            child.margin = '5px'

    def _add_handler(self, node):
        if isinstance(node, widgets.Select):
            node.on_trait_change(
                self._fits_display.set_fits_file_callback(demo=self._demo,
                                                          image_dir=self._directory),
                str('value'))
            return
        if hasattr(node, 'children'):
            for child in node.children:
                self._add_handler(child)

{'display': 'IPython.display.display', 'random': 'random'}


#
# @lc app=leetcode id=429 lang=python3
#
# [429] N-ary Tree Level Order Traversal
#
# https://leetcode.com/problems/n-ary-tree-level-order-traversal/description/
#
# algorithms
# Medium (68.92%)
# Likes:    2283
# Dislikes: 101
# Total Accepted:    206.5K
# Total Submissions: 296.5K
# Testcase Example:  '[1,null,3,2,4,null,5,6]'
#
# Given an n-ary tree, return the level order traversal of its nodes' values.
#
# Nary-Tree input serialization is represented in their level order traversal,
# each group of children is separated by the null value (See examples).
#
#
# Example 1:
#
#
#
#
# Input: root = [1,null,3,2,4,null,5,6]
# Output: [[1],[3,2,4],[5,6]]
#
#
# Example 2:
#
#
#
#
# Input: root =
# [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
# Output: [[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]]
#
#
#
# Constraints:
#
#
# The height of the n-ary tree is less than or equal to 1000
# The total number of nodes is between [0, 10^4]
#
#
#
from typing import List
from algo_input import run, NTreeNode as Node
from types import MethodType
# @lc code=start
"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

class Solution:

    def levelOrder(self, root: 'Node') -> List[List[int]]:
        if not root:
            return []
        res, left, level = [], 0, [root]
        while left < len(level):
            res.append([])
            for idx in range(left, len(level)):
                node = level[idx]
                res[-1].append(node.val)
                node.children and level.extend(node.children)
                left += 1
        return res

# @lc code=end
if __name__ == "__main__":
    run(
        MethodType(Solution.levelOrder, Solution()),
        [
            [[Node.create_tree([1, None, 3, 2, 4, None, 5, 6])],
             [[1], [3, 2, 4], [5, 6]]],
            [[
                Node.create_tree([
                    1, None, 2, 3, 4, 5, None, None, 6, 7, None, 8, None, 9,
                    10, None, None, 11, None, 12, None, 13, None, None, 14
                ])
            ], [[1], [2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13], [14]]],
            [[Node.create_tree([1])], [[1]]],
            [[Node.create_tree([])], []],
            [[Node.create_tree([1, None, 2, 3, None, 4, 5, None, 6, 7])],
             [[1], [2, 3], [4, 5, 6, 7]]],
        ],
    )


from django.db import models
from helpers.models import Model
from payroll.models import PayScale, PayGrade, SalaryGroup, DeductionGroup, BonusGroup
from leave.models import LeaveGroup
from accounts.choices import gender_choices, year_choices, current_year
from payroll.models import DeductionGroup

class EmployeeIdentification(Model):
    employee_code = models.CharField(max_length=255, unique=True, null=True)
    title = models.CharField(max_length=255, choices=[('mr', 'Mr.'), ('mrs', 'Mrs.'), ('miss', 'Miss')],
                             null=True)
    first_name = models.CharField(max_length=255, blank=True, null=True)
    middle_name = models.CharField(max_length=255, blank=True, null=True)
    last_name = models.CharField(max_length=255, blank=True, null=True)
    display_name = models.CharField(max_length=255, blank=True, null=True)
    profile_picture = models.ImageField(upload_to='accounts/profile_pic/', blank=True, null=True)
    employee_id = models.CharField(max_length=255, unique=True, null=True)
    gender = models.CharField(max_length=20, null=True, choices=gender_choices)

    def __str__(self):
        return "%s %s %s" % (self.first_name, self.middle_name, self.last_name)

class JobInformation(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE,
                                 related_query_name='employee_job_informations',
                                 related_name='employee_job_information', null=True)
    company = models.ForeignKey('setting.Company', on_delete=models.SET_NULL, null=True,
                                limit_choices_to={'status': 'active'})
    business_unit = models.ForeignKey('setting.BusinessUnit', on_delete=models.SET_NULL, null=True, blank=True,
                                      limit_choices_to={'status': 'active'})
    division = models.ForeignKey('setting.Division', blank=True, null=True, on_delete=models.SET_NULL,
                                 limit_choices_to={'status': 'active'})
    department = models.ForeignKey('setting.Department', blank=True, null=True, on_delete=models.SET_NULL,
                                   limit_choices_to={'status': 'active'})
    project = models.ForeignKey('setting.Project', on_delete=models.SET_NULL, null=True, blank=True,
                                limit_choices_to={'status': 'active'})

    designation = models.ForeignKey('setting.Designation', blank=True, null=True, on_delete=models.SET_NULL,
                                    related_name='setting_designation', limit_choices_to={'status': 'active'})
    branch = models.ForeignKey('setting.Branch', blank=True, null=True, on_delete=models.SET_NULL,
                                    related_name='setting_branch', limit_choices_to={'status': 'active'})
    report_to = models.ForeignKey(EmployeeIdentification, blank=True, null=True, on_delete=models.SET_NULL,
                                  related_name='report_to')
    additional_report_to = models.ForeignKey(EmployeeIdentification, blank=True, null=True, on_delete=models.SET_NULL,
                                             related_name='additional_report_to')
    pay_group = models.CharField(max_length=10, blank=True, null=True)
    pay_scale = models.ForeignKey(PayScale, blank=True, null=True, on_delete=models.SET_NULL,
                                  limit_choices_to={'status': True})
    pay_grade = models.ForeignKey(PayGrade, blank=True, null=True, on_delete=models.SET_NULL,
                                  limit_choices_to={'status': True})
    job_status = models.ForeignKey('setting.JobStatus', blank=True, null=True, on_delete=models.SET_NULL,
                                   limit_choices_to={'status': True})
    employment_type = models.ForeignKey('setting.EmploymentType', blank=True, null=True, on_delete=models.SET_NULL,
                                        limit_choices_to={'status': True})
    date_of_offer = models.DateField(null=True, blank=True)
    date_of_joining = models.DateField(null=True)
    status = models.BooleanField(default=True, blank=True)

    class Meta:
        get_latest_by = ['updated_at']

class Employment(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, related_query_name='employments',
                                 related_name='employment', null=True)
    confirmation_after = models.IntegerField(blank=True, null=True)
    confirmation_after_unit = models.CharField(max_length=10,
                                               choices=[('days', 'Days'), ('months', 'Months'), ('years', 'Years')],
                                               blank=True, null=True)
    confirmation_date = models.DateField(null=True, blank=True)
    date_of_actual_confirmation = models.DateField(null=True, blank=True)

class EndOfContract(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True, blank=True)
    due_on = models.DateField(null=True, blank=True)
    date_of_settlement = models.DateField(null=True, blank=True)
    effective_date = models.DateField(null=True, blank=True)

class Separation(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    type_of_resign = models.CharField(max_length=50, choices=[('sack', 'Sack'), ('resign', 'Resign'),
                                                              ('accidental_separation', 'Accidental Separation')],
                                      default='sack')
    date_of_sack = models.DateField(blank=True, null=True)
    date_of_resign = models.DateField(blank=True, null=True)
    date_of_settlement = models.DateField(blank=True, null=True)
    effective_date = models.DateField(blank=True, null=True)

class Retirement(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    due_on = models.DateField(blank=True, null=True)
    date_of_settlement = models.DateField(blank=True, null=True)
    effective_date = models.DateField(blank=True, null=True)

class Payment(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True,
                                 related_name='employee_payment')
    payment_mode = models.CharField(max_length=20, choices=[('bank', 'Bank'), ('cash', 'Cash'), ('cheque', 'Cheque')],
                                    default='Bank')
    pay_schedule = models.CharField(max_length=20, choices=[('monthly', 'Monthly'), ('weekly', 'Weekly')],
                                    default='monthly')
    employee_bank_name = models.ForeignKey('setting.Bank', on_delete=models.SET_NULL, blank=True, null=True,
                                           limit_choices_to={'status': 'active'})
    employee_bank_AC_name = models.CharField(max_length=255, blank=True, null=True,
                                             verbose_name='Employee Bank A/C Name')
    bank_branch_code = models.CharField(max_length=255, blank=True, null=True)
    bank_AC_no = models.IntegerField(blank=True, null=True, verbose_name='Bank A/C No')
    routing_number = models.CharField(max_length=255, blank=True, null=True)

class SalaryStructure(Model):
    STATUS_CHOICES = (
        ('active', 'Active'),
        ('inactive', 'Inactive')
    )

    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True,
                                 related_name='employee_salary_structure')
    salary_group = models.ForeignKey(SalaryGroup, null=True, on_delete=models.SET_NULL,
                                     limit_choices_to={'status': 'active'})
    bonus_group = models.ForeignKey(BonusGroup, blank=True, null=True, on_delete=models.SET_NULL,
                                    limit_choices_to={'status': True})
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='active'
    )

    from_date = models.DateField()
    to_date = models.DateField(blank=True, null=True)
    reason_of_salary_modification = models.TextField(blank=True, null=True)

class Personal(Model):
    RELIGION_CHOICES = [
        ('', '-------'),
        ('Islam', 'Islam'),
        ('Hinduism', 'Hinduism'),
        ('Christianity', 'Christianity'),
        ('Buddhism', 'Buddhism'),
        ('Other', 'Other')
    ]
    BLOOD_GROUP_CHOICES = [
        ('', '-------'),
        ('A+', 'A+'),
        ('O+', 'O+'),
        ('B+', 'B+'),
        ('AB+', 'AB+'),
        ('A-', 'A-'),
        ('O-', 'O-'),
        ('B-', 'B-'),
        ('AB-', 'AB-'),
    ]
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True,
                                 related_name='personal_employee')
    date_of_birth = models.DateField(null=True)
    place_of_birth = models.ForeignKey('setting.Countries', on_delete=models.SET_NULL, null=True, blank=True)
    birth_certificate = models.FileField(upload_to='accounts/birth_certificate/', blank=True, null=True)
    nationality = models.CharField(max_length=255, null=True)
    passport_number = models.IntegerField(blank=True, null=True)
    nid_or_ssn_number = models.IntegerField(blank=True, null=True, verbose_name='NID/SSN Number')
    passport_expiry_date = models.DateField(blank=True, null=True)
    nid = models.FileField(upload_to='accounts/nid_pic/', blank=True, null=True, verbose_name='NID')
    passport = models.FileField(upload_to='accounts/passport_pic/', blank=True, null=True)
    tin_number = models.IntegerField(blank=True, null=True, verbose_name='TIN Number')
    TIN = models.FileField(upload_to='accounts/tin_pic/', blank=True, null=True)
    visa_type = models.CharField(max_length=10, blank=True, null=True)
    visa_number = models.IntegerField(blank=True, null=True)
    work_permit_no = models.IntegerField(blank=True, null=True)
    work_permit_expiry_date = models.DateField(blank=True, null=True)
    work_permit_doc = models.FileField(upload_to='accounts/work_permit_pic/', blank=True, null=True)
    driving_licence_no = models.IntegerField(blank=True, null=True)
    driving_licence_expiry_date = models.DateField(blank=True, null=True)
    driving_licence_doc = models.FileField(upload_to='accounts/driving_licence_pic/', blank=True, null=True)

    mothers_name = models.CharField(max_length=255, blank=True, null=True)
    fathers_name = models.CharField(max_length=255, blank=True, null=True)
    marital_status = models.CharField(max_length=20, choices=[('married', 'Married'), ('unmarried', 'Unmarried')],
                                      default='married')
    spouse_name = models.CharField(max_length=255, blank=True, null=True)
    no_of_child = models.IntegerField(default=0, blank=True, null=True)
    height_ft = models.IntegerField(blank=True, null=True, verbose_name='Height')
    height_in = models.IntegerField(blank=True, null=True)
    weight = models.IntegerField(blank=True, null=True)
    weight_unit = models.CharField(max_length=10, choices=[('kg', 'kg'), ('gm', 'gm')], default='kg',
                                   null=True)
    blood_group = models.CharField(max_length=20, blank=True, choices=BLOOD_GROUP_CHOICES, default='')
    identification_mark = models.CharField(max_length=255, blank=True, null=True)
    religion = models.CharField(max_length=20, blank=True, choices=RELIGION_CHOICES, default='')
    caste = models.CharField(max_length=255, blank=True, null=True)
    mother_tongue = models.CharField(max_length=255, blank=True, null=True)
    police_station_address = models.TextField(blank=True, null=True)
    fingerprint = models.FileField(upload_to='accounts/fingerprint/', blank=True, null=True)
    signature = models.FileField(upload_to='accounts/signature/', blank=True, null=True)

    preferred_food = models.TextField(blank=True, null=True)
    hobby = models.TextField(blank=True, null=True)

class AddressAndContact(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    official_cell_number = models.CharField(max_length=100, null=True)
    personal_cell_number = models.CharField(max_length=100, null=True)
    official_email_ID = models.EmailField(blank=True, null=True)
    personal_email_ID = models.EmailField(blank=True, null=True)

    present_address = models.TextField(verbose_name='Address Line', null=True)
    present_country = models.ForeignKey('setting.Countries', on_delete=models.SET_NULL, null=True,
                                        verbose_name='Country', related_name='prensent_country')
    present_state = models.ForeignKey('setting.States', on_delete=models.SET_NULL, null=True, verbose_name='State',
                                      related_name='prensent_state')
    present_city = models.ForeignKey('setting.Cities', on_delete=models.SET_NULL, null=True, verbose_name='City',
                                     related_name='prensent_city')
    present_thana = models.CharField(max_length=255, null=True, blank=True, verbose_name='Thana')
    present_postal_code = models.IntegerField(blank=True, null=True, verbose_name='Postal Code')
    present_contact_person = models.CharField(max_length=255, blank=True, null=True, verbose_name='Contact Person')
    present_phone_number = models.CharField(max_length=100, null=True, verbose_name='Phone Number')
    permanent_address = models.TextField(verbose_name='Address Line', null=True, blank=True)

    permanent_country = models.ForeignKey('setting.Countries', on_delete=models.SET_NULL, null=True,
                                          verbose_name='Country', related_name='permanent_country', blank=True)
    permanent_city = models.ForeignKey('setting.Cities', on_delete=models.SET_NULL, null=True, verbose_name='City',
                                       related_name='permanent_city', blank=True)
    permanent_state = models.ForeignKey('setting.States', on_delete=models.SET_NULL, null=True, verbose_name='State',
                                        related_name='permanent_state', blank=True)
    permanent_thana = models.CharField(max_length=255, blank=True, null=True, verbose_name='Thana')
    permanent_postal_code = models.IntegerField(blank=True, null=True, verbose_name='Postal Code')
    permanent_contact_person = models.TextField(blank=True, null=True, verbose_name='Contact Person')
    permanent_phone_number = models.CharField(max_length=100, verbose_name='Phone Number', blank=True, null=True)

class EmergencyContact(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    name = models.CharField(max_length=255)
    relationship = models.CharField(max_length=255)
    address = models.TextField(blank=True, null=True)
    contact = models.CharField(max_length=100)
    email = models.EmailField(blank=True, null=True)

class Family(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    name_of_family_member = models.CharField(max_length=255)
    relationship_with_employee = models.CharField(max_length=255)
    DOB = models.DateField(blank=True, null=True)
    age = models.IntegerField(blank=True, null=True)
    gender = models.CharField(max_length=20, null=True, choices=gender_choices)
    employed = models.NullBooleanField(default=True)
    dependent = models.NullBooleanField(default=True)

class Education(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    degree = models.CharField(max_length=255, null=True)
    university = models.CharField(max_length=255, verbose_name='University/College/School/Institute', null=True)
    subject = models.CharField(max_length=255, verbose_name='Discipline/Subject', null=True)
    year_of_completion = models.IntegerField(choices=year_choices(), default=current_year, null=True)
    result_type = models.CharField(max_length=10, choices=[('CGPA', 'CGPA'), ('GPA', 'GPA'), ('division', 'Division')],
                                   default='CGPA')
    result_of_gpa = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='Result')
    out_of = models.CharField(max_length=10, choices=[('4.0', '4.0'), ('5.0', '5.0')], null=True, blank=True)
    result_of_division = models.CharField(max_length=10, choices=[('1st', '1st'), ('2nd', '2nd'), ('3rd', '3rd')],
                                          blank=True, null=True, verbose_name='Result')
    grade = models.CharField(max_length=5,
                             choices=[('A+', 'A+'), ('A', 'A'), ('A-', 'A-'), ('B+', 'B+'), ('B', 'B'), ('B-', 'B-'),
                                      ('C+', 'C+'), ('C', 'C'), ('D', 'D'), ('F', 'F')], blank=True, null=True)
    marks = models.IntegerField(blank=True, null=True)
    certificate = models.FileField(upload_to='accounts/certificate/', blank=True, null=True)

    class Meta:
        unique_together = ('employee', 'degree')

class Skill(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    skill_name = models.CharField(max_length=255, null=True)
    description = models.TextField(blank=True, null=True)
    skill_level = models.CharField(max_length=255, null=True,
                                   choices=[('beginner', 'Beginner'), ('intermediate', 'Intermediate'),
                                            ('developing', 'Developing'), ('expert', 'Expert')], default='beginner')

    class Meta:
        unique_together = ('employee', 'skill_name')

class Training(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    training_title = models.CharField(max_length=255, null=True)
    country = models.ForeignKey('setting.Countries', on_delete=models.SET_NULL, null=True, blank=True)
    training_year = models.IntegerField(choices=year_choices(), default=current_year, null=True)
    institute = models.CharField(max_length=255, null=True)
    duration = models.IntegerField(null=True)
    duration_unit = models.CharField(max_length=10,
                                     choices=[('hours', 'Hours'), ('day', 'Day'), ('month', 'Month'), ('year', 'Year')],
                                     default='hours')
    certificate = models.FileField(upload_to='accounts/certificate/', blank=True, null=True)

    class Meta:
        unique_together = ('employee', 'training_title')

class ProfessionalCertificate(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    certificate_title = models.CharField(max_length=255, null=True)
    country = models.ForeignKey('setting.Countries', on_delete=models.SET_NULL, null=True, blank=True)
    institute = models.CharField(max_length=255, null=True)
    institute_address = models.TextField(blank=True, null=True)
    duration = models.IntegerField(null=True)
    duration_unit = models.CharField(max_length=10, choices=[('day', 'Day'), ('month', 'Month'), ('year', 'Year')],
                                     default='day')
    certificate = models.FileField(upload_to='accounts/certificate/', blank=True, null=True)

    class Meta:
        unique_together = ('employee', 'certificate_title')

class LanguageProficiency(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    language = models.CharField(max_length=255, null=True)
    description = models.TextField(blank=True, null=True)
    read = models.CharField(max_length=3, choices=[('yes', 'Yes'), ('no', 'No')], default='yes')
    write = models.CharField(max_length=3, choices=[('yes', 'Yes'), ('no', 'No')], default='yes')
    speak = models.CharField(max_length=3, choices=[('yes', 'Yes'), ('no', 'No')], default='yes')

    class Meta:
        unique_together = ('employee', 'language')

class EmploymentHistory(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    organization = models.CharField(max_length=255, null=True)
    designation = models.CharField(max_length=255, null=True)
    department = models.CharField(max_length=255, blank=True, null=True)
    start_from = models.DateField(verbose_name='From', null=True)
    to = models.DateField(null=True)
    salary = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='Salary (Gross)', null=True, blank=True)

class Reference(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    ref_person_name = models.CharField(max_length=255, verbose_name='Ref. Person Name', null=True)
    relationship = models.CharField(max_length=255, null=True)
    designation = models.CharField(max_length=255, null=True)
    official_cell_number = models.CharField(max_length=255, verbose_name='Cell Number (Official)', null=True)
    personal_cell_number = models.CharField(max_length=255, verbose_name='Cell Number (Personal)', null=True,
                                            blank=True)
    official_email = models.EmailField(verbose_name='Email Address (Official)', null=True)
    personal_email = models.EmailField(verbose_name='Email Address (Personal)', null=True, blank=True)
    organization_name = models.CharField(max_length=255)
    address_line = models.TextField(null=True)
    country = models.ForeignKey('setting.Countries', on_delete=models.SET_NULL, null=True, blank=True)
    city = models.ForeignKey('setting.Cities', on_delete=models.SET_NULL, null=True, blank=True)
    state = models.ForeignKey('setting.States', on_delete=models.SET_NULL, null=True, blank=True)
    thana = models.CharField(max_length=255, blank=True, null=True)
    postal_code = models.IntegerField(blank=True, null=True)
    contact_person = models.CharField(max_length=255, blank=True, null=True, verbose_name='Contact Person Name')
    phone_number = models.CharField(max_length=100, null=True)

class Asset(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    asset_category = models.CharField(max_length=255, null=True)
    asset_brand_name = models.CharField(max_length=255, null=True)
    description = models.TextField(blank=True, null=True)
    serial_number = models.CharField(max_length=255, null=True)
    date_loaned = models.DateField(null=True)
    date_returned = models.DateField(null=True)

class Documents(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    resume = models.FileField(upload_to='accounts/resume/', blank=True, null=True)
    cover_letter = models.FileField(upload_to='accounts/cover_letter/', blank=True, null=True)
    appointment_letter = models.FileField(upload_to='accounts/appointment_letter/', blank=True, null=True)
    resign_letter = models.FileField(upload_to='accounts/resign_letter/', blank=True, null=True)

class OthersDocuments(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    title = models.CharField(max_length=255, null=True)
    file = models.FileField(upload_to='accounts/others_documents/', blank=True, null=True)

    class Meta:
        unique_together = ('employee', 'title')

class Attendance(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True,
                                 related_name='employee_attendance')
    schedule_type = models.ForeignKey('attendance.ScheduleMaster', on_delete=models.SET_NULL, null=True, blank=True,
                                      limit_choices_to={'status': True})
    punching_id = models.CharField(max_length=100, verbose_name='Punching ID', blank=True, null=True)
    calendar_master = models.ForeignKey('attendance.CalendarMaster', on_delete=models.SET_NULL, null=True, blank=True,
                                        related_name='employee_calendar', verbose_name='Calendar',
                                        limit_choices_to={'status': True})

class LeaveManage(Model):
    employee = models.ForeignKey(EmployeeIdentification, on_delete=models.CASCADE, null=True)
    leave_group = models.ForeignKey(LeaveGroup, on_delete=models.SET_NULL, null=True, blank=True,
                                    limit_choices_to={'status': True})
    overtime = models.BooleanField(default=False)
    overtime_group = models.ForeignKey('attendance.OvertimeRule', on_delete=models.SET_NULL, null=True, blank=True,
                                       related_name='employee_overtime', verbose_name='Select Overtime')
    deduction = models.BooleanField(default=False)
    deduction_group = models.ForeignKey(DeductionGroup, on_delete=models.SET_NULL, null=True, blank=True,
                                        related_name='employee_leave_deduction', limit_choices_to={'status': 'active'})


'''
Parses domain.aggl, WorldModel.aggl, Target.aggt, results.plan file for Learning Data
'''
import sys
import xml.etree.ElementTree as ET
sys.path.append("/usr/local/share/agm")
# import native AGGL Parser to Wrapper
from parseAGGL import AGMFileDataParsing
# import native XML Parse to Wrapper
from xmlModelParser import *

class Parser:

	# Maps id to it's type

	def __init__(self):
		# action_list contains list of the actions
		self.action_list = []
		# Contains mapping of id to type
		self.typeMap = {}
		# Contains mapping of pair of id's to the relations the have
		self.relMap = {}
		# tgt_actions list of all target values (actions)
		self.tgt_actions = []
		# Below two variables starting with attr_ contains training attributes for our Naive Bayes Classifier
		self.attr_node = []
		# what relations would exist between pair of id's as per target's (planner's context) requirement
		self.attr_link = []

	def parse_domain(self, fileName):
		'''
		A parser for Domain Files (.aggl extension)
		This reads domain file and gathers knowledge about action_list which contains all possible actions:
		'''
		agmData = AGMFileDataParsing.fromFile(fileName)
		for rule in agmData.agm.rules:
			self.action_list.append(rule.name)
		# f = open(fileName)
		# parse_flag = False
		# count = 0
		# '''Since actions are out of all nested loops,
		# therefore count=0 indicates to get ready to read an action name.
		# '''
		# for line in f:
		# 	if "===" in line and not parse_flag:
		# 		''' Ignore visual editor stuff, parsing starts after we encounter
		# 		=== delimiter.
		# 		'''
		# 		parse_flag = True
		# 	elif parse_flag:
		# 		# Assumption : { } and action name are not present on the same line
		# 		if "{" in line:
		# 			count += 1
		# 		elif "}" in line:
		# 			count -= 1
		# 		elif count == 0 and line.strip() != "":
		# 			if "hierarchical" in line:
		# 				self.action_list.append(line.split()[1])
		# 			else:
		# 				self.action_list.append(line.split()[0])
		# f.close()
		# return self.action_list

	def parse_initM(self, fileName):
		'''
		Parse initial world model (.xml extension)

		This functions fills-in following variables:
		i) typeMap : Maps id's to type
		ii) relMap : Contains relations between two id's
		'''

		# Re-initializing variables necessary while training and testing
		self.typeMap = {}
		self.relMap = {}

		tree = ET.parse(fileName)
		root = tree.getroot()
		for child in root:
			if child.tag == "symbol":
				self.typeMap[int(child.attrib['id'])] = child.attrib['type']
			elif child.tag == "link":
				id1 = int(child.attrib['src'])
				id2 = int(child.attrib['dst'])
				id_pair = (id1, id2)
				if id_pair not in self.relMap:
					self.relMap[id_pair] = [child.attrib['label']]
				else:
					self.relMap[id_pair].append(child.attrib['label'])

	def parse_plan(self, fileName):
		'''
		Parses result file (.plan extension)
		Fills in self.tgt_actions which contains all the actions involved in the plan.
		'''
		self.tgt_actions = []
		f = open(fileName)
		for line in f:
			line = line.strip()
			if line.startswith("#!*"):
				line = line[3:].strip()
			elif line.startswith("*"):
				line = line[1:].strip()
			self.tgt_actions.append(line.split("@")[0])
		f.close()

	def parse_target(self, fileName):
		'''
		Parses target files (.aggt extension)
		'''
		self.attr_node = []
		self.attr_link = []
		# Keep tracks of keys used to represent some object type
		var_map = {}
		f = open(fileName)
		flag = False
		for line in f:
			if "{" in line:
				flag = True
			elif "}" in line:
				# Preconditions are not considered while learning.
				break
			if flag:
				if ":" in line:
					line = line.split(":")
					final_type = line[1].split("(")[0].strip()
					line[0] = line[0].strip()
					# If id is given, initial type is matched to type of id
					if line[0].isdigit():
						init_type = self.typeMap[int(line[0])]
					else:
						''' If id is not given, initial type is assumed to be same as final type
						and the mapping is recorded.
						'''
						init_type = final_type
						var_map[line[0]] = final_type
					self.attr_node.append((init_type, final_type))

				elif "->" in line:
					line = line.split("->")
					temp = line[1].split("(")
					# line[0] basically represents src id
					line[0] = line[0].strip()
					# temp[0] basically represents dst id.
					temp[0] = temp[0].strip()
					# rel stores relations between two id's.
					rel = temp[1].split(")")[0].strip()

					# There can be four cases, src id is int/symbol or dst id is int/symbol
					if line[0].isdigit() and temp[0].isdigit():
						id1 = int(line[0])
						id2 = int(temp[0])
						id_pair = (id1, id2)
						type1 = self.typeMap[id1]
						type2 = self.typeMap[id2]

						self.attr_link.append((type1, rel, type2))
						if id_pair in self.relMap:
							for relation in self.relMap[id_pair]:
								self.attr_link.append((type1, relation, type2))
								self.attr_link.append((type1, relation, rel, type2))

					else:
						if not line[0].isdigit():
							if not temp[0].isdigit():
								type1 = var_map[line[0]]
								type2 = var_map[temp[0]]

								self.attr_link.append((type1, rel, type2))
								for id_pair in self.relMap:
									if type1 == self.typeMap[id_pair[0]] and type2 == self.typeMap[id_pair[1]]:
										for relation in self.relMap[id_pair]:
											self.attr_link.append((type1, relation, type2))
											self.attr_link.append((type1, relation, rel, type2))

							else:
								id2 = int(temp[0])
								type1 = var_map[line[0]]
								type2 = self.typeMap[id2]

								self.attr_link.append((type1, rel, type2))
								for id_pair in self.relMap:
									if self.typeMap[id_pair[0]] == type1 and id_pair[1] == id2:
										for relation in self.relMap[id_pair]:
											self.attr_link.append((type1, relation, type2))
											self.attr_link.append((type1, relation, rel, type2))

						else:
							id1 = int(line[0])
							type1 = self.typeMap[id1]
							type2 = var_map[temp[0]]

							self.attr_link.append((type1, rel, type2))
							for id_pair in self.relMap:
								if id_pair[0] == id1 and self.typeMap[id_pair[1]] == type2:
									for relation in self.relMap[id_pair]:
										self.attr_link.append((type1, relation, type2))
										self.attr_link.append((type1, relation, rel, type2))

		f.close()


# Customer database management in covid-19
import mysql.connector as db
import datetime

class Customer:

    def __init__(self):
        self.name = input("Enter Your name: ")
        self.phone = input("Enter Your Phone Number: ")
        self.email = input("Enter Your email: ")
        self.temperature = input("Enter Your temperature: ")
        self.intime = str(datetime.datetime.today())
        self.outtime = ""
        print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")

    def show_details(self):
        print("Name: {}\n"
              "Phone.no: {}\n"
              "Email: {}\n"
              "Temperature: {}\n"
              "In-time: {}\n"
              "Out-time: {}".format(self.name, self.phone, self.email, self.temperature, self.intime, self.outtime))

class DB:

    def __init__(self):
        self.connection = db.connect(user="root", password="", database="utkdb", host="localhost")
        self.cursor= self.connection.cursor()

    def execute_sql_write(self,sql):
        self.cursor.execute(sql)
        self.connection.commit()

    def execute_sql_read(self,sql):
        self.cursor.execute(sql)
        data = self.cursor.fetchall()
        return data

def main():

    while True:

        print("=========================================")
        print("Welcome to customer management")
        print("``````````````````````````````")
        print("Press 1 to Register customer and mark entry.")
        print("Press 2 to mark exit of customer.")
        print("Press 3 to update customer.")
        print("Press 4 to fetch customer on the basis of Phone number.")
        print("Press 5 to fetch all customers.")
        print("Press 0 for exit.")
        print("=========================================")

        choice = int(input("Enter your choice: "))
        dbRef = DB()

        if choice == 0:
            break

        if choice == 1:
            cRef = Customer()
            cRef.show_details()

            save = input("Enter yes to save: ")
            if save == "yes":
                sql = "insert into Customer values(null, '{}', '{}', '{}', {}, '{}', '{}')".format(cRef.name, cRef.phone, cRef.email,
                                                                                cRef.temperature,cRef.intime,
                                                                                cRef.outtime)
                dbRef.execute_sql_write(sql)
                print("DATA REGISTERED")

        elif choice == 2:
            phone = input("Enter your phone number: ")
            outtime =str(datetime.datetime.today())
            sql = "update customer set outtime = '{}' where phone = '{}'".format(outtime,phone)

            update = input("Enter yes to Update: ")
            if update == "yes":
                dbRef.execute_sql_write(sql)
                print("EXECUTED")

        elif choice == 3:
            cRef = Customer()
            cRef.show_details()

            update = input("Enter yes to save: ")
            if update == "yes":
                sql = "update customer set temperature={} where phone = '{}'".format(cRef.temperature,cRef.phone)
                dbRef.execute_sql_write(sql)
                print("DATA UPDATED")

        elif choice == 4:
            phone = int(input("Enter your phone number: "))
            sql = "select * from customer where phone = '{}'".format(phone)
            data = dbRef.execute_sql_read(sql)
            print(data)

        elif choice == 5:
            sql= "select * from customer"
            data = dbRef.execute_sql_read(sql)
            for row in data:
                print(row)

        else:
            print("Wrong option selected.....Try Again")

if __name__ == '__main__':
    main()