#! /usr/bin/env python3
#------------------------ IMPORTS --------------------------#
from __future__ import annotations
from app import *
from websocket import WebSocketConnectionClosedException
from threading import Thread
from time import sleep, time
from random import random, uniform

@dataclass(slots=True)
class Receiver:
    #Pointers
    session: DiscordWrapper
    config: ConfigManager
    menu: MainMenu

    #Objects
    captcha: Captcha = field(init=False)
    profile: Profile = field(default_factory=Profile)
    message: Message = field(default_factory=Message)
    category: MessageCategory = field(default_factory=MessageCategory)
    event: dict = None

    #Flags
    is_ready: bool = False

    def __post_init__(self) -> None:
        '''Setups captcha.'''
        self.captcha = Captcha(api_key=self.config.ocr_api_key, menu=self.menu)

    @property
    def name(self) -> str:
        '''Returns the class name in the correct format.'''
        return f'{self.__class__.__name__}'

    def check_event(self, response: dict) -> bool:
        '''Checks if event is targeted to the user (sent by the application to the selected channel),
        also defines the self.event.'''
        if not response:
            return False

        try:
            e = response['d']
            e_name = response['t']
            e_channel = e['channel_id']
            e_author = e['author']['id']
        except (KeyError, TypeError):
            return False

        if e_name in TARGET_EVENT_NAMES:
            if e_channel == self.config.channel_id:
                if e_author == APPLICATION_ID:
                    self.event = e
                    return True
        return False

    def run(self) -> None:
        '''Main loop, continuously checks for new gateway events and properly handles it.'''
        self.is_ready = True
        while True:
            try:
                response = self.session.receive_event()
            except WebSocketConnectionClosedException as e:
                debugger.log(e, f'{self.name} - run - reconnection')
                #Connection lost
                self.is_ready = False
                self.menu.notify('[!] Connection lost, attempting to reconnect...', NotificationPriority.HIGH)
                if self.session.reconnect():
                    self.is_ready = True
                    self.menu.notify('[*] Reconnection succeeded.')
                    continue
                else:
                    self.menu.kill()
                    print(f'[E] Reconnection failed. Exception: {e}')
                    break
            except Exception as e:
                debugger.log(e, f'{self.name} - run - reconnection')
                break

            if not self.check_event(response):
                #Invalid/Irrelevant gateway event
                continue

            self.message.make(self.event)

            if self.captcha.detected and not self.captcha.regenerating:
                if self.message.content == 'You may now continue.':
                    #Captcha bypassed
                    self.menu.rcv_bypasses += 1
                    self.captcha.reset()
                    self.menu.notify('[*] Captcha bypassed !')
                else:
                    if self.message.content.find('Incorrect code') > -1:
                        self.menu.notify('[*] Incorrect code.', NotificationPriority.LOW)
                        continue
                    else:
                        #Message sent by the bot while captcha is detected
                        debugger.log(self, f'{self.name} - run (Message sent by the AFB while captcha is detected)')
                        break
            elif self.captcha.regenerating:
                if self.captcha.detect(self.event):
                    self.captcha.solve()
                    continue
                else:
                    #No detection while regenerating
                    debugger.log(self, f'{self.name} - run (No detection while regenerating)')
                    exit()
            else:
                if self.captcha.detect(self.event):
                    self.menu.notify('[!] Captcha detected !', NotificationPriority.NORMAL)
                    self.captcha.solve()
                    continue
                else:
                    if self.message.title:
                        #Normal messages
                        if self.message.title == self.category.fish:
                            #Fish (/fish or button) messages
                            self.menu.items = self.message.build()
                            self.menu.rcv_streak += 1
                        elif self.message.title.find(self.category.profile) > -1:
                            #Profile (/profile) messages
                            self.profile.update(self.message.description)
                            self.menu.notify('[*] Profile updated.')
                        elif self.message.title.find(self.category.charms) > -1:
                            #Charms (/charms) messages
                            self.profile.charms.update(self.message.description)
                            self.menu.notify('[*] Charms updated.')
                        elif self.message.title.find(self.category.buffs) > -1:
                            #Buffs/multipliers (/buffs) messages
                            self.profile.buffs.update(self.message.description)
                            self.menu.notify('[*] Buffs updated.')
                        elif self.message.title.find(self.category.quests) > -1:
                            #Quest list (/quests) messages
                            self.profile.quests.update(self.message.description)
                            self.menu.notify('[*] Quests updated.')
                        elif self.message.title.find(self.category.leaderboard) > -1:
                            #Leaderboard (/pos) messages
                            self.profile.leaderboard.update(self.message.description)
                            self.menu.notify('[*] Leaderboards updated.')
                        #Todo: read '... boost ended' message and inform scheduler (?)
                        #Your fishing boost ended!
                        #Your treasure boost ended!
                        else:
                            #Unhandled titled message
                            self.menu.notify(f'{sanitize(self.message.title)}: {sanitize(self.message.description)}')
                            pass
                    else:
                        #Untitled messages
                        if self.message.content:
                            if self.message.content.find('You must wait') > -1:
                                #Intentional short cooldown
                                self.menu.notify(
                                    '[*] If automatic, this short cooldown is intentional to ensure non-bot behavior.',
                                    NotificationPriority.VERY_LOW
                                    )
                                pass
                            else:
                                #Untitled message
                                self.menu.notify(f'[*] {self.message.untitled}')
                        else:
                            #Untitled with empty content  - probably embeded only with description
                            if menu.is_alive:
                                self.menu.notify(f'[*] {self.message.untitled}')
                            else:
                                if self.message.untitled.find('You hired a worker for the next') > -1 \
                                    or self.message.untitled.find('You already have a worker working') > -1:
                                    print(f'[*] {self.message.untitled}')
                            pass

        self.is_ready = False
        exit()

@dataclass(slots=True)
class Dispatcher:
    '''Dispatcher class, responsible for making, sending commands related to
    captcha and fish commands.'''
    #Pointers
    session: DiscordWrapper
    config: ConfigManager
    menu: BaseMenu
    sch: Scheduler
    rcv: Receiver
    captcha: Captcha = field(init=False)
    message: Message = field(init=False)

    #Objects
    cooldown: CooldownManager = field(init=False)

    #Flags
    in_cooldown: bool = False
    paused: bool = False

    def __post_init__(self) -> None:
        #Setting up pointers to improve organization
        self.captcha = self.rcv.captcha
        self.message = self.rcv.message

        #Instantiate cooldown manager
        self.cooldown = CooldownManager(user_cooldown=self.config.user_cooldown)

    def make_command(self, cmd: str, name: str, value: str, type: int = 3) -> tuple:
        '''Builds a tuple containing the command (str) and the 'options' parameter (dict).'''
        parameters = {
            "type": type,
            "name": name,
            "value": value
        }
        return (cmd, parameters)

    @property
    def name(self) -> str:
        '''Returns the class name in the correct format.'''
        return f'{self.__class__.__name__}'

    @property
    def pause(self) -> None:
        '''Play/Pause switch.'''
        if self.sch.status == SchStatus.BREAK:
            self.sch.interrupt_break()
        if self.paused:
            self.paused = False
            self.menu.notify('[*] Autofishbot resumed.')
        else:
            self.paused = True
            self.menu.notify('[*] Autofishbot paused.')

    @property
    def timeout(self) -> float:
        '''Timeout cooldown for general commands (other than fish commands).'''
        return self.cooldown.custom(
            mu= uniform(3, 5),
            sigma= random()
        )

    def run(self) -> None:
        '''Main loop, send commands'''
        _delay = 0.2

        while True:
            if not self.rcv.is_ready:
                sleep(_delay)
                continue

            if self.captcha.detected and not self.captcha.regenerating:
                if self.captcha.solving or len(self.captcha.answers) > 0:
                    try:
                        answer = self.captcha.answers.pop()
                        self.menu.notify(f'[!] Attempting code: "{answer}".')
                        cmd, param = self.make_command('verify', 'answer', answer)
                        self.session.request(command=cmd, parameters=param, category=COMMAND)

                        sleep(self.timeout)
                    except IndexError:
                        continue
                else:
                    if self.captcha.regens < MAX_CAPTCHA_REGENS:
                        self.captcha.regens += 1
                        self.menu.notify(f'[!] Regenerating captcha ({self.captcha.regens + 1}/{MAX_CAPTCHA_REGENS})', NotificationPriority.HIGH)

                        #This will force a new event to be analyzed by the
                        #detect() method but also keep the captcha.regens counter
                        self.captcha.regenerating = True

                        cmd, param = self.make_command('verify', 'answer', 'regen')
                        self.session.request(command=cmd, parameters=param, category=COMMAND)

                        #This sleep timeout might be needed in case of really slow
                        #connections, it might be caused by a bad proxy or internet
                        #?Further testing needed
                        sleep(1)
                    else:
                        self.menu.notify(f'[!] MAXIMUM CAPTCHA REGENS EXCEEDED, WAITING FOR MANUAL INPUT !', NotificationPriority.VERY_HIGH)
                        #Max regens attempts exceeded, waits for manual input
                        while self.captcha.detected:
                            sleep(1)
            else:
                while  self.captcha.busy \
                    or self.captcha.regenerating \
                    or self.sch.status in [SchStatus.BUSY, SchStatus.BREAK]:
                        sleep(_delay)

                if not self.captcha.detected and not self.paused and self.menu.is_alive:
                    _start = time()

                    if self.message.id and self.message.play_id:
                        if self.session.request(message_id=self.message.id, custom_id=self.message.play_id, category=BUTTON):
                            #Successfull interaction with buttons
                            pass
                        else:
                            #Failed interaction, reset ids to trigger ussage of slash commands
                            self.message.reset_ids()
                    else:
                        self.session.request(command='fish', category=COMMAND)

                    self.in_cooldown = True
                    sleep(abs(self.cooldown.new() - (time() - _start)))
                    self.in_cooldown = False
                else:
                    sleep(_delay)

#------------------------ INIT --------------------------#
if __name__ == "__main__":
    print(f'\n[*] Starting...')

    #Loads config
    config = ConfigManager()

    #Setup debugger
    debugger.setup(config.debug)

    #Instantiate menu
    if config.compact_mode:
        menu = CompactMenu()
    else:
        menu = MainMenu()

    #Instantiate session
    session = DiscordWrapper(
        config=config,
        menu=menu,
        auto_connect=True)

    #Instantiate receiver
    receiver = Receiver(
        session=session,
        config=config,
        menu=menu)

    #Instantiate Scheduler
    scheduler = Scheduler(
        session=session,
        config=config,
        menu=menu,
        captcha=receiver.captcha)

    #Instantiate dispatcher
    dispatcher = Dispatcher(
        session=session,
        config=config,
        menu=menu,
        sch=scheduler,
        rcv=receiver)

    #Async flow
    rcv_thread = Thread(target=receiver.run, daemon=True, name='Receiver')
    sch_thread = Thread(target=scheduler.run, args=(dispatcher,), daemon=True, name='Scheduler')
    dsp_thread = Thread(target=dispatcher.run, daemon=True, name='Dispatcher')

    rcv_thread.start()
    sch_thread.start()
    dsp_thread.start()

    #Start menu
    menu.run(
        config=config,
        dispatcher=dispatcher,
        profile=receiver.profile,
        scheduler=scheduler,
        threads=[rcv_thread, sch_thread, dsp_thread]
    )

    if config.fish_on_exit and menu.rcv_streak > 0:
        cmd, data = scheduler.commands.worker.data
        session.request(command=cmd, parameters=data)
        sleep(3)

    session.disconnect()
    exit(f'\n[!] User exited.')


import random as rand
import threading
from random import random

NumeroPoblaciones = 16

#Este porgrama Crean N poblaciones cada una con 20 genraciones utilizando un solo proceso pero el proceso se ayuda con 2 hilos para realizar el la de cruza

"""
Objetivo del programa:
	Resolver el problema de la mochila con un algoritmo genetico

Estructura del programa:
	Alelos:			peso y valor
					Se podria decir que un alelo es la parte atomica de una solucion, es indivisible y representa un valor en la estructura de nuestra solucion

	Genes:			objeto = (peso, valor)
					Los genes son elementos que conforman un cromosoma o individuo, en este caso el individuo es la mochila y el gen un objeto de la mochila

	Cromosomas:	[objeto_1, objeto_2, ... , objeto_n]
					Los cromosomas son soluciones potenciales al problema que tratamos de resolver, en este caso un cromosoma (o individuo) representa una mochila
Funciones utilizadas:
	len(x)					Devuelve la cantidad de objetos en x
	radom.randint(0, n)		Devuelve un entero entre 0 y n
	lista.pop(i)			Elimina el i-esimo objeto de la lista, en caso de no proporcionar un index elimina el ultimo objeto en la lista
	random.choice(x)		Devuelve un elemento aleatorio de x
	lista.append(a)			Ingresa en la lista el parametro a en el tope
	max(lista)				Devuele el valor mayor en lista
	lista.index(x)			Devuelve la posicion de la lista del elemento x
	float(x)				cast a float de x, puede ser una cadena o entero

Material util:
	Documentacion oficial de python:	https://docs.python.org/2/
		-random:						https://docs.python.org/2/library/random.html - aqui podran ver como modificar una semilla
		-listas y tuplas:				https://docs.python.org/2/tutorial/datastructures.html
		-hilos:							https://docs.python.org/2/library/threading.html
"""
##	Variables globales
W = 50											#peso que soporta la mochila
genes = []										#cada gen representa un objeto de la mochila
tPoblacion = 50									#cantidad de individuos por poblacion
num_Generaciones = 20							#veces en las que se iterara el algoritmo
c_objetos = 125	#input("Ingrese la cantidad de objetos a generar: ")	#cantidad de objetos que a generar (recomiendo un valor entre 100 y 150; valor maximo soportado (en pruebas): 870)

probabilidad_Mutacion = []					#probabilidad de mutacion definismos una para cada poblacion a crear
for i in range(NumeroPoblaciones):
	probabilidad_Mutacion.append(random()) #Creasmo un rand entre 0 y 1
	print("ProbabilidadMutacion para el hilo [", i , "]: ", probabilidad_Mutacion[i])

##	Funciones
def inicializar_individuo():					#funcion para generar individuos
	global genes, W
	peso = 0									#peso que lleva el individuo generado
	mochila = [ ]								#individuo, cada individuo es una solucion posible
	while (peso < W):
		pos = rand.randint(0, len(genes)-1)		#posicion (index) aleatoria de la lista de objetos
		objeto = genes[pos]						#objeto que hace referencia el index
		if (peso > W):
			mochila.pop()						#sacar el ultimo objeto de la mochila
		elif ((objeto[0] + peso) > W ):			#condicion para verificar el peso de la mochila con el peso del objeto

			pass								#objeto[0] es el peso, ya que: obj = (peso, valor)
		else:
			mochila.append(objeto)				#agregar objeto a la mochila
			peso = peso + objeto[0]				#sumar el peso del objeto al peso de la mochila
		#print "Agregado: ", objeto
	return mochila

def generar_Poblacion(n):							#funcion para generar la poblacion inicial
	poblacion = []									#poblacion a generar, es una lista de listas (arreglo bidimensional)
	for i in range(n):
		poblacion.append(inicializar_individuo())	#insertar un individuo (mochila) en la poblacion n-veces
	return poblacion

def cruza(padre1, padre2):						#cruza entre individuos, retorna un nuevo individuo
	#print padre1
	#print padre2
	global W									#peso de la mochila
	offspring = []								#individuo a generar
	peso_individuo = 0							#peso del individuo (nueva mochila)
	aux1 = padre1[:]							#copiar padre1 en un auxiliar, con el objetico de modificarlo. Si hacemos: aux1 = padre1; y modificamos aux1 tambien modificamos a padre1
	aux2 = padre2[:]							#copiar padre2 en un auxiliar

	while (peso_individuo < W):
		#print "peso individuo: ", peso_individuo
		#print "aux1", aux1
		#print "aux2", aux2
		#print "a OFFSPRING", offspring

												#este 'switch' es para evitar usar un auxiliar vacio
		if not aux1 and not aux2:				#not aux1 significa que aux1 no tiene elementos, es equivalente a len(aux1) == 0
			return offspring
		elif not aux1:							#aux1 es vacio?
			a = 0								#tomar elementos de aux2
		elif not aux2:							#aux2 es vacio?
			a = 1								#tomar elementos de aux1
		else:
			a = rand.randint(0, 1)				#en caso de que ningun auxiliar sea vacio elegimos uno aleatoriamente

		if (a):										#seleccion del padre que hereda
			objeto = rand.choice(aux1)				#objeto a heredar
			if (objeto[0] + peso_individuo > W):	#objeto[0] = peso; esto es para ver si la mochila se desborda (hablando de su peso)
				aux1.pop( aux1.index(objeto) )		#eliminar objeto de espacio muestral
			else:
				offspring.append(objeto)			#insertar objeto
				peso_individuo = peso_individuo + objeto[0]	#sumar peso a la mochila
				aux1.pop( aux1.index(objeto) )		#eliminar objeto de espacio muestral (para evitar repeticiones)
		else:									#lo mismo pero con otro padre
			objeto = rand.choice(aux2)				#objeto a heredar
			if (objeto[0] + peso_individuo > W):	#objeto[0] = peso; esto es para ver si la mochila se desborda (hablando de su peso)
				aux2.pop( aux2.index(objeto) )		#eliminar objeto de espacio muestral
			else:
				offspring.append(objeto)			#insertar objeto
				peso_individuo = peso_individuo + objeto[0]	#sumar peso a la mochila
				aux2.pop( aux2.index(objeto) )		#eliminar objeto de espacio muestral (para evitar repeticiones)

	return offspring

def peso(individuo):				#funcion para saber el peso de una mochila
	peso = 0
	for gen in individuo:
		peso = peso + gen[0]
	return float(peso)

def valor(individuo):				#funcion para saber el valor acumulado de una mochila
	v = 0
	for gen in individuo:
		v = v + gen[0]
	return float(v)

def aptitud(individuo):							#calcular peso y valor para determinar la aptitud de la mochila
	#print "INIDIVIDUO:	", individuo
	suma_peso = 0.0								#peso total de la mochila
	suma_valor = 0.0							#valor total de la mochila
	for gen in individuo:
		suma_peso = suma_peso + gen[0]			#gen[0] <- peso
		suma_valor = suma_valor + gen[1]		#gen[1] <- valor
	aptitud = suma_valor/suma_peso				#relacion valor/peso
	return aptitud

def nueva_Poblacion(old_population):			#fucion para generar una nueva poblacion
	global tPoblacion
	siguiente_Generacion = []
	siguiente_Generacion1 = []
	siguiente_Generacion2 = []

	threads = list() #lista para los hilos
	#print "Hilos generados para cruza: ", 2 ##apartamos dos posisciones para los 2 hilos
	for i in range(2):
		threads.append(i)

	#A cada hilo le mandamos una parte del tamanio de la poblacion
	threads[0] = threading.Thread(target=cruzarDosMochilas, args=(old_population[:tPoblacion//2],siguiente_Generacion1))
	threads[0].start()

	threads[1] = threading.Thread(target=cruzarDosMochilas, args=(old_population[tPoblacion//2:],siguiente_Generacion2))
	threads[1].start()

	threads[0].join()
	threads[1].join()

	siguiente_Generacion = siguiente_Generacion1 + siguiente_Generacion2

	return siguiente_Generacion

def cruzarDosMochilas(old_population,siguiente_Generacion): #como se reciben por referencia no se necesita retornar nada

	for x in range(len(old_population)//2):		#en cada iteracion se generan dos individuos
		#print "DEBUG DEBUG DEBUG DEBUG"
		padre1 = old_population[x]
		padre2 = old_population[(len(old_population)//2) + x]	#no hay que olvidar que estamos iterando en N/2 elementos, mientras que tenemos N elementos
		#print "p1", padre1
		#print "p2", padre2

		offs1 = cruza(padre1, padre2)			#hijo 1
		offs2 =	cruza(padre2, padre1)			#hijo 2
		#print "o1", offs1
		#print "o2", offs2

		siguiente_Generacion.append(offs1)	#insertar hijo 1 en la nueva poblacion
		siguiente_Generacion.append(offs2)		#insertar hijo 2 en la nueva poblacion

#metodo de seleccion:	torneo					#El metodo del torneo elige al individuo con mayor aptitud de una poblacion
												#Este metodo no es recomendable para generar diversidad
												#Aunque es una buena manera de mantener al mejor individuo por varias generaciones hasta que llegue uno mejor
def seleccion_Torneo(poblacion):
	aptitudes = []									#aptitud de cada miembro de la poblacion
	for individuo in poblacion:
		fitness =  aptitud(individuo)				#calcular aptitud de cada individuo
		#print fitness
		aptitudes.append(fitness)					#insertar en una lista las aptitudes de los individuos

	pos = aptitudes.index(max(aptitudes))			#posicion del mejor individuo
	mejor = poblacion[pos]							#mejor individuo
	#print "POS: ", pos
	#print "MEJOR: ", mejor

	return mejor

def mutar(individuo,probabilidad_Mutacion):									#mutacion
	global W, genes				#se usaran los objetos generados para reemplazar un objeto del individuo (mochila) que se envia como parametro
	prob = rand.random()								#probabilidad de mutar aleaotoria entre [0.0, 1.0]
	peso_individuo = peso(individuo)					#peso del individuo
	aux = individuo[:]									#copia del individuo a mutar, este valor sera modificado

	if (prob < probabilidad_Mutacion):					#en caso de ser verdadero el individuo sera mutado
		pos = rand.randint(0, len(individuo)-1)			#posicion del objeto a modificar

		while True:										#ciclo equivalente a:	do {...} while ( )
			piv = rand.randint(0, len(genes)-1)			#seleccionar un objeto de los genes
			objeto = genes[piv]							#el objeto que reemplazara o sera insertado en la mochila. Las dos ultimas lineas son equivalentes a: objeto = rand.choice(genes)
			#print objeto

			if (objeto[0] + peso(individuo) <= W):			#en este escenario el objeto sera insertado en vez de sustituir a otro, ya que puede entrar a la mochila sin romper la regla del peso
				aux.append(objeto)
				break
			elif (individuo[pos] != genes[piv]):			#en caso de no cumplirse: volver a generar el objeto que reemplazara
				aux[pos] = objeto							#sustituir objeto
				break

		return aux

###	MAIN

def main():
													#dummy main() o main() falso
	global tPoblacion, c_objetos, genes, num_Generaciones

	global NumeroPoblaciones

	print("Numero de Poblaciones a generar: ", NumeroPoblaciones)
	for i in range(NumeroPoblaciones):
		#INICIALIZACION
		while len(genes) != c_objetos:							#en este ciclo se van a generar los objetos que llevaremos en la mochila
			obj = (rand.randint(1, 30), rand.randint(0, 30))	#acotar el dominio. El peso empieza en 1, ya que si es cero existe la posibilidad de que se ingresen infinitos objetos con peso 0
			if obj not in genes:								#determinar si el objeto a generar ya se ha generado y existe en la lista
				genes.append(obj)								#insertar objeto (gen) en la lista de objetos (genes)

		#print "Objetos generados:"
		#for objeto in genes: print objeto						#imprime los objetos generados

		"""
		aqui van los hilos										#los hilos se generan despues de los objetos ya que si generamos objetos en cada hilo estariamos resolviendo un problema diferente en cada hilo
		"""

		poblacion = generar_Poblacion(tPoblacion)				#poblacion inicial
		#print "Poblacion inicial (f0):"
		#i = 0
		#for individuo in poblacion:
		#	i = i + 1
		#	print "Individuo ", i, ":	",  individuo			#imprime los miembros de la poblacion generada

		mejor_solucion = seleccion_Torneo(poblacion)			#seleccion del mejor individuo
		print("Poblacion",i,"Generacion: 0 aptitud de la mejor solucion:	", aptitud(mejor_solucion))

		for x in range(num_Generaciones):
			#CRUZA
			poblacion = nueva_Poblacion(poblacion)				#sustituir la poblacion actual con una nueva
			#MUTACION
			for individuo in poblacion:
				individuo = mutar(individuo,probabilidad_Mutacion[i])					#mutar a cada individuo, en la funcion mutar() se determina si es mutado o no
			#SELECCION
			poblacion[0] = mejor_solucion						#elitisimo, la mejor solucion se mantiene en la siguiente poblacion
			#mejor_solucion = seleccion_Torneo(poblacion)

			#imprimir poblacion
			#for I in range(len(poblacion)-1):
			#	individuo = poblacion[I]
			#	print I, "	", individuo

			mejor_solucion = seleccion_Torneo(poblacion)		#volver a determinar la mejor solucion
			if x == 19:
				print("Poblacion",i,"Generacion: ", x+1, "aptitud de la mejor solucion:	 ", aptitud(mejor_solucion))
			#print "Mutacion"

if __name__ == "__main__":								#main del programa
	main()


#!/usr/bin/env python
# -*- encoding:utf-8 -*-

"""
   Training part of the Deep QA model

"""

from __future__ import print_function
from __future__ import division

__copyright__ = "Copyright (c) 2017 Xuming Lin. All Rights Reserved"
__author__    = "Xuming Lin"
__date__      = "2017-08-21:23:42:17"

from qacnn import QACNN
import tensorflow as tf
import datetime
import operator
import data

# Config函数
class Config(object):
    def __init__(self, vocab_size):
        # 输入序列(句子)长度
        self.sequence_length = 200
        # 循环数
        self.num_epochs = 100000
        # batch大小
        self.batch_size = 100
        # 词表大小
        self.vocab_size = vocab_size
        # 词向量大小
        self.embedding_size = 100
        # 不同类型的filter,相当于1-gram,2-gram,3-gram和5-gram
        self.filter_sizes = [1, 2, 3, 5]
        # 隐层大小
        self.hidden_size = 80
        # 每种filter的数量
        self.num_filters = 512
        # 论文里给的是0.0001
        self.l2_reg_lambda = 0.
        # dropout
        self.keep_prob = 1.0
        # 学习率
        # 论文里给的是0.01
        self.lr = 0.01
        # margin
        # 论文里给的是0.009
        self.m = 0.05
        # 设定GPU的性质,允许将不能在GPU上处理的部分放到CPU
        # 设置log打印
        self.cf = tf.ConfigProto(allow_soft_placement=True, log_device_placement=False)
        # 只占用20%的GPU内存
        self.cf.gpu_options.per_process_gpu_memory_fraction = 0.2

print('Loading Data...')

# 词映射ID
vocab = data.vocab_data

# 配置文件
config = Config(len(vocab['word2id']))

# 开始训练和测试
with tf.device('/gpu:0'):
    with tf.Session(config=config.cf) as sess:
        # 建立CNN网络
        cnn = QACNN(config, sess)
        # 训练函数
        def train_step(x_batch_1, x_batch_2, x_batch_3):
            feed_dict = {
                cnn.q: x_batch_1,
                cnn.aplus: x_batch_2,
                cnn.aminus: x_batch_3,
                cnn.keep_prob: config.keep_prob
            }
            _, step, loss, accuracy = sess.run(
                [cnn.train_op, cnn.global_step, cnn.loss, cnn.accu],
                feed_dict)
            time_str = datetime.datetime.now().isoformat()
            print("{}: step {}, loss {:g}, acc {:g}".format(time_str, step, loss, accuracy))
        # 测试函数
        def dev_step():
            scoreDict = dict()
            for qids, x_test_1, x_test_2, labels in data.load_valid(config.batch_size, config.sequence_length, config.sequence_length):
                feed_dict = {
                    cnn.q: x_test_1,
                    cnn.aplus: x_test_2,
                    cnn.aminus: x_test_2,
                    cnn.keep_prob: 1.0
                }
                batch_scores = sess.run(cnn.q_ap_cosine, feed_dict)
                for score, qid, label in zip(batch_scores, qids, labels):
                    scoreDict.setdefault(qid, list)
                    scoreDict[qid].append([score, label])
            lev1 = .0
            lev0 = .0
            for k, v in scoreDict.items():
                v.sort(key=operator.itemgetter(0), reverse=True)
                score, flag = v[0]
                if flag == '1':
                    lev1 += 1
                if flag == '0':
                    lev0 += 1
            # 回答的正确数和错误数
            print('回答正确数 ' + str(lev1))
            print('回答错误数 ' + str(lev0))
            print('准确率 ' + str(float(lev1)/(lev1+lev0)))

        # 每5000步测试一下
        evaluate_every = 5000
        # 开始训练和测试
        sess.run(tf.global_variables_initializer())
        for i in range(config.num_epochs):
            for (_, x_batch_1, x_batch_2, x_batch_3) in data.load_train(config.batch_size, config.sequence_length, config.sequence_length):
                train_step(x_batch_1, x_batch_2, x_batch_3)
                if (i+1) % evaluate_every == 0:
                    print("\n测试{}:".format((i+1)/evaluate_every))
                    dev_step()
                    print


import uno
import unohelper

from com.sun.star.awt.FontWeight import BOLD

from com.sun.star.ucb.ConnectionMode import OFFLINE

from com.sun.star.logging.LogLevel import ALL

from com.sun.star.mail.MailServiceType import IMAP
from com.sun.star.mail.MailServiceType import SMTP

from com.sun.star.rest.HTTPStatusCode import NOT_FOUND
from com.sun.star.rest import HTTPException
from com.sun.star.rest import RequestException

from com.sun.star.uno import Exception as UnoException

from ..transferable import Transferable

from ..unotool import createService
from ..unotool import getConfiguration
from ..unotool import getConnectionMode
from ..unotool import getResourceLocation
from ..unotool import getStringResource
from ..unotool import getUrl
from ..unotool import hasInterface

from ..mailertool import getMailService
from ..mailertool import getMailMessage
from ..mailertool import getMessageImage

from ..logger import LogController
from ..logger import RollerHandler

from ..oauth2 import g_service

from ..configuration import g_identifier
from ..configuration import g_extension
from ..configuration import g_mailservicelog
from ..configuration import g_logo
from ..configuration import g_logourl
from ..configuration import g_chunk

from ..user import User

from .ispdbserver import IspdbServer

from email.utils import parseaddr
import xml.etree.ElementTree as ET
from time import sleep
from threading import Thread
import validators
import traceback

class IspdbModel(unohelper.Base):
    def __init__(self, ctx, sender, readonly):
        self._ctx = ctx
        self._sender = sender
        self._readonly = readonly
        self._servers = None
        self._services = [SMTP.value, IMAP.value]
        self._offline = 0
        self._diposed = False
        self._version = 0
        self._listener = None
        secure = {0: 3, 1: 4, 2: 5}
        unsecure = {0: 0, 1: 1, 2: 2}
        self._levels = {0: unsecure, 1: secure, 2: secure}
        self._connections = {0: 'Insecure', 1: 'SSL', 2: 'TLS'}
        self._authentications = {0: 'None', 1: 'Login', 2: 'OAuth2'}
        configuration = getConfiguration(ctx, g_identifier)
        self._url = configuration.getByName('IspDBUrl')
        self._timeout = configuration.getByName('ConnectTimeout')
        self._logger = LogController(ctx, g_mailservicelog)
        self._resolver = getStringResource(ctx, g_identifier, g_extension)
        self._resources = {'Step': 'IspdbPage%s.Step',
                           'Title': 'IspdbPage%s.Title.%s',
                           'PageLabel': 'IspdbPage%s.Label1.Label',
                           'PagesLabel': 'IspdbPages.Label1.Label',
                           'Progress': 'IspdbPage2.Label2.Label.%s',
                           'Security': 'IspdbPages.Label10.Label.%s',
                           'SendTitle': 'SendDialog.Title',
                           'SendSubject': 'SendDialog.TextField2.Text',
                           'SendMessage': 'SendDialog.TextField3.Text',
                           'ThreadTitle': 'SendThread.Title'}

    @property
    def Email(self):
        name, address = parseaddr(self._sender)
        return address

    @property
    def Sender(self):
        return self._sender
    @Sender.setter
    def Sender(self, sender):
        self._sender = sender

    @property
    def Offline(self):
        return self._offline
    @Offline.setter
    def Offline(self, offline):
        self._offline = offline

    def resolveString(self, resource):
        return self._stringResource.resolveString(resource)

# IspdbModel getter methods called by IspdbWizard
    def dispose(self):
        self._diposed = True
        if self._listener is not None:
            self._logger.removeModifyListener(self._listener)
            self._listener = None

# IspdbModel getter methods called by WizardPages 1
    def getSender(self):
        return self._sender, not self._readonly

# IspdbModel getter methods called by WizardPages 2 and 4
    def isDisposed(self):
        return self._diposed

# IspdbModel getter methods called by WizardPage1
    def isEmailValid(self, sender):
        name, address = parseaddr(sender)
        if validators.email(address):
            return True
        return False

# IspdbModel getter methods called by WizardPage2
    def getServerConfig(self, *args):
        self._version += 1
        Thread(target=self._getServerConfig, args=args).start()

    def _getServerConfig(self, progress, update):
        # FIXME: Because we call this thread in the WizardPage.activatePage(),
        # FIXME: if we want to be able to navigate through the Wizard roadmap
        # FIXME: without GUI refreshing problem then we need to pause this thread.
        sleep(0.2)
        auto = False
        config = None
        progress(10)
        url = getUrl(self._ctx, self._url)
        progress(20)
        mode = getConnectionMode(self._ctx, url.Server)
        progress(30)
        user = User(self._ctx, self.Sender)
        progress(40)
        request = createService(self._ctx, g_service)
        progress(50)
        if request is None:
            offset = 1 if user.isNew() else 2
            status = (100, offset, BOLD)
        elif mode == OFFLINE:
            offset = 3 if user.isNew() else 4
            status = (100, offset, BOLD)
        else:
            progress(60)
            try:
                config = self._getIspdbConfig(user, request, url.Complete)
            except RequestException as e:
                offset = 5 if user.isNew() else 6
                status = (100, offset, BOLD)
            else:
                progress(80)
                if not user.isNew():
                    status = (100, 7)
                elif config is None:
                    status = (100, 8)
                else:
                    auto = True
                    status = (100, 9)
        self._servers = IspdbServer(user, config)
        self._offline = mode
        progress(*status)
        update(self.getPageTitle(2), auto, user.getReplyToState(), user.ReplyToAddress, user.getImapState(), mode)

    def _getIspdbConfig(self, user, request, url):
        config = None
        parameter = request.getRequestParameter('getIspdbConfig')
        parameter.Url = '%s%s' % (url, user.getUserDomain())
        parameter.NoAuth = True
        response = request.execute(parameter)
        if response.Ok:
            config = self._parseIspdbConfig(user, response)
        elif response.StatusCode != NOT_FOUND:
            response.raiseForStatus()
        response.close()
        return config

    def _parseIspdbConfig(self, user, response):
        smtps = []
        imaps = []
        config = {}
        map1 = {'plain': 0, 'SSL': 1, 'STARTTLS': 2}
        map2 = {'none': 0, 'password-cleartext': 1, 'plain': 1,
                'password-encrypted': 1, 'secure': 1, 'OAuth2': 2}
        map3 = {'%EMAILLOCALPART%': 0, '%EMAILADDRESS%': 1, '%EMAILDOMAIN%': 2}
        parser = ET.XMLPullParser(('end', ))
        iterator = response.iterContent(g_chunk, False)
        while iterator.hasMoreElements():
            # FIXME: As Decode is False we obtain a sequence of bytes
            parser.feed(iterator.nextElement().value)
            for event, element in parser.read_events():
                if element.tag != 'emailProvider':
                    continue
                for child in element.findall('outgoingServer'):
                    if child.get('type') == 'smtp':
                        smtps.append(self._parseServer(child, map1, map2, map3))
                for child in element.findall('incomingServer'):
                    if child.get('type') == 'imap':
                        imaps.append(self._parseServer(child, map1, map2, map3))
        config[SMTP.value] = tuple(smtps)
        config[IMAP.value] = tuple(imaps)
        if user.isNew():
            user.enableImap(len(imaps) > 0)
        return config

    def _parseServer(self, element, map1, map2, map3):
        server = {}
        server['ServerName'] = element.find('hostname').text
        server['Port'] = int(element.find('port').text)
        server['ConnectionType'] = map1.get(element.find('socketType').text, 1)
        server['AuthenticationType'] = map2.get(element.find('authentication').text, 1)
        server['LoginMode'] = map3.get(element.find('username').text, 1)
        return server

# IspdbModel setter methods called by WizardPage2
    def enableReplyTo(self, enabled):
        self._servers.User.enableReplyTo(enabled)
        return self._servers.User.ReplyToAddress

    def setReplyToAddress(self, replyto):
        self._servers.User.setReplyToAddress(replyto)

    def enableIMAP(self, imap):
        self._servers.User.enableImap(bool(imap))
        return self._offline

# IspdbModel getter methods called by WizardPage3 / WizardPage4
    def refreshView(self, version):
        return version < self._version

    def getVersion(self):
        return self._version

    def getConfig(self, service):
        config = {}
        config['First'] = self._servers.isFirst(service)
        config['Last'] = self._servers.isLast(service)
        config['Page'] = self._servers.getServerPage(service)
        config['Default'] = self._servers.isDefaultPage(service)
        config.update(self._servers.getCurrentServer(service))
        config['UserName'] = self._getUserName(service)
        config['Password'] = self._getPassword(service)
        return config

    def isConnectionValid(self, host, port):
        return self._isHostValid(host) and self._isPortValid(port)

    def isStringValid(self, value):
        if validators.length(value, min=1):
            return True
        return False

    def previousServerPage(self, service, server):
        self._servers.updateCurrentServer(service, server)
        self._servers.decrementIndex(service)

    def nextServerPage(self, service, server):
        self._servers.updateCurrentServer(service, server)
        self._servers.incrementIndex(service)

    def updateConfiguration(self, service, server, user):
        self._servers.updateCurrentServer(service, server)
        self._servers.User.updateConfig(service, user)

    def saveConfiguration(self):
        self._servers.User.saveConfig()

    def getSecurity(self, i, j):
        level = self._levels.get(i).get(j)
        message = self.getSecurityMessage(level)
        return message, level

# IspdbModel getter methods called by WizardPage5
    def addLogListener(self, listener):
        self._logger.addModifyListener(listener)
        self._listener = listener

    def getLogContent(self):
        return self._logger.getLogContent(True)

    def connectServers(self, *args):
        Thread(target=self._connectServers, args=args).start()

    def _connectServers(self, reset, progress, setlabel, setstep):
        handler = RollerHandler(self._ctx, self._logger.Name)
        level = self._getLogLevel(handler)
        i = 0
        connect = True
        range = 100
        count = 2 if self._servers.User.useIMAP() else 1
        reset(count * range)
        for service in self._services:
            if service == SMTP.value or self._servers.User.useIMAP():
                setlabel(service, self._getHost(service), self._getPort(service))
                context = self._servers.User.getConnectionContext(service)
                authenticator = self._servers.User.getAuthenticator(service)
                if not self._connectServer(context, authenticator, service, i * range, progress):
                    connect = False
                    break
                i += 1
        self._setLogLevel(handler, level)
        setstep(4 if connect else 2)

    def _connectServer(self, context, authenticator, service, i, progress):
        connect = False
        progress(i + 25)
        server = getMailService(self._ctx, service)
        progress(i + 50)
        try:
            server.connect(context, authenticator)
        except UnoException as e:
            progress(i + 100)
        else:
            progress(i + 75)
            if server.isConnected():
                server.disconnect()
                connect = True
                progress(i + 100)
            else:
                progress(i + 100)
        return connect

    def _getHost(self, service):
        return self._servers.getServerHost(service)

    def _getPort(self, service):
        return self._servers.getServerPort(service)

    def sendMessage(self, *args):
        Thread(target=self._sendMessage, args=args).start()

    def _sendMessage(self, recipient, subject, message, reset, progress, setstep):
        handler = RollerHandler(self._ctx, self._logger.Name)
        level = self._getLogLevel(handler)
        transferable = Transferable(self._ctx, self._logger)
        if self._servers.User.useIMAP():
            i = 100
            reset(200)
            threadid = self._uploadMessage(transferable, subject, progress)
        else:
            i = 0
            reset(100)
            threadid = None
        progress(i + 5)
        smtp = SMTP.value
        context = self._servers.User.getConnectionContext(smtp)
        authenticator = self._servers.User.getAuthenticator(smtp)
        step = 3
        progress(i + 25)
        server = getMailService(self._ctx, smtp)
        progress(i + 50)
        try:
            server.connect(context, authenticator)
        except UnoException as e:
            print("IspdbModel._sendMessage() 1 Error: %s" % e.Message)
        else:
            progress(i + 75)
            if server.isConnected():
                #body = MailTransferable(self._ctx, message, False)
                body = transferable.getByString(message)
                mail = getMailMessage(self._ctx, self.Sender, recipient, subject, body)
                interface = 'com.sun.star.mail.XMailMessage2'
                if hasInterface(mail, interface) and threadid is not None:
                    mail.ThreadId = threadid
                    print("IspdbModel._sendMessage() 2 ******************")
                print("IspdbModel._sendMessage() 3: %s - %s" % (type(mail), mail))
                try:
                    server.sendMailMessage(mail)
                    print("IspdbModel._sendMessage() 4: %s" % mail.MessageId)
                except UnoException as e:
                    print("IspdbModel._sendMessage() 5 Error: %s - %s" % (e.Message, traceback.format_exc()))
                else:
                    step = 5
                server.disconnect()
        progress(i + 100)
        self._setLogLevel(handler, level)
        setstep(step)

    def _uploadMessage(self, transferable, subject, progress):
        mail = msgid = None
        imap = IMAP.value
        context = self._servers.User.getConnectionContext(imap)
        authenticator = self._servers.User.getAuthenticator(imap)
        progress(10)
        server = getMailService(self._ctx, imap)
        progress(20)
        try:
            server.connect(context, authenticator)
        except UnoException as e:
            print("IspdbModel._uploadMessage() 1 Error: %s" % e.Message)
        else:
            progress(40)
            if server.isConnected():
                try:
                    folder = server.getSentFolder()
                    if server.hasFolder(folder):
                        message = self._getThreadMessage()
                        #body = MailTransferable(self._ctx, message, True)
                        body = transferable.getByString(message)
                        mail = getMailMessage(self._ctx, self.Sender, self.Email, subject, body)
                        progress(60)
                        server.uploadMessage(folder, mail)
                except UnoException as e:
                    print("IspdbModel._uploadMessage() 2 Error: %s - %s" % (e.Message, traceback.format_exc()))
                else:
                    interface = 'com.sun.star.mail.XMailMessage2'
                    if mail is not None and hasInterface(mail, interface):
                        msgid = mail.MessageId
                        print("IspdbModel._uploadMessage() 3 *****************")
                progress(80)
                server.disconnect()
        progress(100)
        print("IspdbModel._uploadMessage() 4 MessageId: %s" % msgid)
        return msgid

    def _getLogLevel(self, handler):
        self._logger.addRollerHandler(handler)
        level = self._logger.Level
        self._logger.Level = ALL
        return level

    def _setLogLevel(self, handler, level):
        self._logger.removeRollerHandler(handler)
        self._logger.Level = level

# IspdbModel getter methods called by SendDialog
    def validSend(self, recipient, subject, message):
        enabled = all((self.isEmailValid(recipient),
                       self.isStringValid(subject),
                       self.isStringValid(message)))
        return enabled

# IspdbModel private getter methods called by WizardPage3 / WizardPage4
    def _getUserName(self, service):
        if self._servers.User.isNew():
            username = self._getUserNameFromEmail(service)
        else:
            username = self._servers.User.getUserName(service)
        return username

    def _getUserNameFromEmail(self, service):
        mode = self._servers.getLoginMode(service)
        return self.Email.partition('@')[mode] if mode != 1 else self.Email

    def _getPassword(self, service):
        return self._servers.User.getPassword(service)

    def _isHostValid(self, host):
        if validators.domain(host):
            return True
        return False

    def _isPortValid(self, port):
        if validators.between(port, min=1, max=1023):
            return True
        return False

# IspdbModel private getter methods called by WizardPage5
    def _getThreadMessage(self):
        title = self._getThreadTitle()
        path = '%s/%s' % (g_extension, g_logo)
        url = getResourceLocation(self._ctx, g_identifier, path)
        logo = getMessageImage(self._ctx, url)
        return '''\
<!DOCTYPE html>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

 %s

''' % (g_extension, logo, g_logourl, title)

# IspdbModel private shared methods
    def _getServicesCount(self):
        return len(self._services)

# IspdbModel StringResource methods
    def getPageStep(self, pageid):
        resource = self._resources.get('Step') % pageid
        return self._resolver.resolveString(resource)

    def getPageTitle(self, pageid):
        resource = self._resources.get('Title') % (pageid, self._offline)
        return self._resolver.resolveString(resource)

    def getPageLabel(self, pageid, *format):
        resource = self._resources.get('PageLabel') % pageid
        return self._resolver.resolveString(resource) % format

    def getPagesLabel(self, service):
        resource = self._resources.get('PagesLabel')
        label = self._resolver.resolveString(resource)
        return label % (service, self.Email)

    def getProgressMessage(self, value):
        resource = self._resources.get('Progress') % value
        return self._resolver.resolveString(resource)

    def getSecurityMessage(self, level):
        resource = self._resources.get('Security') % level
        return self._resolver.resolveString(resource)

    def getSendTitle(self):
        resource = self._resources.get('SendTitle')
        title = self._resolver.resolveString(resource)
        return title % self.Email

    def getSendSubject(self):
        resource = self._resources.get('SendSubject')
        return self._resolver.resolveString(resource)

    def getSendMessage(self):
        resource = self._resources.get('SendMessage')
        return self._resolver.resolveString(resource)

    def _getThreadTitle(self):
        resource = self._resources.get('ThreadTitle')
        return self._resolver.resolveString(resource) % g_extension


from flask_sqlalchemy import SQLAlchemy

__database = None

SESSION = 'Session'
SESSION_SCHEMA = '(sid, logout)'
MODEL = 'Model'
MODEL_SCHEMA = '(sid, mid, model)'
PROBANNO = 'Probanno'
PROBANNO_SCHEMA = '(fasta_id, sid, probs_id)'
PROBS = 'Probs'
PROBS_SCHEMA = '(id, probs, name)'
JOB = 'Jobs'
JOB_SCHEMA = '(jid, sid, job, target, status)'
UPDATE_JOB_SCHEMA = 'sid = ?, job = ?, target = ?, status = ?'  # for use in update queries, make sure agrees with JOB_SCHEMA
TABLES = {SESSION: 'sid', MODEL: 'mid', PROBANNO: 'fasta_id', JOB: 'jid'}

FIND_BY_ID_QUERY = 'SELECT * FROM {0} WHERE {1} = ?'
FIND_MODEL_QUERY = 'SELECT * FROM ' + MODEL + ' WHERE sid = ? AND mid = ?'
FIND_PROBANNO_QUERY = 'SELECT * FROM ' + PROBANNO + ' pr, ' + PROBS + \
                      ' pb WHERE pr.probs_id = pb.id AND pr.fasta_id = ?'
RETRIEVE_PROBANNO_QUERY = 'SELECT * FROM ' + PROBANNO + ' pr, ' + PROBS + \
                          ' pb WHERE pr.probs_id = pb.id AND pr.sid = ? AND pr.fasta_id = ?'
INSERT_INTO_SESSION_QUERY = 'INSERT INTO ' + SESSION + SESSION_SCHEMA + ' VALUES (?, ?)'
INSERT_INTO_MODEL_QUERY = 'INSERT INTO ' + MODEL + MODEL_SCHEMA + ' VALUES (?, ?, ?)'
INSERT_INTO_PROBANNO_QUERY = 'INSERT INTO ' + PROBANNO + PROBANNO_SCHEMA + ' VALUES (?, ?, ?)'
INSERT_INTO_PROBS_QUERY = 'INSERT INTO ' + PROBS + PROBS_SCHEMA + ' VALUES (?, ?, ?)'
INSERT_INTO_JOBS_QUERY = 'INSERT INTO ' + JOB + JOB_SCHEMA + ' VALUES (?, ?, ?, ?, ?)'
LIST_MODELS_QUERY = 'SELECT mid FROM ' + MODEL + ' WHERE sid = ?'
UPDATE_IN_JOBS_QUERY = 'UPDATE ' + JOB + ' SET ' + UPDATE_JOB_SCHEMA + ' WHERE jid = ?'
LIST_PROBANNOS_QUERY = 'SELECT pr.fasta_id as fasta_id, pb.name as name, pr.probs_id as probs_id FROM ' + \
                       PROBANNO + ' pr, ' + PROBS + ' pb WHERE pr.sid=? AND pr.probs_id = pb.id'
COUNT_ALL_PROBS = 'SELECT probs_id, count(*) as count FROM ' + PROBANNO + ' GROUP BY probs_id'
LIST_JOBS_QUERY = 'SELECT * FROM ' + JOB + '  WHERE sid = ?'
CLEAR_SESSION_QUERY = 'DELETE FROM {0} WHERE sid = ?'
CLEAR_PROBS_QUERY = 'DELETE FROM ' + PROBS + ' WHERE id in ("{0}")'
CLEAR_PROBANNO_QUERY = 'DELETE FROM ' + PROBANNO + ' WHERE fasta_id= ?'

DELETE_MODEL_QUERY = 'DELETE FROM ' + MODEL + ' WHERE sid = ? AND mid = ?'

def set_db(app, filename):
    global __database
    """
    Sets the sqlite3 DB to the given file
    :param filename: path to DB file
    :return: None
    """
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + filename
    __database = SQLAlchemy(app)

def find_by_id(table, obj_id):
    if table not in TABLES:
        return None
    curs = __database.engine
    cmd = FIND_BY_ID_QUERY.format(table, TABLES[table])
    # print obj_id
    result = curs.execute(cmd, [obj_id]).fetchone()
    return tuple(result.values()) if result is not None else None

def find_model(session_id, model_id):
    curs = __database.engine
    result = curs.execute(FIND_MODEL_QUERY, [session_id, model_id]).fetchone()
    return None if result is None else result.values()

def insert_session(session_id, log_out_time):
    curs = __database.engine
    curs.execute(INSERT_INTO_SESSION_QUERY, [session_id, log_out_time])

def insert_model(sid, mid, model):
    curs = __database.engine
    curs.execute(INSERT_INTO_MODEL_QUERY, [sid, mid, model])

def insert_probanno(fasta_id, name, sid, probs_id, likelihoods):
    # context manager handles transaction, commit and roll-back cases
    with __database.engine.begin() as connection:
        connection.execute(INSERT_INTO_PROBANNO_QUERY, [fasta_id, sid, probs_id])
        connection.execute(INSERT_INTO_PROBS_QUERY, [probs_id, likelihoods, name])

def list_models(session_id):
    curs = __database.engine
    result = curs.execute(LIST_MODELS_QUERY, [session_id]).fetchall()
    return None if result is None else [r[0] for r in result]

def list_probannos(session_id):
    curs = __database.engine
    result = curs.execute(LIST_PROBANNOS_QUERY, [session_id]).fetchall()
    return None if result is None else [list(r) for r in result]

def list_jobs(session_id):
    curs = __database.engine
    result = curs.execute(LIST_JOBS_QUERY, [session_id]).fetchall()
    return None if result is None else [list(r) for r in result]

def update_job(jid, session, job, target, status):
    curs = __database.engine
    # jid must go last to set parameter in where clause
    curs.execute(UPDATE_IN_JOBS_QUERY, [session, job, target, status, jid])

def insert_job(jid, session, job, target, status):
    curs = __database.engine
    curs.execute(INSERT_INTO_JOBS_QUERY, [jid, session, job, target, status])

def clear_session_values(sid, clear_session=False):
    curs = __database.engine
    probs_dict = dict([tuple(v) for v in curs.execute(COUNT_ALL_PROBS)])
    probs = curs.execute(LIST_PROBANNOS_QUERY, [sid])
    probs = '", "'.join([str(v[2]) for v in probs if probs_dict[str(v[2])] == 1])
    if probs != '':
        curs.execute(CLEAR_PROBS_QUERY.format(probs))
    curs.execute(CLEAR_SESSION_QUERY.format(PROBANNO), [sid])
    curs.execute(CLEAR_SESSION_QUERY.format(JOB), [sid])
    curs.execute(CLEAR_SESSION_QUERY.format(MODEL), [sid])
    if clear_session:
        curs.execute(CLEAR_SESSION_QUERY.format(SESSION), [sid])

def clear_probanno(fasta_id):
    curs = __database.engine
    curs.execute(CLEAR_PROBANNO_QUERY, [fasta_id])

# Use this with in-process lookups like /api/probanno/calculate GET, for cache checking
def find_probanno(fasta_id):
    curs = __database.engine
    result = curs.execute(FIND_PROBANNO_QUERY, [fasta_id]).fetchone()
    return None if result is None else result.values()

def insert_probanno_record(fasta_id, session_id, probs_id):
    curs = __database.engine
    curs.execute(INSERT_INTO_PROBANNO_QUERY, [fasta_id, session_id, probs_id])

# Use this with out-bound fetches like /api/probanno GET
def retrieve_probanno(session_id, fasta_id):
    curs = __database.engine
    result = curs.execute(RETRIEVE_PROBANNO_QUERY, [session_id, fasta_id]).fetchone()
    return None if result is None else result.values()

def delete_model(session_id, model_id):
    curs = __database.engine
    curs.execute(DELETE_MODEL_QUERY, [session_id, model_id])


"""
Feature 4 - Upload File for Assessment Authoring
"""

import sys
import behave
from uuid import uuid4
sys.path.append("../")
from common.models import (User)
from e2e.setup import post_method, set_cache, get_cache, delete_test_files_from_gcs
from environment import TEST_ASSESSMENT_SUBMISSION_FILE_PATH
from e2e.test_object_schemas import (TEST_USER)
from e2e.test_config import API_URL_ASSESSMENT_SERVICE
import json

API_URL = API_URL_ASSESSMENT_SERVICE

#------------------------------------------------------------------------------
# Scenario 1: LXE wants to upload a single file for assessment authoring
#------------------------------------------------------------------------------
@behave.given(
    "that valid user_id exists"
)
def step_impl_1(context):
  user_dict = {**TEST_USER}
  user_dict["email"] = f"{uuid4()}@gmail.com"
  user = User.from_dict(user_dict)
  user.user_type_ref = ""
  user.user_id = ""
  user.save()
  user.user_id = user.id
  user.update()

  context.user_id = user.user_id

  set_cache("user_id_for_file_upload", user.user_id)

@behave.when(
    "API request is sent to upload a file for assessment authoring"
)
def step_impl_2(context):
  content_file = open(TEST_ASSESSMENT_SUBMISSION_FILE_PATH,"rb")
  content_file_input_dict = {"content_file":
              ("sample_assessment_authoring.txt", content_file, "text/plain")}

  content_res = post_method(
            url=f"{API_URL_ASSESSMENT_SERVICE}/assessment-authoring/upload-sync/{context.user_id}",
            files=content_file_input_dict,
            )
  context.res_json = content_res.json()
  context.status_code = content_res.status_code

@behave.then("the API will upload file to user-id/temp folder on GCS")
def step_impl_3(context):
    assert context.status_code == 200
    assert context.res_json["success"] is True
    assert context.res_json["message"] == "Successfully uploaded file"

    expected_file_path = f"assessments/{context.user_id}"
    expected_file_path+="/temp/sample_assessment_authoring.txt"

    assert context.res_json["data"]["resource_path"] == expected_file_path

    files_to_delete = [context.res_json["data"]["resource_path"]]
    set_cache("files_to_delete", json.dumps(files_to_delete))

#------------------------------------------------------------------------------
# Scenario 2: LXE wants to reupload a file with same name for assessment authoring
#------------------------------------------------------------------------------
@behave.given(
    "that valid user_id exists and file is already uploaded"
)
def step_impl_1(context):
  context.user_id = get_cache("user_id_for_file_upload")

@behave.when(
    "API request is sent to reupload a file for assessment authoring"
)
def step_impl_2(context):
  content_file = open(TEST_ASSESSMENT_SUBMISSION_FILE_PATH,"rb")
  content_file_input_dict = {"content_file":
              ("sample_assessment_authoring.txt", content_file, "text/plain")}

  content_res = post_method(
            url=f"{API_URL_ASSESSMENT_SERVICE}/assessment-authoring/upload-sync/{context.user_id}",
            files=content_file_input_dict,
            )
  context.res_json = content_res.json()
  context.status_code = content_res.status_code

@behave.then("the API will respond with validation error for already existing file with same name")
def step_impl_3(context):
    assert context.status_code == 422
    assert context.res_json["success"] is False
    assert context.res_json["message"] == f"File with same name already exists"

    # Delete Uploaded File
    files_to_delete = json.loads(get_cache("files_to_delete"))
    delete_test_files_from_gcs(files_to_delete)

#------------------------------------------------------------------------------
# Scenario 3: LXE wants to upload a file for assessment authoring with invalid user_id
#------------------------------------------------------------------------------
@behave.given(
    "that user_id is invalid"
)
def step_impl_1(context):
  context.user_id = "invalid_user_id"

@behave.when(
    "API request is sent to upload a file for assessment authoring with invalid user_id"
)
def step_impl_2(context):
  content_file = open(TEST_ASSESSMENT_SUBMISSION_FILE_PATH,"rb")
  content_file_input_dict = {"content_file":
              ("sample_assessment_authoring_1.txt", content_file, "text/plain")}

  content_res = post_method(
            url=f"{API_URL_ASSESSMENT_SERVICE}/assessment-authoring/upload-sync/{context.user_id}",
            files=content_file_input_dict,
            )
  context.res_json = content_res.json()
  context.status_code = content_res.status_code

@behave.then("the API will respond with validation error for invalid user_id")
def step_impl_3(context):
  assert context.status_code == 404
  assert context.res_json["success"] is False
  assert context.res_json["message"] == f"User with user_id {context.user_id} not found"


# author: Sebastian Coraccio
# date: 2017-05-10
# description:
# This Program will take a directory and make a image sheet out of the images
# within the folder.
#
# Images must be numbered so they are read in the correct order.
# Only png images can be in the directory
# All images need to have the same height and width

from PIL import Image
import os

def createSheet():
  image_files = []

  print("""Welcome to the Image Sheet Creator.
This Program will take a directory and make a image sheet out of the images
within the folder.
NOTE: Images must be numbered so they are read in the correct order.
NOTE: Only png images can be in the directory
NOTE: All images need to have the same height and width
""")

  directory = input("Enter the name of the directory which holds the .png files. ")

  # Get all files from the directory
  image_files = os.listdir(directory)

  # The first image will be used for default height and width values
  info_image = Image.open(directory + "/" + image_files[0])
  width = info_image.width
  height = info_image.height

  # Images are stitched together horizontally, so calculate the sheet width
  sheet_width = width * len(image_files)
  sheet_height = height

  sheet = Image.new('RGBA', (sheet_width, sheet_height))

  # Loop through the images and add them the sheet
  counter = 0
  for image in image_files:
    img = Image.open(directory + "/" + image)

    if not img.width == width and not img.height == height:
      print(image + " does not have the correct dimensions.")
      return

    sheet.paste(im=img, box=(width * counter,0))
    counter += 1

  file_name = directory + "_image_sheet.png"
  sheet.save(file_name)

  print("\nThe image sheet has been saved to " + file_name)
  print("Here are the sheetOptions for use with Corona SDK:\n")

  # Create the sheetOptions variable needed by Corona SDK
  corona_sheet = "local sheetOptions =\n{{\n\twidth = {0}\n\theight = {1}\n\tnumFrames = {2}\n}}".format(width, height, len(image_files))
  print(corona_sheet)

  print("\nAll done. Exiting Image Sheeter.")

if __name__ == "__main__":
  createSheet()


import telebot

from telebot import types

bot = telebot.TeleBot("5588347087:AAH3-qJmgvb6BYid-AzH4G_bEl2cb2pnsw0")

@bot.message_handler(commands = ['start'])
def welcome(message):

    #keyboard
    markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
    item1 = types.KeyboardButton("Да")
    item2 = types.KeyboardButton("Конечно")
    markup.add(item1, item2)

    bot.send_message(message.chat.id, 'Привет, Никита додик?', reply_markup=markup)

bot.polling(none_stop=True)


#multiple return values

def format_name(fname, lname):
    if fname == "" or lname == "":
        return "You didnt put in names"

    formatted_fname = fname.title()
    formatted_lname = lname.title()

    return f"{formatted_fname} {formatted_lname}"

print(format_name("colIn", "waGner"))


import sys

sys.stdin = open('input.txt', 'r')
read = sys.stdin.readline

N = int(read().rstrip())
fibo = [0, 1]
for i in range(1, N):
    fibo.append(fibo[i - 1] + fibo[i])

print(fibo[-1])


import PyMpc.Units as u
from PyMpc import *
from mpc_utils_html import *
import opensees.utils.tcl_input as tclin

def makeXObjectMetaData():

	# mDen
	at_mDen = MpcAttributeMetaData()
	at_mDen.type = MpcAttributeType.QuantityScalar
	at_mDen.name = 'mDen'
	at_mDen.group = 'Non-linear'
	at_mDen.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('mDen')+'
') +
		html_par('mass density') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	#at_mDen.dimension = u.M/u.L**3

	# eNot
	at_eNot = MpcAttributeMetaData()
	at_eNot.type = MpcAttributeType.Real
	at_eNot.name = 'eNot'
	at_eNot.group = 'Non-linear'
	at_eNot.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('eNot')+'
') +
		html_par('initial void ratio') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# A
	at_A = MpcAttributeMetaData()
	at_A.type = MpcAttributeType.Real
	at_A.name = 'A'
	at_A.group = 'Non-linear'
	at_A.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('A')+'
') +
		html_par('constant for elastic shear modulus') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# n
	at_n = MpcAttributeMetaData()
	at_n.type = MpcAttributeType.Real
	at_n.name = 'n'
	at_n.group = 'Non-linear'
	at_n.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('n')+'
') +
		html_par('pressure dependency exponent for elastic shear modulus') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# nu
	at_nu = MpcAttributeMetaData()
	at_nu.type = MpcAttributeType.Real
	at_nu.name = 'nu'
	at_nu.group = 'Non-linear'
	at_nu.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('nu')+'
') +
		html_par('Poisson\'s ratio') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# a1
	at_a1 = MpcAttributeMetaData()
	at_a1.type = MpcAttributeType.Real
	at_a1.name = 'a1'
	at_a1.group = 'Non-linear'
	at_a1.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('a1')+'
') +
		html_par('peak stress ratio coefficient (etaMax = a1 + b1*Is)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# b1
	at_b1 = MpcAttributeMetaData()
	at_b1.type = MpcAttributeType.Real
	at_b1.name = 'b1'
	at_b1.group = 'Non-linear'
	at_b1.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('b1')+'
') +
		html_par('peak stress ratio coefficient (etaMax = a1 + b1*Is)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# a2
	at_a2 = MpcAttributeMetaData()
	at_a2.type = MpcAttributeType.Real
	at_a2.name = 'a2'
	at_a2.group = 'Non-linear'
	at_a2.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('a2')+'
') +
		html_par('max shear modulus coefficient (Gn_max = a2 + b2*Is)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# b2
	at_b2 = MpcAttributeMetaData()
	at_b2.type = MpcAttributeType.Real
	at_b2.name = 'b2'
	at_b2.group = 'Non-linear'
	at_b2.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('b2')+'
') +
		html_par('max shear modulus coefficient (Gn_max = a2 + b2*Is)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# a3
	at_a3 = MpcAttributeMetaData()
	at_a3.type = MpcAttributeType.Real
	at_a3.name = 'a3'
	at_a3.group = 'Non-linear'
	at_a3.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('a3')+'
') +
		html_par('min shear modulus coefficient (Gn_min = a3 + b3*Is)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# b3
	at_b3 = MpcAttributeMetaData()
	at_b3.type = MpcAttributeType.Real
	at_b3.name = 'b3'
	at_b3.group = 'Non-linear'
	at_b3.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('b3')+'
') +
		html_par('min shear modulus coefficient (Gn_min = a3 + b3*Is)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# fd
	at_fd = MpcAttributeMetaData()
	at_fd.type = MpcAttributeType.Real
	at_fd.name = 'fd'
	at_fd.group = 'Non-linear'
	at_fd.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('fd')+'
') +
		html_par('degradation constant') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# muNot
	at_muNot = MpcAttributeMetaData()
	at_muNot.type = MpcAttributeType.Real
	at_muNot.name = 'muNot'
	at_muNot.group = 'Non-linear'
	at_muNot.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('muNot')+'
') +
		html_par('dilatancy coefficient (monotonic loading)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# muCyc
	at_muCyc = MpcAttributeMetaData()
	at_muCyc.type = MpcAttributeType.Real
	at_muCyc.name = 'muCyc'
	at_muCyc.group = 'Non-linear'
	at_muCyc.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('muCyc')+'
') +
		html_par('dilatancy coefficient (cyclic loading)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# sc
	at_sc = MpcAttributeMetaData()
	at_sc.type = MpcAttributeType.QuantityScalar
	at_sc.name = 'sc'
	at_sc.group = 'Non-linear'
	at_sc.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('sc')+'
') +
		html_par('dilatancy strain') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_sc.dimension = u.F/u.L**2

	# M
	at_M = MpcAttributeMetaData()
	at_M.type = MpcAttributeType.Real
	at_M.name = 'M'
	at_M.group = 'Non-linear'
	at_M.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('M')+'
') +
		html_par('critical state stress ratio') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# patm
	at_patm = MpcAttributeMetaData()
	at_patm.type = MpcAttributeType.QuantityScalar
	at_patm.name = 'patm'
	at_patm.group = 'Non-linear'
	at_patm.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('patm')+'
') +
		html_par('atmospheric pressure (in appropriate units)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_patm.dimension = u.F/u.L**2

	# Optional
	at_Optional = MpcAttributeMetaData()
	at_Optional.type = MpcAttributeType.Boolean
	at_Optional.name = 'Optional'
	at_Optional.group = 'Non-linear'
	at_Optional.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('Optional')+'
') +
		html_par('') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# ssl1
	at_ssl1 = MpcAttributeMetaData()
	at_ssl1.type = MpcAttributeType.Real
	at_ssl1.name = 'ssl1'
	at_ssl1.group = 'Optional parameters'
	at_ssl1.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('ssl1')+'
') +
		html_par('void ratio of quasi steady state (QSS-line) at pressure p1 (default = 0.877)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_ssl1.setDefault(0.877)

	# ssl2
	at_ssl2 = MpcAttributeMetaData()
	at_ssl2.type = MpcAttributeType.Real
	at_ssl2.name = 'ssl2'
	at_ssl2.group = 'Optional parameters'
	at_ssl2.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('ssl2')+'
') +
		html_par('void ratio of quasi steady state (QSS-line) at pressure p2 (default = 0.877)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_ssl2.setDefault(0.877)

	# ssl3
	at_ssl3 = MpcAttributeMetaData()
	at_ssl3.type = MpcAttributeType.Real
	at_ssl3.name = 'ssl3'
	at_ssl3.group = 'Optional parameters'
	at_ssl3.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('ssl3')+'
') +
		html_par('void ratio of quasi steady state (QSS-line) at pressure p3 (default = 0.873)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_ssl3.setDefault(0.873)

	# ssl4
	at_ssl4 = MpcAttributeMetaData()
	at_ssl4.type = MpcAttributeType.Real
	at_ssl4.name = 'ssl4'
	at_ssl4.group = 'Optional parameters'
	at_ssl4.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('ssl4')+'
') +
		html_par('void ratio of quasi steady state (QSS-line) at pressure p4 (default = 0.870)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_ssl4.setDefault(0.870)

	# ssl5
	at_ssl5 = MpcAttributeMetaData()
	at_ssl5.type = MpcAttributeType.Real
	at_ssl5.name = 'ssl5'
	at_ssl5.group = 'Optional parameters'
	at_ssl5.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('ssl5')+'
') +
		html_par('void ratio of quasi steady state (QSS-line) at pressure p5 (default = 0.860)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_ssl5.setDefault(0.860)

	# ssl6
	at_ssl6 = MpcAttributeMetaData()
	at_ssl6.type = MpcAttributeType.Real
	at_ssl6.name = 'ssl6'
	at_ssl6.group = 'Optional parameters'
	at_ssl6.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('ssl6')+'
') +
		html_par('void ratio of quasi steady state (QSS-line) at pressure p6 (default = 0.850)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_ssl6.setDefault(0.850)

	# ssl7
	at_ssl7 = MpcAttributeMetaData()
	at_ssl7.type = MpcAttributeType.Real
	at_ssl7.name = 'ssl7'
	at_ssl7.group = 'Optional parameters'
	at_ssl7.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('ssl7')+'
') +
		html_par('void ratio of quasi steady state (QSS-line) at pressure p7 (default = 0.833)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_ssl7.setDefault(0.833)

	# ssl8
	at_ssl8 = MpcAttributeMetaData()
	at_ssl8.type = MpcAttributeType.Real
	at_ssl8.name = 'ssl8'
	at_ssl8.group = 'Optional parameters'
	at_ssl8.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('ssl8')+'
') +
		html_par('void ratio of quasi steady state (QSS-line) at pressure p8 (default = 0.833)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_ssl8.setDefault(0.833)

	# ssl9
	at_ssl9 = MpcAttributeMetaData()
	at_ssl9.type = MpcAttributeType.Real
	at_ssl9.name = 'ssl9'
	at_ssl9.group = 'Optional parameters'
	at_ssl9.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('ssl9')+'
') +
		html_par('void ratio of quasi steady state (QSS-line) at pressure $p9 (default = 0.833)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_ssl9.setDefault(0.833)

	# ssl10
	at_ssl10 = MpcAttributeMetaData()
	at_ssl10.type = MpcAttributeType.Real
	at_ssl10.name = 'ssl10'
	at_ssl10.group = 'Optional parameters'
	at_ssl10.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('ssl10')+'
') +
		html_par('void ratio of quasi steady state (QSS-line) at pressure p10 (default = 0.833)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)

	# hsl
	at_hsl = MpcAttributeMetaData()
	at_hsl.type = MpcAttributeType.Real
	at_hsl.name = 'hsl'
	at_hsl.group = 'Optional parameters'
	at_hsl.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('hsl')+'
') +
		html_par('void ratio of upper reference state (UR-line) for all pressures (default = 0.895)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_hsl.setDefault(0.895)

	# p1
	at_p1 = MpcAttributeMetaData()
	at_p1.type = MpcAttributeType.QuantityScalar
	at_p1.name = 'p1'
	at_p1.group = 'Optional parameters'
	at_p1.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('p1')+'
') +
		html_par('pressure corresponding to ssl1 (default = 1.0 kPa)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_p1.setDefault(1.0)#kPa
	at_p1.dimension = u.F/u.L**2

	# p2
	at_p2 = MpcAttributeMetaData()
	at_p2.type = MpcAttributeType.QuantityScalar
	at_p2.name = 'p2'
	at_p2.group = 'Optional parameters'
	at_p2.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('p2')+'
') +
		html_par('pressure corresponding to ssl1 (default = 10.0 kPa)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_p2.setDefault(10.0)#kPa
	at_p2.dimension = u.F/u.L**2

	# p3
	at_p3 = MpcAttributeMetaData()
	at_p3.type = MpcAttributeType.QuantityScalar
	at_p3.name = 'p3'
	at_p3.group = 'Optional parameters'
	at_p3.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('p3')+'
') +
		html_par('pressure corresponding to ssl1 (default = 30.0 kPa)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_p3.setDefault(30.0)#kPa
	at_p3.dimension = u.F/u.L**2

	# p4
	at_p4 = MpcAttributeMetaData()
	at_p4.type = MpcAttributeType.QuantityScalar
	at_p4.name = 'p4'
	at_p4.group = 'Optional parameters'
	at_p4.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('p4')+'
') +
		html_par('pressure corresponding to ssl1 (default = 50.0 kPa)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_p4.setDefault(50.0)#kPa
	at_p4.dimension = u.F/u.L**2

	# p5
	at_p5 = MpcAttributeMetaData()
	at_p5.type = MpcAttributeType.QuantityScalar
	at_p5.name = 'p5'
	at_p5.group = 'Optional parameters'
	at_p5.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('p5')+'
') +
		html_par('pressure corresponding to ssl1 (default = 100.0 kPa)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_p5.setDefault(100.0)#kPa
	at_p5.dimension = u.F/u.L**2

	# p6
	at_p6 = MpcAttributeMetaData()
	at_p6.type = MpcAttributeType.QuantityScalar
	at_p6.name = 'p6'
	at_p6.group = 'Optional parameters'
	at_p6.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('p6')+'
') +
		html_par('pressure corresponding to ssl1 (default = 200.0 kPa)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_p6.setDefault(200.0)#kPa
	at_p6.dimension = u.F/u.L**2

	# p7
	at_p7 = MpcAttributeMetaData()
	at_p7.type = MpcAttributeType.QuantityScalar
	at_p7.name = 'p7'
	at_p7.group = 'Optional parameters'
	at_p7.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('p7')+'
') +
		html_par('pressure corresponding to ssl1 (default = 400.0 kPa)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_p7.setDefault(400.0)#kPa
	at_p7.dimension = u.F/u.L**2

	# p8
	at_p8 = MpcAttributeMetaData()
	at_p8.type = MpcAttributeType.QuantityScalar
	at_p8.name = 'p8'
	at_p8.group = 'Optional parameters'
	at_p8.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('p8')+'
') +
		html_par('pressure corresponding to ssl1 (default = 400.0 kPa)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_p8.setDefault(400.0)#kPa
	at_p8.dimension = u.F/u.L**2

	# p9
	at_p9 = MpcAttributeMetaData()
	at_p9.type = MpcAttributeType.QuantityScalar
	at_p9.name = 'p9'
	at_p9.group = 'Optional parameters'
	at_p9.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('p9')+'
') +
		html_par('pressure corresponding to ssl1 (default = 400.0 kPa)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_p9.setDefault(400.0)#kPa
	at_p9.dimension = u.F/u.L**2

	# p10
	at_p10 = MpcAttributeMetaData()
	at_p10.type = MpcAttributeType.QuantityScalar
	at_p10.name = 'p10'
	at_p10.group = 'Optional parameters'
	at_p10.description = (
		html_par(html_begin()) +
		html_par(html_boldtext('p10')+'
') +
		html_par('pressure corresponding to ssl1 (default = 400.0 kPa)') +
		html_par(html_href('http://opensees.berkeley.edu/wiki/index.php/Stress_Density_Material','Stress Density Material')+'
') +
		html_end()
		)
	at_p10.setDefault(400.0)#kPa
	at_p10.dimension = u.F/u.L**2

	xom = MpcXObjectMetaData()
	xom.name = 'StressDensityModel'
	xom.Xgroup = 'Other nD Materials'
	xom.addAttribute(at_mDen)
	xom.addAttribute(at_eNot)
	xom.addAttribute(at_A)
	xom.addAttribute(at_n)
	xom.addAttribute(at_nu)
	xom.addAttribute(at_a1)
	xom.addAttribute(at_b1)
	xom.addAttribute(at_a2)
	xom.addAttribute(at_b2)
	xom.addAttribute(at_a3)
	xom.addAttribute(at_b3)
	xom.addAttribute(at_fd)
	xom.addAttribute(at_muNot)
	xom.addAttribute(at_muCyc)
	xom.addAttribute(at_sc)
	xom.addAttribute(at_M)
	xom.addAttribute(at_patm)
	xom.addAttribute(at_Optional)
	xom.addAttribute(at_ssl1)
	xom.addAttribute(at_ssl2)
	xom.addAttribute(at_ssl3)
	xom.addAttribute(at_ssl4)
	xom.addAttribute(at_ssl5)
	xom.addAttribute(at_ssl6)
	xom.addAttribute(at_ssl7)
	xom.addAttribute(at_ssl8)
	xom.addAttribute(at_ssl9)
	xom.addAttribute(at_ssl10)
	xom.addAttribute(at_hsl)
	xom.addAttribute(at_p1)
	xom.addAttribute(at_p2)
	xom.addAttribute(at_p3)
	xom.addAttribute(at_p4)
	xom.addAttribute(at_p5)
	xom.addAttribute(at_p6)
	xom.addAttribute(at_p7)
	xom.addAttribute(at_p8)
	xom.addAttribute(at_p9)
	xom.addAttribute(at_p10)

	# use_Optional-dep
	xom.setVisibilityDependency(at_Optional, at_ssl1)
	xom.setVisibilityDependency(at_Optional, at_ssl2)
	xom.setVisibilityDependency(at_Optional, at_ssl3)
	xom.setVisibilityDependency(at_Optional, at_ssl4)
	xom.setVisibilityDependency(at_Optional, at_ssl5)
	xom.setVisibilityDependency(at_Optional, at_ssl6)
	xom.setVisibilityDependency(at_Optional, at_ssl7)
	xom.setVisibilityDependency(at_Optional, at_ssl8)
	xom.setVisibilityDependency(at_Optional, at_ssl9)
	xom.setVisibilityDependency(at_Optional, at_ssl10)
	xom.setVisibilityDependency(at_Optional, at_hsl)
	xom.setVisibilityDependency(at_Optional, at_p1)
	xom.setVisibilityDependency(at_Optional, at_p2)
	xom.setVisibilityDependency(at_Optional, at_p3)
	xom.setVisibilityDependency(at_Optional, at_p4)
	xom.setVisibilityDependency(at_Optional, at_p5)
	xom.setVisibilityDependency(at_Optional, at_p6)
	xom.setVisibilityDependency(at_Optional, at_p7)
	xom.setVisibilityDependency(at_Optional, at_p8)
	xom.setVisibilityDependency(at_Optional, at_p9)
	xom.setVisibilityDependency(at_Optional, at_p10)

	return xom

def writeTcl(pinfo):

	#nDMaterial StressDensityModel $matTag $mDen $eNot $A $n $nu $a1 $b1 $a2 $b2
	# $a3 $b3 $fd $muNot $muCyc $sc $M $patm <$ssl1 $ssl2 $ssl3 $ssl4 $ssl5 $ssl6
	# $ssl7 $ssl8 $ssl9 $ssl10 $hsl $p1 $p2 $p3 $p4 $p5 $p6 $p7 $p8 $p9 $p10>
	xobj = pinfo.phys_prop.XObject
	tag = xobj.parent.componentId

	# mandatory parameters
	mDen_at = xobj.getAttribute('mDen')
	if(mDen_at is None):
		raise Exception('Error: cannot find "mDen" attribute')
	mDen = mDen_at.quantityScalar

	eNot_at = xobj.getAttribute('eNot')
	if(eNot_at is None):
		raise Exception('Error: cannot find "eNot" attribute')
	eNot = eNot_at.real

	A_at = xobj.getAttribute('A')
	if(A_at is None):
		raise Exception('Error: cannot find "A" attribute')
	A = A_at.real

	n_at = xobj.getAttribute('n')
	if(n_at is None):
		raise Exception('Error: cannot find "n" attribute')
	n = n_at.real

	nu_at = xobj.getAttribute('nu')
	if(nu_at is None):
		raise Exception('Error: cannot find "nu" attribute')
	nu = nu_at.real

	a1_at = xobj.getAttribute('a1')
	if(a1_at is None):
		raise Exception('Error: cannot find "a1" attribute')
	a1 = a1_at.real

	b1_at = xobj.getAttribute('b1')
	if(b1_at is None):
		raise Exception('Error: cannot find "b1" attribute')
	b1 = b1_at.real

	a2_at = xobj.getAttribute('a2')
	if(a2_at is None):
		raise Exception('Error: cannot find "a2" attribute')
	a2 = a2_at.real

	b2_at = xobj.getAttribute('b2')
	if(b2_at is None):
		raise Exception('Error: cannot find "b2" attribute')
	b2 = b2_at.real

	a3_at = xobj.getAttribute('a3')
	if(a3_at is None):
		raise Exception('Error: cannot find "a3" attribute')
	a3 = a3_at.real

	b3_at = xobj.getAttribute('b3')
	if(b3_at is None):
		raise Exception('Error: cannot find "b3" attribute')
	b3 = b3_at.real

	fd_at = xobj.getAttribute('fd')
	if(fd_at is None):
		raise Exception('Error: cannot find "fd" attribute')
	fd = fd_at.real

	muNot_at = xobj.getAttribute('muNot')
	if(muNot_at is None):
		raise Exception('Error: cannot find "muNot" attribute')
	muNot = muNot_at.real

	muCyc_at = xobj.getAttribute('muCyc')
	if(muCyc_at is None):
		raise Exception('Error: cannot find "muCyc" attribute')
	muCyc = muCyc_at.real

	sc_at = xobj.getAttribute('sc')
	if(sc_at is None):
		raise Exception('Error: cannot find "sc" attribute')
	sc = sc_at.quantityScalar

	M_at = xobj.getAttribute('M')
	if(M_at is None):
		raise Exception('Error: cannot find "M" attribute')
	M = M_at.real

	patm_at = xobj.getAttribute('patm')
	if(patm_at is None):
		raise Exception('Error: cannot find "patm" attribute')
	patm = patm_at.quantityScalar

	# optional paramters
	sopt = ''

	Optional_at = xobj.getAttribute('Optional')
	if(Optional_at is None):
		raise Exception('Error: cannot find "Optional" attribute')
	Optional = Optional_at.boolean
	if Optional:
		ssl1_at = xobj.getAttribute('ssl1')
		if(ssl1_at is None):
			raise Exception('Error: cannot find "ssl1" attribute')
		ssl1 = ssl1_at.real

		ssl2_at = xobj.getAttribute('ssl2')
		if(ssl2_at is None):
			raise Exception('Error: cannot find "ssl2" attribute')
		ssl2 = ssl2_at.real

		ssl3_at = xobj.getAttribute('ssl3')
		if(ssl3_at is None):
			raise Exception('Error: cannot find "ssl3" attribute')
		ssl3 = ssl3_at.real

		ssl4_at = xobj.getAttribute('ssl4')
		if(ssl4_at is None):
			raise Exception('Error: cannot find "ssl4" attribute')
		ssl4 = ssl4_at.real

		ssl5_at = xobj.getAttribute('ssl5')
		if(ssl5_at is None):
			raise Exception('Error: cannot find "ssl5" attribute')
		ssl5 = ssl5_at.real

		ssl6_at = xobj.getAttribute('ssl6')
		if(ssl6_at is None):
			raise Exception('Error: cannot find "ssl6" attribute')
		ssl6 = ssl6_at.real

		ssl7_at = xobj.getAttribute('ssl7')
		if(ssl7_at is None):
			raise Exception('Error: cannot find "ssl7" attribute')
		ssl7 = ssl7_at.real

		ssl8_at = xobj.getAttribute('ssl8')
		if(ssl8_at is None):
			raise Exception('Error: cannot find "ssl8" attribute')
		ssl8 = ssl8_at.real

		ssl9_at = xobj.getAttribute('ssl9')
		if(ssl9_at is None):
			raise Exception('Error: cannot find "ssl9" attribute')
		ssl9 = ssl9_at.real

		ssl10_at = xobj.getAttribute('ssl10')
		if(ssl10_at is None):
			raise Exception('Error: cannot find "ssl10" attribute')
		ssl10 = ssl10_at.real

		hsl_at = xobj.getAttribute('hsl')
		if(hsl_at is None):
			raise Exception('Error: cannot find "hsl" attribute')
		hsl = hsl_at.real

		p1_at = xobj.getAttribute('p1')
		if(p1_at is None):
			raise Exception('Error: cannot find "p1" attribute')
		p1 = p1_at.quantityScalar

		p2_at = xobj.getAttribute('p2')
		if(p2_at is None):
			raise Exception('Error: cannot find "p2" attribute')
		p2 = p2_at.quantityScalar

		p3_at = xobj.getAttribute('p3')
		if(p3_at is None):
			raise Exception('Error: cannot find "p3" attribute')
		p3 = p3_at.quantityScalar

		p4_at = xobj.getAttribute('p4')
		if(p4_at is None):
			raise Exception('Error: cannot find "p4" attribute')
		p4 = p4_at.quantityScalar

		p5_at = xobj.getAttribute('p5')
		if(p5_at is None):
			raise Exception('Error: cannot find "p5" attribute')
		p5 = p5_at.quantityScalar

		p6_at = xobj.getAttribute('p6')
		if(p6_at is None):
			raise Exception('Error: cannot find "p6" attribute')
		p6 = p6_at.quantityScalar

		p7_at = xobj.getAttribute('p7')
		if(p7_at is None):
			raise Exception('Error: cannot find "p7" attribute')
		p7 = p7_at.quantityScalar

		p8_at = xobj.getAttribute('p8')
		if(p8_at is None):
			raise Exception('Error: cannot find "p8" attribute')
		p8 = p8_at.quantityScalar

		p9_at = xobj.getAttribute('p9')
		if(p9_at is None):
			raise Exception('Error: cannot find "p9" attribute')
		p9 = p9_at.quantityScalar

		p10_at = xobj.getAttribute('p10')
		if(p10_at is None):
			raise Exception('Error: cannot find "p10" attribute')
		p10 = p10_at.quantityScalar

		sopt += '{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}'.format(
			ssl1, ssl2, ssl3, ssl4, ssl5, ssl6, ssl7, ssl8, ssl9, ssl10, hsl, p1.value,
			p2.value, p3.value, p4.value, p5.value, p6.value, p7.value, p8.value, p9.value, p10.value)

	str_tcl = '{}nDMaterial StressDensityModel {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}\n'.format(
			pinfo.indent, tag, mDen.value, eNot, A, n, nu, a1, b1, a2, b2, a3, b3, fd, muNot, muCyc, sc.value, M, patm.value, sopt)

	# now write the string into the file
	pinfo.out_file.write(str_tcl)


class Node:
    def __init__(self, value, next=None):
        self.value: float = value
        self.next: Node = next

class LinkedList:
    def __init__(self):
        self.head: Node = None

    def insert(self, element: float):
        if not self.head:
            node = Node(element, self.head)
            self.head = node
        elif element < self.head.value:
            node = Node(element, self.head)
            self.head = node

        else:
            current_node = self.head
            while True:
                if current_node.next is None:
                    current_node.next = Node(element)
                    break
                elif current_node.value <= element <= current_node.next.value:
                    node = Node(element, current_node.next)
                    current_node.next = node
                    break
                current_node = current_node.next

    def __str__(self):
        to_print = ""
        current_position = self.head
        while current_position is not None:
            to_print += str(current_position.value) + " "
            current_position = current_position.next

        return to_print

linked_list = LinkedList()

line = input()
while line != "":
    for item in line.split(" "):
        if item != "":
            linked_list.insert(int(item))

    line = input()

print(linked_list)