'''
Il tris e' un popolarissimo gioco. Si gioca su una griglia quadrata di 3×3 caselle.
A turno, i due giocatori scelgono una cella vuota e vi disegnano il proprio simbolo
(un giocatore ha come simbolo una "o" e l'avversario una 'x').
Vince il giocatore che riesce a disporre tre dei propri simboli in linea retta
orizzontale, verticale o diagonale. Se la griglia viene riempita
senza che nessuno dei giocatori sia riuscito a completare una linea
retta di tre simboli, il gioco finisce in parità. Nel caso in cui il gioco
finisse in parità, la partita è detta "patta".
Per convenzione a griglia vuota la prima mossa spetta sempre al giocatore 'o'

Una configurazione del gioco e' dunque univocamente determinata  dal contenuto della griglia.

Nel seguito assumiamo che il contenuto della griglia sia  rappresentato tramite  lista di liste.
La dimensione della lista di liste M e'  3x3 ed   M[i][j] contiene  '', 'x', o 'o'  a seconda
che la cella della griglia appartenente all'iesima riga e j-ma colonna sia ancora libera,
contenga il simbolo 'x' o contenga il simbolo 'o'.

Data una configurazione C del gioco, l'albero di gioco per C e' l'albero che
si ottiene ricorsivamente partendo dalla configurazione C e assegnando come figli le configurazioni
che e' possibile ottenere da C con una mossa ulteriore del gioco. Ovviamente  risulteranno
foglie dell'albero i possibili esiti della partita vale a dire le diverse configurazioni cui e'
possibile arrivare partendo da C e che rappresentano patte, vittorie per 'o' o vittorie per 'x'.
Se veda ad esempio l'immagine albero_di_gioco.png che mostra l' albero di gioco che si ottiene a partire
dalla configurazione rappresentata da [['x', 'o', 'o'], ['x', 'x', 'o'], ['', '', '']]

Si consideri la seguente Classe di oggetti:

class NodoTris:
    def __init__(self, griglia):
        self.nome = griglia
        self.lista_figli = []

Bisogna progettare le seguente  funzione

gen_tree(griglia)
che, data la configurazione di gioco griglia,  costruisce l'albero di gioco che si ottiene a partire
dalla configurazione griglia e ne restituisce la radice. I nodi dell'albero devono essere
oggetti della classe NodoTris.

Per testare la correttezza della vostra implementazione di gen_tree() il grade utilizzera' quattro metodi
della   classe NodoTris che dovete comunque implementare:

1)
tipo(self)
che, dato un nodo NodoTris, restituisce:
 'o' se la configurazione rappresentata dal nodo e' una configurazione di vittoria per il giocatore 'o'
 'x' se la configurazione rappresentata dal nodo e' una configurazione di vittoria per il giocatore 'x'
 '-' se la configurazione rappresentata dal nodo e' una configurazione di patta
 '?' se la configurazione rappresentata dal nodo e' una configurazione di gioco non ancora terminato

2)
esiti(self)
che, dato un nodo radice di un albero di gioco, restituisce una tripla con i possibili
esiti della partita che ha come configurazione iniziale quella rappresentata dal nodo.
Piu' precisamente: il primo elemento della tripla  è il numero di  patte possibili,
il secondo è il numero di possibili vittorie  per il giocatore 'o' mentre  il terzo elemento
e' il numero di possibili vittorie per il giocatore 'x'.

3)
vittorie_livello(self, giocatore, h)
che, dato un nodo radice di un albero di gioco, uno dei due giocatori ed un intero h,
restituisce il numero di nodi che rappresentano una vittoria per il giocatore e si
trovano ad altezza h nell'albero. In altri termini restituisce il numero di vittorie possibili
per giocatore in esattamente h mosse, nella partita che ha come configurazione iniziale
quella rappresentata dalla radice dell'albero.

4)
strategia_vincente(self,giocatore)
che, dato un nodo radice di un albero di gioco ed uno dei due giocatori, restituisce True o False.
Restituisce True  se  giocatore ha una strategia vincente  nella partita
che ha come configurazione iniziale quella rappresentata dal nodo radice, False altrimenti.

Nota che un giocatore ha una strategia vincente rispetto ad una certa configurazione se,
qualunque siano le mosse dell'avversario ha sempre la possibilita' di rispondere in modo
che la partita termini con la sua vittoria.

Potete ovviamente definire ulteriori  funzioni e altri metodi per la   Classe NodiTris
se li  ritenete utili al fine della risoluzione del compito.

Potete assumere che le configurazioni di gioco rappresentate da griglia siano sempre configurazioni
lecite (vale a dire ottenute dopo un certo numero di mosse a parire dalla griglia vuota).

AVVERTENZE: non usare caratteri non ASCII, come le lettere accentate; non
importare moduli che non sono nella libreria standard.

ATTENZIONE: i test vengono eseguiti con un timeout globale di 2*N secondi (se il grader esegue N test).
'''
from copy import deepcopy
class NodoTris:
    def __init__(self, griglia):
        self.nome = griglia
        self.lista_figli = [] #lista dei nodi figli

    def tipo(self):
        self.p=''
        if self.nome[0][0] == self.nome[0][1]  and self.nome[0][1] == self.nome[0][2]:
            self.p=self.nome[0][0]
        if self.nome[1][0] == self.nome[1][1] and self.nome[1][1] == self.nome[1][2]:
            self.p=self.nome[1][0]
        if self.nome[2][0] == self.nome[2][1]  and self.nome[2][1]== self.nome[2][2]:
            self.p=self.nome[2][0]
        if self.nome[2][0] == self.nome[2][1]  and self.nome[2][1]== self.nome[2][2]:
            self.p=self.nome[2][0]
        if self.nome[0][0] == self.nome[1][1]  and self.nome[1][1]== self.nome[2][2]:
            self.p=self.nome[0][0]
        if self.nome[0][2] == self.nome[1][1]  and self.nome[1][1]== self.nome[2][0]:
            self.p=self.nome[2][0]
        if self.nome[0][0] == self.nome[1][0]  and self.nome[1][0]== self.nome[2][0]:
            self.p=self.nome[2][0]
        if self.nome[0][1] == self.nome[1][1]  and self.nome[1][1]== self.nome[2][1]:
            self.p=self.nome[0][1]
        if self.nome[0][2] == self.nome[1][2]  and self.nome[1][2]== self.nome[2][2]:
            self.p= self.nome[2][2]
        trovato = False
        if self.p=='':
            for x in self.nome:
                for y in x:
                    if y == '':
                        trovato= True
                        break
            if trovato == True:
                 self.p='?'
            else: self.p= '-'
        return self.p

    def esiti(self):
        self.patta= 0
        self.x= 0
        self.o= 0
        self.patta,self.o,self.x= contaesiti(self.patta,self.o,self.x,self)
        return self.patta,self.o,self.x

    def vittorie_livello(self, giocatore, h):
        self.vittorie=0
        self.vittorie= livelli(self,0,giocatore,self.vittorie,h)
        return self.vittorie

    def strategia_vincente(self,giocatore):
        for x in self.lista_figli:
            o,x= strategia(x,giocatore,0,0)
        if giocatore == 'x':
            if x > o:
                return True
            else: return False
        else:
            if o > x:
                return True
            else: return False

def gen_tree(griglia):
    radice= NodoTris(griglia)
    spazi= spazivuoti(griglia)
    if spazi %2 != 0:
        radice.lista_figli=figli(griglia,'o',radice)
    else:
        radice.lista_figli=figli(griglia,'x',radice)
    return radice

def figli(griglia,giocatore,alb):
    i=0
    '''print(alb.tipo(),'alb.nome:',alb.nome)'''

    while i < 3:
        j=0
        while j < 3:

            if controlla(alb.nome) == False:
                if griglia[i][j] == '':
                    ripa= deepcopy(griglia)
                    ripa[i][j]= giocatore
                    figlio=NodoTris(ripa)
                    alb.lista_figli+=[figlio]
                    spazi=spazivuoti(ripa)

                    if spazi %2 != 0:
                        figlio.lista_figli=figli(ripa,'o',figlio)

                    else:
                        figlio.lista_figli=figli(ripa,'x',figlio)

            j+=1
        i+=1

    return alb.lista_figli

def contaesiti(patta,o,x,nodo):

    if nodo.tipo() == '-':
       patta+=1

    elif nodo.tipo() == 'x':
         x+=1

    elif nodo.tipo() == 'o':
        o+=1

    i=0
    while i< len(nodo.lista_figli):

        patta,o,x=contaesiti(patta,o,x,nodo.lista_figli[i])
        i+=1
    return patta,o,x

def printa(padre,griglia,livello):
    print(livello,'padre:',gen_tree(griglia).nome)
    for x in gen_tree(griglia).lista_figli:
        print(livello,'figlio:',x.nome)
        printa(x,x.nome,livello+1)

def spazivuoti(griglia):
    i=0
    cont=0
    while i < 3:
        j=0
        while j<3:
            if griglia[i][j]=='':
                cont+=1
            j+=1
        i+=1
    return cont

def livelli(nodo,livello,giocatore,vittorie,h):
    if livello == h:
        if nodo.tipo() == giocatore:

            vittorie+=1
    elif livello < h:
        i=0

        while i < len(nodo.lista_figli):

            vittorie= livelli(nodo.lista_figli[i],livello+1,giocatore,vittorie,h)
            i+=1

    return vittorie

def strategia(nodo,giocatore,o,x):

    if nodo.tipo() == 'o':
       o+=1
    elif nodo.tipo() == 'x':
       x+=1
    i=0
    while i < len(nodo.lista_figli):
        o,x=strategia(nodo.lista_figli[i],giocatore,o,x)
        i+=1
    return o,x

def printfigli(griglia):
    for x in gen_tree(griglia).lista_figli[1].lista_figli[0].lista_figli[0].lista_figli:
        print(x.nome)

'''def gen_tree(griglia):
    nodo= NodoTris(griglia)
    if controlla(nodo.nome) == False:
        a,b=figli(nodo.nome)
        for x in range(0,b):
            nodo.lista_figli.append(gen_tree(a[x]))
    return nodo

def figli(griglia):
    lista=[]
    possibili=[]
    n=0
    x=0
    o=0
    for r in range(0,3):
        for c in range(0,3):
            if griglia[r][c] == 'x':
                x+=1
            elif griglia[r][c] == 'o':
                o+=1
            else:
                n+=1
                possibili.append((r,c))
    if x == o:
        for i in range(0,n):
            app= deepcopy(griglia)
            app[possibili[i][0]][possibili[i][1]]= 'o'
            lista.append(app)

    if o > x and o < 5:
        for i in range(0,n):
            app= deepcopy(griglia)
            app[possibili[i][0]][possibili[i][1]]= 'o'
            lista.append(app)
    return lista,n
'''
def controlla(nome):
        if nome[0][0] == nome[0][1]  and nome[0][1] == nome[0][2] and nome[0][0] != '':
            return True
        elif nome[1][0] == nome[1][1] and nome[1][1] == nome[1][2] and nome[1][0] != '':
            return True
        elif nome[2][0] == nome[2][1]  and nome[2][1]== nome[2][2] and nome[2][0] != '':
            return True
        elif nome[2][0] == nome[2][1]  and nome[2][1]== nome[2][2] and nome[2][0] != '':
            return True
        elif nome[0][0] == nome[1][1]  and nome[1][1]== nome[2][2] and nome[0][0] != '':
            return True
        elif nome[0][2] == nome[1][1]  and nome[1][1]== nome[2][0] and nome[0][2] != '':
            return True
        elif nome[0][0] == nome[1][0]  and nome[1][0]== nome[2][0] and nome[0][0] != '':
            return True
        elif nome[0][1] == nome[1][1]  and nome[1][1]== nome[2][1] and nome[0][1] != '':
            return True
        elif nome[0][2] == nome[1][2]  and nome[1][2]== nome[2][2] and nome[0][2] != '':
            return True
        return False

def printO(nodo,cont=0):
    if nodo.tipo() == 'o':
        cont+=1
        print('nodo:',nodo.nome)
    for x in nodo.lista_figli:
        cont=printO(x,cont)

    return cont

def printalivello(nodo,livello,cont):
    if livello == 5 and nodo.tipo()== 'o':
        print('griglia:',nodo.nome)
        cont+=1
    for x in nodo.lista_figli:
        cont=printalivello(x,livello+1,cont)

    return cont


import sys

from lib import REPO, VERSION_SRC, parseArgs, trim

from trimTei0 import (
    corpusPre as a0,
    trimVolume as v0,
    trimDocBefore as b0,
    trimDocPrep as d0,
    trimPage as t0,
    processPage as p0,
    trimDocPost as e0,
    corpusPost as c0,
)
from trimTei1 import (
    corpusPre as a1,
    trimVolume as v1,
    trimDocBefore as b1,
    trimDocPrep as d1,
    trimPage as t1,
    processPage as p1,
    trimDocPost as e1,
    corpusPost as c1,
)
from trimTei2 import (
    corpusPre as a2,
    trimVolume as v2,
    trimDocBefore as b2,
    trimDocPrep as d2,
    trimPage as t2,
    processPage as p2,
    trimDocPost as e2,
    corpusPost as c2,
)
from trimTei3 import (
    corpusPre as a3,
    trimVolume as v3,
    trimDocBefore as b3,
    trimDocPrep as d3,
    trimPage as t3,
    processPage as p3,
    trimDocPost as e3,
    corpusPost as c3,
)

from trimTei4 import (
    corpusPre as a4,
    trimVolume as v4,
    trimDocBefore as b4,
    trimDocPrep as d4,
    trimPage as t4,
    processPage as p4,
    trimDocPost as e4,
    corpusPost as c4,
)

corpusPre = [a0, a1, a2, a3, a4]
trimVolume = [v0, v1, v2, v3, v4]
trimDocBefore = [b0, b1, b2, b3, b4]
trimDocPrep = [d0, d1, d2, d3, d4]
trimPage = [t0, t1, t2, t3, t4]
processPage = [p0, p1, p2, p3, p4]
trimDocPost = [e0, e1, e2, e3, e4]
corpusPost = [c0, c1, c2, c3, c4]

HELP = f"""
Convert TEI source to simplified pseudo TEI,
stage {{stage}} (must be 0 .. {len(trimPage) - 1}).

python3 trimTei.py {{stage}} [volume] [page] [--help]

--help: print this text amd exit

volume: only process this volume; default: all volumes
page  : only process letter that starts at this page; default: all letters
"""

def main():
    args = [] if len(sys.argv) == 1 else list(sys.argv[1:])

    if "--help" in args:
        print(HELP.format(stage="?"))
        return True

    if len(args) == 0:
        print("Specify a stage!")
        print(HELP.format(stage="?"))
        return False

    stage = args.pop(0)

    if stage.isdigit():
        stage = int(stage)
    else:
        print(f"{stage} is not a number!")
        print(HELP.format(stage=stage))
        return False

    if stage >= len(trimPage):
        print(HELP.format(stage=stage))
        return False

    (good, vol, lid, kwargs, pargs) = parseArgs(args)

    if not good:
        return False

    print(f"TEI trimmer stage {stage} for {REPO}")
    print(f"TEI source version = {VERSION_SRC}")

    return trim(
        stage,
        vol,
        lid,
        corpusPre[stage],
        trimVolume[stage],
        trimDocBefore[stage],
        trimDocPrep[stage],
        trimPage[stage],
        processPage[stage],
        trimDocPost[stage],
        corpusPost[stage],
        *pargs,
        **kwargs,
    )

sys.exit(0 if main() else 1)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""A Random Walk Simulation """

# Python code for 2D random walk.
# Source: https://www.geeksforgeeks.org/random-walk-implementation-python/

import click
import raster_walker as rw
import vector_walker as vw

# user input – click
_total_steps_option = [
    click.option(
        "--total_steps",
        "-ts",
        default=10000,
        type=int,
        help="Specify the number of total steps for each random walker,"
             "Default is 10,000, Minimum 100",
    )
]

_total_walkers_option = [
    click.option(
        "--total_walkers",
        "-tw",
        default=1,
        type=int,
        help="Specify the number of total walkers, Default is 1, Minimum is 1",
    )
]

_step_size_option = [
    click.option(
        "--step_size",
        "-ss",
        default=1,
        type=int,
        help="Specify the size of the steps taken, Default is 1",
    )
]

_landscape_option = [
    click.option(
        "--landscape",
        "-l",
        default=False,
        type=bool,
        help="Specify whether a grid landscape exists as base layer or not, Default is False",
    )
]

_start_point_option = [
    click.option(
        "--start_point",
        "-sp",
        default=False,
        type=bool,
        help="Specify whether the walkers shall start from the same point or not, Default is False",
    )
]

_mov_pattern_option = [
    click.option(
        "--mov_pattern",
        "-mp",
        default=False,
        type=bool,
        help="Specify the neighborhood movement pattern, False is Neumann, True is Moor",
    )
]

def add_options(options):
    """Functions adds options to cli."""

    def _add_options(func):
        for option in reversed(options):
            func = option(func)
        return func
    return _add_options

@click.group()
@click.option('--verbose', '-v', is_flag=False, help="Will print verbose messages.")
def cli(verbose: bool) -> None:
    if verbose:
        click.echo("We are in the verbose mode. Which does not make any"
                   "difference right now.. but hey, have fun!")

@cli.command()
@add_options(_total_steps_option)
@add_options(_total_walkers_option)
@add_options(_step_size_option)
@add_options(_landscape_option)
@add_options(_start_point_option)
@add_options(_mov_pattern_option)
def run(
        total_steps: int,
        total_walkers: int,
        step_size: int,
        landscape: bool,
        start_point: bool,
        mov_pattern: bool
) -> None:
    """ execute command to generate random walkers """
    run_random_walkers(total_steps, total_walkers, step_size, landscape, start_point, mov_pattern)

def run_random_walkers(total_steps, total_walkers, step_size, landscape, diff_start, mov_pattern):
    """
    executes the random walker tool based on input data
    :param total_steps: number of total steps of the random walker
    :param total_walkers: number of walkers
    :param step_size: step size for each step
    :param landscape: boolean, if True, 2D area with obstacles is generated as base layer
    :param diff_start: boolean, if True, different start points for each walker
    :param mov_pattern: boolean, if True, Moor'sche neighboorhood is used, else Neumann
    :return:
    """

    # adjust wrong input
    total_steps = max(total_steps, 100)
    total_walkers = max(total_walkers, 1)

    # diverted because of the completely different implementation methods ->
    # could be done better in the future
    if landscape:
        # percentage of how much space obstacles shall block
        fill_percentage = 0.1
        landscape_raster = rw.create_raster(total_steps, fill_percentage)
        walk = rw.r_walker(total_steps, landscape_raster)
        # plot landscape raster with obstacles and walker
        rw.plot_raster(walk, total_steps)
    else:
        # creating two arrays for containing x and y coordinate
        # of size equals to the number of size and filled up with 0's
        x_arr, y_arr = vw.create_walking_space(total_steps)

        # multiple walkers
        list_x, list_y = vw.multiple_v_walkers(x_arr, y_arr, total_steps, total_walkers,
                                               step_size, diff_start, mov_pattern)
        vw.plot_v_walkers(total_steps, total_walkers, list_x, list_y)

if __name__ == "__main__":
    run_random_walkers(100, 5, 1, True, True, True)


from flask import Flask, render_template, request, redirect, url_for, flash
app = Flask(__name__)

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from database_setup import Base, Category, CategoryItem

engine = create_engine('sqlite:///itemcatalog.db')
Base.metadata.bind = engine

DBSession = sessionmaker(bind=engine)
session = DBSession()

@app.route('/')
@app.route('/itemcatalog')
def itemCatalog():
    categories = session.query(Category).all()
    return render_template('template.html', categories=categories)

@app.route('/itemcatalog/<string:category_name>')
def specificCategory(category_name):
    category = session.query(Category).filter_by(name=category_name).one()
    items = session.query(CategoryItem).filter_by(category_id = category.id)
    return render_template('item_template.html', category=category, items=items)

@app.route('/itemcatalog/<int:category_id_int>')
def specificCategoryInt(category_id_int):
    category = session.query(Category).filter_by(id=category_id_int).one()
    return redirect(url_for('specificCategory', category_name=category.name))

@app.route('/itemcatalog/<string:category_name>/new', methods=['GET','POST'])
def newCategoryItem(category_name):
    category = session.query(Category).filter_by(name=category_name).one()
    items = session.query(CategoryItem).filter_by(category_id = category.id)
    if request.method == 'POST':
        newItem = CategoryItem(name = request.form['name'], description = request.form['description'], category_id = category.id)
        session.add(newItem)
        session.commit()
        flash("new item created!")
        return redirect(url_for('specificCategory', category_name=category_name))
    else:
        return render_template('newCategoryItem.html', category=category, items=items)

@app.route('/itemcatalog/<string:category_name>/<int:item_id>/edit', methods=['GET','POST'])
def editCategoryItem(category_name, item_id):
    category = session.query(Category).filter_by(name=category_name).one()
    editedItem = session.query(CategoryItem).filter_by(category_id=category.id, id=item_id).one()
    if request.method == 'POST':
        if request.form['name']:
            editedItem.name = request.form['name']
        if request.form['description']:
            editedItem.description = request.form['description']
        session.add(editedItem)
        session.commit()
        return redirect(url_for('specificCategory', category_name=category.name))
    else:
        return render_template('editCategoryItem.html', category_name=category.name, item=editedItem)

@app.route('/itemcatalog/<string:category_name>/<int:item_id>/delete', methods=['GET','POST'])
def deleteCategoryItem(category_name, item_id):
    category = session.query(Category).filter_by(name=category_name).one()
    editedItem = session.query(CategoryItem).filter_by(category_id=category.id, id=item_id).one()
    if request.method == 'POST':
        session.delete(editedItem)
        session.commit()
        return redirect(url_for('specificCategory', category_name=category.name))
    else:
        return render_template('deleteCategoryItem.html', category_name=category.name, item=editedItem)

if __name__ == '__main__':
    app.secret_key = 'SuperSecretKey'
    app.debug = True
    app.run(host = '0.0.0.0', port = 5000)


# ------------------------------------------------------------
# Copyright (c) 2017-present, SeetaTech, Co.,Ltd.
#
# Licensed under the BSD 2-Clause License.
# You should have received a copy of the BSD 2-Clause License
# along with the software. If not, See,
#
#     <https://opensource.org/licenses/BSD-2-Clause>
#
# ------------------------------------------------------------
"""Loss ops."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from dragon.core.eager import context
from dragon.core.ops import activation_ops
from dragon.core.ops import loss_ops_lib
from dragon.core.ops.utils import ArgHelper
from dragon.core.ops.utils import OpSchema

@OpSchema.num_inputs(2)
def ctc_loss(inputs, padding_mask=-1, **kwargs):
    r"""Compute the ctc loss with batched labels.
    `[Graves & Gomez, 2006] <http://www.cs.utoronto.ca/~graves/icml_2006.pdf>`_.

    The shape of ``logit`` and ``label`` should be :math:`(T, N, C)`,
    :math:`(N, C)` respectively, where :math:`T` is the sequence length,
    :math:`N` is the batch size, and :math:`C` is max label length. The range
    of ``labels`` should be :math:`[1, C)`, as :math:`0` is reserved for blank.

    Use ``padding_mask`` to fill it when length is not sufficient.

    Parameters
    ----------
    inputs : Sequence[dragon.Tensor]
        The tensor ``logit`` and ``label``.
    padding_mask : int, optional, default=-1
        The mask for padding the redundant labels.

    Returns
    -------
    dragon.Tensor
        The output tensor.

    """
    args = ArgHelper.parse(locals())
    inputs[0] = activation_ops.softmax(inputs[0], axis=2)
    op_lib = loss_ops_lib.Operator
    if context.executing_eagerly():
        raise NotImplementedError
    else:
        return op_lib.blend('CTCLoss', **args)

@OpSchema.num_inputs(1, 2)
def l1_loss(inputs, reduction='mean', **kwargs):
    r"""Compute the element-wise absolute value difference.

    The **L1Loss** function is defined as:

    .. math:: \text{L1Loss}(x, y) = |x - y|

    Examples:

    ```python
    logit = dragon.constant([1., 2., 3.])
    target = dragon.constant([0., 0., 0.])
    print(dragon.losses.l1_loss([logit, target]))  # 2.0
    ```

    Parameters
    ----------
    inputs : Sequence[dragon.Tensor]
        The tensor ``logit`` and ``target``.
    reduction : {'none', 'sum', 'mean'}, optional
        The reduction method.

    Returns
    -------
    dragon.Tensor
        The output tensor.

    """
    args = ArgHelper.parse(locals())
    args['reduction'] = reduction.upper()
    op_lib = loss_ops_lib.L1Loss
    if context.executing_eagerly():
        return op_lib \
            .instantiate(reduction=args['reduction']) \
            .apply(inputs)
    else:
        return op_lib.blend(**args)

@OpSchema.num_inputs(1, 2)
def l2_loss(inputs, reduction='mean', **kwargs):
    r"""Compute the element-wise squared error.

    The **L2Loss** function is defined as:

    .. math:: \text{L2Loss}(x, y) = (x - y)^{2}

    Examples:

    ```python
    logit = dragon.constant([1., 2., 3.])
    target = dragon.constant([0., 0., 0.])
    print(dragon.losses.l2_loss([logit, target]))  # 4.666666
    ```

    Parameters
    ----------
    inputs : Sequence[dragon.Tensor]
        The tensor ``logit`` and ``target``.
    reduction : {'none', 'sum', 'mean'}, optional
        The reduction method.

    Returns
    -------
    dragon.Tensor
        The output tensor.

    """
    args = ArgHelper.parse(locals())
    args['reduction'] = reduction.upper()
    op_lib = loss_ops_lib.L2Loss
    if context.executing_eagerly():
        return op_lib \
            .instantiate(reduction=args['reduction']) \
            .apply(inputs)
    else:
        return op_lib.blend(**args)

@OpSchema.num_inputs(2)
def nll_loss(
    inputs,
    axis=1,
    ignore_index=None,
    reduction='valid',
    **kwargs
):
    r"""Compute the negative likelihood loss with sparse labels.

    The **NLLLoss** function is defined as:

    .. math:: \text{NLLLoss}(p_{t}) = -\log(p_{t})

    Parameters
    ----------
    inputs : Sequence[dragon.Tensor]
        The tensor ``logit`` and ``label``.
    axis : int, optional, default=1
        The reduce axis, can be negative.
    ignore_index : int, optional
        The label index to ignore.
    reduction : {'none', 'sum', 'mean', 'valid'}, optional
        The reduction method.

    Returns
    -------
    dragon.Tensor
        The output tensor.

    """
    args = ArgHelper.parse(locals())
    args['reduction'] = reduction.upper()
    op_lib = loss_ops_lib.NLLLoss
    if context.executing_eagerly():
        return op_lib  \
            .instantiate(
                axis=axis,
                reduction=args['reduction'],
                ignore_index=ignore_index,
            ).apply(inputs)
    else:
        return op_lib.blend(**args)

@OpSchema.num_inputs(2)
def sigmoid_cross_entropy(inputs, reduction='valid', **kwargs):
    r"""Compute the sigmoid cross entropy with contiguous targets.

    The **CrossEntropy** function is defined as:

    .. math:: \text{CrossEntropy}(p_{t}) = -\log(p_{t})

    Examples:

    ```python
    logit = dragon.constant([0.1, 0.2, 0.3, 0.4])
    target = dragon.constant([0., 0., 1., 1.])
    print(dragon.losses.sigmoid_cross_entropy([logit, target]))  # 0.65247655
    ```

    Parameters
    ----------
    inputs : Sequence[dragon.Tensor]
        The tensor ``logit`` and ``target``.
    reduction : {'none', 'sum', 'mean', 'valid'}, optional
        The reduction method.

    Returns
    -------
    dragon.Tensor
        The output tensor.

    """
    args = ArgHelper.parse(locals())
    args['reduction'] = reduction.upper()
    op_lib = loss_ops_lib.SigmoidCrossEntropy
    if context.executing_eagerly():
        return op_lib \
            .instantiate(reduction=args['reduction']) \
            .apply(inputs)
    else:
        return op_lib.blend(**args)

@OpSchema.num_inputs(2)
def sigmoid_focal_loss(
    inputs,
    axis=1,
    alpha=0.25,
    gamma=2.,
    negative_index=None,
    reduction='valid',
    **kwargs
):
    r"""Compute the sigmoid focal loss with sparse labels.
    `[Lin et.al, 2017] <https://arxiv.org/abs/1708.02002>`_.

    The Focal loss function is defined as:

    .. math:: \text{FocalLoss}(p_{t}) = -(1 - p_{t})^{\gamma}\log(p_{t})

    Parameters
    ----------
    inputs : Sequence[dragon.Tensor]
        The tensor ``logit`` and ``label``.
    axis : int, optional, default=1
        The reduce axis, can be negative.
    alpha : float, optional, default=0.25
        The scale factor on the rare class.
    gamma : float, optional, default=2.
        The exponential decay factor on the easy examples.
    negative_index : int, optional
        The negative class index.
    reduction : {'none', 'sum', 'mean', 'valid'}, optional
        The reduction method.

    Returns
    -------
    dragon.Tensor
        The output tensor.

    """
    args = ArgHelper.parse(locals())
    args['alpha'] = float(args['alpha'])
    args['gamma'] = float(args['gamma'])
    args['reduction'] = reduction.upper()
    op_lib = loss_ops_lib.SigmoidFocalLoss
    if context.executing_eagerly():
        return op_lib \
            .instantiate(
                axis=axis,
                alpha=args['alpha'],
                gamma=args['gamma'],
                negative_index=negative_index,
                reduction=args['reduction'],
            ).apply(inputs)
    else:
        return op_lib.blend(**args)

@OpSchema.num_inputs(1, 2)
def smooth_l1_loss(inputs, beta=1., reduction='mean', **kwargs):
    r"""Compute the element-wise error transited from L1 and L2.
    `[Girshick, 2015] <https://arxiv.org/abs/1504.08083>`_.

    The **SmoothL1Loss** function is defined as:

    .. math::
        \text{SmoothL1Loss}(x, y) =
            \begin{cases}
                0.5 * (x - y)^{2} / beta, & \text{ if } |x - y| < beta \\
                |x - y| - 0.5 * beta, & \text{ otherwise }
            \end{cases}

    Parameters
    ----------
    inputs : Sequence[dragon.Tensor]
        The tensor ``logit`` and ``target``.
    beta : float, optional, default=1.
        The transition point from L1 to L2 loss
    reduction : {'none', 'sum', 'mean'}, optional
        The reduction method.

    Returns
    -------
    dragon.Tensor
        The output tensor.

    """
    args = ArgHelper.parse(locals())
    args['beta'] = float(args['beta'])
    args['reduction'] = reduction.upper()
    op_lib = loss_ops_lib.SmoothL1Loss
    if context.executing_eagerly():
        return op_lib \
            .instantiate(
                beta=args['beta'],
                reduction=args['reduction'],
            ).apply(inputs)
    else:
        return op_lib.blend(**args)

@OpSchema.num_inputs(2)
def softmax_cross_entropy(inputs, axis=1, reduction='mean', **kwargs):
    r"""Compute the softmax cross entropy with contiguous targets.

    The **CrossEntropy** function is defined as:

    .. math:: \text{CrossEntropy}(p_{t}) = -\log(p_{t})

    Examples:

    ```python
    logit = dragon.constant([[0.5, 0.5], [0.3, 0.7]])
    target = dragon.constant([[0., 1., ], [1., 0.]])
    print(dragon.losses.softmax_cross_entropy([logit, target]))  # 0.8030813
    ```

    Parameters
    ----------
    inputs : Sequence[dragon.Tensor]
        The tensor ``logit`` and ``target``.
    axis : int, optional, default=1
        The axis to reduce, can be negative.
    reduction : {'none', 'sum', 'mean'}, optional
        The reduction method.

    Returns
    -------
    dragon.Tensor
        The output tensor.

    """
    args = ArgHelper.parse(locals())
    args['reduction'] = reduction.upper()
    op_lib = loss_ops_lib.SoftmaxCrossEntropy
    if context.executing_eagerly():
        return op_lib \
            .instantiate(
                axis=axis,
                reduction=args['reduction'],
            ).apply(inputs)
    else:
        return op_lib.blend(**args)

@OpSchema.num_inputs(2)
def sparse_softmax_cross_entropy(
    inputs,
    axis=1,
    ignore_index=None,
    reduction='valid',
    **kwargs
):
    r"""Compute the softmax cross entropy with sparse labels.

    The **CrossEntropy** function is defined as:

    .. math:: \text{CrossEntropy}(p_{t}) = -\log(p_{t})

    Examples:

    ```python
    logit = dragon.constant([[0.5, 0.5], [0.3, 0.7]])
    label = dragon.constant([1, 0])
    print(dragon.losses.sparse_softmax_cross_entropy([logit, label]))  # 0.8030813
    ```

    Parameters
    ----------
    inputs : Sequence[dragon.Tensor]
        The tensor ``logit`` and ``label``.
    axis : int, optional, default=1
        The axis to reduce, can be negative.
    ignore_index : int, optional
        The label index to ignore.
    reduction : {'none', 'sum', 'mean', 'valid'}, optional
        The reduction method.

    Returns
    -------
    dragon.Tensor
        The output tensor.

    """
    args = ArgHelper.parse(locals())
    args['reduction'] = reduction.upper()
    op_lib = loss_ops_lib.SparseSoftmaxCrossEntropy
    if context.executing_eagerly():
        return op_lib \
            .instantiate(
                axis=axis,
                reduction=args['reduction'],
                ignore_index=ignore_index,
            ).apply(inputs)
    else:
        return op_lib.blend(**args)


import sqlite3

#Open or Creates de database file
conn = sqlite3.connect('users.sqlite')
cur = conn.cursor()

#Creates the table "Users" with 3 fields
cur.execute('''CREATE TABLE IF NOT EXISTS Users
    (id INTEGER PRIMARY KEY, name TEXT UNIQUE, memberId TEXT UNIQUE)''')

name = input("Name: ").upper()
mId = input("Member ID: ").upper()

cur.execute('INSERT OR IGNORE INTO Users (name, memberId) VALUES ( ?, ? )', ( name, mId ) )

conn.commit()
cur.close()


def get_data(aTuple):
    nums = ()
    words = ()
    for t in aTuple:
        nums = nums + (t[0],)
        if t[1] not in words:
            words = words + (t[1],)
    min_nums = min(nums)
    max_nums = max(nums)
    unique_words = len(words)

    return (min_nums, max_nums, unique_words)

(small, large, words) = get_data(((1, 'mine'),
                                  (2, 'yours'),
                                  (3, 'our'),
                                  (4, 'mine')))

print("small:" + str(small) + "\n" + "large:" + str(large) + "\n" + "words:" + str(words))


import os
basedir = os.path.abspath(os.path.dirname(__file__))

WTF_CSRF_ENABLED = True
SECRET_KEY = 'you-will-never-guess'
WHOOSH_BASE = os.path.join(basedir, 'search.db')
MAX_SEARCH_RESULTS = 20

SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
SQLALCHEMY_RECORD_QUERIES = True
# slow database query threshold (in seconds)
DATABASE_QUERY_TIMEOUT = 0.5


#calss header
class _PARRY():
	def __init__(self,):
		self.name = "PARRY"
		self.definitions = [u'to defend yourself from a weapon or an attack by pushing the weapon away or by putting something between your body and the weapon', u'to manage cleverly to avoid dealing with a difficult question or some criticism: ']

		self.parents = []
		self.childen = []
		self.properties = []
		self.jsondata = {}

		self.specie = 'verbs'

	def run(self, obj1 = [], obj2 = []):
		return self.jsondata


import numpy as np
import RungeKutta4 as rk4
from scipy.integrate import quad as calc_integral
from scipy.interpolate import interp1d as interp
from multiprocessing import cpu_count
from multiprocessing.dummy import Pool as pool
from tqdm import tqdm
from boundary_value_problem import solve_boundary_value_problem_state
from boundary_value_problem import solve_boundary_value_problem_costate

def norm_of_vector_function(func, left_bound, right_bound, integral_error=1.49e-08):
    return np.sqrt(calc_integral(lambda t: (np.array(func(t))**2).sum(), left_bound, right_bound,
                                 epsabs=integral_error))[0]

def solve_optimal_control_problem(Lagrangian, endpoint_cost, initial_time, terminal_time, right_side_of_state_equation,
                                  initial_state, initial_control, deriv_Lagrangian_state, deriv_Lagrangian_control,
                                  deriv_endpoint_cost_state, deriv_right_side_of_state_equation_state,
                                  deriv_right_side_of_state_equation_control, control_lower_bound=None,
                                  control_upper_bound=None, calc_error_arg=1e-4, calc_error_func=1e-4,
                                  calc_error_grad=1e-4, iter_lim=1000, mode='projection', interpolation_kind='cubic',
                                  integral_error=1.49e-08, ode_error=1e-4, default_step=0.01,
                                  result_keys=('control', 'state', 'costate', 'gradient', 'functional'),
                                  print_iter=False):

    if mode != 'projection' and mode != 'conditional':
        raise ValueError('Invalid key for "mode"!')

    is_projection = True if mode == 'projection' else False

    target_functional = lambda state, control, time_t0, time_T:\
        calc_integral(lambda t: Lagrangian(state(t), control(t), t), time_t0, time_T, epsabs=integral_error)[0] +\
        endpoint_cost(state(time_T))

    control_results, state_results, costate_results, grads_results, functional_results = [], [], [], [], []

    control_current, control_next = np.random.random(initial_control.shape), initial_control.copy()
    control_results.append(control_next.copy())
    time_grid = np.linspace(initial_time, terminal_time, initial_control.shape[1])
    functional_current, functional_next = float(np.random.rand()), float(np.random.rand())
    grad_current, grad_next = np.zeros_like(initial_control), np.zeros_like(initial_control)

    if control_lower_bound is None:
        control_lower_bound_grid = -np.ones_like(initial_control) * np.infty
    else:
        bound_pool = pool(np.minimum(time_grid.size, cpu_count()))
        control_lower_bound_grid = bound_pool.map(control_lower_bound, time_grid)
        control_lower_bound_grid = np.array(control_lower_bound_grid).T
    if control_upper_bound is None:
        control_upper_bound_grid = np.ones_like(initial_control) * np.infty
    else:
        bound_pool = pool(np.minimum(time_grid.size, cpu_count()))
        control_upper_bound_grid = bound_pool.map(control_upper_bound, time_grid)
        control_upper_bound_grid = np.array(control_upper_bound_grid).T

    iterations = tqdm(range(iter_lim))

    for i in iterations:

        if print_iter:
            print('\n\nIndex of iteration %i' % (i+1))
            print('Interpolation of control')

        control_next_interpolated = interp(time_grid, control_next, kind=interpolation_kind, bounds_error=False,
                                           fill_value='extrapolate')

        if print_iter:
            print('Finding solution of the state equation')

        time_for_state_next, state_next = \
            rk4.solve_ode(initial_time, initial_state, initial_time, terminal_time,
                          lambda t, x: right_side_of_state_equation(x, control_next_interpolated(t), t), ode_error,
                          print_iter=print_iter)

        if print_iter:
            print('Interpolation of the state')

        state_next_interpolated = \
            interp(time_for_state_next, state_next, kind=interpolation_kind, bounds_error=False,
                   fill_value='extrapolate') \
            if time_for_state_next.size > 3 else interp(time_for_state_next, state_next, bounds_error=False,
                                                        fill_value='extrapolate')
        state_results.append(state_next_interpolated(time_grid))

        if print_iter:
            print('Finding solution of the costate equation')

        time_for_costate_next, costate_next = \
            rk4.solve_ode(terminal_time, -deriv_endpoint_cost_state(state_next_interpolated(terminal_time)),
                          initial_time, terminal_time,
                          lambda t, psi: deriv_Lagrangian_state(
                              state_next_interpolated(t),
                              control_next_interpolated(t),
                              t
                          ) - np.dot(psi, deriv_right_side_of_state_equation_state(
                              state_next_interpolated(t),
                              control_next_interpolated(t),
                              t
                          )),
                          ode_error, print_iter=print_iter)

        if print_iter:
            print('Interpolation of the costate')

        costate_next_interpolated = \
            interp(time_for_costate_next, costate_next, kind=interpolation_kind, bounds_error=False,
                   fill_value='extrapolate') \
            if time_for_costate_next.size > 3 else interp(time_for_costate_next, costate_next, bounds_error=False,
                                                          fill_value='extrapolate')
        costate_results.append(costate_next_interpolated(time_grid))

        if print_iter:
            print('Gradient\'s calculation')

        grad_pool = pool(np.minimum(time_grid.size, cpu_count()))
        grad_current = grad_next.copy()
        grad_next = grad_pool.map(lambda t:
                                  deriv_Lagrangian_control(state_next_interpolated(t), control_next_interpolated(t), t)
                                  - np.dot(costate_next_interpolated(t),
                                           deriv_right_side_of_state_equation_control(state_next_interpolated(t),
                                                                                control_next_interpolated(t),
                                                                                t)), time_grid)
        grad_next = np.array(grad_next).T
        grad_pool.close()
        grad_pool.join()
        grads_results.append(grad_next)

        if print_iter:
            print('Functional\'s calculation')

        functional_current = functional_next
        functional_next = target_functional(state_next_interpolated, control_next_interpolated, initial_time,
                                            terminal_time)
        functional_results.append(functional_next)

        if np.linalg.norm(control_next - control_current) < calc_error_arg or \
                np.linalg.norm(grad_next) < calc_error_grad or \
                np.abs(functional_next - functional_current) < calc_error_func:
            iterations.close()
            break

        step = default_step \
            if i == 0 \
            else np.abs(np.dot((control_next - control_current).ravel(), (grad_next - grad_current).ravel())) / \
                 np.linalg.norm(grad_next - grad_current) ** 2

        if print_iter:
            print('Finding next value of control')

        if is_projection:
            control_next_intermediate = control_next - step * grad_next
            control_next_intermediate = np.where(control_next_intermediate > control_upper_bound_grid,
                                                 control_upper_bound_grid, control_next_intermediate)
            control_next_intermediate = np.where(control_next_intermediate < control_lower_bound_grid,
                                                 control_lower_bound_grid, control_next_intermediate)
            control_current = control_next.copy()
            control_next = control_next_intermediate.copy()
            control_results.append(control_next.copy())
        else:
            step = step if step >= 0.0 else 0.0
            step = step if step <= 1.0 else 1.0

            control_next_intermediate = np.where(grad_next >= 0, control_lower_bound_grid, control_upper_bound_grid)
            control_current = control_next.copy()
            control_next = control_next + step * (control_next_intermediate - control_next)
            control_results.append(control_next.copy())

    return {result_keys[0]: control_results, result_keys[1]: state_results, result_keys[2]: costate_results,
            result_keys[3]: grads_results, result_keys[4]: np.array(functional_results)}

def solve_optimal_control_problem_parabolic_de(
        initial_control, nu, grid_interval_num_space, grid_interval_num_time, space_max, time_max, terminal_state,
        calc_error_arg=1e-4, calc_error_func=1e-4, calc_error_grad=1e-4, iter_lim=1000, mode='projection',
        default_step=0.01, result_keys=('control', 'state', 'costate', 'gradient', 'functional'), print_iter=False):
    initial_control = np.array(initial_control)
    terminal_state = np.array(terminal_state)
    if initial_control.size != grid_interval_num_time+1:
        raise ValueError('initial_control.size != grid_interval_num_time+1')
    if terminal_state.size != grid_interval_num_space+1:
        raise ValueError('terminal_state.size != grid_interval_num_space+1')

    if mode != 'projection' and mode != 'conditional':
        raise ValueError('Invalid key for "mode"!')

    is_projection = True if mode == 'projection' else False

    control_current, control_next = np.random.random(initial_control.shape) * 10, initial_control.copy()
    state_next = np.zeros((grid_interval_num_space + 1, grid_interval_num_time + 1))
    costate_next = np.zeros((grid_interval_num_space + 1, grid_interval_num_time + 1))
    functional_current, functional_next = np.random.rand(1)[0] * 10, np.random.rand(1)[0] * 10
    grad_current, grad_next = np.random.random(initial_control.shape) * 10, np.random.random(initial_control.shape) * 10

    control_results, state_results, costate_results, grads_results, functional_results = [], [], [], [], []
    control_results.append(initial_control.copy())
    control_lower_bound, control_upper_bound = -np.ones_like(initial_control), np.ones_like(initial_control)

    iterations = tqdm(range(iter_lim))

    for i in iterations:
        state_next = solve_boundary_value_problem_state(
            control_next, nu, grid_interval_num_space, grid_interval_num_time, space_max, time_max)
        functional_next = trapezoid_on_grid((state_next[:, -1] - terminal_state) ** 2, 0.0, space_max)
        costate_next = solve_boundary_value_problem_costate(
            control_next, nu, grid_interval_num_space, grid_interval_num_time, space_max, time_max,
            2 * (state_next[:, -1] - terminal_state)
        )
        grad_next = nu * costate_next[-1, :]

        state_results.append(state_next)
        costate_results.append(costate_next)
        grads_results.append(grad_next)
        functional_results.append(functional_next)

        # print('arg: %f' % np.sqrt(trapezoid_on_grid((control_next - control_current) ** 2, 0.0, time_max)))
        # print('grad: %f' % np.sqrt(trapezoid_on_grid(grad_next ** 2, 0.0, time_max)))
        # print('func: %f' % np.abs(functional_next - functional_current))

        if np.sqrt(trapezoid_on_grid((control_next - control_current) ** 2, 0.0, time_max)) < calc_error_arg or \
                np.sqrt(trapezoid_on_grid(grad_next ** 2, 0.0, time_max)) < calc_error_grad or \
                np.abs(functional_next - functional_current) < calc_error_func:
            break

        step = default_step \
            if i == 0 \
            else np.abs(np.dot((control_next - control_current).ravel(), (grad_next - grad_current).ravel())) / \
                 np.linalg.norm(grad_next - grad_current) ** 2

        control_current = control_next.copy()
        grad_current = grad_next.copy()
        functional_current = functional_next

        if is_projection:
            control_next_intermediate = control_next - step * grad_next
            control_next_intermediate = np.where(control_next_intermediate > control_upper_bound,
                                                 control_upper_bound, control_next_intermediate)
            control_next_intermediate = np.where(control_next_intermediate < control_lower_bound,
                                                 control_lower_bound, control_next_intermediate)
            control_next = control_next_intermediate.copy()

        else:
            step = step if step >= 0.0 else 0.0
            step = step if step <= 1.0 else 1.0

            control_next_intermediate = np.where(grad_next >= 0, control_lower_bound, control_upper_bound)
            control_next = control_next + step * (control_next_intermediate - control_next)

        control_results.append(control_next)

    return {result_keys[0]: control_results, result_keys[1]: state_results, result_keys[2]: costate_results,
            result_keys[3]: grads_results, result_keys[4]: np.array(functional_results)}

def trapezoid_on_grid(integrand_values_on_grid, a, b):
    integrand_values_on_grid = np.array(integrand_values_on_grid)
    return (b - a) / (integrand_values_on_grid.size - 1) * \
           (-0.5 * integrand_values_on_grid[0] - 0.5 * integrand_values_on_grid[-1] + integrand_values_on_grid.sum())


# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf8')
import jinja2  # pip install jinja2
from indexed import IndexedOrderedDict  # pip install indexed.py
from PIL import Image  # pip install Pillow
import os

templateLoader = jinja2.FileSystemLoader( searchpath="./" )
templateEnv = jinja2.Environment(loader=templateLoader)

#with open('../stylesheets/main.minified.css', "w") as out:
#    out.write("Update minified css!")
os.system('pleeease compile')

pictures = {
    "Stenlitografi": IndexedOrderedDict([
        ("Sommar I", (16, 23)),
        ("Sommar II", (16, 21.5)),
        ("Bro", (18.6, 24.1)),
        ("Vattenmelon", (18.0, 22.5)),
        ("Fruktfat", (17.0, 23.0)),
        ("Snöfall", (18.5, 24.2)),
        ("Kaffe", (17.5, 23.3)),
        ("Igelkott", (18.7, 23.6)),
        ("Lyktsken", (18.5, 23.2)),
        ("Läshörna", (17.5, 22.2)),
        ("Teaterloge", (18.2, 23.0)),
        ("Bro i snö", (18.0, 22.7)),
        ("Rådhuset III", (17.5, 22.2)),
        ("Park", (17.4, 21.8)),
        ("Grönsaksdans", (17.0, 21.6)),
        ("Melittadans", (18.0, 22.7)),
        ("Folk", (18.7, 24.4)),
        ("The High Sign", (16.0, 22.0)),
        ("Runt hörnet", (18.2, 25.0)),
        ("Tornhuset", (19.5, 25.4)),
        ("Tågkupé", (18.2, 24.5)),
        ("Väska och handske", (18.5, 24.5)),
        ("November", (18.2, 24.7)),
        ("Bokstavsmänniskor", (19.0, 25.0)),
        ("Dans", (25.8, 29.6)),
        ("Dirigent", (18.0, 24.7)),
        ("Festnatt", (18.5, 25.2)),
        ("Gymnastik", (18.5, 24.9)),
        ("Höstalfabet", (19.0, 25.0)),
        ("Kappa", (18.0, 24.0)),
        ("Mat i kvadrat", (18.5, 24.5)),
        ("Melonmåne", (18.5, 24.6)),
        ("Skor I", (25.0, 28.0)),
        ("Skor II", (24.8, 27.9)),
        ("Möte med väskor", (17.5, 23.3)),
        ("Koltrast", (25.1, 18.5)),
        ("Jazz", (18.6, 24.7)),
        ("Grävling med höstlöv", (25.0, 18.3)),
        ("Fjärilar", (18.3, 22.0)),
        ]),
    "Linoleumsnitt": IndexedOrderedDict([
        ("Berså", (9.5, 14.0)),
        ("Drottning Zucchini och kung Palsternacka", (9.5, 14.8)),
        ("Rotfrukter", (9.6, 15.0)),
        ("På ån", (20.0, 13.2)),
        ("Fågelskrämma", (20.0, 13.7)),
        ("Paraplyer", (20.0, 13.1)),
        ("Kronärtskockor", (20.0, 13.8)),
        ("Skor", (10.0, 14.8)),
        ("Herrskor", (10.7, 14.7)),
        ("Handske och vante", (9.5, 15.1)),
        ("Rulla hatt", (10.0, 14.9)),
        ("Kavaj", (10.6, 14.7)),
        ("Päls och stövlar", (10.7, 14.6)),
        ("Fåtöljhörna", (10.5, 14.6)),
        ("Garderob", (11.2, 14.6)),
        ("Skridskor", (11.2, 14.6)),
        ("Ryggsäck", (11.2, 14.6)),
        ("Måntelefon", (11.3, 14.5)),
        ("Visp och osthyvel", (15.0, 15.0)),
        ("Sardinburk", (15.0, 15.0)),
        ("Frukostmöte", (15.0, 15.0)),
        ("Diskho", (15.0, 15.0)),
        ]),
    "Bokstavsbilder": IndexedOrderedDict([
        ("A", (7.2, 11.0)),
        ("B", (7.6, 9.6)),
        ("C", (7.3, 11.2)),
        ("D", (7.8, 10.0)),
        ("E", (6.8, 8.7)),
        ("F", (7.4, 9.3)),
        ("G", (7.4, 9.7)),
        ("H", (7.3, 9.8)),
        ("I", (5.6, 8.2)),
        ("J", (7.0, 11.2)),
        ("K", (7.5, 9.8)),
        ("L", (6.8, 8.6)),
        ("M", (8.5, 9.7)),
        ("N", (7.1, 9.6)),
        ("O", (7.8, 9.5)),
        ("P", (7.8, 9.3)),
        ("Q", (6.7, 6.6)),
        ("R", (7.9, 9.5)),
        ("S", (7.8, 10.0)),
        ("T", (7.3, 11.2)),
        ("U", (7.3, 9.8)),
        ("V", (7.0, 9.8)),
        ("W", (7.9, 9.3)),
        ("X", (6.4, 7.9)),
        ("Y", (7.1, 11.8)),
        ("Z", (7.3, 11.1)),
        ("Å", (7.9, 9.9)),
        ("Ä", (6.7, 11.0)),
        ("Ö", (6.7, 7.0)),
        ]),
    "Djur": IndexedOrderedDict([
        ("Järtecken III", (39.5, 31.5)),
        ("Schimpanserna", (34.6, 37.7)),
        ("Servitören", (34.9, 36.7)),
        ("Laman", (24.4, 34.6)),
        ("Ankdamm", (27.8, 37.4)),
        ("Groda", (6.5, 7.8)),
        ("Padda", (7.0, 6.7)),
        ("Åsnevandring", (24.8, 29.3)),
        ("Rocka", (7, 9.7)),
        ("Flygekorre", (7.6, 9.7)),
        ("Björnen sover", (12.7, 9.8)),
        ("Ugglor i mossen", (12.7, 9.8)),
        ]),
    "Figurer": IndexedOrderedDict([
        ("Porträtt I", (12.5, 15.4)),
        ("Porträtt II", (11.6, 17.2)),
        ("Porträtt III", (14.5, 18.0)),
        ("Porträtt IV", (16.3, 19.1)),
        ("Ljuset manglas", (34.3, 24.7)),
        ("Svarta Ormen I", (12.7, 19.6)),
        ("Svarta Ormen II", (14.7, 19.0)),
        ("Svarta Ormen III", (14.7, 18.4)),
        ]),
    "Höst": IndexedOrderedDict([
        ("Höstlöv", (31.0, 39.4)),
        ("Blåst", (21.7, 27.5)),
        ("Löv", (24.3, 30.0)),
        ("Båtresa", (29.5, 32.7)),
        ]),
    "Interiör": IndexedOrderedDict([
        ("Hyresrummet", (31.7, 39.8)),
        ("Köket", (29.6, 40.7)),
        (""E de inte hemst?"", (29.8, 40.8)),
        ("Rummet med draperierna", (29.7, 36.8)),
        ("Orientalisk dans", (15.1, 16.9)),
        ("Ostaffär", (18.5, 21.5)),
        ]),
    "Landsbygd": IndexedOrderedDict([
        ("Skördearbete", (40.3, 30.1)),
        ("Busshållplats", (28.4, 38.5)),
        ("Tornado", (24.6, 31.3)),
        ("Skörd", (14.7, 20.5)),
        ("Afton i Mähren", (32.4, 28.6)),
        ("Hönsgård", (16.0, 18.3)),
        ("Kor", (9.8, 9.9)),
        ("Får", (9.7, 10.1)),
        ("Grisar", (9.6, 9.9)),
        ("Svart får", (8.7, 9.7)),
        ("Tax i kohage", (12, 9.5)),
        ("Kultingar", (7.8, 9.8)),
        ]),
    "Mänskliga": IndexedOrderedDict([
        ("Sybehöriga", (21.6, 27.5)),
        ("Samtal med osthyvel", (14.3, 19.7)),
        ("Kannor", (14.3, 19.7)),
        ("Snurror", (16.3, 19.7)),
        ("Vispdans", (15.6, 19.7)),
        ("Dammsugare", (21.7, 27.5)),
        ("Grafisk dans", (22.6, 24.5)),
        ("ABC", (31.0, 36.4)),
        ("b och a", (18.1, 24.4)),
        ("Bokstavsregn", (30.5, 36.0)),
        ("Dansare", (32.7, 39.3)),
        ("Fruktfolk", (21.5, 27.5)),
        ("Hattmöte", (18.2, 24.3)),
        ("Isdans", (23.4, 24.1)),
        ("Melitta och tesil", (17.0, 19.6)),
        ("Sporre", (14.0, 17.9)),
        ("Handskas", (27.5, 21.6)),
        ("Väskparad", (27.7, 21.7)),
        ("Hårresande", (17.6, 24.5)),
        ("Kikare", (19.6, 22.4)),
        ("Solfjädrar", (21.6, 27.6)),
        ("Basfiol", (9.8, 14.4)),
        ("Harpa", (9.8, 14.3)),
        ("Horn", (9.8, 14.3)),
        ("Invecklade", (26.5, 32.4)),
        ("Katt bland hermeliner", (9.7, 13.7)),
        ("Klipp", (22.1, 27)),
        ("Lånta fjädrar", (9.8, 14.1)),
        ("Trumslagare", (9.8, 14.3)),
        ("Xylofon", (9.6, 14.4)),
        ("Spår I", (27.9, 33)),
        ("Spår II", (21.7, 27.4)),
        ]),
    "Restaurang": IndexedOrderedDict([
        ("Grekisk restaurang", (30.0, 34.1)),
        ("Gatukafé", (21.7, 27.4)),
        ("Restaurangen", (29.5, 39.6)),
        ("Marketenteriet", (35.5, 43.3)),
        ]),
    "Skola": IndexedOrderedDict([
        ("Skoltid", (12.6, 19.2)),
        ("Kulspel", (14.8, 17.8)),
        ("Matbespisning", (9.9, 14.8)),
        ("Simlektion", (11.8, 17.3)),
        ("Hoppa bock", (14.7, 18.9)),
        ("Syslöjd", (14.6, 18.0)),
        ("Lekplats", (32.9, 39.3)),
        ]),
    "Sommar": IndexedOrderedDict([
        ("Husvagnar", (32.0, 41.3)),
        ("På stranden", (39.6, 29.5)),
        ("Snäckor", (31.9, 39.6)),
        ("Blommor", (31.0, 39.7)),
        ("Sommarstugan", (11.0, 16.1)),
        ("Badhotellet", (34.0, 39.2)),
        ("Vid havet", (19.2, 24.5)),
        ("Badstranden", (42.3, 37.2)),
        ("Boule", (43.0, 33.3)),
        ("Castiglione del Lago", (24.4, 31.7)),
        ]),
    "Stad": IndexedOrderedDict([
        ("Hörnhuset", (32.4, 38.4)),
        ("Skyltfönster", (22.1, 27.1)),
        ("Trafik", (31.7, 39.4)),
        ("Raggarbilar", (29.6, 37.6)),
        ("Viadukt", (29.0, 32.5)),
        ("Utsikt", (31.3, 37.5)),
        ("Kväll i Tjeckien", (29.5, 32.7)),
        ("Spårvagn", (21.7, 27.5)),
        ("Pistolaffären", (29.9, 40.8)),
        ("Järtecken I", (31.7, 39.8)),
        ("Järtecken II", (39.5, 30.4)),
        ("Järntrappan", (29.8, 40.8)),
        ("Annonspelare", (21.5, 27.6)),
        ("Spårvagnshållplats", (21.6, 27.4)),
        ("Stjärnfall", (29.5, 34.2)),
        ]),
    "Tivoli": IndexedOrderedDict([
        ("Pariserhjul", (29.5, 34.3)),
        ("Kanonkung", (32.7, 39.8)),
        ("Tekoppskarusell", (29.5, 34.4)),
        ("Tivolinatt", (29.5, 34.5)),
        ("Lufttur", (12.2, 16.6)),
        ("Cirkus", (41.5, 34.1)),
        ("Balansakt", (9.8, 15.6)),
        ("Clown", (14.7, 14.8)),
        ]),
    "Vinter": IndexedOrderedDict([
        ("Snöpromenad", (31.5, 37.5)),
        ("Snöyra", (21.8, 27.0)),
        ("Kälktur", (16.2, 20.7)),
        ("Skidtur", (16.2, 20.7)),
        ("Snöbollskrig", (16.7, 20.6)),
        ("Snögubbe", (16.2, 20.7)),
        ("Vinterbad", (16.2, 20.5)),
        ("Vintervandring", (28.5, 34.5)),
        ("Skridskoåkning", (29.3, 36.9)),
        ]),
}

#json.dump(pictures["Tivoli"], f, indent=2)
#pictures = json.load(f, object_pairs_hook=IndexedOrderedDict)

def valid_path(path):
    if path == "Å":
        return "Aa"
    if path == "Ä":
        return "Ae"
    if path == "Ö":
        return "Oe"
    path = path.replace(" ", "_")
    path = path.replace("å", "a")
    path = path.replace("Å", "A")
    path = path.replace("ä", "a")
    path = path.replace("Ä", "A")
    path = path.replace("ö", "o")
    path = path.replace("Ö", "O")
    path = path.replace("é", "e")
    path = path.replace("É", "E")
    path = path.replace("è", "e")
    path = path.replace("?", "")
    path = path.replace('"', "")
    path = path.replace(""", "")
    return path

def image_size(path):
    res = [-1]
    try:
        img = Image.open('../' + path)
        res = img.size
    except IOError:
        # File doesn't exist, fine
        pass
    return res

# Gallery pages
template_file = "template_gallery.html"
template = templateEnv.get_template(template_file)

for title in pictures:
    if title == "Stenlitografi":
        subdir = ''
    elif title == "Linoleumsnitt":
        subdir = ''
    else:
        subdir = 'koppargrafik/'

    template_vars = {"title": title,
                     "pictures": pictures[title],
                     "subdir": subdir,
                     "valid_path": valid_path,
                     }

    output = template.render(template_vars)

    path_title = valid_path(title).lower()
    if title == "Stenlitografi":
        index_file = "../%s/bilder/index.html" % path_title
    elif title == "Linoleumsnitt":
        index_file = "../%s/index.html" % path_title
    else:
        index_file = "../koppargrafik/%s/index.html" % path_title
    with open(index_file, "w+") as f:
        f.write(output.encode('utf-8'))
        f.close()

# Most other pages
template_file = "template.html"
template = templateEnv.get_template(template_file)

descriptions = {"presentation": "Konstnär som arbetar med grafik på heltid. Gör stentryck på Grafiska Museet i Helsingborg. Bilderna är föreställande utan att vara realistiska och har ofta ett naivistiskt drag.",
                "aktuellt": "Information om kommande och pågående utställningar.",
                "koppargrafik": "Koppargrafik. Etsningar, mezzotinter och torrnålsgravyrer.",
                "stenlitografi": "Stenlitografier tryckta på Grafiska museet i Helsingborg.",
                "övrigt": "Övrig konst. Klippans kulturpris och målning på Bofinkenskolan.",
                "cv": "Utställningar: Vårsalongen, Liljevalchs; Vårsalongen, Vikingsberg; Dunkers Kulturhus etc.",
                "länkar": "Länksamling. Tidningsartiklar, konstnärsföreningar etc.",
                "kontakt": "Kontaktuppgifter vid intresse av köp av grafiska blad eller utställningar.",
                "copyright": "Bilderna är skyddade enligt lagen om upphovsrätt.",
                }

for title in descriptions:
    template_vars = {"title": title,
                     "description": descriptions[title],
                     "valid_path": valid_path,
                     "image_size": image_size,
                     }

    output = template.render(template_vars)

    path_title = valid_path(title).lower()
    index_file = "../%s/index.html" % path_title
    with open(index_file, "w+") as f:
        f.write(output.encode('utf-8'))
        f.close()

# Start page
# Fix manually!


"""Setup for the mktanalytics package."""

import setuptools

with open('README.md') as f:
    README = f.read()

setuptools.setup(
    author="Payton Ong",
    author_email="payton.ong@gmail.com",
    name='mktanalytics',
    license="MIT",
    description='mktanalytics is a python package for analyzing markets.',
    version='v0.0.2',
    long_description=README,
    url='https://github.com/paytonong/mktanalytics',
    packages=setuptools.find_packages(),
    python_requires=">=3.5",
    install_requires=['numpy', 'pandas'],
    classifiers=[
        # Trove classifiers
        # (https://pypi.python.org/pypi?%3Aaction=list_classifiers)
        'Development Status :: 3 - Alpha',
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Topic :: Software Development :: Libraries',
        'Topic :: Software Development :: Libraries :: Python Modules',
        'Intended Audience :: Developers',
    ],
)


import logging
from datetime import timedelta
from decimal import Decimal

from django.conf import settings
from django.contrib import messages
from django.contrib.postgres.fields import DateTimeRangeField
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import Count
from django.db.models import F
from django.db.models import Sum
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.dateparse import parse_datetime
from django.utils.translation import gettext_lazy as _
from unidecode import unidecode

from .managers import OrderQuerySet
from .managers import ProductQuerySet
from tickets.models import ShopTicket
from utils.models import CreatedUpdatedModel
from utils.models import UUIDModel
from utils.slugs import unique_slugify

logger = logging.getLogger("bornhack.%s" % __name__)

class CustomOrder(CreatedUpdatedModel):
    text = models.TextField(help_text=_("The invoice text"))

    customer = models.TextField(help_text=_("The customer info for this order"))

    amount = models.IntegerField(
        help_text=_("Amount of this custom order (in DKK, including VAT)."),
    )

    paid = models.BooleanField(
        verbose_name=_("Paid?"),
        help_text=_(
            "Check when this custom order has been paid (or if it gets cancelled out by a Credit Note)",
        ),
        default=False,
    )

    danish_vat = models.BooleanField(help_text="Danish VAT?", default=True)

    def __str__(self):
        return "custom order id #%s" % self.pk

    @property
    def vat(self):
        if self.danish_vat:
            return Decimal(round(self.amount * Decimal(0.2), 2))
        else:
            return 0

class Order(CreatedUpdatedModel):
    class Meta:
        unique_together = ("user", "open")
        ordering = ["-created"]

    products = models.ManyToManyField(
        "shop.Product",
        through="shop.OrderProductRelation",
    )

    user = models.ForeignKey(
        "auth.User",
        verbose_name=_("User"),
        help_text=_("The user this shop order belongs to."),
        related_name="orders",
        on_delete=models.PROTECT,
    )

    paid = models.BooleanField(
        verbose_name=_("Paid?"),
        help_text=_("Whether this shop order has been paid."),
        default=False,
    )

    # We are using a nullable BooleanField here to ensure that we only have one open order per user at a time.
    # This "hack" is possible since postgres treats null values as different, and thus we have database level integrity.
    open = models.BooleanField(
        blank=True,
        null=True,
        verbose_name=_("Open?"),
        help_text=_('Whether this shop order is open or not. "None" means closed.'),
        default=True,
    )

    CREDIT_CARD = "credit_card"
    BLOCKCHAIN = "blockchain"
    BANK_TRANSFER = "bank_transfer"
    IN_PERSON = "in_person"

    PAYMENT_METHODS = [CREDIT_CARD, BLOCKCHAIN, BANK_TRANSFER, IN_PERSON]

    PAYMENT_METHOD_CHOICES = [
        (CREDIT_CARD, "Credit card"),
        (BLOCKCHAIN, "Blockchain"),
        (BANK_TRANSFER, "Bank transfer"),
        (IN_PERSON, "In Person"),
    ]

    payment_method = models.CharField(
        max_length=50,
        choices=PAYMENT_METHOD_CHOICES,
        default="",
        blank=True,
    )

    cancelled = models.BooleanField(default=False)

    refunded = models.BooleanField(
        verbose_name=_("Refunded?"),
        help_text=_("Whether this order has been refunded."),
        default=False,
    )

    customer_comment = models.TextField(
        verbose_name=_("Customer comment"),
        help_text=_("If you have any comments about the order please enter them here."),
        default="",
        blank=True,
    )

    invoice_address = models.TextField(
        help_text=_(
            "The invoice address for this order. Leave blank to use the email associated with the logged in user.",
        ),
        blank=True,
    )

    notes = models.TextField(
        help_text="Any internal notes about this order can be entered here. They will not be printed on the invoice or shown to the customer in any way.",
        default="",
        blank=True,
    )

    pdf = models.FileField(null=True, blank=True, upload_to="proforma_invoices/")

    objects = OrderQuerySet.as_manager()

    def __str__(self):
        return "shop order id #%s" % self.pk

    def get_number_of_items(self):
        return self.products.aggregate(sum=Sum("orderproductrelation__quantity"))["sum"]

    @property
    def vat(self):
        return Decimal(self.total * Decimal(0.2))

    @property
    def total(self):
        if self.products.all():
            return Decimal(
                self.products.aggregate(
                    sum=Sum(
                        models.F("orderproductrelation__product__price")
                        * models.F("orderproductrelation__quantity"),
                        output_field=models.IntegerField(),
                    ),
                )["sum"],
            )
        else:
            return False

    def get_coinify_callback_url(self, request):
        """Check settings for an alternative COINIFY_CALLBACK_HOSTNAME otherwise use the one from the request"""
        if (
            hasattr(settings, "COINIFY_CALLBACK_HOSTNAME")
            and settings.COINIFY_CALLBACK_HOSTNAME
        ):
            host = settings.COINIFY_CALLBACK_HOSTNAME
        else:
            host = request.get_host()
        return (
            "https://"
            + host
            + str(reverse_lazy("shop:coinify_callback", kwargs={"pk": self.pk}))
        )

    def get_coinify_thanks_url(self, request):
        return (
            "https://"
            + request.get_host()
            + str(reverse_lazy("shop:coinify_thanks", kwargs={"pk": self.pk}))
        )

    def get_epay_accept_url(self, request):
        return (
            "https://"
            + request.get_host()
            + str(reverse_lazy("shop:epay_thanks", kwargs={"pk": self.pk}))
        )

    def get_cancel_url(self, request):
        return (
            "https://"
            + request.get_host()
            + str(reverse_lazy("shop:order_detail", kwargs={"pk": self.pk}))
        )

    def get_epay_callback_url(self, request):
        return (
            "https://"
            + request.get_host()
            + str(reverse_lazy("shop:epay_callback", kwargs={"pk": self.pk}))
        )

    @property
    def description(self):
        return "Order #%s" % self.pk

    def get_absolute_url(self):
        return str(reverse_lazy("shop:order_detail", kwargs={"pk": self.pk}))

    def create_tickets(self, request=None):
        tickets = []
        for order_product in self.orderproductrelation_set.all():
            # if this is a Ticket product?
            if order_product.product.ticket_type:
                query_kwargs = {
                    "product": order_product.product,
                    "ticket_type": order_product.product.ticket_type,
                }

                if order_product.product.ticket_type.single_ticket_per_product:
                    # This ticket type is one where we only create one ticket
                    ticket, created = order_product.shoptickets.get_or_create(
                        **query_kwargs
                    )

                    if created:
                        msg = (
                            "Created ticket for product %s on order %s (quantity: %s)"
                            % (
                                order_product.product,
                                order_product.order.pk,
                                order_product.quantity,
                            )
                        )
                        tickets.append(ticket)
                    else:
                        msg = (
                            "Ticket already created for product {} on order {}".format(
                                order_product.product,
                                order_product.order.pk,
                            )
                        )

                    if request:
                        messages.success(request, msg)
                else:
                    # We should create a number of tickets equal to OrderProductRelation quantity
                    already_created_tickets = order_product.shoptickets.filter(
                        **query_kwargs
                    ).count()
                    tickets_to_create = max(
                        0,
                        order_product.quantity - already_created_tickets,
                    )

                    # create the number of tickets required
                    if tickets_to_create > 0:
                        for _i in range(
                            0,
                            (order_product.quantity - already_created_tickets),
                        ):
                            ticket = order_product.shoptickets.create(**query_kwargs)
                            tickets.append(ticket)

                        msg = "Created {} tickets of type: {}".format(
                            order_product.quantity,
                            order_product.product.ticket_type.name,
                        )
                        if request:
                            messages.success(request, msg)

                # and mark the OPR as ticket_generated=True
                order_product.ticket_generated = True
                order_product.save()

        return tickets

    def mark_as_paid(self, request=None):
        self.paid = True
        self.open = None
        self.create_tickets(request)
        self.save()

    def mark_as_refunded(self, request=None):
        if not self.paid:
            msg = "Order %s is not paid, so cannot mark it as refunded!" % self.pk
            if request:
                messages.error(request, msg)
            else:
                print(msg)
        else:
            self.refunded = True
            # delete any tickets related to this order
            tickets = ShopTicket.objects.filter(opr__order=self)
            if tickets.exists():
                msg = "Order {} marked as refunded, deleting {} tickets...".format(
                    self.pk,
                    tickets.count(),
                )
                if request:
                    messages.success(request, msg)
                else:
                    print(msg)
                tickets.delete()
            else:
                msg = "Order %s marked as refunded, no tickets to delete" % self.pk
                if request:
                    messages.success(request, msg)
                else:
                    print(msg)
            self.save()

    def mark_as_cancelled(self, request=None):
        if self.paid:
            msg = "Order %s is paid, cannot cancel a paid order!" % self.pk
            if request:
                messages.error(request, msg)
            else:
                print(msg)
        else:
            self.cancelled = True
            self.open = None
            self.save()

    def is_not_ticket_generated(self):
        if self.orderproductrelation_set.filter(ticket_generated=True).count() == 0:
            return True
        else:
            return False

    def is_partially_ticket_generated(self):
        if (
            self.orderproductrelation_set.filter(ticket_generated=True).count() != 0
            and self.orderproductrelation_set.filter(ticket_generated=False).count()
            != 0
        ):
            # some products are handed out, others are not
            return True
        else:
            return False

    def is_fully_ticket_generated(self):
        if self.orderproductrelation_set.filter(ticket_generated=False).count() == 0:
            return True
        else:
            return False

    @property
    def ticket_generated_status(self):
        if self.is_not_ticket_generated():
            return "no"
        elif self.is_partially_ticket_generated():
            return "partially"
        elif self.is_fully_ticket_generated():
            return "fully"
        else:
            return False

    @property
    def coinifyapiinvoice(self):
        if not self.coinify_api_invoices.exists():
            return False

        for tempinvoice in self.coinify_api_invoices.all():
            # we already have a coinifyinvoice for this order, check if it expired
            if not tempinvoice.expired:
                # this invoice is not expired, we are good to go
                return tempinvoice

        # nope
        return False

    @property
    def filename(self):
        return "bornhack_proforma_invoice_order_%s.pdf" % self.pk

class ProductCategory(CreatedUpdatedModel, UUIDModel):
    class Meta:
        verbose_name = "Product category"
        verbose_name_plural = "Product categories"

    name = models.CharField(max_length=150)
    slug = models.SlugField()
    public = models.BooleanField(default=True)
    weight = models.IntegerField(
        default=100,
        help_text="Sorting weight. Heavier items sink to the bottom.",
    )

    def __str__(self):
        return self.name

    def save(self, **kwargs):
        self.slug = unique_slugify(
            self.name,
            slugs_in_use=self.__class__.objects.all().values_list("slug", flat=True),
        )
        super().save(**kwargs)

class ProductStatsManager(models.Manager):
    def with_ticket_stats(self):
        return (
            self.filter(
                orderproductrelation__order__paid=True,
                orderproductrelation__order__refunded=False,
            )
            .annotate(total_units_sold=Sum("orderproductrelation__quantity"))
            .annotate(profit=F("price") - F("cost"))
            .annotate(total_income=F("price") * F("total_units_sold"))
            .annotate(total_cost=F("cost") * F("total_units_sold"))
            .annotate(total_profit=F("profit") * F("total_units_sold"))
            .annotate(paid_order_count=Count("orderproductrelation__order"))
        )

class Product(CreatedUpdatedModel, UUIDModel):
    class Meta:
        verbose_name = "Product"
        verbose_name_plural = "Products"
        ordering = ["available_in", "price", "name"]

    category = models.ForeignKey(
        "shop.ProductCategory",
        related_name="products",
        on_delete=models.PROTECT,
    )

    name = models.CharField(max_length=150)
    slug = models.SlugField(unique=True, max_length=100)

    price = models.IntegerField(
        help_text=_("Price of the product (in DKK, including VAT)."),
    )

    description = models.TextField()

    available_in = DateTimeRangeField(
        help_text=_(
            "Which period is this product available for purchase? | "
            "(Format: YYYY-MM-DD HH:MM) | Only one of start/end is required",
        ),
    )

    ticket_type = models.ForeignKey(
        "tickets.TicketType",
        on_delete=models.PROTECT,
        null=True,
        blank=True,
    )

    stock_amount = models.IntegerField(
        help_text=(
            "Initial amount available in stock if there is a limited "
            "supply, e.g. fridge space"
        ),
        null=True,
        blank=True,
    )

    cost = models.IntegerField(
        default=0,
        help_text="The cost for this product, including VAT. Used for profit calculations in the economy system.",
    )

    comment = models.TextField(
        blank=True,
        help_text="Internal comments for this product.",
    )

    objects = ProductQuerySet.as_manager()
    statsobjects = ProductStatsManager()

    def __str__(self):
        return f"{self.name} ({self.price} DKK)"

    def clean(self):
        if self.category.name == "Tickets" and not self.ticket_type:
            raise ValidationError("Products with category Tickets need a ticket_type")

    def is_available(self):
        """Is the product available or not?

        Checks for the following:

        - Whether now is in the self.available_in
        - If a stock is defined, that there are items left
        """
        predicates = [self.is_time_available]
        if self.stock_amount:
            predicates.append(self.is_stock_available)
        return all(predicates)

    @property
    def is_time_available(self):
        now = timezone.now()
        time_available = now in self.available_in
        return time_available

    def is_old(self):
        now = timezone.now()
        if hasattr(self.available_in, "upper") and self.available_in.upper:
            return self.available_in.upper < now
        return False

    def is_upcoming(self):
        now = timezone.now()
        return self.available_in.lower > now

    @property
    def left_in_stock(self):
        if self.stock_amount:
            # All orders that are not open and not cancelled count towards what has
            # been "reserved" from stock.
            #
            # This means that an order has either been paid (by card or blockchain)
            # or is marked to be paid with cash or bank transfer, meaning it is a
            # "reservation" of the product in question.
            sold = OrderProductRelation.objects.filter(
                product=self,
                order__open=None,
                order__cancelled=False,
            ).aggregate(Sum("quantity"))["quantity__sum"]

            total_left = self.stock_amount - (sold or 0)

            return total_left
        return None

    @property
    def is_stock_available(self):
        if self.stock_amount:
            stock_available = self.left_in_stock > 0
            return stock_available
        # If there is no stock defined the product is generally available.
        return True

class OrderProductRelation(CreatedUpdatedModel):
    def __str__(self):
        return f'#{self.order}: {self.quantity} {self.product}'
    order = models.ForeignKey("shop.Order", on_delete=models.PROTECT)
    product = models.ForeignKey("shop.Product", on_delete=models.PROTECT)
    quantity = models.PositiveIntegerField()
    ticket_generated = models.BooleanField(default=False)

    @property
    def total(self):
        return Decimal(self.product.price * self.quantity)

    def clean(self):
        if self.ticket_generated and not self.order.paid:
            raise ValidationError(
                "Product can not be handed out when order is not paid.",
            )

class EpayCallback(CreatedUpdatedModel, UUIDModel):
    class Meta:
        verbose_name = "Epay Callback"
        verbose_name_plural = "Epay Callbacks"
        ordering = ["-created"]

    payload = models.JSONField()
    md5valid = models.BooleanField(default=False)

    def __str__(self):
        return f"callback at {self.created} (md5 valid: {self.md5valid})"

class EpayPayment(CreatedUpdatedModel, UUIDModel):
    class Meta:
        verbose_name = "Epay Payment"
        verbose_name_plural = "Epay Payments"
        ordering = ["created"]

    order = models.OneToOneField("shop.Order", on_delete=models.PROTECT)
    callback = models.ForeignKey("shop.EpayCallback", on_delete=models.PROTECT)
    txnid = models.IntegerField()

class CreditNote(CreatedUpdatedModel):
    class Meta:
        ordering = ["-created"]

    amount = models.DecimalField(max_digits=10, decimal_places=2)

    text = models.TextField(help_text="Description of what this credit note covers")

    pdf = models.FileField(null=True, blank=True, upload_to="creditnotes/")

    user = models.ForeignKey(
        "auth.User",
        verbose_name=_("User"),
        help_text=_("The user this credit note belongs to, if any."),
        related_name="creditnotes",
        on_delete=models.PROTECT,
        null=True,
        blank=True,
    )

    customer = models.TextField(
        help_text="Customer info if no user is selected",
        blank=True,
        default="",
    )

    danish_vat = models.BooleanField(help_text="Danish VAT?", default=True)

    paid = models.BooleanField(
        verbose_name=_("Paid?"),
        help_text=_(
            "Whether the amount in this creditnote has been paid back to the customer.",
        ),
        default=False,
    )

    sent_to_customer = models.BooleanField(default=False)

    def clean(self):
        errors = []
        if self.user and self.customer:
            msg = "Customer info should be blank if a user is selected."
            errors.append(ValidationError({"user", msg}))
            errors.append(ValidationError({"customer", msg}))
        if not self.user and not self.customer:
            msg = "Either pick a user or fill in Customer info"
            errors.append(ValidationError({"user", msg}))
            errors.append(ValidationError({"customer", msg}))
        if errors:
            raise ValidationError(errors)

    def __str__(self):
        if self.user:
            return "creditnote#{} - {} DKK (customer: user {})".format(
                self.id,
                self.amount,
                self.user.email,
            )
        else:
            return "creditnote#{} - {} DKK (customer: {})".format(
                self.id,
                self.amount,
                self.customer,
            )

    @property
    def vat(self):
        if self.danish_vat:
            return Decimal(round(self.amount * Decimal(0.2), 2))
        else:
            return 0

    @property
    def filename(self):
        return "bornhack_creditnote_%s.pdf" % self.pk

class Invoice(CreatedUpdatedModel):
    order = models.OneToOneField(
        "shop.Order",
        null=True,
        blank=True,
        on_delete=models.PROTECT,
    )
    customorder = models.OneToOneField(
        "shop.CustomOrder",
        null=True,
        blank=True,
        on_delete=models.PROTECT,
    )
    pdf = models.FileField(null=True, blank=True, upload_to="invoices/")
    sent_to_customer = models.BooleanField(default=False)

    def __str__(self):
        if self.order:
            return "invoice#{} - shop order {} - {} - total {} DKK (sent to {}: {})".format(
                self.id,
                self.order.id,
                self.order.created,
                self.order.total,
                self.order.user.email,
                self.sent_to_customer,
            )
        elif self.customorder:
            return (
                "invoice#%s - custom order %s - %s - amount %s DKK (customer: %s)"
                % (
                    self.id,
                    self.customorder.id,
                    self.customorder.created,
                    self.customorder.amount,
                    unidecode(self.customorder.customer),
                )
            )

    @property
    def filename(self):
        return "bornhack_invoice_%s.pdf" % self.pk

    def regretdate(self):
        return self.created + timedelta(days=15)

    @property
    def get_order(self):
        if self.order:
            return self.order
        else:
            return self.customorder

class CoinifyAPIInvoice(CreatedUpdatedModel):
    coinify_id = models.IntegerField(null=True)
    invoicejson = models.JSONField()
    order = models.ForeignKey(
        "shop.Order",
        related_name="coinify_api_invoices",
        on_delete=models.PROTECT,
    )

    def __str__(self):
        return "coinifyinvoice for order #%s" % self.order.id

    @property
    def expired(self):
        return parse_datetime(self.invoicejson["expire_time"]) < timezone.now()

class CoinifyAPICallback(CreatedUpdatedModel):
    headers = models.JSONField()
    payload = models.JSONField(blank=True)
    body = models.TextField(default="")
    order = models.ForeignKey(
        "shop.Order",
        related_name="coinify_api_callbacks",
        on_delete=models.PROTECT,
    )
    authenticated = models.BooleanField(default=False)

    def __str__(self):
        return f"order #{self.order.id} callback at {self.created}"

class CoinifyAPIRequest(CreatedUpdatedModel):
    order = models.ForeignKey(
        "shop.Order",
        related_name="coinify_api_requests",
        on_delete=models.PROTECT,
    )
    method = models.CharField(max_length=100)
    payload = models.JSONField()
    response = models.JSONField()

    def __str__(self):
        return f"order {self.order.id} api request {self.method}"


"""检视数据集"""
import json
import os

import cv2
import numpy as np
from public_data import IMAGE_SIZE,JSON_PATH

class FileOperator:
    def __init__(self):
        # 读取训练数据
        self.images = []
        self.labels = []
        self.face_num = None

    def resize_image(self, image, height=IMAGE_SIZE, width=IMAGE_SIZE):
        """按照指定图像大小调整尺寸"""
        top, bottom, left, right = (0, 0, 0, 0)

        # 获取图像尺寸
        h, w, _ = image.shape  # (237, 237, 3)

        # 对于长宽不相等的图片，找到最长的一边
        longest = max(h, w)

        # 计算短边需要增加多上像素宽度使其与长边等长
        if h < longest:
            dh = longest - h
            top = dh // 2
            bottom = dh - top
        elif w < longest:
            dw = longest - w
            left = dw // 2
            right = dw - left
        else:
            pass

        # 填充边界所用的颜色
        border_color = [0, 0, 0]

        # 将当前图片填充成正方形
        # cv2.BORDER_CONSTANT表示填充颜色由value的值指定
        constant = cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT, value=border_color)

        # 将图片缩放为指定大小
        return cv2.resize(constant, (height, width))

    def read_path(self, images_path):
        """从指定文件夹读取其下所有jpg格式图片以及其对应的标签名"""
        cv2.namedWindow('test')
        # self.images.clear()
        # self.labels.clear()
        # self.face_num = None
        for dir_item in os.listdir(images_path):

            # 从初始路径开始叠加，合并成可识别的操作路径
            full_path = os.path.abspath(os.path.join(images_path, dir_item))

            if os.path.isdir(full_path):  # 如果是文件夹，继续递归调用
                self.read_path(full_path)
            else:  # 文件
                if dir_item.endswith('.jpg'):
                    image = cv2.imread(full_path)
                    # image = self.resize_image(image, IMAGE_SIZE, IMAGE_SIZE)

                    self.images.append(image)
                    self.labels.append(images_path.split('\\')[-1])
                    cv2.imshow(images_path.split('\\')[-1], image)
                    c = cv2.waitKey(10)
                    if c & 0xFF == ord('q'):
                        break
        cv2.destroyAllWindows()
        return self.images, self.labels

    def load_dataset(self, dataset_path):
        """从指定路径读取训练数据"""
        # self.images, self.labels = self.read_path(dataset_path)
        self.read_path(dataset_path)
        print('labels:', self.labels)

        self.images = np.array(self.images)
        print(self.images.shape)

        # 转为集合去除重复项
        labels1 = list(set(self.labels))
        self.face_num = len(labels1)
        print('face_num:', self.face_num)
        name_list_dict = {}
        for i in range(self.face_num):
            name_list_dict[i] = labels1[i]
        with open(JSON_PATH, 'w') as f:
            f.write(json.dumps(name_list_dict))
        for index, name in name_list_dict.items():
            for i in range(len(self.labels)):
                if self.labels[i] == name:
                    self.labels[i] = index
        # print(labels)
        self.labels = np.array(self.labels)
        print(self.labels)
        return self.images, self.labels, self.face_num

if __name__ == '__main__':
    np.set_printoptions(threshold=5000)
    fo = FileOperator()
    images, labels, face_num = fo.load_dataset("./data")
    print(face_num)


n = int(input())

a = n // 10
b = n % 10
count = 0

while(1):
    count += 1
    c = (a + b) % 10
    tmp = b * 10 + c

    if n == tmp:
        break

    else:
        a, b = b, c

print(count)