import pandas as pd
from khayyam import JalaliDate

def hijri_to_georgian(string):
    y,m,d = string.split('-')
    jalali = JalaliDate(int(y), int(m), int(d))
    return jalali.todate().strftime('%Y-%m-%d')

def georgian_to_hijri(datetime):
    return JalaliDate(datetime).strftime('%Y-%m-%d')

def miladi_base(df, config):
    resample_period = { 'minute': 'T', 'hourly': 'H', 'daily': 'D', 'monthly': 'M'}
    df = df.resample(resample_period[config['time']]).mean()

    if 'order' in config.keys():
        order = config['order']
    else:
        order = 1

    df.interpolate(method=config['interpolation'], order=order, inplace=True)

    df.reset_index(inplace=True)

    str_format = {'minute':'%Y-%m-%d %H:%M:00' ,'hourly':'%Y-%m-%d %H:00:00',
                    'daily':'%Y-%m-%d', 'monthly':'%Y-%m-01'}
    df['time'] = df['time'].dt.strftime(str_format[config['time']])
    return df

def shamsi_base(df, config):
    df['georg'] = df['time'].apply(hijri_to_georgian)
    df['georg'] = pd.to_datetime(df['georg'])
    df.set_index('georg', inplace=True)

    if 'order' in config.keys():
        order = config['order']
    else:
        order = 1

    if 'skip_holiday' in config.keys():
        skip_holiday = config['skip_holiday']
    else:
        skip_holiday = False

    if config['time'] == 'daily':
        df = df.resample('D').mean()
        if skip_holiday:
            df['weekday'] = df.index.map(lambda x: JalaliDate(x).weekday())
            df = df[(df.weekday != 5) & (df.weekday != 6)]
        df.interpolate(method=config['interpolation'], order=order, inplace=True)
        df['time'] = df.index.map(georgian_to_hijri)
        df.reset_index(inplace=True)
        return df[['time', 'vol']]

    elif config['time'] == 'monthly':
        df = df.resample('D').mean()
        df['ym'] = df.index.map(lambda x: f'{str(JalaliDate(x).year)}-{str(JalaliDate(x).month)}')
        df['weekday'] = df.index.map(lambda x: JalaliDate(x).weekday())
        if skip_holiday:
            df = df[(df.weekday != 5) & (df.weekday != 6)]
        df = df.groupby('ym')[['vol', 'ym']].apply(lambda x: x.interpolate(method=config['interpolation'], order=order, limit_direction="both"))
        df = df.groupby(['ym']).mean().interpolate(method=config['interpolation'], order=order)
        df.reset_index(inplace=True)
        df['time'] = df['ym'].apply(lambda x: f"{x.split('-')[0]}-{x.split('-')[1].zfill(2)}-01")
        return df[['time', 'vol']]

def miladi(data, config):
    df = pd.DataFrame(data)
    df['time'] = pd.to_datetime(df['time'])
    df = df.set_index('time')
    return miladi_base(df, config).to_json()

def shamsi(data, config):
    if config['service'] == 1:
        return shamsi_base(pd.DataFrame(data), config).to_json()
    if config['service'] == 2:
        df = pd.DataFrame(data)
        df['time'] = df['time'].apply(lambda x: georgian_to_hijri(pd.to_datetime(x)))
        return shamsi_base(df, config).to_json()


#!/usr/bin/env python
# encoding: utf-8
# 作者：Ksar4
# 日期：2020/10/19 10:52
# 工具：PyCharm
# Python版本：3.7.0

import time

class elevator():
    def __init__(self,len_min,len_max):
        self.len_min = int(len_min)
        self.len_max = int(len_max)
        self.lost_floor=[14,18]

    def run_elevator(self):
        while True:
            time.sleep(1)
            print("------------------------------------")
            try:
                floor = int(input("请输入想到达的楼层"))
            except:
                print("请正确的输入数字楼层")
                continue
            if floor > self.len_max :
                print("最高层数为%s,已选择楼层%s,超出最大层数请重新选择"%(self.len_max,floor))
                continue
            if floor < self.len_min :
                print("最低层数为%s,已选择楼层%s,超出最低层数请重新选择"%(self.len_min,floor))
                continue

            if floor in self.lost_floor:
                print("不存在%s楼层,请重新选择"%floor)
                continue

            print("已到达%s层"%floor)

if __name__ == "__main__":
    e=""
    try:
        e = elevator(-5,40)
    except:
        print("电梯配置失败")
    if e:
        e.run_elevator()


from django.urls import path

from . import views

app_name = 'books'

urlpatterns = [
    path('', views.ManageBookView.as_view(), name='manage_book'),
    path('add/', views.AddBookView.as_view(), name='add_book'),
    path('edit/<int:pk>', views.EditBookView.as_view(), name='edit_book'),
    path('delete/<int:pk>', views.deleteBook, name='delete_book'),
]


import argparse
from pathlib import Path
from hydra import initialize_config_dir, compose

def parse_args():
    parser = argparse.ArgumentParser("Classifire")
    parser.add_argument("--data_source", type=str)
    parser.add_argument(
        "--log_dir", type=str, default="logs/experiments/runs/default/2022-11-01_16-54-20"
    )
    return parser.parse_args()

def main():
    from src.predict_pipline import predict

    args = parse_args()
    data_source = Path(args.data_source)
    log_dir = Path(args.log_dir)
    with initialize_config_dir(
        version_base="1.1", config_dir=str(log_dir.joinpath("config").absolute()), job_name="app"
    ):
        cfg = compose(
            config_name="config",
            return_hydra_config=True,
            overrides=["original_work_dir=${hydra.runtime.cwd}"],
        )
        predict(config=cfg, log_dir=log_dir, data_source=data_source)

if __name__ == "__main__":
    main()


import numpy as np

class DMD:
    def __init__(self, projected=False):
        self.projected = projected
        self.snapshots = None
        self.eigenvalues = None
        self.eigenvectors = None
        self.omegas = None
        self.modes = None
        self.coefficients = None
        self.dt = None
        self.dim = None

    def solve(self, x, dt, rank):
        """
        :param x:
        :param dt:
        :param rank:
        :return:
        """
        self.clear()
        self.dim = x.shape[1:]
        x = DMD.create_tallskinny(x)
        self.snapshots = x
        self.dt = dt
        x1 = x[:, :-1]
        x2 = x[:, 1:]

        u, s, v = np.linalg.svd(x1, full_matrices=False)
        v = v.T
        u = u[:, :rank]
        v = v[:, :rank]
        s = s[:rank]

        a_tilde = np.linalg.multi_dot([u.T, x2, v]) * np.reciprocal(s)
        eigenvalues, eigenvectors = np.linalg.eig(a_tilde)

        self.eigenvalues = eigenvalues
        self.omegas = np.log(self.eigenvalues)/self.dt
        self.eigenvectors = eigenvectors
        if self.projected:
            self.modes = u.dot(eigenvectors)
        else:
            self.modes = ((x2.dot(v) * np.reciprocal(s)).dot(eigenvectors))

        self.compute_coefficients(False)

    def compute_coefficients(self, exact = False):
        """

        :param exact:
        :return:
        """
        if exact:
            self.coefficients = np.linalg.solve(self.modes, self.snapshots[:, 0])
        else:
            self.coefficients = np.linalg.lstsq(self.modes, self.snapshots[:, 0])[0]

    def evolve(self, time_array=None, mask=None):
        """
        Evolves the dynamical system in time according to the
        eigendecomposition of A.
        :param time_array: array of time for which to approximate state.
            If None, then approximate state at the time points
            where snapshots are taken
        :return: array where each column is approximated state at time t
        """

        masked_eigenvalues = self.eigenvalues
        masked_modes = self.modes
        if mask is not None:
            masked_eigenvalues[~mask] = 0
            masked_modes[:, ~mask] = 0

        if time_array is None:
            num_snapshots = self.snapshots.shape[1]
            tbase = np.outer(masked_eigenvalues, np.ones(num_snapshots))
            tpower = np.arange(start=0, stop=num_snapshots)
        else:
            tbase = np.outer(masked_eigenvalues, np.ones(time_array.size))
            tpow = time_array/self.dt

        y1 = np.power(tbase, tpower)
        y2 = y1 * self.coefficients[:, None]
        return masked_modes.dot(y2)

    def create_mask(self, threshold = 0.1):
        return np.abs(self.omegas) < threshold

    def clear(self):
        self.snapshots = None
        self.eigenvalues = None
        self.eigenvectors = None
        self.omegas = None
        self.modes = None
        self.coefficients = None
        self.dt = None
        self.dim = None

    def plot_omegas(self, ax):
        return ax.scatter(np.real(self.omegas), np.imag(self.omegas))

    @staticmethod
    def create_tallskinny(x):
        """
        :param x:
        :return:
        """
        cols = x.shape[0]
        rows = int(x.size/x.shape[0])
        data_matrix = np.zeros(shape=(rows, cols))

        for i in range(cols):
            data_matrix[:, i] = x[i].flatten(order = 'C')

        return data_matrix


from django import forms
from .models import Recipe

class RecipeForm(forms.ModelForm):
    class Meta:
        model = Recipe
        fields = (
                    'title', 'source', 'contributor',
                    'serves', 'prep_time', 'cook_time',
                    'ingredient_list', 'ingredient_list', 'instructions',
                    'meal_type', 'dish_type'
                    )

class UrlRecipeForm(forms.Form):
    url = forms.CharField(label = 'url', max_length = 200)
    contributor = forms.CharField(label = 'your name', max_length = 50)


from portage import os
from _emerge.AsynchronousTask import AsynchronousTask
from _emerge.PollScheduler import PollScheduler

class AsyncScheduler(AsynchronousTask, PollScheduler):

	def __init__(self, max_jobs=None, max_load=None, **kwargs):
		AsynchronousTask.__init__(self)
		PollScheduler.__init__(self, **kwargs)

		if max_jobs is None:
			max_jobs = 1
		self._max_jobs = max_jobs
		self._max_load = max_load
		self._error_count = 0
		self._running_tasks = set()
		self._remaining_tasks = True
		self._term_check_id = None
		self._loadavg_check_id = None

	def _poll(self):
		if not (self._is_work_scheduled() or self._keep_scheduling()):
			self.wait()
		return self.returncode

	def _cancel(self):
		self._terminated.set()
		self._termination_check()

	def _terminate_tasks(self):
		for task in list(self._running_tasks):
			task.cancel()

	def _next_task(self):
		raise NotImplementedError(self)

	def _keep_scheduling(self):
		return self._remaining_tasks and not self._terminated.is_set()

	def _running_job_count(self):
		return len(self._running_tasks)

	def _schedule_tasks(self):
		while self._keep_scheduling() and self._can_add_job():
			try:
				task = self._next_task()
			except StopIteration:
				self._remaining_tasks = False
			else:
				self._running_tasks.add(task)
				task.scheduler = self._sched_iface
				task.addExitListener(self._task_exit)
				task.start()

		# Triggers cleanup and exit listeners if there's nothing left to do.
		self.poll()

	def _task_exit(self, task):
		self._running_tasks.discard(task)
		if task.returncode != os.EX_OK:
			self._error_count += 1
		self._schedule()

	def _start(self):
		self._term_check_id = self._event_loop.idle_add(self._termination_check)
		if self._max_load is not None and \
			self._loadavg_latency is not None and \
			(self._max_jobs is True or self._max_jobs > 1):
			# We have to schedule periodically, in case the load
			# average has changed since the last call.
			self._loadavg_check_id = self._event_loop.timeout_add(
				self._loadavg_latency, self._schedule)
		self._schedule()

	def _wait(self):
		# Loop while there are jobs to be scheduled.
		while self._keep_scheduling():
			self._event_loop.iteration()

		# Clean shutdown of previously scheduled jobs. In the
		# case of termination, this allows for basic cleanup
		# such as flushing of buffered output to logs.
		while self._is_work_scheduled():
			self._event_loop.iteration()

		if self._term_check_id is not None:
			self._event_loop.source_remove(self._term_check_id)
			self._term_check_id = None

		if self._loadavg_check_id is not None:
			self._event_loop.source_remove(self._loadavg_check_id)
			self._loadavg_check_id = None

		if self._error_count > 0:
			self.returncode = 1
		else:
			self.returncode = os.EX_OK

		return self.returncode


n = int(input())
arr = []
for _ in range(n):
    x = int(input())
    arr.append(x)

if n <= 2:
    print(sum(arr))
else:
    tmp = arr[:]  # 얕은 복사(tmp = 원본)
    arr[1] = arr[0] + arr[1]
    arr[2] = max(tmp[1] + tmp[2], tmp[0] + tmp[2])

    for i in range(3, n):
        arr[i] = max(max(arr[:i - 2]) + tmp[i - 1] + tmp[i], max(arr[:i - 1]) + tmp[i])
    print(max(arr))


def f(x):
    return x**2-5*x-9

def falsePosition(x0,x1,e):
    step = 1
    condition = True

    while condition:
        x2 = x0 - (((x1-x0) * f(x0))/( f(x1) - f(x0) ))
        print('Iteration-%d, x2 = %0.6f and f(x2) = %0.6f' % (step, x2, f(x2)))

        if f(x0) * f(x2) < 0:
            x1 = x2
        else:
            x0 = x2

        step = step + 1
        condition = abs(f(x2)) > e

    print('\nRequired root is: %0.8f' % x2)

x0 = input('x0: ')
x1 = input('x1: ')
e = input('Tol: ')

x0 = float(x0)
x1 = float(x1)
e = float(e)

if f(x0) * f(x1) > 0.0:
    print('f(a) * f(b) already > 0.')
    print('Try Again with different guess values.')
else:
    falsePosition(x0,x1,e)


from operator import index
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
import pyodbc
import pandas as pd
from tabulate import tabulate

"""
DB is connected to python

"""
connection = pyodbc.connect('Driver={SQL Server};'
                      'Server=CB-CH-L-0062;'
                      'Database=hotel_db;'
                      'Trusted_Connection=yes;')

cursor = connection.cursor()

data= pd.read_sql("select * from menu",con=connection)

connection.timeout = 60
connection.autocommit = True

print("**********************    Hi Welcome to Hotel xxxx       **********************")

print("       MENU           ")

print(tabulate(data, headers = 'keys', tablefmt = 'psql'))

print("    ******  Please order your food  *****     ")

name =input(" Enter Your Name :")

food_name = input("Hi {} Give the food name :".format(name))

food_list = food_name.split(',')

dict={}
for a in food_list:
    val=input(" Quantity of {} :".format(a))
    cursor.execute("Insert into order_details(food_name,qty,customer_name) values(?,?,?)",a,val,name)
    dict[a]=val

print("***********   Please Confirm Your Order      ***********")

order_query="select food_name as Food, qty as Quantity from Order_details where customer_name=''{}''".format(name)
order_display=pd.read_sql("select food_name as Food, qty as Quantity from Order_details where customer_name={}".format(name),con=connection)

print(tabulate(order_display, headers = 'keys', tablefmt = 'psql'))

print("************** PLEASE CONFIRM YOUR ORDER *************")

bill=pd.read_sql("select isnull(max(bill_no),0) as cnt from bill_table",con=connection)

bill_no=int(bill["cnt"])+1

amount=0
c_amount=0
m_amount=0
b_amout=0
l_amount=0
for key,value in dict.items():
    if key=="Chicken Biriyani":
        c_amount+=180*int(value)
        cursor.execute("insert into bill_table(bill_no,customer_name,foodname,quantity,bill_amount) values(?,?,?,?,?)",bill_no,name,key,value,c_amount)
    elif key=="Mutton Biriyani":
        m_amount+=250*int(value)
        cursor.execute("insert into bill_table(bill_no,customer_name,foodname,quantity,bill_amount) values(?,?,?,?,?)",bill_no,name,key,value,m_amount)
    elif key=="Black Forest":
        b_amout+=100*int(value)
        cursor.execute("insert into bill_table(bill_no,customer_name,foodname,quantity,bill_amount) values(?,?,?,?,?)",bill_no,name,key,value,b_amout)
    elif key=="Lassi":
        l_amount+=80*int(value)
        cursor.execute("insert into bill_table(bill_no,customer_name,foodname,quantity,bill_amount) values(?,?,?,?,?)",bill_no,name,key,value,l_amount)

amount=c_amount+m_amount+b_amout+l_amount

print("******************* Please Check Your Bill *******************")

query="select bill_no as [Bill Number], foodname as  Dish , quantity as Quantity, bill_amount as Amount from bill_table where bill_no={}".format(bill_no)
bill_data = pd.read_sql(query,con=connection)

print(tabulate(bill_data, headers = 'keys', tablefmt = 'psql'))

print("Your Bill Amount is : {}".format(amount))


"""Base interface for backends."""

class GeolocationBackend(object):
    """Base interface for backends."""

    def __init__(self, ip):
        """Construct class.

        :param ip: Ip address
        :type: str
        """
        self._ip = ip
        self._continent = None
        self._country = None
        self._geo_data = None
        self._raw_data = {}

    def geolocate(self):
        """Call geoloaction api. Should be overriden."""
        raise NotImplementedError()

    def _parse(self):
        """Parse raw data and store them in object attributes."""
        raise NotImplementedError()

    def data(self):
        """Return the parsed data.

        :return: Parsed data.
        :rtype: dict
        """
        self._parse()

        return {
            'ip': self._ip,
            'continent': self._continent,
            'county': self._country,
            'geo': self._geo_data,
            'raw_data': self._raw_data
        }


# Built-in modules.
import json

# Third-party modules.
import requests
import numpy
import cv2

# My modules.
import const

class FaceApiClient:

    FACE_API_BASE_URL = 'https://japaneast.api.cognitive.microsoft.com/face/v1.0'  # noqa: E501

    @classmethod
    def detect_mat(cls, mat: numpy.ndarray) -> dict:

        # mat をバイナリに変換します。
        encode_succeeded, buffer = cv2.imencode('.png', mat)
        bytes_image = buffer.tobytes()

        # detection を行います。
        return cls.detect(bytes_image)

    @classmethod
    def detect(cls, bytes_image: bytes) -> dict:

        url = f'{cls.FACE_API_BASE_URL}/detect'
        params = {
            'recognitionModel': 'recognition_02',
        }
        headers = {
            'Content-Type': 'application/octet-stream',
            'Ocp-Apim-Subscription-Key':
                const.AZURE_COGNITIVE_SERVICES_SUBSCRIPTION_KEY,
        }
        response = requests.post(url=url,
                                 params=params,
                                 headers=headers,
                                 data=bytes_image)
        return response.json()

    @classmethod
    def identify(cls, person_group_id: str, face_ids: list) -> dict:

        url = f'{cls.FACE_API_BASE_URL}/identify'
        headers = {
            'Content-Type': 'application/json',
            'Ocp-Apim-Subscription-Key':
                const.AZURE_COGNITIVE_SERVICES_SUBSCRIPTION_KEY,
        }
        # NOTE: payload は積載物って意味。
        payload = {
            'personGroupId': person_group_id,
            'faceIds': face_ids,
            'maxNumOfCandidatesReturned': 1,
            'confidenceThreshold': .65,
        }
        response = requests.post(url=url,
                                 headers=headers,
                                 data=json.dumps(payload))
        return response.json()


from functools import reduce

with open('input.txt') as f:
    list_of_strs = f.read().splitlines()

integerize = lambda x: int(x)
list_of_ints =  list(map(integerize,list_of_strs))

duplicate_freq = False
iter = 0
freq = 0
past_freqs = []

while not duplicate_freq:
    print('Iteration',iter,'...')
    iter += 1
    for i in list_of_ints:
        freq += i
        if freq in past_freqs:
            duplicate_freq = True
            print('Duplicate frequency!',freq)
            break
        past_freqs.append(freq)


# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def pathSum(self, root: 'TreeNode', sum: 'int') -> 'int':
        if not root:
            return 0
        return self.dfs(root, sum) + self.pathSum(
            root.left, sum) + self.pathSum(root.right, sum)

    def dfs(self, root, sum):
        # count the number of paths starting from the node
        ans = 0
        if not root:
            return ans
        if root.val == sum:
            ans += 1
        ans += self.dfs(root.left, sum - root.val)
        ans += self.dfs(root.right, sum - root.val)
        return ans


import penman
import re
from penman.graph import Graph
def get_deep_amr(amr):
    t = penman.parse(amr)
    var, branches = t.node
    branches = branches[1:]
    if len(branches) == 1 and len(branches[0][1]) ==1:
        return 2
    if len(branches) == 0:
        return 1
    deep = 0
    for branche in branches:
        role, target = branche
        try:
            sub_amr = penman.format(target)
            deep = max(deep, get_deep_amr(sub_amr) + 1)
        except:
            deep = max(deep, 2)
    return deep

def get_given_deep_amr(amr, Deepth=3):
    indent=3
    amr = penman.decode(amr)
    lines = penman.encode(amr, indent=indent)
    lines = lines.split('\n')
    instances = amr.instances()
    v2name = {}
    for ins in instances:
        v2name[ins[0]] = ins[-1]
    need_lines = []
    deeps = []
    miss_nodes = []
    for line in lines:
        if ':' in line:
            blank = line.split(':')[0]
        else:
            blank = ''
        deep_line = len(blank) // indent
        if deep_line <= Deepth:
            need_lines.append(line)
            deeps.append(deep_line)
        else:
            node = re.findall('\(<.*/', line)
            if len(node):
                node = node[0][1:-1].strip()
                miss_nodes.append(node)
    for idx in range(len(need_lines)):
        now_line = need_lines[idx]
        now_deep = deeps[idx]

        if idx + 1 < len(need_lines):
            next_line = need_lines[idx+1]
            next_deep = deeps[idx+1]
            if now_line.endswith(')') or next_deep > now_deep:
                continue
            if next_deep == now_deep:
                if ' (' in now_line:
                    need_lines[idx] = need_lines[idx] + ' )'
            elif next_deep < now_deep:
                if ' (' in now_line:
                    add_deep = now_deep - next_deep +1
                else:
                    add_deep = now_deep - next_deep
                need_lines[idx] += ')' * add_deep
        else:
            if not now_line.endswith(')'):
                need_lines[idx] += ')' * (now_deep + 1)

    final_lines = []
    for line in need_lines:
        try:
            pointer = re.findall('<pointer-\d+>', line)[0]
        except:
            pointer = "<pointer-never>"
        if pointer in miss_nodes:
            line = line.replace(pointer, '( {} / {} )'.format(pointer, v2name[pointer]))
        final_lines.append(line)

    res_amr = '\n'.join(final_lines)
    return res_amr

def get_all_sub_triples(triples_res, node, all_triples, have_add):
    for triple in all_triples:
        if triple in triples_res:
            continue
        src, rel, tgt = triple
        if src == node:
            triples_res.add(triple)
            if tgt not in have_add and rel != ':instance':
                get_all_sub_triples(triples_res, tgt, all_triples, have_add)
                have_add.add(tgt)

def get_sub_amrs(amr, DEEP=4):
    g = penman.decode(amr)
    all_vars = set()
    for triples in g.triples:
        src, rel, tgt = triples
        if rel == ':instance':
            all_vars.add(src)
    all_amr = []
    for var in all_vars:
        triples_res = set()
        have_add = set()
        get_all_sub_triples(triples_res, var, g.triples, have_add)
        sub_amr  = penman.encode(Graph(triples_res), top=var)
        if get_deep_amr(sub_amr) >= DEEP:
            all_amr.append(sub_amr)
    return all_amr

if __name__ == '__main__':
    amr = """
    (u / underscore-01
    :ARG0 (e / experience-01
             :ARG0 (p / person
                      :wiki "Wang_Shi_(entrepreneur)"
                      :name (n / name
                               :op1 "Wang"
                               :op2 "Shi"))
             :ARG1 (c / condemn-01
                      :ARG0 (p2 / public)
                      :ARG1 p))
    :ARG1 (r / relation-03
             :ARG0 (c3 / class
                       :mod (r2 / rich)
                       :part-of (s / society
                                   :mod (c5 / country
                                            :wiki "China"
                                            :name (n2 / name
                                                      :op1 "China"))))
             :ARG2 (c4 / class
                       :mod (p3 / poor)
                       :part-of s)
             :ARG1-of (t / tense-03))
    :degree (e2 / extent
                :mod (c2 / certain)))
     """
    print(get_given_deep_amr(amr, 4))
    # assert False
    lines = open('../data/amrs/train.txt').readlines()
    amr = ""
    from tqdm import tqdm
    for line in tqdm(lines):
        if "# ::" in line:
            continue
        if line != '\n':
            amr = amr + " " + line
        else:
            if amr == "":
                continue
            for dp in range(1, get_deep_amr(amr)):
                sub_amr = get_given_deep_amr(amr, dp)
                if get_deep_amr(sub_amr) != dp + 1:
                    import ipdb; ipdb.set_trace()
            amr = ""


class Solution(object):
    def validTree(self, numCourses, prerequisites):
        """
        :type n: int
        :type edges: List[List[int]]
        :rtype: bool
        """
        # Number of edges = Number of nodes - 1 and it's acyclic (i.e directed graph with no cycle)
        # Number of edges = Number of nodes - 1 and it's connected (Note: the 1st condition implies that the connected graph has no cycle)
        if len(prerequisites) != numCourses - 1: return False
        G = [[] for _ in range(numCourses)]
        visited = set()
        for post, pre in prerequisites: # undirected graph
            G[post].append(pre)
            G[pre].append(post)
        # BFS
        parent = [-1] * numCourses
        dq = deque([0]) # start with the first node
        while dq:
            cur = dq.popleft()
            visited.add(cur)
            for nxt in G[cur]:
                if nxt not in visited:
                    parent[nxt] = cur
                    dq.append(nxt)
                elif nxt in visited and nxt != parent[cur]:
                    # if a nbr is in visited and the nbr is not the parent, then we have a cycle.
                    return False
        # If the graph is connected then all vertices must be visited
        return len(visited) == numCourses

        # DFS
        # Simple extension of cycle finding algorithm for directed graphs.
        # You need to include the parent as well in the DFS call.
        if self.dfs(0, -1, G, visited):
            return False
        if len(visited) != numCourses:
            # If the graph is connected then all vertices must be visited
            return False
        return True

    def dfs(self, node, parent, G, visited):
        # return true if we find a cycle
        visited.add(node)
        for nbr in G[node]:
            if nbr not in visited:
                if self.dfs(nbr, node, G, visited):
                    return True
            elif nbr in visited and nbr != parent:
                # if a nbr is in visited and the nbr is not the parent, then we have a cycle.
                return True
        return False


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

# Sample program
# read and write value of IO using b-cap

# b-cap Lib URL
# https://github.com/DENSORobot/orin_bcap

import pybcapclient.bcapclient as bcapclient

# set IP Address , Port number and Timeout of connected Robot Controller (RC8,RC8A,COBOTTA,RC9)
host = "192.168.0.1"
port = 5007
timeout = 2000

# set Parameter
# If you want to connect to RC9, please select "VRC9" as the provider name.
# If you want to connect to RC8, RC8A, or COBOTTA, select "VRC" as the provider name.
Name = ""
Provider = "CaoProv.DENSO.VRC"
# Provider = "CaoProv.DENSO.VRC9"
Machine = "localhost"
Option = ""

# Connection processing of tcp communication
m_bcapclient = bcapclient.BCAPClient(host, port, timeout)
print("Open Connection")

# start b_cap Service
m_bcapclient.service_start("")
print("Send SERVICE_START packet")

# Connect to RC8 (RC8(VRC)provider)
hCtrl = m_bcapclient.controller_connect(Name, Provider, Machine, Option)
print("Connect " + Provider)
# get IO128 Object Handl
IOHandl = 0
IOHandl = m_bcapclient.controller_getvariable(hCtrl, "IO128", "")
# read value of I[1]
retIO = m_bcapclient.variable_getvalue(IOHandl)
print("Read Variable IO128 = %s" % retIO)

# read value of IO[128]
# switching IO state
newval = not(retIO)
# write value of IO[128]
m_bcapclient.variable_putvalue(IOHandl, newval)
print("Write Variable :newval = %s" % newval)
# read value of IO[128]
retIO = m_bcapclient.variable_getvalue(IOHandl)
print("Read Variable IO128 = %s" % retIO)

# read and write value of IO[130]-[145] word type (Unsigned 16-bit data.)
# get Object Handl
IOWHandl = 0
IOWHandl = m_bcapclient.controller_getvariable(hCtrl, "IOW130", "")

# read value
retIOW = m_bcapclient.variable_getvalue(IOWHandl)
print("Read Variable IOW130 = %s" % retIOW)

# write value
writevalue = -1  # writevalue = 0b1111111111111111
m_bcapclient.variable_putvalue(IOWHandl, writevalue)
# read value
retIOW = m_bcapclient.variable_getvalue(IOWHandl)
print("Read Variable IOW130 = %s" % retIOW)

# Disconnect
if(IOHandl != 0):
    m_bcapclient.variable_release(IOHandl)
    print("Release IO128")
if(IOWHandl != 0):
    m_bcapclient.variable_release(IOWHandl)
    print("Release IOW130")
# End If
if(hCtrl != 0):
    m_bcapclient.controller_disconnect(hCtrl)
    print("Release Controller")
# End If
m_bcapclient.service_stop()
print("B-CAP service Stop")


from User import *
import string

class Teacher(User):

    def __init__(self, user_name, science_degree, phone_number, email, scientific_direction):
        super().__init__(user_name)
        self.science_degree = science_degree
        self.phone_number = phone_number
        self.email = email
        self.scientific_direction = scientific_direction

    @property
    def science_degree(self) -> str:
        return self.__science_degree

    @science_degree.setter
    def science_degree(self, value: str) -> None:
        if isinstance(value, str):
            if len(value) < 10:
                s = ''.join(value.split())
                if s.isalpha() == True:
                    self.__science_degree = value
                else:
                    log2.warning(f'Пользователь {self.user_name} в названии научной степени ввел цифры')
                    raise ValueError('В названии научной степени не должно быть цифр')
            else:
                log2.warning(f'Пользователь {self.user_name} в названии научной ввёл слишком много букв')
                raise ValueError('Превышен лимит в 10 букв')
        else:
            raise ValueError('Введите строку')

    @property
    def phone_number(self) -> str:
        return self.__phone_number

    @phone_number.setter
    def phone_number(self, value: str) -> None:
        if isinstance(value, str) and value.isdigit() == True:
            if len(value) == 10:
                self.__phone_number = value
            else:
                log2.warning(f'Пользователь {self.user_name} ввёл слишком много цифр в номере телефона')
                raise ValueError('Номер телефона должен содержать 10 цифр')
        else:
            log2.warning(f'Пользователь {self.user_name} вместо номера телефона ввёл буквы')
            raise ValueError('В номере телефона должны содержаться только цифры')

    @staticmethod
    def set_email(new_email: str) -> str:

        ''' Данная функция проверяет валидность введеного емэйл адреса по шаблону Abc@mail.com '''

        dog_index = new_email.find('@')
        if isinstance(new_email, str) and new_email.count('@') == 1 and new_email.count('.') == 1 \
                and any(i in string.ascii_letters for i in new_email[dog_index:]) \
                and '.' in new_email[dog_index:] \
                and any(i in string.ascii_letters for i in new_email[:dog_index]):
            return new_email
        else:
            log2.warning(f'Пользователь ввёл невалидный адрес почты ')
            raise ValueError('Невалидный адрес почты')

    @property
    def email(self) -> str:
        return self.__email

    @email.setter
    def email(self, value) -> None:
        self.__email = self.set_email(value)

    @property
    def scientific_direction(self) -> str:
        return self.__scientific_direction

    @scientific_direction.setter
    def scientific_direction(self, value) -> None:

        if isinstance(value, str) and len(value) < 100 and value.isdigit() == False:
            self.__scientific_direction = value
        else:
            log2.warning(f'Пользователь {self.user_name} попытался ввести в поле "Научное направление" из цифр или непонятных символов')
            raise ValueError('Это поле не должно состоять только из цифр')

    def get_info(self) -> None:

        log.info(f'Пользователь {self.user_name} запросил информацию')
        print(f'Имя: {self.user_name}; Научная степень: {self.science_degree}; Рабочий номер телефона: {self.phone_number};'
              f' Адрес почты:  {self.email}; Научное направление: {self.scientific_direction}')


#!/usr/bin/env.python
# -*- coding:utf-8 -*-
a, b, c = map(int, input().split())

d = {10:'A',11:'B',12:'C'}
def cal(x):
    l = ''
    while x > 0:
        x,j = divmod(x,13)
        if j>9:
            j = d[j]
        l = l+str(j)
    return l[::-1]

a = cal(a)
b = cal(b)
c = cal(c)

print('#{:0>2}{:0>2}{:0>2}'.format(a,b,c))


from scraper.utils import read_excel_file
from scraper.items import ScraperItem
from scraper import PROJECT_ROOT_DIR

from urllib.parse import urlencode

import scrapy
import json
import re

class HeadersMiddleware:
    def process_request(self, request, spider):
        headers = {
            "authority": "www.amazon.ae",
            "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
            "cache-control": "max-age=0",
            "device-memory": "8",
            "downlink": "7.65",
            "dpr": "2",
            "ect": "4g",
            "rtt": "150",
            "sec-ch-device-memory": "8",
            "sec-ch-dpr": "2",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-viewport-width": "865",
            "sec-fetch-dest": "document",
            "sec-fetch-mode": "navigate",
            "sec-fetch-site": "none",
            "sec-fetch-user": "?1",
            "upgrade-insecure-requests": "1",
            "viewport-width": "865",
        }

        request.headers.update(headers)

class AmazonSpider(scrapy.Spider):
    name = "amazon.ae"
    allowed_domains = ["amazon.ae"]
    custom_settings = {
        "CONCURRENT_REQUESTS": 1,
        "DOWNLOAD_DELAY": 10,
        "DOWNLOADER_MIDDLEWARES": {
            "scraper.middlewares.UserAgentMiddleware": 1000,
            HeadersMiddleware: 1100,
        },
    }

    BASE_URL = "https://www.amazon.ae/stores/page/9978F790-7843-44F0-834F-92C086AC4A19/search"
    BASE_URL_PARAMS = {
        "ref_": "ast_bln",
        "terms": "",
    }

    def start_requests(self):
        params = self.BASE_URL_PARAMS.copy()
        df = read_excel_file(filepath=f"{PROJECT_ROOT_DIR}/products.xlsx")
        for item in df[["Product Number", "Product Name EN"]].values.tolist():
            product_number, product_name = item
            params["terms"] = product_number
            print(f"{self.BASE_URL}?{urlencode(params)}",'--=========================')
            yield scrapy.Request(
                url=f"{self.BASE_URL}?{urlencode(params)}",
                callback=self.parse,
                meta={
                    "_product_number": product_number,
                    "_product_name": product_name,
                },
            )

    def parse(self, response):
        products = re.findall(r"products\":(\[.+\"version\":\"2.0\"}]),", response.text)
        products = products[0] if products else "[]"
        products = json.loads(products)

        product = products[0] if products else {}
        buy_options = product.get("buyingOptions")
        buy_option = buy_options[0].get("price", {}) if buy_options else {}

        try:
            discount_price = buy_option.get("priceToPay", {}).get("moneyValueOrRange", {}).get("value", {}).get("amount", 0)
            price = buy_option.get("basisPrice", {}).get("moneyValueOrRange", {}).get("value", {}).get("amount", 0)

            yield ScraperItem(
                product_number=response.meta["_product_number"],
                product_name=response.meta["_product_name"],
                discount_price=discount_price,
                price=price,
            )

        except AttributeError:
            pass


import click

from interface import confirm, prompt, service_layer, echo, display

import container_optimisation.container_loading as container_loading

def home(prompt_function):
    def decorated_function(* args, ** kwargs):
        try :
            return prompt_function(* args, ** kwargs)
        except (click.exceptions.Abort, KeyboardInterrupt):
            click.echo("\nBack to the menu")
            print("\n")
            return
    return decorated_function

def load() -> None:
    """
    Loads the database
    """
    service_layer.load()

def save_and_quit() -> None:
    """
    Saves the database then quits
    """
    service_layer.save_and_quit()

# Package

def add_one_package() -> None:
    """
    Adds one package to the database by asking its information
    to the user then echos the new package id.
    """
    keep_looping = True
    while keep_looping:
        package_informations = prompt.package_information("package")
        if confirm.package(package_informations):
            new_package_id = service_layer.add_one_package(package_informations)
            echo.id_package(new_package_id)
            keep_looping = False
    print("\n")

@home
def add_packages_to_database() -> None:
    """
    Adds packages to the database by two possible ways :
    - Adds the packages one by one by asking their information
        to the user then echos the new package id
    - Adds N times packages sharing the same informations then echos all the new packages id
    """

    if confirm.enter_one_by_one():
        for _ in range(prompt.number_packages()):
            add_one_package()
    else:
        # we have to separate the register of the informations about the package
        # and the creation of the nb same packages
        for _ in range(prompt.number_references()):
            keep_looping = True
            while keep_looping:
                packages_informations = prompt.package_information("package")
                nb_packages = prompt.number_packages()
                packages_id_of_this_reference = set()

                if confirm.package_reference_and_amount(packages_informations, nb_packages):

                    keep_looping = False

                    for _ in range(nb_packages):
                        new_package_id = service_layer.add_one_package(packages_informations)
                        packages_id_of_this_reference.add(new_package_id)

                    echo.ids(packages_id_of_this_reference)
                    print()
@home
def del_packages_from_database() -> None:
    """Deletes N packages from the database by asking their id to the user.
        N : an input asked to the user"""

    for _ in range(prompt.number_packages()):
        keep_looping = True
        while keep_looping:
            package_id = prompt.package_id()
            if confirm.del_objects():
                service_layer.del_one_package(package_id)
                keep_looping = False
    print("\n")

@home
def change_status_package() -> None:
    """
    Changes the status of a package by asking the user
    its id and its new status.
    """

    keep_looping = True
    while keep_looping:
        package_id = prompt.package_id()
        new_status = prompt.new_package_status()
        if confirm.change_status(package_id, new_status):
            service_layer.change_package_status(package_id, new_status)
            keep_looping = False
        print("\n")

# DropOff

@home
def declare_dropoff() -> None: # fonction destinée aux DropOffs dans le sens où on rentre les packages par id
    """
    Adds a new dropoff to the database by asking the user
    its informations and the packages to add to this dropoff.
    """

    keep_looping = True
    while keep_looping:
        dropoff_informations = prompt.dropoff_information()
        if confirm.dropoff(dropoff_informations):
            keep_looping = False
    dropoff_id = service_layer.declare_dropoff(dropoff_informations)
    echo.id_dropoff(dropoff_id)
    register_packages_in_a_dropoff(dropoff_id)

@home
def register_packages_in_a_dropoff(dropoff_id: str) -> None: # fonction destinée aux DropOffs dans le sens où on rentre les packages par id
    """
    Adds packages to an dropoff one by one by asking their id
    to the user.

    Parameters :
        dropoff_id (str) : The id of the dropoff to add packages to
    """

    for _ in range(prompt.number_packages()):
        keep_looping = True
        while keep_looping:
            package_id = prompt.package_id()
            if confirm.pick_package(package_id):
                service_layer.register_package_in_a_dropoff_by_id(package_id, dropoff_id)
                click.echo("Package added")
                keep_looping = False
            else:
                click.echo("Aborted")

@home
def declare_dropoff_actual_arrival() -> None: # fonction destinée aux futurs DropOffs et potentiellement aux dropoff
    """
    - Changes the arrival date of an dropoff by asking the user
    its id and the actual arrival date to the warehouse.
    - Updates the status of the packages of the given dropoff to `"warehouse"`.
    """

    keep_looping = True
    while keep_looping:
        id_dropoff = prompt.dropoff_id()
        actual_arrival_date = prompt.date("Arrival date ")
        if confirm.update_dropoff(id_dropoff, actual_arrival_date):
            service_layer.declare_dropoff_actual_arrival(id_dropoff, actual_arrival_date)
            keep_looping = False

@home
def change_arrival_date() -> None:
    """ Update the arrival date of a dropoff """

    keep_looping = True
    while keep_looping:
        dropoff_id = prompt.dropoff_id()
        actual_arrival_date = prompt.date("Arrival date ")
        if confirm.update_dropoff(dropoff_id, actual_arrival_date):
            service_layer.update_dropoff_arrival_date(dropoff_id, actual_arrival_date)
            keep_looping = False

@home
def add_packages_to_a_dropoff() -> None:
    """ Add packages to a dropoff after its declaration """
    dropoff_id = prompt.dropoff_id()
    register_packages_in_a_dropoff(dropoff_id)

@home
def del_packages_of_a_dropoff() -> None:
    """ Del Packages of a Dropoff after its declaration """
    dropoff_id = prompt.dropoff_id()
    package_id = prompt.package_id()
    confirm.remove_package_from_dropoff(package_id, dropoff_id)
    service_layer.remove_package_from_dropoff(package_id, dropoff_id)

@home
def del_dropoffs() -> None:
    """
    Deletes N dropoffs from the database by asking their id to the user.
        N : an input asked to the user
    """
    for _ in range(prompt.number_dropoffs()):
        keep_looping = True
        while keep_looping:
            dropoff_id = prompt.dropoff_id()
            if confirm.del_objects():
                service_layer.del_dropoff(dropoff_id)
                keep_looping = False
        print("\n")

# Shipment

@home
def declare_shipment() -> None: # fonction destinée aux Shipments
    """
    Adds a new shipment to the database by asking the user
    its informations and the packages to add to this shipment.
    """

    keep_looping = True
    while keep_looping:
        shipment_informations = prompt.shipment_information()
        keep_looping = not confirm.shipment(shipment_informations)
    shipment_id = service_layer.declare_shipment(shipment_informations)
    echo.id_shipment(shipment_id)
    register_packages_in_an_shipment(shipment_id)

@home
def register_packages_in_an_shipment(shipment_id: str) -> None: # fonction destinée aux Shipments
    """
    Adds packages to an shipment by two different ways :
    - Adds the packages one by one by asking their information or their id
    to the user. Then echos the new package id of newly created packages.
    - Adds N times packages sharing the same informations.
    The package informations used as reference can be entered by the user
    or determined thanks to the id of an existing package

    Parameters :
        shipment_id (str) : The id of the shipment to add packages to
    """

    if confirm.enter_one_by_one():
        for _ in range(prompt.number_packages()):

            if confirm.enter_packages_by_id():
                keep_looping = True
                while keep_looping:
                    package_id = prompt.package_id()
                    if confirm.pick_package(package_id):
                        service_layer.register_package_in_a_shipment_by_id(package_id, shipment_id)
                        click.echo("Package added to the shipment")
                        keep_looping = False
                    else:
                        click.echo("Aborted")
                    print("\n")
            else:
                keep_looping = True
                while keep_looping:
                    package_informations = prompt.package_information("shipment")
                    if confirm.package(package_informations):
                        new_package_id = service_layer.add_one_package(package_informations)
                        service_layer.register_package_in_a_shipment_by_id(new_package_id, shipment_id)
                        echo.id_package(new_package_id)
                        keep_looping = False
                print("\n")

    else:
        for _ in range(prompt.number_references()):
            keep_looping = True
            while keep_looping:
                if confirm.enter_packages_by_id():
                    print("Prompt the id of the package you want to use as reference")
                    package_id = prompt.package_id()
                    nb_packages = prompt.number_packages()
                    packages_id_of_this_reference = set()

                    if confirm.package_reference_by_id_and_amount(package_id, nb_packages):
                        keep_looping = False
                        for _ in range(nb_packages):
                            new_package_id = service_layer.add_one_package_by_id(package_id)
                            service_layer.register_package_in_a_shipment_by_id(new_package_id, shipment_id)
                            packages_id_of_this_reference.add(new_package_id)

                else:
                    packages_informations = prompt.package_information("shipment")
                    nb_packages = prompt.number_packages()
                    packages_id_of_this_reference = set()

                    if confirm.package_reference_and_amount(packages_informations, nb_packages):
                        keep_looping = False
                        for _ in range(nb_packages):
                            new_package_id = service_layer.add_one_package(packages_informations)
                            service_layer.register_package_in_a_shipment_by_id(new_package_id, shipment_id)
                            packages_id_of_this_reference.add(new_package_id)
            echo.ids(packages_id_of_this_reference)
            print("\n")

@home

def update_status_shipment() -> None:
    """ Change the status of a given shipment"""
    keep_looping = True
    while keep_looping:
        shipment_id = prompt.shipment_id()
        new_status = prompt.new_shipment_status()
        if confirm.change_status(shipment_id, new_status):
            service_layer.change_shipment_status(shipment_id, new_status)
            keep_looping = False
        print("\n")

@home
def add_packages_to_a_shipment() -> None:
    """ Add packages to a shipment """
    shipment_id = prompt.shipment_id()
    register_packages_in_an_shipment(shipment_id)

@home
def del_packages_to_a_shipment() -> None:
    """ Del packages to a shipment """
    del_packages_from_database()

@home
def declare_shipment_actual_departure_from_warehouse() -> None:
    """
    - Changes the departure date of an shipment by asking the user
    its id and the actual departure date from the warehouse.
    - Updates the status of the given shipment to `"outbound"`.
    - Updates the status of the packages of the given shipment to `"shipbound"`.
    """

    keep_looping = True
    while keep_looping:
        shipment_id = prompt.shipment_id()
        actual_departure_date_from_warehouse = prompt.date("Departure date from warehouse ")
        if confirm.exit_shipment(shipment_id):
            service_layer.declare_shipment_actual_departure_from_warehouse(actual_departure_date_from_warehouse, shipment_id)
            keep_looping = False

@home
def declare_shipment_actual_delivery() -> None:
    """
    - Changes the delivery date of an shipment by asking the user
    its id and the actual delivery date from the warehouse.
    - Updates the status of the given shipment to `"delivered"`.
    - Updates the status of the packages of the given shipment to `"delivered"`.
    """

    keep_looping = True
    while keep_looping:
        shipment_id = prompt.shipment_id()
        actual_delivery_date = prompt.date("Delivered date ")
        if confirm.delivered_shipment(shipment_id, actual_delivery_date):
            service_layer.declare_shipment_actual_delivery(actual_delivery_date, shipment_id)
            keep_looping = False

@home
def del_shipments() -> None:
    """
    Deletes N shipments from the database by asking their id to the user.
        N : an input asked to the user
    """
    for _ in range(prompt.number_shipments()):
        keep_looping = True
        while keep_looping:
            shipment_id = prompt.shipment_id()
            if confirm.del_objects():
                service_layer.del_shipment(shipment_id)
                keep_looping = False
        print("\n")

# groupage

@home
def declare_groupage() -> None:
    """
    Adds a new groupage to the database by asking the user
    its informations and the shipments to add to this groupage.
    """

    freight_forwarder = prompt.freight_forwarder()
    groupage_id = service_layer.declare_groupage(freight_forwarder)
    for _ in range(prompt.number_shipments()):
        keep_looping = True
        while keep_looping:
            shipment_id = prompt.shipment_id()
            if confirm.shipment_to_add(shipment_id):
                service_layer.add_shipment_to_a_groupage(groupage_id, shipment_id)
                keep_looping = False
            else:
                print("Aborted")
    echo.id_groupage(groupage_id)

@home
def del_groupages() :
    """
    Deletes N groupages from the database by asking their id to the user.
        N : an input asked to the user
    """
    for _ in range(prompt.number_groupages()):
        keep_looping = True
        while keep_looping:
            groupage_id = prompt.groupage_id()
            if confirm.del_objects():
                service_layer.del_groupage(groupage_id)
                keep_looping = False
        print("\n")

# Trip

@home
def declare_trip():
    ship_name = prompt.ship_name()
    departure_date = prompt.departure_date()
    trip_id = service_layer.declare_trip(ship_name, departure_date)
    for _ in range(prompt.number_groupages()):
        keep_looping = True
        while keep_looping:
            groupage_to_add_id = prompt.groupage_id()
            if confirm.groupage_to_add(groupage_to_add_id):
                service_layer.add_groupage_to_a_trip(trip_id, groupage_to_add_id)
                keep_looping = False
            else:
                print("Canceled, enter the right groupage")
    echo.id_trip(trip_id)

@home
def del_trip():
    """
    Deletes N trips from the database by asking their id to the user.
        N : an input asked to the user
    """
    for _ in range(prompt.number_trips()):
        keep_looping = True
        while keep_looping:
            trip_id = prompt.trip_id()
            if confirm.del_objects():
                service_layer.del_trip(trip_id)
                keep_looping = False
        print("\n")

@home
def load_trip() -> None:
    """Change the statuses of the packages """

    trip_id = prompt.trip_id()
    service_layer.load_packages_in_trip(trip_id)
    print("Packages and Shipments status changed")

    print("\n")

@home
def select_available_containers():
    nb_container_available = prompt.number_containers_available()
    nb_container_wide = prompt.number_containers_wide()
    nb_container_standard = nb_container_available - nb_container_wide
    return service_layer.available_containers(nb_container_wide, nb_container_standard)

@home
def plan_loading() -> None:
    """
    Proposes a plan to load a particular trip
    Generates the associated pdf
    """

    trip_id = prompt.trip_id()
    available_containers = select_available_containers()
    groupage_placements = container_loading.trip_loading(trip_id, available_containers)
    display.plot_trip_loading_proposal(groupage_placements)
    click.echo(f"We propose the following plannification to load the trip n°{trip_id}")
    if confirm.plan_loading():
        display.save_trip_loading_proposal(groupage_placements, trip_id)
        display.generate_validated_pdf_loading_plan(trip_id)
        container_loading.validate_trip_loading_proposal(groupage_placements)
    else :
        print("You can update the trip to have a new loading proposal. '\n'")

@home
def add_groupage_to_a_trip():
    trip_id = prompt.trip_id()
    for _ in range(prompt.number_groupages()):
        keep_looping = True
        while keep_looping:
            groupage_id = prompt.groupage_id()
            if confirm.add_objects():
                service_layer.add_groupage_to_trip(trip_id, groupage_id)
                keep_looping = False
        print("\n")
@home
def del_groupage_from_a_trip():
    trip_id = prompt.trip_id()
    for _ in range(prompt.number_groupages()):
        keep_looping = True
        while keep_looping:
            groupage_id = prompt.groupage_id()
            if confirm.del_objects():
                service_layer.del_groupage_from_trip(trip_id, groupage_id)
                keep_looping = False
        print("\n")

# Containers

@home
def add_containers():
    for _ in range(prompt.number_containers()):
        type_container = click.prompt("Type of container ",
        default="standard", type=click.Choice(("palet_wide","standard"), case_sensitive=False))
        container_id = service_layer.add_container_to_database(type_container)
        echo.id_container(container_id)
        print("\n")

@home
def del_containers():
    for _ in range(prompt.number_containers()):
        keep_looping = True
        while keep_looping:
            container_id = prompt.container_id()
            if confirm.del_objects():
                service_layer.del_container_from_database(container_id)
                keep_looping = False
        print("\n")


from datetime import datetime
from datetime import date

# returns todays date
datetime.today()

today = datetime.today()
# datetime object- important because you cant mix these objects
type(today)

todaydate = date.today()
todaydate

# note that when using date, the object type is a date and note a datetime
type(todaydate)

todaydate.month
todaydate.year
todaydate.day

# assigning the date to a variable
christmas = date(2022, 12, 5)

# can see that the type is automatically a timedelta
christmas - todaydate

# using the days attribute of the datetime class returns an integer
(christmas - todaydate).days

if christmas != today:
    print(f'its not christmas yet! there are still {(christmas - todaydate).days} days left')
else:
    print('yay its christmas')


import imp
from celery import shared_task
import requests
import json
from .models import CompanyNew, TicketInterest
from datetime import datetime, timedelta
import pytz
from django.core.mail import send_mail
from email_sender.settings import EMAIL_HOST_USER

@shared_task(name="send_daily")
def send_daily():
    print('started')
    today = datetime.now().replace(hour=0, minute=0,second=0,microsecond=0)
    CompanyNew.objects.filter(datetime_created__lt=today).delete()

    tickets = list(TicketInterest.objects.exclude(users_subscribed=None).prefetch_related('users_subscribed'))
    group_by_tickets={

    }
    news=list(CompanyNew.objects.all())
    for nnew in news:
        if nnew.related in tickets:
            group_by_tickets[nnew.related]=group_by_tickets[nnew.related] if nnew.related in group_by_tickets else []
            group_by_tickets[nnew.related].append(nnew.summary)
    for gt in group_by_tickets:
        users_subscr = list(gt.users_subscribed.all())
        emails = [us.email for us in users_subscr]
        content =group_by_tickets[gt]
        send_mail(
            json.dumps(content[:5]),
            'Summary',
            EMAIL_HOST_USER,
            emails,
            fail_silently=False,
        )


"""
Project:
File:       differential_operators.py
Created by: louise
On:         8/22/17
At:         4:10 PM
"""

import numpy as np

def forward_gradient(im):
    """
    Function to compute the forward gradient of the image I.
    Definition from: http://www.ipol.im/pub/art/2014/103/, p208
    :param im: numpy array [MxN], input image
    :return: numpy array [MxNx2], gradient of the input image, the first channel is the horizontal gradient, the second
    is the vertical gradient.
    """
    h, w = im.shape
    gradient = np.zeros((h, w, 2), im.dtype)  # Allocate gradient array
    # Horizontal direction
    gradient[:, :-1, 0] = im[:, 1:] - im[:, :-1]
    # Vertical direction
    gradient[:-1, :, 1] = im[1:, :] - im[:-1, :]

    return gradient

def backward_divergence(grad):
    """
    Function to compute the backward divergence.
    Definition in : http://www.ipol.im/pub/art/2014/103/, p208

    :param grad: numpy array [NxMx2], array with the same dimensions as the gradient of the image to denoise.
    :return: numpy array [NxM], backward divergence
    """

    h, w = grad.shape[:2]
    div = np.zeros((h, w), grad.dtype)  # Allocate divergence array
    # Horizontal direction
    d_h = np.zeros((h, w), grad.dtype)
    d_h[:, 0] = grad[:, 0, 0]
    d_h[:, 1:-1] = grad[:, 1:-1, 0] - grad[:, :-2, 0]
    d_h[:, -1] = -grad[:, -2:-1, 0].flatten()

    # Vertical direction
    d_v = np.zeros((h, w), grad.dtype)
    d_v[0, :] = grad[0, :, 1]
    d_v[1:-1, :] = grad[1:-1, :, 1] - grad[:-2, :, 1]
    d_v[-1, :] = -grad[-2:-1, :, 1].flatten()

    # Divergence
    div = d_h + d_v
    return div


import signal
import uuid
import socket, sys
import hashlib
import threading
import os,random
from pathlib import Path
#from os import fork
from os.path import exists

def prepIp(ip):
    ip=ip.split(".")
    for n in range(0,len(ip)):
        group=""
        for i in range(0,3-len(ip[n])):
            group+="0"
        group+=ip[n]
        ip[n]=group
    return ".".join(ip)

def LocalIp():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("8.8.8.8", 80))
    ip=s.getsockname()[0]
    s.close()
    return str(ip)
def Resize(stringa, dim):
        tmp=""
        for n in range(0,dim-len(stringa)):
            tmp+="0"
        tmp+=stringa
        return tmp

def calcoloMD5(filename):
    file = open(filename, 'rb')
    stringaMD5 = hashlib.md5()
    dati = 0
    while dati != b'':
        dati = file.read()
        stringaMD5.update(dati)

    stringaMD5 = stringaMD5.hexdigest().upper()
    return stringaMD5

def Login(porta):
    ip=prepIp(LocalIp())
    porta=Resize(str(porta),5)
    client=SendData(f"LOGI{ip}{porta}",ipDirectory,80)
    client.recv(4)
    return client.recv(16).decode(),porta
#metodo che invia al server la richiesta di condivisione del file
def Aggiungi(sessionID,filename):
    md5 = calcoloMD5(f"{percorso}/{filename}")
    print(f"MD5 del file {filename}: {md5}")
    client=SendData(f"ADDF{sessionID}{md5}{filename.ljust(100)}",ipDirectory,80)
    client.recv(4)
    totfile=client.recv(3).decode()
    client.close()
    return totfile,filename,md5

def SendData(send,ip,port):
    client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    try:
        client.connect((ip,port))
    except:
        print("il server non è raggiungibile, riprova più tardi")
        exit()

    client.send(str(send).encode())
    return client
def Ricerca(sessionid, descrizione):
    client=SendData(f"FIND"+sessionid+descrizione,ipDirectory,80)
    client.recv(4)
    nmd5=int(client.recv(3).decode())
    print(f"ci sono {nmd5} file che combaciano con '{descrizione}'\n")
    for n in range(0,nmd5):
        md5=client.recv(32).decode()
        name=client.recv(100).decode()
        tot=int(client.recv(3).decode())
        print(f"MD5: {md5} | Nome: {name}\n")
        for i in range(0,tot):
            print(f"\tIP: {client.recv(15).decode()} | PORTA: {int(client.recv(5).decode())}")
    client.close()

def showFile(path):
    files = os.listdir(path)
    for i in files:
        print(i)

def InvioFile(peer,file):
    send="ARET"
    if(not Path(file).is_file()):
        peer.send(("ARET"+"000000").encode())
        return
    fd = os.open(file, os.O_RDONLY)
    dim=os.path.getsize(file)
    filechunk=dim//4096
    last=dim%4096
    if(last!=0):filechunk+=1
    send+=Resize(str(filechunk),6)
    peer.send(send.encode())
    send=""
    for n in range(0,dim//4096):
        send+="04096"
        peer.send(send.encode())

        send=os.read(fd,4096)
        peer.send(send)
        send=""
    if(last!=0):
        send+=Resize(str(last),5)
        peer.send(send.encode())
        send=os.read(fd,4096)
        peer.send(send)
    os.close(fd)

def AvvioAscoltoServer(porta):
    sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(("",int(porta)))
    sock.listen(10)
    while True:
        client,addr= sock.accept()
        pid=os.fork()
        #pid=0
        if(pid==0):
            richiesta=client.recv(4).decode()
            if(richiesta=="RETR"):
                md5=client.recv(32).decode()
                for n in range(0,len(listaFileCondivisi)):
                    if(listaFileCondivisi[n][1]==md5 and Path(percorso+"/"+listaFileCondivisi[n][0]).is_file()==True and calcoloMD5(percorso+"/"+listaFileCondivisi[n][0])==md5):
                        InvioFile(client,percorso+"/"+listaFileCondivisi[n][0])
                        break
                client.send(("ARET"+"000000").encode())
            client.close()
            exit()

def DownloadFilePeer(ip,port,md5,namefile):
    send="RETR"+md5
    client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    try:
        client.connect((ip,int(port)))
    except:
        print("il server non è raggiungibile, riprova più tardi")

        return
    client.send(send.encode())
    client.recv(4)
    pid=os.fork()
    if(pid==0):
        chunk=int(client.recv(6).decode())
        if(chunk==0):
            print("il file è vuoto oppure non è più in condivisione\n")
            exit()
        if(exists("download/"+namefile)): os.remove("download/"+namefile)
        fd = os.open("download/"+namefile, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o777)
        for n in range(0,chunk):
            buf=client.recv(int(client.recv(5).decode()))
            os.write(fd,buf)
        os.close(fd)
        print("ricevuto il file "+namefile)
        client2=SendData("RREG"+sessionid+md5+prepIp(ip)+Resize(str(port),5),ipDirectory,80)
        client2.recv(4)
        print(f"il file è stato scaricato {int(client2.recv(5).decode())} volte")
        client2.close()

        client.close()
        exit()

def RimuoviFile(sessionid,md5):
    send="DELF"+sessionid+md5
    client=SendData(send,ipDirectory,80)
    client.recv(4)
    totmd5=client.recv(3).decode()
    print(f"il file {list(filter(lambda x: x[1]==md5,listaFileCondivisi))[0][0]} è stato rimosso dalla condivisione\nCopie del file ancora in condivisione {totmd5}")

def AggiornaFileCondivisi(listaFileCondivisi,listaNuova):
    files = os.listdir(percorso)
    for n in files:
        listaNuova.append([n,calcoloMD5(percorso+"/"+n)])

    #rimozione file non più presenti
    for n in range(0,len(listaFileCondivisi)):
        tmp=list(filter(lambda x:x[1]==listaFileCondivisi[n][1],listaNuova))
        if(len(tmp)==0):
            RimuoviFile(sessionid,listaFileCondivisi[n][1])

    for n in range(0, len(listaNuova)):
        tmp=list(filter(lambda x:x[1]==listaNuova[n][1],listaFileCondivisi))
        if(len(tmp)==0):
            Aggiungi(sessionid,listaNuova[n][0])
    listaFileCondivisi=listaNuova.copy()
    return listaFileCondivisi.copy()

def Logout():
    send="LOGO"+sessionid
    client=SendData(send,ipDirectory,80)
    client.recv(4)
    nfile=client.recv(3).decode()
    print("Log out effettuato\nSono stati rimossi dalla condivisione "+nfile+" file")
    os.abort()

def Ctrl_c(signal,frame):
    Logout()

ipDirectory=str(sys.argv[1])
#ipDirectory="192.168.1.110"
sessionid, porta=Login(str(random.choice(range(49152,65536))))
if(sessionid=="0000000000000000"):
    print("Si è verificato un errore durante il processo di Log-in")
    exit()

print(f"Il tuo sessionID = {sessionid}")

signal.signal(signal.SIGINT, Ctrl_c)

server_client=threading.Thread(target=AvvioAscoltoServer,args=(porta,))
server_client.start()
listaFileCondivisi=[]
listaNuova=[]
percorso = "file_condivisi"

while True:
    scelta = input("Scegli azione da svolgere: \n1)Aggiorna file condivisi\n2)Ricerca file\n3)Ricevi file\n4)Logout\n")
    if(scelta == "1"):
        listaFileCondivisi=AggiornaFileCondivisi(listaFileCondivisi,listaNuova)
        listaNuova.clear()

    elif(scelta == "2"):
        daCercare = input("scegli il file da cercare\n")
        if(len(daCercare)<1 or len(daCercare)>20):
            print("contenuto non valido")
            continue
        Ricerca(sessionid,daCercare)

    elif(scelta == "3"):
        ipPeer=input("Inserisci l'ip del peer dal quale scaricare\n")
        portaPeer=input("Inserisci la porta del peer\n")
        md5Download=input("inserisci l'md5 del file da scaricare\n")
        namefile=input("nome con il quale salvare il file\n")
        DownloadFilePeer(ipPeer,portaPeer,md5Download,namefile)

    elif(scelta == "4"):
        Logout()

    else:
        print("Opzione non valida")


# -*- encoding: utf-8 -*-
import odoo.addons.decimal_precision as dp
from dateutil.relativedelta import relativedelta
from odoo import models, fields, api, exceptions, _
from datetime import date, datetime
from odoo.exceptions import Warning

class WizardStatusCuotas(models.TransientModel):
    _name = 'loan.wizard.generar.cuotas'
    _description = 'Asistente de Status de Cuotas'
    _rec_name = 'prestamo_id'

    def _get_prestamo(self):
        contexto = self._context
        if 'active_id' in contexto:
            loan_obj = self.env["loan.management.loan"].browse(contexto['active_id'])
            return loan_obj

    # Información General
    date_generation = fields.Date(string="Fecha de Generación", required=True, default=fields.Date.today)
    prestamo_id = fields.Many2one("loan.management.loan", "Prestamo", default=_get_prestamo)
    lista_prestamos = fields.Many2one("loan.management.loan", "Prestamo Numero")
    @api.multi
    def generar_status_cuotas(self, prestamo_id):
        date_generation = fields.Date.today()
        if self.date_generation:
            date_generation = self.date_generation
        # WR:llave para uno o todos los que cumplen el search, se aumenta el campo de seleccion por prestamo
        # print "prestamo_id=%s " % self.lista_prestamos.id
        # print "si entro con dato prestamo_id   ",prestamo_id
        if prestamo_id:
            # print "voy por uno"
            prestamos_obj = self.env["loan.management.loan"].search([('id', '=', self.lista_prestamos.id)])
        if self.lista_prestamos.id in (False, None):
            # print "voy por todos..."
            prestamos_obj = self.env["loan.management.loan"].search([('state', '=', 'progreso')])
        for prestamo in prestamos_obj:
            for cuota in prestamo.cuota_ids:
                if date_generation > cuota.fecha_pago and not cuota.state == 'pagada' and not cuota.saldo_pendiente == 0.0:
                    fecha_run = (datetime.strptime(date_generation, '%Y-%m-%d'))
                    fecha_cuota = (datetime.strptime(cuota.fecha_pago, '%Y-%m-%d'))
                    diferencia_fecha = (fecha_run - fecha_cuota).days #relativedelta(day=fecha_cuota.day, months=fecha_cuota.month)
                    mora = 0.0
                    if round(cuota.saldo_pendiente, 10) >= round(cuota.monto_cuota, 10):
                        #print "Cuota de prestamo igual a saldo"
                        mora = ((cuota.monto_cuota * (prestamo.mora_id.tasa_mora / 100)) / 30) * diferencia_fecha
                        cuota.saldo_pendiente = cuota.monto_cuota + mora
                        cuota.mora = cuota.saldo_pendiente - cuota.monto_cuota
                    else:
                        mora = ((cuota.saldo_pendiente * (prestamo.mora_id.tasa_mora / 100)) / 30) * diferencia_fecha
                        cuota.saldo_pendiente = cuota.saldo_pendiente + mora
                        cuota.mora = mora
                    cuota.write({'state': 'morosa'})
                if date_generation == cuota.fecha_pago and not cuota.state == 'pagada' and not cuota.saldo_pendiente == 0.0:
                    cuota.write({'state': 'vigente'})
                    cuota.mora = 0.0
                    # evitar sobre escribir cuando se ha pagado parcial en estado no vigente WHRC
                    if round(cuota.saldo_pendiente,2) == round(cuota.monto_cuota,2):
                        cuota.saldo_pendiente = cuota.monto_cuota

class WizardGeneratePlanCuotas(models.TransientModel):
    """DCLS"""
    _name = 'loan.wizard.generar.plan.cuotas'
    _description = 'Asistente Generar Plan Cuotas'

    @api.multi
    def generate_plan_cuotas(self):
        context = dict(self._context or {})
        active_ids = context.get('active_ids', []) or []

        for record in self.env['loan.management.loan'].browse(active_ids):
            if record.state not in ('cotizacion'):
                raise UserError(_("Selected loan(s) cannot be confirmed as they are not in 'Aprobado' state."))

            record.get_generar_cuotas()
        return {'type': 'ir.actions.act_window_close'}

class WizardAprobarPlan(models.TransientModel):
    """DCLS"""
    _name = 'loan.wizard.approve'
    _description = 'Asistente Aprobar Plan Cuotas'

    @api.multi
    def generate_approve(self):
        context = dict(self._context or {})
        active_ids = context.get('active_ids', []) or []

        for record in self.env['loan.management.loan'].browse(active_ids):
            if record.state not in ('progress','desembolso'):
                raise UserError(_("Selected loan(s) cannot be confirmed."))

            record.action_aprobar()
        return {'type': 'ir.actions.act_window_close'}

class WizardGenerarContabilidad(models.TransientModel):
    """DCLS"""
    _name = 'loan.wizard.generate.accounting'
    _description = 'Asistente Generar Contabilidad'

    @api.multi
    def generate_accounting(self):
        context = dict(self._context or {})
        active_ids = context.get('active_ids', []) or []

        for record in self.env['loan.management.loan'].browse(active_ids):
            if record.state not in ('aprobado','desembolso'):
                raise UserError(_("Selected loan(s) cannot be confirmed."))

            record.generar_contabilidad()
        return {'type': 'ir.actions.act_window_close'}


# This example uses a csv file with the format:
#
# date        value
#--------------------
# 1/1/2022    10
# 1/1/2022    10
# 1/1/2022    10
# ...
# 1/1/2022    10
# with 11 rows, and the date values are strings
# The ARIMA model forecasts the next 10 days, and plots it using pyplot.

import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA

# Load data
data = pd.read_csv('data.csv')
data['date'] = pd.to_datetime(data['date'])
data.set_index('date', inplace=True)

# Train ARIMA model
model = ARIMA(data, order=(1, 1, 1))
model_fit = model.fit()

# Forecast the next 10 days
future_dates = pd.date_range(start='1/15/2022', periods=10, freq='D')
future_pred = model_fit.forecast(steps=10)

# Plot the predicted values
future_dates_df = pd.DataFrame({'date': future_dates, 'predicted_value': future_pred})

fig, ax = plt.subplots()
ax.plot(data.index, data['value'], label='actual values')
ax.plot(future_dates_df['date'], future_dates_df['predicted_value'], label='predicted values')
ax.legend()

# Set the x-axis limits to show only the relevant days
start_date = data.index.max() - pd.DateOffset(days=30)
end_date = future_dates.max() + pd.DateOffset(days=1)
ax.set_xlim(start_date, end_date)

plt.show()


import torch
import torch.nn as nn
import torch.nn.functional as F

from typing import Callable, Tuple, Union, List

# TODO: Need better system for determining what nodes are illegal to add
# example usage get_illegal_set(tree:SyntaxNode) -> set{'var','const',...}

class SyntaxNode(object):
    # stored op properties in class attribute
    n_args = {
        "+": 2,
        "-": 2,
        "*": 2,
        "/": 2,
        #"^": 2,
        "sin": 1,
        "cos": 1,
        "exp": 1,
        "log": 1,
        "var": 0,
        "const": 0,
        "start": 1,
        "none": 0}

    op_list = {
        "+": torch.add,
        "-": torch.subtract,
        "*": torch.multiply,
        "/": torch.divide,
        #"^": torch.pow,
        "sin": torch.sin,
        "cos": torch.cos,
        "exp": torch.exp,
        "log": torch.log,
        "var": None,
        "const": torch.rand,
        "start": lambda x: x,
        "none": None}

    illegal = {
        '+': set(),
        '-': set(),
        '*': set(),
        '/': {'/'},
        #'^': ['exp', 'log'],
        'sin': {'sin', 'cos', 'exp', 'log'},
        'cos': {'sin', 'cos', 'exp', 'log'},
        'exp': {'sin', 'cos', 'exp', 'log'},
        'log': {'sin', 'cos', 'exp', 'log'},
        'var': set(),
        'const': set(),
        'start': {'var','const'},
        'none': set()}

    # TODO: Probably a better way to track parameters, potentially a memory leak
    parameters = {}

    instance_counter = 0

    def __init__(self, op: str, parent:'SyntaxNode' = None, data:torch.tensor = None):
        self.left = None
        self.right = None
        self.tree_idx = None
        self.parent = parent
        self.data = data
        assert op in SyntaxNode.op_list.keys()
        self.value = op
        self.illegal = SyntaxNode.illegal[self.value]

        # operation, number of arguments
        self.op = SyntaxNode.op_list[op]
        self.n_args = SyntaxNode.n_args[op]

        # update parameters
        if self.value == 'start':
            SyntaxNode.instance_counter += 1
            self.tree_idx = SyntaxNode.instance_counter
            SyntaxNode.parameters[self.tree_idx] = []
        elif self.value == 'const':
            self.tree_idx = self.parent.tree_idx
            self.op = self.op(1, requires_grad =True)
            SyntaxNode.parameters[self.tree_idx].append(self.op)
        else:
            self.tree_idx = self.parent.tree_idx

    def __bool__(self):
        return True

    def __del__(self):
        if self.value == 'start':
            if self.tree_idx in SyntaxNode.parameters.keys():
                #print(f'deleting {self.tree_idx} from param keys: {SyntaxNode.parameters.keys()}')
                del SyntaxNode.parameters[self.tree_idx]

    def get_embedding(self, type_idx: int) -> torch.tensor:
        return F.one_hot(torch.tensor(type_idx), len(SyntaxNode.op_list.keys())).view(1, -1).float()

    def add_node(self, node:'SyntaxNode') -> bool:
        if self.n_args != 0:
            if self.left == None:
                self.left = node
                return True
            elif (self.n_args != 1) and (self.right == None):
                self.right = node
                return True
            else:
                return False
        else:
            return False

    def from_preorder(self, node_list: list) -> list:
        if self.n_args == 2:
            self.left = SyntaxNode(node_list.pop(0), self)
            node_list = self.left.from_preorder(node_list)
            self.right = SyntaxNode(node_list.pop(0), self)
            return self.right.from_preorder(node_list)
        elif self.n_args == 1:
            self.left = SyntaxNode(node_list.pop(0), self)
            return self.left.from_preorder(node_list)
        else:
            return node_list

    def get_function(self, X: torch.Tensor) -> Callable:
        if self.n_args == 1:
            return self.op(self.left.get_function(X))
        elif self.n_args == 2:
            return self.op(self.left.get_function(X),
                           self.right.get_function(X))
        else:
            if (self.op != None):
                return self.op
            else:
                return X

    def get_preorder(self) -> list:
        traversal = [self.value]
        if self.left != None:
            traversal += self.left.get_preorder()
        if self.right != None:
            traversal += self.right.get_preorder()
        return traversal

    def get_leaf_nodes(self) -> list:
        leaf_list = []
        if self.left is None and self.right is None:
            leaf_list.append(self)
        else:
            leaf_list += self.left.get_leaf_nodes()
            if self.right is not None:
                leaf_list += self.right.get_leaf_nodes()
        return leaf_list

    def get_last(self):
        if self.n_args == 2:
            if self.left != None:
                res = self.left.get_last()
                if res:
                    return res
            else:
                return self
            if self.right != None:
                res = self.right.get_last()
                if res:
                    return res
                else:
                    return False
            else:
                return self
        elif self.n_args == 1:
            if self.left != None:
                return self.left.get_last()
            else:
                return self
        else:
            return False

    def get_parent_state(self) -> torch.tensor:
        parent = self.get_last()
        return parent.data

    def get_sibling_token(self) -> torch.tensor:
        parent = self.get_last()
        if parent.n_args == 2:
            if parent.left == None:
                return self.get_embedding(list(SyntaxNode.op_list.keys()).index('none'))
            else:
                return self.get_embedding(list(SyntaxNode.op_list.keys()).index(parent.left.value))
        else:
            return self.get_embedding(list(SyntaxNode.op_list.keys()).index('none'))

    def get_sibling(self):
        parent = self.get_last()
        if parent.n_args == 2:
            if parent.left == None:
                return None
            else:
                return parent.left
        else:
            return None

    def append(self, node_type:str, data:torch.tensor = None) -> bool:
        last_parent = self.get_last()
        return last_parent.add_node(SyntaxNode(node_type, parent=last_parent, data=data))

def __get_tree_depth(root:SyntaxNode) -> int:
    if root == None:
        return 0
    left_depth = __get_tree_depth(root.left)
    right_depth = __get_tree_depth(root.right)
    return 1 + max(left_depth, right_depth)

def get_tree_depth(root:SyntaxNode) -> int:
    return __get_tree_depth(root) - 2

def tree_from_preorder(preorder:list) -> SyntaxNode:
    root = SyntaxNode(preorder.pop(0))
    root.from_preorder(preorder)
    return root

def get_expression(root:SyntaxNode) -> str:
    # TODO: Fix this implementation, doesn't work properly with incomplete expressions
    if root.value != 'start':
        if root.left == None and root.right == None:
            if (root.value != 'const') and (root.value != 'var'):
                return root.value
            elif root.value == 'var':
                return 'x'
            else:
                return str(round(root.op.item(),3))
        elif root.right == None:
            return root.value + '(' + get_expression(root.left) + ')'
        else:
            return '('+ get_expression(root.left) + root.value + get_expression(root.right) + ')'
    else:
        return get_expression(root.left)

def get_expression_refactor(root:SyntaxNode) -> str:
    if root != None:
        if root.n_args == 2:
            return '( {} {} {} )'.format(get_expression_refactor(root.left), root.value, get_expression_refactor(root.right))
        elif root.n_args == 1:
            return root.value + f'( {get_expression_refactor(root.left)} )'
        else:
            if root.value == 'var':
                return 'x'
            elif root.value == 'const':
                return str(round(root.op.item(),3))
            else:
                return '?'
    else:
        return '?'

def tree_complete(root: SyntaxNode) -> bool:
    ''' Test to see if tree is complete'''
    if root.n_args == 2:
        if root.left == None:
            return False
        elif root.right == None:
            return False
        else:
            return tree_complete(root.left) and tree_complete(root.right)
    elif root.n_args == 1:
        if root.left == None:
            return False
        else:
            return tree_complete(root.left)
    else:
        return True

def get_node_depth(node: SyntaxNode, counter = 0) -> int:
    if node.value != 'start':
        return get_node_depth(node.parent, counter+1)
    else:
        return counter

def child_of(node: SyntaxNode, nodetypes: set) -> bool:
    if node.value == 'start':
        return False
    elif node.value in nodetypes:
        return True
    else:
        return child_of(node.parent, nodetypes)

def get_illegal_set(root: SyntaxNode) -> set:
    parent = root.get_last()
    sibling = root.get_sibling()
    # copying the set is important, otherwise we modify the illegal sets for
    # each node globally
    illegal_nodes = SyntaxNode.illegal[parent.value].copy()
    if parent.value in {'+', '-', '/'}:
        if sibling != None:
            if sibling.value in {'const', 'var'}:
                illegal_nodes.add(sibling.value)
    if parent.value == '*':
        if sibling != None:
            if sibling.value in {'const', 'log', 'exp'}:
                illegal_nodes.add(sibling.value)
    if child_of(parent, {'sin', 'cos', 'log', 'exp'}):
        illegal_nodes |= {'sin', 'cos', 'log', 'exp'}
        if parent.value in {'sin', 'cos', 'log', 'exp'}:
            illegal_nodes.add('const')
    return illegal_nodes

if __name__ == '__main__':
    print('reconstruction test:')
    root2 = SyntaxNode("start")
    assert root2.from_preorder(['exp','+','*','const','var','const']) == []
    print(get_expression(root2))

    print('Last node test:')
    print('const + exp(?)')
    preorder = ['start','+','const','exp']
    tree = SyntaxNode(preorder.pop(0))
    _ = [tree.append(node) for node in preorder]
    print(tree.get_last().op)

    print("Non-empty list test:")
    root3 = SyntaxNode("start")
    l = root3.from_preorder(['exp','+','*','const','var','const'])
    print("l:", l)
    print("root3:", get_expression(root3))

    print("Leaf nodes test:")
    leafs = root3.get_leaf_nodes()
    for l in leafs:
        print(l.value, l.n_args)

    print("Parameter Tracking Test:")
    root2.__del__()
    root3.__del__()
    tree.__del__()
    root4 = SyntaxNode("start")
    root4.from_preorder(['+','*','const','var','const'])
    print(SyntaxNode.parameters)
    print(SyntaxNode.parameters.keys())

    print("Fit test")
    root4.__del__()
    root = SyntaxNode('start')
    root.from_preorder(['exp', '+','*','const','var','const'])
    print("Initial Expression:")
    print(get_expression(root))
    print("Target Expression: exp(2.5*x + 6.34)")
    #_ = [param.cuda() for param in SyntaxNode.parameters[root.tree_idx]]
    func_optim = torch.optim.Adam(SyntaxNode.parameters[root.tree_idx], .1)
    def func(x):
        return torch.exp(2.5*x + 6.34)
    X = torch.rand((1,20)) #.cuda()
    y = func(X)
    for step in range(1000):
        func_optim.zero_grad()
        y_hat = root.get_function(X)
        func_loss = torch.mean((y_hat - y)**2)
        func_loss.backward()
        if step % 100 == 0:
            print(func_loss.item())
        func_optim.step()
    print("Fit Expression:")
    print(get_expression(root))

    print("Append Test:")
    root.__del__()
    root = SyntaxNode('start')
    root.append('exp')
    root.append('+')
    root.append('*')
    root.append('const')
    root.append('var')
    root.append('const')
    print(get_expression(root))

    print("Complete tree test")
    root.__del__()
    root = SyntaxNode('start')
    root.append('-')
    root.append('log')
    root.append('const')
    leafs = root.get_leaf_nodes()
    for l in leafs:
        print(l.value, l.n_args)
    print(get_expression(root))
    print(tree_complete(root))

    print("Illegal set test:")
    root.__del__()
    root = SyntaxNode('start')
    preorder = ['exp', '+', '*', 'var', 'const']
    for op in preorder:
        root.append(op)
        print(get_expression_refactor(root))
        print(get_illegal_set(root))

    print("Child of Test:")
    root.__del__()
    root = SyntaxNode('start')
    root.append('exp')
    root.append('+')
    root.append('*')
    root.append('const')
    root.append('var')
    print(root.get_last().value)
    print(root.get_last().parent.value)
    print(child_of(root.get_last(), {'exp'}))

    root.__del__()
    root = SyntaxNode('start')
    root.append('+')
    print(root.get_last().value)
    print(get_illegal_set(root))

    print("Tree depth test:")
    root.__del__()
    root = SyntaxNode('start')
    root.from_preorder(['exp', '+', '*', 'const', 'var', 'const'])
    print(get_tree_depth(root))


import csv

from django.core.management.base import BaseCommand

from autharch_sharc.editor.models import SharcIIIF

class Command(BaseCommand):
    help = "Import iiif_urls in csv."
    verbose = 1  # show logging on screen

    min_row_length = 5
    iif_created = 0
    events_updated = 0
    event_links = {}

    # def add_arguments(self, parser) -> None:
    #     # csv file to parse
    #     parser.add_argument("csvfile", type=argparse.FileType("r"))

    def parse_iiif_row(self, csv_line, column_format, last_rcin, department="") -> str:
        """Parse the various sheets of the manifests,
        looking for IIIF uris.
        column_format is a dict of which columns contain primary/secondary
        uris, as well as metadata if present
        """
        # check for event id
        if len(csv_line) > 0:
            # Date, Creator, Title, RCIN, Blurb
            """
            iiif_uri = models.CharField(blank=True, null=True, max_length=512)
            rcin = models.CharField(blank=True, null=True, max_length=256)
            images_available = models.TextField(blank=True, null=True)
            department = models.CharField(blank=True, null=True, max_length=256)
            """
            # todo will populate this once format of sheet is more clear

            if "iiif_uri" in column_format:
                rcin = csv_line[0]
                if "images_available" in column_format:
                    try:
                        images_available = csv_line[column_format["images_available"]]
                    except IndexError:
                        images_available = ""
                else:
                    images_available = ""
                if len(rcin) == 0 and len(last_rcin) > 0:
                    # Use RCIN from previous line
                    rcin = last_rcin
                if len(rcin) > 0:
                    for uri_column in column_format["iiif_uri"]:
                        iiif_uri = csv_line[uri_column]

                        if len(iiif_uri) > 0 and (
                            "http" in iiif_uri or "www" in iiif_uri
                        ):
                            if "http" not in iiif_uri:
                                # Add https at the front to make the link work
                                iiif_uri = "https://" + iiif_uri
                            # if len(rcin) == 0:
                            #     # get the end of the uri
                            #     # use first column as the rcin
                            #     rcin_pattern = re.compile(r"https://.*/(.*)/\n*$")
                            #     result = rcin_pattern.search(iiif_uri)
                            #     if result:
                            #
                            #         rcin = result.group(1)

                            if (
                                len(iiif_uri) > 0
                                and "https" in iiif_uri
                                and len(rcin) > 0
                            ):
                                try:
                                    order = 1
                                    # check if this rcin has been uploaded already
                                    # incrememnt order
                                    previous_uris = SharcIIIF.objects.filter(
                                        rcin=rcin
                                    ).order_by("order")
                                    if previous_uris.count() > 0:
                                        last = previous_uris.last()
                                        order = last.order + 1
                                    if (
                                        SharcIIIF.objects.filter(
                                            rcin=rcin,
                                            iiif_uri=iiif_uri.strip(),
                                        ).count()
                                        == 0
                                    ):
                                        (
                                            event,
                                            created,
                                        ) = SharcIIIF.objects.get_or_create(
                                            rcin=rcin,
                                            iiif_uri=iiif_uri.strip(),
                                            images_available=images_available,
                                            department=department,
                                            order=order,
                                        )
                                        if created:
                                            self.iif_created += 1
                                        else:
                                            self.events_updated += 1
                                        print(
                                            "{}:{}:{}\n".format(rcin, iiif_uri, order)
                                        )
                                except ValueError:
                                    pass
                    return rcin
        return ""

    def parse_iiif_sheet(self, filename, column_format, department) -> None:
        with open(filename, "r") as csv_file:
            csvfile = csv.reader(csv_file, delimiter=",")
            print("{}\n".format(department))
            last_rcin = ""
            for csv_line in csvfile:
                last_rcin = self.parse_iiif_row(
                    csv_line, column_format, last_rcin, department
                )

    def handle(self, *args, **options):
        # Reset objects
        # SharcIIIF.objects.all().delete()
        # import sheets
        totals = {}
        # EH commented out as they are complete, final upload only
        """
        self.parse_iiif_sheet(
            "data/ShaRC_manifests_photographs.csv",
            {"images_available": 2, "iiif_uri": [3]},
            "Photographs",
        )
        totals["Photographs"] = self.iif_created
        self.iif_created = 0

        self.parse_iiif_sheet(
            "data/ShaRC_manifests_paintings.csv",
            {"images_available": 2, "iiif_uri": [3]},
            "Paintings",
        )
        totals["Paintings"] = self.iif_created
        self.iif_created = 0

        self.parse_iiif_sheet(
            "data/ShaRC_manifests_library.csv",
            {"images_available": 2, "iiif_uri": [3]},
            "Library",
        )
        totals["Library"] = self.iif_created
        self.iif_created = 0

        self.parse_iiif_sheet(
            "data/ShaRC_manifests_printroom.csv",
            {"images_available": 2, "iiif_uri": [3]},
            "Print Room",
        )
        totals["Print Room"] = self.iif_created
        self.iif_created = 0

        self.parse_iiif_sheet(
            "data/ShaRC_manifests_archives.csv",
            {"images_available": 2, "iiif_uri": [3]},
            "Archives",
        )
        totals["Archives"] = self.iif_created
        self.iif_created = 0
        self.parse_iiif_sheet(
            "data/ShaRC_manifests_decarts.csv",
            {"images_available": 2, "iiif_uri": [3]},
            "Decorative Arts",
        )
        totals["Decorative Arts"] = self.iif_created
        """
        grand_total = 0

        self.iif_created = 0
        self.parse_iiif_sheet(
            "data/SHARC_final_manifests.csv",
            {"images_available": 2, "iiif_uri": [1]},
            "Final manifests",
        )
        totals["Decorative Arts"] = self.iif_created

        self.stdout.write("Complete.\n\nUpload totals: \n")
        for key, value in totals.items():
            self.stdout.write("{} - {}\n".format(key, value))
            grand_total += value
        self.stdout.write("\n Grand total - {}".format(grand_total))