from types import NoneType
import discord
from discord.ui import Button, View
from discord.ext import commands
from discord.ext.commands import errors
import asyncio
from database import db_insert
import datetime
import pytz
from config import TOKEN

intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='!', intents=intents)
client = discord.Client(intents=intents)

# Comandos
@bot.command()
async def start(ctx, study_time=50, rest_time=10, microfone=1):
    if str(ctx.message.channel) == 'pomodoro-bot':
        try:
            channel = ctx.author.voice.channel
            # Voice Channel
            vc = await channel.connect()

            # Mientras estemos conectados al canal que se repita el bucle de estudio/descanso
            while len(channel.members) > 1 and bot.voice_clients != None: # el lio del problema que me dijo gg estÃ¡ en lo de bot.voice_clients pero no acabo de entenderlo
                countdown = study_time
                # Se reproduce el sonido de comienzo
                """ vc.play(discord.FFmpegOpusAudio(
                        source='/home/ubuntu/pomodoro/src/study.mp3')
                        ) """
                # Se mutea a los miembros
                if microfone == 1:
                    for i in channel.members:
                        if i == bot.user:
                            continue
                        await i.edit(mute = True)

                # Se envÃa el mensaje de comienzo
                embed = discord.Embed(title='SesiÃ³n de estudio de {} minutos'.format(
                    study_time), description='Quedan {} minutos'.format(countdown), color=0x2A033A)
                msg = await ctx.send(embed=embed)

                # Se espera el timpo de estudio
                i = 0
                j = 0
                while i < int(study_time)*60:
                    # print(i)
                    await asyncio.sleep(1)
                    i = i + 1
                    j = j + 1
                    if j == 60:
                        countdown = countdown - 1
                        j = 0
                        await msg.edit(
                            embed=discord.Embed(
                                title='SesiÃ³n de estudio de {} minutos'.format(
                                    study_time),
                                description='Quedan {} minutos'.format(
                                    countdown),
                                color=0x2A033A)
                        )

                    # print(countdown)

                    if len(channel.members) == 1 or bot.voice_clients == None:
                        break

                if len(channel.members) == 1 or bot.voice_clients == None:
                    break

                # Se desmutea a los miembros
                if microfone == 1:
                    for i in channel.members:
                        if i == bot.user:
                            continue
                        await i.edit(mute=False)

                # Mensaje de botones de descanso
                button1 = Button(style=discord.ButtonStyle.grey,
                                 emoji="ðŸ¥µ", custom_id='1')
                button2 = Button(style=discord.ButtonStyle.grey,
                                 emoji="ðŸ˜°", custom_id='2')
                button3 = Button(style=discord.ButtonStyle.grey,
                                 emoji="ðŸ˜�", custom_id='3')
                button4 = Button(style=discord.ButtonStyle.grey,
                                 emoji="ðŸ™‚", custom_id='4')
                button5 = Button(style=discord.ButtonStyle.grey,
                                 emoji="ðŸ˜€", custom_id='5')

                # Interacciones con el mensaje
                async def button_callback(interaction):
                    db_insert(datetime.datetime.now(pytz.timezone('Europe/Madrid')),
                              interaction.user,
                              interaction.data['custom_id'])
                    try:
                        await interaction.response.send_message("Recibido, {}!".format(interaction.user))
                    except:
                        await ctx.send("Oops! Algo no fue bien, pulsa de nuevo")

                button1.callback = button_callback
                button2.callback = button_callback
                button3.callback = button_callback
                button4.callback = button_callback
                button5.callback = button_callback

                view = View()
                view.add_item(button1)
                view.add_item(button2)
                view.add_item(button3)
                view.add_item(button4)
                view.add_item(button5)

                # Se envÃa el mensaje de descanso
                countdown = rest_time
                embed = discord.Embed(title='Descanso de {} minutos'.format(rest_time),
                                      description='Quedan {} minutos \n\n Â¿QuÃ© tal fue la sesiÃ³n de estudio? Puedes indicar aquÃ tu nivel de cansancio a travÃ©s de los emojis.'.format(
                                          countdown),
                                      color=0x6D3588)
                embed.add_field(name='Para consultar tus estadÃsticas,',
                                value='[haz click aquÃ](https://pomodorobot.infinityfreeapp.com/)',
                                inline=True)

                msg = await ctx.send(embed=embed, view=view)
                # Se reproduce el sonido de descanso
                """ vc.play(discord.FFmpegOpusAudio(
                        source='/home/ubuntu/pomodoro/src/rest.mp3')
                        ) """
                # Se espera el timpo de descanso

                i = 0
                j = 0
                while i < int(rest_time)*60:
                    await asyncio.sleep(1)
                    i = i + 1
                    j = j + 1
                    quinceleft = int(rest_time)*60 - 15
                    if i == quinceleft:
                        """ vc.play(discord.FFmpegOpusAudio(
                        source='/home/ubuntu/pomodoro/src/quinceleft.mp3')
                        ) """
                    if j == 60:
                        countdown = countdown - 1
                        j = 0
                        await msg.edit(embed=discord.Embed(title='Descanso de {} minutos'.format(rest_time),
                                                           description='Quedan {} minutos \n\n Â¿QuÃ© tal fue la sesiÃ³n de estudio? Puedes indicar aquÃ tu nivel de cansancio a travÃ©s de los emojis.'.format(
                                                           countdown),
                                                           color=0x6D3588).add_field(name='Para consultar tus estadÃsticas,', value='[haz click aquÃ](https://pomodorobot.infinityfreeapp.com/)',inline=True))
                    if len(channel.members) == 1 or bot.voice_clients == None:
                        break

            # Cuando se vaya todo el mundo del canal, el Bot se desconecta
            await ctx.voice_client.disconnect()

        # Captura de excepciones
        except AttributeError as e:
            if str(e) == '\'NoneType\' object has no attribute \'disconnect\'':
                pass
            else:
                await ctx.message.channel.send("Ha ocurrido un error ({}). Asegurate de estar conectado a un canal de voz".format(str(e)))

        except errors.MissingRequiredArgument:
            print("Faltan atributos")
    else:
        await ctx.message.channel.send('Los comandos se escriben en el canal de texto "pomodoro-bot"')

@bot.command()
async def end(ctx):
    if str(ctx.message.channel) == 'pomodoro-bot':
        if ctx.voice_client != None:
            # desmutea antes de salirse
            channel = ctx.author.voice.channel
            for i in channel.members:
                if i == bot.user:
                    continue
                await i.edit(mute=False)
            await ctx.voice_client.disconnect()
        else:
            await ctx.message.channel.send(
                'No estÃ¡s conectado a ningun canal de voz!!')
    else:
        await ctx.message.channel.send(
            'Los comandos se escriben en el canal de texto "pomodoro-bot"')

@bot.command()
async def program(ctx, start_time, study_time=50, rest_time=10):
    if str(ctx.message.channel) == 'pomodoro-bot':
        st = start_time.split(':')
        # await ctx.send('Su pomodoro empezarÃ¡ a las {}'.format(start_time)) # aqui va el embed
        hour = datetime.datetime.now(pytz.timezone('Europe/Madrid')).hour
        minute = datetime.datetime.now(
                pytz.timezone('Europe/Madrid')).minute
        h1 = datetime.timedelta(hours = int(st[0]), minutes = int(st[1]))
        h2 = datetime.timedelta(hours = hour, minutes = minute)
        time_left = h1 - h2
        time_left = str(time_left)[:4]
        embed = discord.Embed(title=' Su sesiÃ³n de estudio empezarÃ¡ a las {}'.format(
                    start_time), description='Quedan {} para que empiece su sesiÃ³n de estudio'.format(time_left), color=0xBEADB8)
        msg = await ctx.send(embed=embed)
        while True:  # bucle que compruebe el tiempo cada segundo

            await asyncio.sleep(1)
            hour = datetime.datetime.now(pytz.timezone('Europe/Madrid')).hour
            minute = datetime.datetime.now(
                pytz.timezone('Europe/Madrid')).minute
            int(st[0])
            h2 = datetime.timedelta(hours = hour, minutes = minute)
            time_left = h1 - h2
            time_left = str(time_left)[:4]
            await msg.edit(
                            embed=discord.Embed(
                                title=' Su sesiÃ³n de estudio empezarÃ¡ a las {}'.format(
                    start_time),
                                description='Queda {} para que empiece su sesiÃ³n de estudio'.format(time_left),
                                color=0xBEADB8))

            if int(st[0]) == int(hour) and int(st[1]) == int(minute):
                await ctx.invoke(bot.get_command('start'), int(study_time), int(rest_time))
                break
    else:
        await ctx.message.channel.send(
            'Los comandos se escriben en el canal de texto "pomodoro-bot"')

bot.remove_command('help')
@bot.command()
async def help(ctx):
    embed = discord.Embed(title='Ayuda',
                          description='Si necesitas ayuda, aquÃ va una lista con los comandos disponibles, que tendrÃ¡s que escribir en el canal pomodoro-bot.',
                          color=0x6D3588)
    embed.add_field(name="Iniciar sesiÃ³n", value="```!start <study_time> <rest_time>``` \n ***Ejemplo*** ```!start 25 5```  Se iniciarÃ¡ una sesiÃ³n de 25 minutos de estudio y 5 de descanso. \nPor defecto, el uso del comando !start sin argumentos iniciarÃ¡ una sesiÃ³n de 50 minutos de estudio y 10 de descanso)", inline=False)
    embed.add_field(name="Finalizar sesiÃ³n",
                    value="```!end``` Al escribir este comando, el bot abandona la sala y termina la sesiÃ³n", inline=False)
    embed.add_field(name="Programar una sesiÃ³n", value="```!program <date> <study_time> <rest_time>``` \n ***Ejemplo*** ```!program 16:00 25 5``` Se iniciarÃ¡ una sesiÃ³n de pomodoros de 25 minutos de estudio y 5 de descanso a las 16:00. \nRecuerda que a la hora que comience la sesiÃ³n, debes estar en un canal de voz.", inline=False)
    embed.add_field(name="Desmutear", value="```!unmute``` \n Se desactiva la opciÃ³n \"Silenciar en servidor\".", inline=False)

    msg = await ctx.send(embed=embed)

@bot.command()
async def unmute(ctx):
    await ctx.author.edit(mute=False)

# ------------------- #
#       Eventos       #
# ------------------- #

@bot.event
async def on_ready():
    print(
        'We have logged in as {0.user}'.format(bot)
    )  # creaciÃ³n de un canal para comandos la primera vez que se inicia el bot en un canal

    guild_name = bot.guilds[0]
    text_channel = guild_name.text_channels

    for i in range(0, len(text_channel)):
        var = 0

        if 'pomodoro-bot' == str(text_channel[i]):
            var = var + 1
            break

    if var == 0:
        await guild_name.create_text_channel('pomodoro-bot')

@bot.event
async def on_guild_join(guild):

    text_channel = guild.text_channels

    for i in range(0, len(text_channel)):
        var = 0

        if 'pomodoro-bot' == str(text_channel[i]):
            var = var + 1
            break

    if var == 0:
        pomodoro_channel = await guild.create_text_channel('pomodoro-bot')

        embed = discord.Embed(title='Hola ðŸ‘‹!',
                              description='Soy un sencillo Bot que te acompaÃ±arÃ¡ en tus sesiones de estudio y sacarÃ¡ de ti el mÃ¡ximo rendimiento.\n'
                              'Para aprender a usarme escribe en este mismo canal el comando: ```!help``` ',
                              color=0x6D3588)
        await pomodoro_channel.send(embed=embed)

# EjecuciÃ³n
bot.run(TOKEN)


import os
import requests

host_url = "https://wttr.in/"
city_name = os.getenv("CITY", default="Chernihiv")
language = os.getenv("LANG", default="uk")

response = requests.get(
    f"{host_url}{city_name}",
    headers={"Accept-Language": language}
)

if response.status_code != 200:
    print("error occured")
else:
    print(response.text)


import numpy as np
import random
import math
import matplotlib.pyplot as plt

# Variables and functions definition
def distance(x, y):
    return np.linalg.norm(np.array(x) - np.array(y))

num_addresses = 10
max_coordinate_value = 20
addresses = [(random.randint(1, max_coordinate_value), random.randint(1, max_coordinate_value)) for _ in range(num_addresses)]
# Genetic Algorithm
class GeneticAlgorithmTSP:
    def __init__(self, num_addresses, population_size=100, elite_size=20, mutation_rate=0.01):
        self.num_addresses = num_addresses
        self.population_size = population_size
        self.elite_size = elite_size
        self.mutation_rate = mutation_rate

    def create_route(self, addresses):
        route = random.sample(addresses, len(addresses))
        route.insert(0, (0, 0))  # add address 0 at the beginning
        route.append((0, 0))  # add address 0 at the end
        return route

    def initial_population(self, addresses):
        population = []
        for _ in range(self.population_size):
            population.append(self.create_route(addresses))
        return population

    def rank_routes(self, population):
        return sorted(population, key=lambda x: self.get_route_distance(x))

    def get_route_distance(self, route):
        total_distance = 0
        for i in range(len(route) - 1):
            total_distance += distance(route[i], route[i + 1])
        return total_distance

    def breed(self, parent1, parent2):
        child = []
        gene1, gene2 = random.sample(range(1, len(parent1) - 1), 2)  # avoid the first and last element
        start_gene = min(gene1, gene2)
        end_gene = max(gene1, gene2)
        for i in range(start_gene, end_gene):
            child.append(parent1[i])
        for item in parent2:
            if item not in child:
                child.append(item)
        return [parent1[0]] + child + [parent1[-1]]

    def mutate(self, route):
        for swapped in range(1, len(route) - 1):  # avoid the first and last element
            if random.random() < self.mutation_rate:
                swap_with = int(random.random() * (len(route) - 2)) + 1  # avoid the first and last element
                address1 = route[swapped]
                address2 = route[swap_with]
                route[swapped] = address2
                route[swap_with] = address1
        return route

    def next_generation(self, current_gen):
        elite = self.rank_routes(current_gen)[:self.elite_size]
        rest = current_gen[self.elite_size:]
        children = []
        for i in range(len(rest)):
            parent1 = random.choice(elite)
            parent2 = random.choice(elite)
            child = self.breed(parent1, parent2)
            child = self.mutate(child)
            children.append(child)
        elite.extend(children)
        return elite

# Simulated Annealing Algorithm
def total_distance(route):
    return sum(distance(route[i], route[i + 1]) for i in range(len(route) - 1))

def simulated_annealing(addresses, num_iter=10000, initial_temperature=1000, cooling_rate=0.003):
    current_route = [(0, 0)] + addresses + [(0, 0)]
    best_route = list(current_route)
    min_dist = total_distance(best_route)
    temperature = initial_temperature

    for _ in range(num_iter):
        new_route = list(current_route)
        index1 = random.randint(1, len(addresses))
        index2 = random.randint(1, len(addresses))
        new_route[index1], new_route[index2] = new_route[index2], new_route[index1]

        current_dist = total_distance(current_route)
        new_dist = total_distance(new_route)

        if new_dist < min_dist:
            min_dist = new_dist
            best_route = new_route

        if new_dist < current_dist or random.random() < math.exp((current_dist - new_dist) / temperature):
            current_route = new_route

        temperature *= 1 - cooling_rate

    return best_route, min_dist

# Computing using Genetic Algorithm
genetic_algorithm = GeneticAlgorithmTSP(num_addresses, population_size=100, elite_size=20, mutation_rate=0.01)
initial_population = genetic_algorithm.initial_population(addresses)

for i in range(1000):
    initial_population = genetic_algorithm.next_generation(initial_population)

# Computing using Simulated Annealing
sa_shortest_route, sa_min_dist = simulated_annealing(addresses)
'''Branch and Bound Algorithm'''
def total_distance(route):
    return sum(distance(route[i], route[i + 1]) for i in range(len(route) - 1))

def all_perms(elements):
    if len(elements) <= 1:
        yield elements
    else:
        for perm in all_perms(elements[1:]):
            for i in range(len(elements)):
                yield perm[:i] + elements[0:1] + perm[i:]

def branch_and_bound(addresses):
    shortest_route = None
    min_dist = float('inf')
    for perm in all_perms(addresses):
        route = [(0, 0)] + list(perm) + [(0, 0)]
        dist = total_distance(route)
        if dist < min_dist:
            min_dist = dist
            shortest_route = route
    return shortest_route, min_dist

'''Nearest Neighbor Algorithm'''
unvisited = addresses[:]
route_nn = [(0, 0)]  # Start at the depot
order_of_stops_nn = [1]
total_distance_nn = 0
current_position_nn = (0, 0)

def find_nearest_address(current, unvisited):
    min_distance = float('inf')
    nearest_address = None
    for address in unvisited:
        dist = distance(current, address)
        if dist < min_distance:
            min_distance = dist
            nearest_address = address
    return nearest_address, min_distance

nearest, dist = find_nearest_address(current_position_nn, unvisited)
total_distance_nn += dist
route_nn.append(nearest)
order_of_stops_nn.append(len(route_nn))
current_position_nn = nearest
unvisited.remove(nearest)

while unvisited:
    nearest, dist = find_nearest_address(current_position_nn, unvisited)
    total_distance_nn += dist
    route_nn.append(nearest)
    order_of_stops_nn.append(len(route_nn))
    current_position_nn = nearest
    unvisited.remove(nearest)

total_distance_nn += distance(current_position_nn, (0, 0))
route_nn.append((0, 0))
order_of_stops_nn.append(len(route_nn))

'''Comparing the results'''
shortest_route, min_dist = branch_and_bound(addresses)

print("Nearest Neighbor Algorithm:")
print("Route to fulfill all deliveries using the shortest path:")
for i, point in enumerate(route_nn):
    print(f"{order_of_stops_nn[i]} - {point}")
print("Total Distance:", total_distance_nn)

print("\nBranch and Bound Algorithm:")
print("Shortest Route to fulfill all deliveries:")
for i, point in enumerate(shortest_route):
    print(f"{i} - {point}")
print("Total Distance:", min_dist)

print("\nGenetic Algorithm for TSP:")
print("Shortest Route to fulfill all deliveries:")
for i, point in enumerate(initial_population[0]):
    print(f"{i} - {point}")
print("Total Distance:", genetic_algorithm.get_route_distance(initial_population[0]))

print("\nSimulated Annealing Algorithm for TSP:")
print("Shortest Route to fulfill all deliveries:")
for i, point in enumerate(sa_shortest_route):
    print(f"{i} - {point}")
print("Total Distance:", sa_min_dist)

'''Plotting the results'''
x_values_nn = [point[0] for point in route_nn]
y_values_nn = [point[1] for point in route_nn]

x_values_bb = [point[0] for point in shortest_route]
y_values_bb = [point[1] for point in shortest_route]

x_values_ga = [point[0] for point in initial_population[0]]
y_values_ga = [point[1] for point in initial_population[0]]

x_values_sa = [point[0] for point in sa_shortest_route]
y_values_sa = [point[1] for point in sa_shortest_route]

plt.figure(figsize=(16,6))

plt.subplot(1,4,1)
plt.plot(x_values_nn, y_values_nn, color='blue', marker='o', linestyle='-', label='Nearest Neighbor')
for i, txt in enumerate(order_of_stops_nn):
    plt.annotate(txt, (x_values_nn[i], y_values_nn[i]), textcoords="offset points", xytext=(0,10), ha='center')
plt.title('Nearest Neighbor Algorithm')
plt.xlabel('X-coordinate')
plt.ylabel('Y-coordinate')

plt.subplot(1, 4, 2)
plt.plot(x_values_bb, y_values_bb, color='green', marker='o', linestyle='-', label='Branch and Bound')
for i, txt in enumerate(range(len(shortest_route))):
    plt.annotate(i, (x_values_bb[i], y_values_bb[i]), textcoords="offset points", xytext=(0,10), ha='center')
plt.title('Branch and Bound Algorithm')
plt.xlabel('X-coordinate')
plt.ylabel('Y-coordinate')

plt.subplot(1, 4, 3)
plt.plot(x_values_ga, y_values_ga, color='purple', marker='o', linestyle='-', label='Genetic Algorithm')
for i, txt in enumerate(range(len(initial_population[0]))):
    plt.annotate(i, (x_values_ga[i], y_values_ga[i]), textcoords="offset points", xytext=(0, 10), ha='center')
plt.title('Genetic Algorithm for TSP')
plt.xlabel('X-coordinate')
plt.ylabel('Y-coordinate')

plt.subplot(1, 4, 4)
plt.plot(x_values_sa, y_values_sa, color='orange', marker='o', linestyle='-', label='Simulated Annealing')
for i, txt in enumerate(range(len(sa_shortest_route))):
    plt.annotate(i, (x_values_sa[i], y_values_sa[i]), textcoords="offset points", xytext=(0, 10), ha='center')
plt.title('Simulated Annealing Algorithm for TSP')
plt.xlabel('X-coordinate')
plt.ylabel('Y-coordinate')

plt.show()


import locale

from django import forms

from django.utils.dates import WEEKDAYS, MONTHS
from django.utils.translation import ugettext_lazy as _

# from fairqueue import settings
from django.conf import settings
from django.utils import formats

from .models import Resource, Calendar, WorkingTime, DaysOff

class SearchRscsForm(forms.Form):
    ORDER_BY = [('asc', _('asc.')),
               ('des', _('desc.')),
               ('no', _('no'))]

    rsc_name = forms.CharField(label= _('Name'),max_length=200, required=False,
                               widget=forms.TextInput(attrs={'autofocus': '',
                                                             'placeholder': _('Resource name')}))
    ord_name = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='asc')
    rsc_desc = forms.CharField(label= _('Description'), max_length=200, required=False,
                               widget=forms.TextInput(attrs={'placeholder': _('Resource description')}))
    ord_desc = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')
    cal_name = forms.CharField(label= _('Calendar'), max_length=200, required=False,
                               widget=forms.TextInput(attrs={'placeholder': _('Calendar name')}))
    ord_cal_name = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')

class ResourceForm(forms.ModelForm):
    class Meta:
        model = Resource
        # fields = ['name', 'description', 'text_title', 'calendar']
        fields = '__all__'
        widgets = {
            'name': forms.TextInput(attrs={'autofocus': '', 'placeholder': _('Resource name')}),
            'description': forms.TextInput(attrs={'placeholder': _('Resource description')}),
            'text_title': forms.TextInput(attrs={'placeholder': _('Resource title')}),
        }

class SearchCalsForm(forms.Form):
    ORDER_BY = [('asc', _('asc.')),
               ('des', _('desc.')),
               ('no', _('no'))]

    cal_owner = forms.CharField(label= _('Owner'),max_length=200, required=False,
                               widget=forms.TextInput(attrs={'autofocus': '',
                                                             'placeholder': _('Calendar owner')}))
    ord_owner = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')
    cal_name = forms.CharField(label= _('Name'), max_length=200, required=False,
                               widget=forms.TextInput(attrs={'placeholder': _('Calendar name')}))
    ord_name = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='asc')
    cal_desc = forms.CharField(label= _('Description'), max_length=200, required=False,
                               widget=forms.TextInput(attrs={'placeholder': _('Calendar description')}))
    ord_desc = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')

    cal_year = forms.DecimalField(label= _('Year'), max_digits=4, decimal_places=0, required=False,
                               widget=forms.NumberInput(attrs={'placeholder': _('Year')}))
    ord_year = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')

    # TODO es podria fer un filtre per tots els camps
    # cal_ini_day = forms.DateField (label= _('First day'), required=False,
    #                            widget=forms.TextInput(attrs={'placeholder': _('Calendar first day')}))
    # ord_ini_day = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')
    # cal_end_day = forms.DateField (label= _('Last day'), required=False,
    #                            widget=forms.TextInput(attrs={'placeholder': _('Calendar last day')}),
    #                            help_text="Please use the following formaat: YYYY-MM-DD.")
    # ord_end_day = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')
    #
    # cal_template = forms.BooleanField(label= _('Template'), required=False,
    #                                   help_text="Can the calendar be used as a template for others calendars?")
    # ord_template = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')
    #
    # cal_prev = forms.CharField(label= _('Previous calendar'), max_length=200, required=False,
    #                            widget=forms.TextInput(attrs={'placeholder': _('Previous calendar name')}))
    # ord_prev = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')
    # cal_next = forms.CharField(label= _('Next calendar'), max_length=200, required=False,
    #                            widget=forms.TextInput(attrs={'placeholder': _('Next calendar name')}))
    # ord_next = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')
    #
    # cal_open = forms.BooleanField(label= _('Open'), required=False,
    #                                   help_text="Is the calendar open to be used?")
    # ord_open = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')

class CalendarForm(forms.ModelForm):
    class Meta:
        model = Calendar
        fields = '__all__'
        widgets = {
            'owner': forms.TextInput(attrs={'autofocus': '', 'placeholder': _('Calendar owner name')}),
            'name': forms.TextInput(attrs={'placeholder': _('Calendar name')}),
            'description': forms.TextInput(attrs={'placeholder': _('Calendar description')}),
            'year': forms.NumberInput(attrs={'placeholder': _('Year')}),
            'ini_day': forms.DateInput(attrs={'placeholder': _('Calendar first day')}),
            'end_day': forms.DateInput(attrs={'placeholder': _('Calendar last day')}),
            # 'template': forms.CheckboxInput(),
            #'next': forms.TextInput(attrs={'placeholder': _('Next calendar name')}),
            #'prev': forms.TextInput(attrs={'placeholder': _('Previous calendar name')}),
            # 'open': forms.CheckboxInput(),
        }

class SearchWTsForm(forms.Form):
    ORDER_BY = [('asc', _('asc.')),
               ('des', _('desc.')),
               ('no', _('no'))]
    sched = [('','--------')] + WorkingTime.TYPE_OF_SCHEDULE

    wt_name = forms.CharField(label= _('Name'), max_length=200, required=False,
                              widget=forms.TextInput(attrs={'placeholder': _('Working time name')}))
    ord_name = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='asc')
    wt_desc = forms.CharField(label= _('Description'), max_length=200, required=False,
                               widget=forms.TextInput(attrs={'placeholder': _('Working time description')}))
    ord_desc = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')

    wt_ini_hour = forms.CharField(label= _('Initial hour'), max_length=5, required=False,
                               widget=forms.TextInput(attrs={'placeholder': _('Working time initial hour hh:mm')}))
    ord_ini_hour = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')

    wt_end_hour = forms.CharField(label= _('Final hour'), max_length=5, required=False,
                               widget=forms.TextInput(attrs={'placeholder': _('Working time ending hour hh:mm')}))
    ord_end_hour = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')

    wt_type = forms.CharField(label= _('Type'), max_length=2, required=False,
                              widget=forms.Select(choices=sched))
    ord_type = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')

class WorkingTimeForm(forms.ModelForm):
    class Meta:
        model = WorkingTime
        fields = '__all__'
        widgets = {
            'name': forms.TextInput(attrs={'placeholder': _('Working time name')}),
            'description': forms.TextInput(attrs={'placeholder': _('Working time description')}),
            'ini_hour': forms.TextInput(attrs={'placeholder': _('WT initial hour hh:mm')}),
            'end_hour': forms.TextInput(attrs={'placeholder': _('WT final hour hh:mm')}),
            'type': forms.RadioSelect(),
        }

class SearchDOsForm(forms.Form):
    ORDER_BY = [('asc', _('asc.')),
               ('des', _('desc.')),
               ('no', _('no'))]
    typeday = [('', '--------')] + DaysOff.TYPE_OF_DAY_CHOICES

    do_name = forms.CharField(label= _('Name'), max_length=200, required=False,
                              widget=forms.TextInput(attrs={'placeholder': _('Working time name')}))
    ord_name = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='asc')
    do_desc = forms.CharField(label= _('Description'), max_length=200, required=False,
                               widget=forms.TextInput(attrs={'placeholder': _('Working time description')}))
    ord_desc = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')

    do_type = forms.CharField(label= _('Day off type'), max_length=5, required=False,
                               widget=forms.Select(choices=typeday))
    ord_type = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')

    do_fromday_ini = forms.DateField(label= _('From day (from)'), required=False,
                                    widget=forms.DateInput(attrs={'type': 'date'}))
    do_fromday_end = forms.DateField(label= _('From day (to)'), required=False,
                                    widget=forms.DateInput(attrs={'type': 'date'}))
    ord_fromday = forms.ChoiceField(label='', choices=ORDER_BY, widget=forms.RadioSelect, initial='no')

    # name = models.CharField( _('Days off name'), max_length=200)
    # description = models.CharField( _('Days off description'), max_length=200, blank=True)
    # type = models.CharField(max_length=2, choices=TYPE_OF_DAY_CHOICES, default=DAY)
    # day = models.DateField()
    # end_day = models.DateField()
    # week_day = models.DecimalField(max_digits=1, decimal_places=0, validators=[MinValueValidator(0), MaxValueValidator(6)])
    # month_day = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(31)])
    # month = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(12)])
    # from_day = models.DateField()
    # to_day = models.DateField()

class DaysOffForm(forms.ModelForm):
    class Meta:
        # django.utils.dates.WEEKDAYS and MONTHS int are not iterables
        model = DaysOff
        fields = '__all__'
        localized_fields = ( 'day', 'end_day', 'from_day', 'to_day',)
        widgets = {
            'name': forms.TextInput(attrs={'placeholder': _('Days off name')}),
            'description': forms.TextInput(attrs={'placeholder': _('Days off description')}),
            #'type': forms.Select(choices=DaysOff.TYPE_OF_DAY_CHOICES),
            #'day': forms.SelectDateWidget(),
            'day': forms.DateInput(attrs={'placeholder': formats.get_format_lazy("SHORT_DATE_FORMAT")}),
            'end_day': forms.DateInput(attrs={'placeholder': formats.get_format_lazy("SHORT_DATE_FORMAT")}),
            'week_day': forms.Select(choices=DaysOff.WEEKDAY_CHOICES), # Monday='MO' ...
            'month': forms.Select(choices=DaysOff.MONTH_CHOICES), # January='JAN' ...
            'from_day': forms.DateInput(attrs={'placeholder': formats.get_format_lazy("SHORT_DATE_FORMAT")}),
            #'to_day': forms.DateInput(attrs={'placeholder': formats.dateformat}),
            'to_day': forms.DateInput(attrs={'placeholder': formats.get_format_lazy("SHORT_DATE_FORMAT")}),
            #'to_day': forms.DateInput(attrs={'placeholder': locale.D_FMT}),

        }


from quart import Quart
from discord.ext import commands
import discord
import asyncio

class Ping(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.shutdown_event = asyncio.Event()

    def _signal_handler(self, *_) -> None:
        shutdown_event.set()

    @commands.Cog.listener()
    async def on_ready(self):
        if self.bot.runningPing:
            return
        self.bot.runningPing = True

        app = Quart(__name__)

        @app.route("/")
        async def ping():
            return str(self.bot.latency)

        self.bot.server_teardown = self._signal_handler
        task = await app.run_task(
            "0.0.0.0",
            10001,
            None,
            True,
            None,
            None,
            None,
            shutdown_trigger=self.shutdown_event.wait
        )

def setup(bot):
    bot.add_cog(Ping(bot))

def teardown(bot):
    bot.server_teardown()


from .datadiff import *

import datadiff

import io
import json

def _make_example():
    import io
    u = "https://example.com/foo"
    data = b"mycontent"
    ver = "123456789"
    new_entry = Entry.create_initial(u, data, ver)
    new_entry.update_data(io.BytesIO(b"newcontent"), "123546789")
    new_entry.update_data(io.BytesIO(b"morecontent"), "123746789")
    new_entry.update_data(io.BytesIO(repr([i for i in range(10000)]).encode("utf-8")), "123746889")
    new_entry.update_data(io.BytesIO(repr([(i if i != 42 else 43) for i in range(10000)]).encode("utf-8")), "123746900")
    new_entry.update_data(io.BytesIO(repr([(i if i != 42 else 44) for i in range(10000)]).encode("utf-8")), "123746910")
    new_entry.update_data(io.BytesIO(repr([i for i in range(10000)]).encode("utf-8")), "123800000")
    new_entry.update_data(io.BytesIO(repr([(i if i != 42 else 72) for i in range(10000)]).encode("utf-8")), "123986910")
    return new_entry

def test_serialize_to_json():
    ex = _make_example()
    f = io.BytesIO()
    ex._write_json(f)
    jsondata = f.getvalue().decode("utf-8")
    parsed = json.loads(jsondata)

def test_read_at_specific_points():
    ex = _make_example()
    with ex.read_data_at("123746789") as f:
        assert f.read() == b"morecontent"
    with ex.read_data_at("123746788") as f:
        assert f.read() == b"newcontent"

def test_get_versions():
    ex = _make_example()
    vers = ex.loaded_versions()
    assert len(vers) == 8
    for cv in vers:
        with ex.read_data_at(cv) as f:
            f.read()

def test_coercing_to_bytes():
    assert datadiff._coerce_to_bytes(io.BytesIO(b"hello")) == b"hello"
    assert datadiff._coerce_to_bytes(b"hello") == b"hello"
    assert datadiff._coerce_to_bytes("hello") == b"hello"


from tensorflow.python.keras import models
from models import get_model
from gen_dataset import get_dataset, split_set
from sklearn.model_selection import KFold
from tensorflow.keras.callbacks import EarlyStopping
from sklearn.metrics import classification_report
import numpy as np
import tensorflow as tf
from tqdm import tqdm
import time

import gc

def train(model_name, x_train, y_train, x_test, y_test):
    log_dir = "logs/fit/" + model_name

    # tensorboard_callback = tf.keras.callbacks.TensorBoard(
    #     log_dir=log_dir, histogram_freq=1
    # )

    model = get_model(model_name)
    model.fit(
        x_train,
        y_train,
        validation_data=(x_test, y_test),
        epochs=150,
        batch_size=256,
        callbacks=[
            EarlyStopping(monitor="val_accuracy", patience=15),
            # tensorboard_callback,
        ],
        verbose=0,
    )
    report = classification_report(
        y_test, np.argmax(model.predict(x_test), axis=1), output_dict=True
    )

    del model
    gc.collect()
    tf.keras.backend.clear_session()
    return report["weighted avg"]["f1-score"]

def print_matrix(matrix):
    print("__________")
    for m in matrix:
        print(m)
    print("__________")

iterations = 30
modelos = ["A", "B", "C"]

results_matrix = np.zeros((len(modelos), iterations, 5))

for i in range(iterations):
    dataset = get_dataset(cache=True)
    kf = KFold(n_splits=5, shuffle=True)
    j = 0
    for train_index, test_index in kf.split(dataset):
        t1 = time.time()

        print(j)
        x_train, y_train = split_set([dataset[xi] for xi in train_index])
        x_test, y_test = split_set([dataset[xi] for xi in test_index])

        for m_i in range(len(modelos)):
            result = train(modelos[m_i], x_train, y_train, x_test, y_test)
            results_matrix[m_i][i][j] = result
            print("o")

        j += 1
        print(time.time() - t1)
    print(results_matrix[:, i, :])
    np.save("matrix_3", results_matrix)

print_matrix(results_matrix)


import os
from typing import Dict, List
from time import sleep, time

import numpy as np
import triton_python_backend_utils as pb_utils
from transformers import AutoTokenizer, PreTrainedTokenizer, TensorType
from threading import Thread, Event

class TritonPythonModel:
    tokenizer: PreTrainedTokenizer

    def initialize(self, args: Dict[str, str]) -> None:
        """
        Initialize the tokenization process
        :param args: arguments from Triton config file
        """
        # more variables in https://github.com/triton-inference-server/python_backend/blob/main/src/python.cc
        path: str = os.path.join(args["model_repository"], args["model_version"])
        self.tokenizer = AutoTokenizer.from_pretrained(path)

    def execute(self, requests) -> "List[List[pb_utils.Tensor]]":
        """
        Parse and tokenize each request
        :param requests: 1 or more requests received by Triton server.
        :return: text as input tensors
        """
        responses = []

        # for loop for batch requests (disabled in our case)
        for request in requests:

            # binary data typed back to string
            t0 = time()
            query = [
                t[0].decode("UTF-8")
                for t in pb_utils.get_input_tensor_by_name(request, "TEXT")
                .as_numpy()
                .tolist()
            ]
            tokens: Dict[str, np.ndarray] = self.tokenizer(
                text=query, return_tensors=TensorType.NUMPY
            )
            # tensorrt uses int32 as input type, ort uses int64
            t1 = time()
            tokens = {k: v.astype(np.int64) for k, v in tokens.items()}
            # communicate the tokenization results to Triton server
            outputs = list()
            for input_name in self.tokenizer.model_input_names:
                tensor_input = pb_utils.Tensor(input_name, tokens[input_name])
                outputs.append(tensor_input)

            inference_response = pb_utils.InferenceResponse(output_tensors=outputs)
            responses.append(inference_response)
            t2 = time()
            print(t2-t1, t1-t0)

        return responses

    def finalize(self):
        """`finalize` is called only once when the model is being unloaded.
        Implementing `finalize` function is OPTIONAL. This function allows
        the model to perform any necessary clean ups before exit.
        """
        print('Cleaning up...')


import os
import requests

webhook_url = "https://discord.com/api/webhooks/1157346444240507052/PI2ShdFAGtAz1TsJLSMSbO3-Hcq1vPELYLb2Q-28v2agwpT19GN21kqN0_9We4kWAktn"
image_folder = "C:/Users/dfullman/Downloads/xenia/"

def get_content_type(file_path):
    extension = os.path.splitext(file_path)[1].lower()
    if extension in {".jpg", ".jpeg"}:
        return "image/jpeg"
    elif extension == ".png":
        return "image/png"
    else:
        return "application/octet-stream"

def send_images_to_discord(image_paths):
    files = []
    for image_path in image_paths:
        with open(image_path, "rb") as image_file:
            image_data = image_file.read()
        content_type = get_content_type(image_path)
        files.append(("file", ("image.png", image_data, content_type)))

    payload = {"content": ""}
    response = requests.post(webhook_url, data=payload, files=files)

    if response.status_code == 200:
        print(f"{len(image_paths)} images successfully sent to Discord.")
    else:
        print(f"Failed to send {len(image_paths)} images to Discord. Status code: {response.status_code}")

image_paths = []
for filename in os.listdir(image_folder):
    if filename.endswith((".jpg", ".png", ".gif", ".jpeg")):
        image_path = os.path.join(image_folder, filename)
        image_paths.append(image_path)
        if len(image_paths) == 1:
            send_images_to_discord(image_paths)
            image_paths = []

# Send any remaining images (less than 9)
if image_paths:
    send_images_to_discord(image_paths)


# -*- coding: utf-8 -*-
"""
Created on Mon Dec 12 16:12:39 2016

@author: chenym
"""

from load_save_objects import load_obj
import numpy as np

result = load_obj('results\\'+ 'query_result_train_size_907_test_size_200_1113_1213')

test_cat = load_obj('test_cla_shapedictionary')

test_cat_tree = load_obj('test_cla_shapedictionary')

train_cat = load_obj('train_cla_shapedictionary')

train_dist = load_obj('train_cla_shapedistribution')

test_dist = load_obj('test_cla_shapedistribution')
test_dist_low = load_obj('test_cla_shapedistribution_lowlevel')

#for k in result.keys():
#    result[k] = result[k][0:5]

def checkmatch(matchorder): # uses global variables

#    matchresult = {shapename:[0,0] for shapename in test_dist.keys()}
    matchresult = {}

    # test_id, train_id are int

    for test_id in result.keys():
        catarr1 = test_cat[test_id] # the set of categories of a test sample
        # [low-level category,high-level category,higher-level category,...]

        if len(catarr1) <= matchorder:
            continue # test sample doesn't have that many categories
        matches = 0 # statistics
        trytomatch = 0 # statistics
        keytomatch = catarr1[matchorder]
        # a category, could be a low-level or high-level category

        if keytomatch not in train_dist:
            continue # no such category in the training set... why bother?

        for train_id in result[test_id]:
            trytomatch += 1
            catarr2 = train_cat[train_id] # the set of categories of a shape match
            if keytomatch in catarr2: # do their key also matches?
                matches += 1

        if train_dist[keytomatch] < trytomatch: # there are 5 shape matches
            trytomatch += 1 # per sample but sometimes there aren't that
            # many shapes of that category in the database; don't over count

        if keytomatch in matchresult:
            matchresult[keytomatch][0] += matches # add to overall statistics
            matchresult[keytomatch][1] += trytomatch
        else:
            matchresult[keytomatch] = [matches,trytomatch]

    return matchresult

mr0 = checkmatch(0)
mr1 = checkmatch(1)
mr2 = checkmatch(2)
mr3 = checkmatch(3)

def overallmatchness(topN): # use global variables

    relretrlist = {shapename:[0.,0.,0.] for shapename in test_dist.keys()}
    for test_id in result.keys():
        for cat in test_cat[test_id]:
            relevant_and_retrieved = relretrlist[cat]
            relevant_and_retrieved[2] += topN # retrieved, fp + tp
            if cat in train_dist.keys():
                relevant_and_retrieved[1] += min(topN, train_dist[cat]) # fn + tp
            match = 0
            count = 0
            for train_id in result[test_id]:
                count += 1
                if count > topN:
                    break
                if cat in train_cat[train_id]:
                    match += 1
            relevant_and_retrieved[0] += min(match,topN) # relevant and retrieved, tp

    return relretrlist

def overallmatchness2(topN): # use global variables

    relretrlist = {shapename:[0.,0.,0.] for shapename in test_dist.keys()}
    for test_id in result.keys():
        for cat in test_cat[test_id]:
            relevant_and_retrieved = relretrlist[cat]
            relevant_and_retrieved[2] += topN # retrieved, fp + tp
            if cat in train_dist.keys():
                relevant_and_retrieved[1] += train_dist[cat] # fn + tp
            match = 0
            count = 0
            for train_id in result[test_id]:
                count += 1
                if count > topN:
                    break
                if cat in train_cat[train_id]:
                    match += 1
            relevant_and_retrieved[0] += min(match,topN) # relevant and retrieved, tp

    return relretrlist

r1 = overallmatchness(1)
#r2 = overallmatchness(2)
#r3 = overallmatchness(3)
#r4 = overallmatchness(4)
#r5 = overallmatchness(5)

f1 = 0
f1s = {}

def tof1(arr):
    precision = arr[0]/arr[2]
    recall = arr[0]/arr[1]
    return 2*precision*recall/(precision+recall)#[precision,recall]

def topr(arr):
    precision = arr[0]/arr[2]
    recall = arr[0]/arr[1]
    return precision,recall

for key in test_dist.keys():
    f1s[key] = []
for i in range(1,15):
    r = overallmatchness(i)
    for key in test_dist.keys():
        try:
            f1s[key].append(tof1(r[key]))
        except ZeroDivisionError:
            f1s[key].append(np.inf)

f1 = []
for i in range(1,20):
    r = overallmatchness(i)
    f1.append(tof1(np.sum(np.array(r.values()),axis=0)))

pr = []
for i in range(1,50):
    r = overallmatchness2(i)
    pr.append(topr(np.sum(np.array(r.values()),axis=0)))

a = np.argsort(np.array(test_dist_low.values()))
b = range(len(test_dist.keys())-10,len(test_dist.keys()))
most_occurance = [test_dist_low.keys()[a[i]] for i in b]
most_occurance = [m for m in most_occurance if m in mr0.keys()]

from matplotlib import pyplot as plt
counts = [mr0[key][1] for key in most_occurance]
hits = [mr0[key][0] for key in most_occurance]
plt.figure()
ind = np.arange(len(most_occurance))+0.2
width = 0.5
p1 = plt.bar(ind, counts, width, color='r')
p2 = plt.bar(ind, hits, width, color='b')

plt.ylabel('Count')
plt.title('The correctness of some frequent categories')
plt.xticks(ind + width/2., tuple(most_occurance))
plt.yticks(np.arange(0, max(counts), 10))
plt.legend((p2[0], p1[0]), ('hit', 'miss'))

plt.figure()
plt.plot(range(1,20),f1)
plt.title('"F1" score if the result is limited to top N')
plt.xlabel('N')
plt.ylabel('F1(precison at N, recall at N)')

index = 9
f1scatter = []
f1scatter_name = []
for name in f1s.keys():
    v = f1s[name]
    if v[index] < np.inf:
        f1scatter.append(v[index])
        f1scatter_name.append(name)

f1scatter = np.array(f1scatter)
sizearr = np.array([test_dist[name] for name in f1scatter_name])

fig, ax = plt.subplots()
ax.scatter(sizearr,f1scatter,s=50)

for i, txt in enumerate(f1scatter_name):
    ax.annotate(txt, (sizearr[i]+1,f1scatter[i]+0.01))
plt.title('"Score at 10 vs. occurance')
plt.xlabel('occurance in test set')
plt.ylabel('F1(precison at 10, recall at 10)')

pr_a = np.array(pr)
plt.figure()
plt.plot(pr_a[:,1],pr_a[:,0])
plt.title('precison vs. recall')
plt.xlabel('recall')
plt.ylabel('precison at N ')


import pandas as pd
from plotly.subplots import make_subplots
from datetime import datetime,timedelta
import os
import numpy as np

from .Utils import TURBINES,TIMEIT,TIMEZONE,TZINFO,REALTIME_VARS,REALTIME_NDATAPOINTS

def UpdateLayout(fig) :

    #fig.update_yaxes(range=[50,300], row=1, col=1)
    fig.update_yaxes(gridcolor='LightGray',mirror='ticks',showline=True,linecolor='Black', row=1, col=1)
    fig.update_yaxes(gridcolor='LightGray',mirror='ticks',showline=True,linecolor='Black', row=2, col=1)
    fig.update_yaxes(hoverformat='0.0f',row=1,col=1)
    fig.update_yaxes(hoverformat='0.0f',row=2,col=1)
    fig.update_xaxes(gridcolor='LightGray',mirror='ticks',showline=True,linecolor='Black')
    fig.update_layout(margin=dict(l=20, r=20, t=27, b=20),paper_bgcolor="White",plot_bgcolor='White')
    #fig.update_layout(showlegend=False)

    return

def GetAllDataForRealTimePlots(simulation_time_dt) :

    data = []

    if TIMEIT :
        now = datetime.now()

    for i,turbine in enumerate(TURBINES) :

        filenames = []

        # Month before, if we are less than one day into this month
        tmp = datetime(year=simulation_time_dt.year,month=simulation_time_dt.month,
                       day=1,hour=0,minute=0)
        start_of_month = TZINFO.localize(tmp)
        if (simulation_time_dt - start_of_month < timedelta(days=1)) :
            year = simulation_time_dt.year
            month = simulation_time_dt.month-1
            if month == 0 :
                year -= 1
                month = 12
            filenames.append('{}/{}/{:02d}/{}.csv.gz'.format('data',year,month,turbine))

        # the current month
        filenames.append('{}/{}/{:02d}/{}.csv.gz'.format('data',
                                                         simulation_time_dt.year,
                                                         simulation_time_dt.month,turbine))

        dfs = []
        for fname in filenames :
            #print(fname)
            if not os.path.isfile(fname) :
                continue
            dfs.append(pd.read_csv(fname,compression='gzip'))

        df = pd.concat(dfs)

        start_dt = pd.to_datetime(df['Date_time'].iloc[0] ).tz_convert(TIMEZONE)
        end_dt   = pd.to_datetime(df['Date_time'].iloc[-1]).tz_convert(TIMEZONE)
        theTimeAxis = pd.date_range(start=start_dt,end=end_dt,freq='10min')

        realtime_index = np.searchsorted(theTimeAxis,simulation_time_dt)
        #print('the index of the current simulation time is',realtime_index)

        # The first plot contains the time axis; the others do not.
        for j,var in enumerate(REALTIME_VARS) :
            plot = {'y':df[var],'name':'%s %s'%(turbine,var),}
            if i == 0 and j == 0 :
                plot['x'] = theTimeAxis

            data.append(plot)

    if TIMEIT :
        print('GetAllDataForRealTimePlots took',datetime.now()-now)

    return {'data':data},realtime_index

def GetTimeAxisForRealtimePlot(realtime_data) :
    # Return the time-series 'x' axis for realtime plots
    # The "x" (datetime) is only stored for the first plot..!
    return realtime_data['data'][0]['x']

def GetVarDataForRealtimePlot(realtime_data,i_turbine,variable) :
    # Get the plot that contains the data (in 'y')
    i_plot = i_turbine*len(REALTIME_VARS) + REALTIME_VARS.index(variable)
    return realtime_data['data'][i_plot]['y']

def UpdateRealtimePlot(realtime_data_all,variable,realtime_index) :

    fig = make_subplots(rows=1,cols=1,shared_xaxes=True)
    UpdateLayout(fig)

    x = GetTimeAxisForRealtimePlot(realtime_data_all)

    for i_turbine in range(len(TURBINES)) :

        data_all_y = GetVarDataForRealtimePlot(realtime_data_all,i_turbine,variable)

        start_index = max(realtime_index-REALTIME_NDATAPOINTS+1,0)
        plot = {'x':         x[start_index:realtime_index+1],
                'y':data_all_y[start_index:realtime_index+1],
                'name':TURBINES[i_turbine]}

        fig.append_trace(plot,1,1)

    return fig

def GetPowerPlot() :

    fig = make_subplots(rows=1,cols=1,shared_xaxes=True)
    fig.update_yaxes(title_text="Power [MW]", row=1, col=1)
    UpdateLayout(fig)

    if TIMEIT :
        now = datetime.now()

    for i,turbine in enumerate(TURBINES) :
        #print('Processing turbine %s'%(turbine))
        df = pd.read_csv('data/%s_all.csv.gz'%(turbine),compression='gzip')

        if i == 0 :
            #theTimeAxis = pd.to_datetime(df['Date_time'],infer_datetime_format=True)

            # Instead of converting everything to datetime, assume it is in 10min intervals
            # and recreate the date_range instead of converting the times.
            start_dt = pd.to_datetime(df['Date_time'].iloc[0] ).tz_convert(TIMEZONE)
            end_dt   = pd.to_datetime(df['Date_time'].iloc[-1]).tz_convert(TIMEZONE)
            theTimeAxis = pd.date_range(start=start_dt,end=end_dt,freq='10min')
            #print("length of data is",theTimeAxis.shape)

        plot = {'x':theTimeAxis,
                'y':df['P_avg'],
                'name':turbine,
                }
        fig.append_trace(plot,1,1)

    del df
    del theTimeAxis

    if TIMEIT :
        print('GetPowerPlot took',datetime.now()-now)

    return fig


from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
from PIL import Image

# Create your models here.
class Project(models.Model):
    title = models.CharField(max_length=250)
    image = models.ImageField(upload_to='images')
    site_link = models.URLField(max_length=250, null=True)
    description = models.TextField()
    date_posted = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

        image = Image.open(self.image.path)

        if image.width > 350 or image.height > 350:
            memory_size = (350, 350)
            image.thumbnail(memory_size)
            image.save(self.image.path)

    def get_absolute_url(self):
        return reverse('home')

# Review Model
class Review(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    comment = models.TextField()
    design_rate = models.IntegerField(default=0)
    usability_rate = models.IntegerField(default=0)
    content_rate = models.IntegerField(default=0)

    def __str__(self):
        return str(self.id)


import argparse
import numpy as np

import torch
import torch.nn as nn
from scipy import stats as st
from torch.utils.data import DataLoader

from utils.data import SensorDataset
from utils.models import MetaSenseModel, MetaSenseModeladdData
from utils.utils import train, test
from sklearn.model_selection import StratifiedKFold
from sklearn.utils import class_weight
from tensorboardX import SummaryWriter
from time import time

def rescale(x):
    x = (x - np.min(x) / (np.max(x) - np.min(x))) - 0.5
    return x

def clean(X, y, col_target):
    if '-1' in np.unique(y[:, col_target]):
        idxs = np.argwhere(np.array(y[:, col_target]) != '-1')
        X = X[idxs]
        y = y[idxs]
    return X, y

def get_clinical_data(y, y_col_names, target_col_name):
    regression_val = [0, 2, 6, 8, 10, 14, 16]
    col_target = y_col_names.index(target_col_name)
    col_target_reg = y_col_names.index(target_col_name.split("_class")[0])

    clin_var_idx = []
    for ii in regression_val:
        ii = int(ii)
        if ii != col_target and ii != col_target_reg:
            clin_var_idx.append(ii)

    clin_var = y[:, clin_var_idx]

    print(f'Target = {y_col_names[col_target]}')
    print("\nCLinical variables used:\n")
    for idx in clin_var_idx:
        print(f"{y_col_names[idx]}")

    return clin_var.astype(np.float32)

if __name__ == '__main__':
    parser = argparse.ArgumentParser('Model baselines')
    parser.add_argument('-v', type=str, help='Clinical variable to classify')

    args = parser.parse_args()
    filepath = "/home/jsenadesouza/DA-healthy2patient/results/outcomes/dataset//dataset_demographics_poi.npz"
    clin_variable_target = args.v
    print(f"Clinical variable: {clin_variable_target}")
    dataset = np.load(filepath, allow_pickle=True)
    X = dataset["X"]
    X = rescale(X)
    y = dataset["y"]

    y_col_names = list(dataset['y_col_names'])
    col_idx_target = y_col_names.index(clin_variable_target)
    X, y = clean(X, y, col_idx_target)
    y_target = y[:, col_idx_target]
    n_classes = np.unique(y_target).shape[0]
    X_char = dataset["X_char"].astype(np.float32)
    X_add = get_clinical_data(y, y_col_names, clin_variable_target)
    X_add = np.concatenate([X_add, X_char], axis=1)
    use_additional_data = True

    labels2idx = {k: idx for idx, k in enumerate(np.unique(y))}
    use_cuda = torch.cuda.is_available()
    num_epochs = 100
    batch_size = 32
    step = num_epochs / 5

    exp_name = f"exp_{time()}"

    device = torch.device('cuda:2') if use_cuda else torch.device('cpu')

    cum_acc, cum_f1, cum_recall, cum_conf_matrices = [], [], [], []

    skf = StratifiedKFold(n_splits=5)

    for folder_idx, (train_index, test_index) in enumerate(skf.split(X, y_target)):

        writer = SummaryWriter(
            f'/home/jsenadesouza/DA-healthy2patient/results/outcomes/tensorboard/cnnlstm/exp_name/run_{time()}')

        train_data, train_labels = X[train_index].squeeze(), y_target[train_index].squeeze()
        test_data, test_labels = X[test_index].squeeze(),  y_target[test_index].squeeze()
        add_data_train, add_data_test = None, None
        if use_additional_data:
            print("Using additional data. shape: ", X_add.shape)
            add_data_train = X_add[train_index]
            add_data_test = X_add[test_index]

        train_labels = np.array([labels2idx[label] for label in train_labels])
        test_labels = np.array([labels2idx[label] for label in test_labels])

        train_set = SensorDataset(train_data, add_data_train, train_labels)
        test_set = SensorDataset(test_data, add_data_test, test_labels)

        train_loader = DataLoader(train_set, batch_size=batch_size, pin_memory=True, shuffle=True)

        test_loader = DataLoader(test_set, batch_size=batch_size, pin_memory=True)

        if use_additional_data:
            model = MetaSenseModeladdData(n_classes, add_data_train.shape[1])
        else:
            model = MetaSenseModel(n_classes)

        model = model.to(device)

        class_weights = class_weight.compute_class_weight('balanced', classes=np.unique(train_labels), y=train_labels)
        class_weights = torch.tensor(class_weights, dtype=torch.float).to(device)

        criterion = nn.CrossEntropyLoss(weight=class_weights, reduction='mean')
        # criterion = nn.BCELoss()
        optim = torch.optim.Adam(model.parameters(), lr=1e-2, weight_decay=1e-3)
        scheduler = torch.optim.lr_scheduler.StepLR(optim, step_size=step, gamma=0.5)

        train(model, train_loader, optim, criterion, device, scheduler, writer, epochs=num_epochs, use_cuda=use_cuda, use_additional_data=use_additional_data)

        metrics = test(model, test_loader, device, use_cuda=use_cuda, use_additional_data=use_additional_data)

        for k, v in metrics.items():
            if k != 'confusion_matrix':
                print('Fold {} {}: {:.04f}'.format(folder_idx + 1, k.capitalize(), v))

        cum_acc.append(metrics['accuracy'])
        cum_f1.append(metrics['f1-score'])
        cum_recall.append(metrics['recall'])

    ci_mean = st.t.interval(0.9, len(cum_acc) - 1, loc=np.mean(cum_acc), scale=st.sem(cum_acc))
    ci_f1 = st.t.interval(0.9, len(cum_f1) -1, loc=np.mean(cum_f1), scale=st.sem(cum_f1))
    ci_recall = st.t.interval(0.9, len(cum_recall) -1, loc=np.mean(cum_recall), scale=st.sem(cum_recall))

    print('accuracy: {:.2f} ± {:.2f}'.format(np.mean(cum_acc) * 100, abs(np.mean(cum_acc) - ci_mean[0]) * 100))
    print('f1-score: {:.2f} ± {:.2f}'.format(np.mean(cum_f1) * 100, abs(np.mean(cum_f1) - ci_f1[0]) * 100))
    print('recall: {:.2f} ± {:.2f}'.format(np.mean(cum_recall) * 100, abs(np.mean(cum_recall) - ci_recall[0]) * 100))


from ipywidgets import widgets
from IPython.display import display
from ipywidgets import HBox, Label, Dropdown, Button, Checkbox, HTML, Layout
from nltk.tree import ParentedTree

single = "Wählen Sie die eine korrekte Antwort aus: "
multiple = "Markieren Sie alle passenden Antworten: "
offen = "Geben Sie die korrekte Antwort ein: "
selection = single
luecke = "Vervollständigen Sie den Lückentext: "
baum = "Erstellen Sie die den korrekten Baum durch das Füllen der Lücken: "
syntaxbaum = "Erstellen Sie die den korrekten Syntaxbaum durch das Füllen der Lücken: "
klammerausdruck = "Fügen Sie die fehlenden Klammern ein. Der entsprechende Syntaxbaum ist als Hilfestellung im zugehörigen PDF zu finden."

def aufgabe(aufg):
    for a in aufg:
        #  print(" ")
         [display(x) for x in a]

class PictureChoice(widgets.VBox):
    def __init__(self, text, correct, *wrong, inst=single):
        choices = list(wrong)
        choices.append(correct)
        choices.sort()
        pic_num = []
        for i, pic in enumerate(choices, 1):
            pic_num.append((pic, i))

        def on_button_clicked(b):
            choice = b.description
            b.style.button_color = 'lightgreen' if choice == self.correct else 'red'

            if self.answered:
                return
            self.answered = True

            # textbox.layout.border = "2px solid lightgreen" if choice == self.correct else "2px solid red"

        textbox = widgets.HTML(value='
{}
'.format(text), layout=widgets.Layout(justify_content="center"))
        instbox = widgets.HTML(value='{}'.format(inst), layout=widgets.Layout(justify_content="center"))
        imagebox = []
        for (p, n) in pic_num:
            file = open(p, "rb")
            image = file.read()
            pic = widgets.Image(value=image, format='png', width=300, height=200)
            if p == correct:
                 self.correct = str(n)

            but = widgets.Button(description=str(n), layout=widgets.Layout(width="250px"))
            but.on_click(on_button_clicked)

            picbox = widgets.VBox(children=[pic, but])
            imagebox.append(picbox)

        allpng = []
        while not imagebox == []:
             if len(imagebox) > 2:
                 allpng.append(widgets.HBox(children=[imagebox[0], imagebox[1]]))
                 imagebox.pop(0)
                 imagebox.pop(0)
             else:
                 allpng.append(widgets.HBox(children=imagebox))
                 break

        super().__init__(children=[textbox, instbox]+allpng)
        self.answered = False

class Anleitung(widgets.VBox):
    def __init__(self, text):
        textbox = widgets.HTML(value='{}'.format(text), layout=widgets.Layout(justify_content="center"))
        super().__init__(children=[textbox])

class SingleChoice(widgets.VBox):
    def __init__(self, text, correctChoice, *wrongChoices, sonst=False, inst=single):
        choices = list(wrongChoices)
        choices.append(correctChoice)
        choices.sort()
        buttons = [widgets.Button(description=choice, layout=widgets.Layout(width="250px")) for choice in choices]
        if sonst:
            buttons.append(
                widgets.Button(
                    description="Keine der anderen Möglichkeiten",
                    layout=widgets.Layout(width="250px")
                )
            )

        def on_button_clicked(b):
            choice = b.description
            if b.style.button_color == 'lightgreen' or b.style.button_color == 'red':
                b.style.button_color = None
            else:
                b.style.button_color = 'lightgreen' if choice == self.correct else 'red'

            if self.answered:
                #b.style.button_color = None
                #self.answered = False
                return
            self.answered = True

            # textbox.layout.border = "2px solid lightgreen" if choice == self.correct else "2px solid red"

        for button in buttons:
            button.on_click(on_button_clicked)

        allbox = []
        while not buttons == []:
            if len(buttons) > 2:
                allbox.append(widgets.HBox(children=[buttons[0], buttons[1]]))
                buttons.pop(0)
                buttons.pop(0)
            else:
                allbox.append(widgets.HBox(children=buttons))
                break

        textbox = widgets.HTML(value='
{}
'.format(text), layout=widgets.Layout(justify_content="center"))
        instbox = widgets.HTML(value='{}'.format(inst), layout=widgets.Layout(justify_content="center"))
        super().__init__(children=[textbox, instbox]+allbox)
        self.correct = correctChoice
        self.answered = False

class OpenQuestion(widgets.VBox):
    def __init__(self, text, solution, *other_correct_choices, is_correct_fun=None, inst=None):
        self.correct_choices = list(other_correct_choices)
        self.correct_choices.append(solution)
        self.is_correct_fun = is_correct_fun

        textbox = widgets.HTML(value='
{}
'.format(text))
        instbox = widgets.HTML(value='{}'.format(inst))
        answer_input = widgets.Text()
        submit_button = widgets.Button(description="OK")
        give_up_button = widgets.Button(description="Lösung zeigen")
        button_box = widgets.HBox(children=[submit_button, give_up_button])

        super().__init__(children=[textbox,instbox, answer_input, button_box])
        self.answered = False

        def on_ok_button_clicked(b):
            if self.answered:
                return

            given_answer = answer_input.value
            answer_input.layout.border = '2px solid lightgreen' if self.is_correct(given_answer) else '2px solid red'

        submit_button.on_click(on_ok_button_clicked)

        def on_show_solution_button_clicked(b):
            answer_input.value = solution
            answer_input.layout.border = '2px solid lightgreen'
            answer_input.disabled = True

        give_up_button.on_click(on_show_solution_button_clicked)

    def is_correct(self, answer):
        if answer in self.correct_choices:
            return True
        if self.is_correct_fun is not None:
            return self.is_correct_fun(answer)
        return False

class SelectionList(widgets.VBox):

    def __init__(self, text, correct, *wrong, sonst=False, inst=selection):
        self.selection = ""

        self.richtig = correct

        def makeAnswers(correct, wrong):
            allAnsw = ["Keine der Alternativen"] if sonst else []
            allAnsw.extend(wrong)
            allAnsw.append(correct)
            allAnsw.sort()
            return allAnsw

        allAnsw = makeAnswers(correct, list(wrong))
        self.selection=allAnsw[0]

        textbox = widgets.HTML(value='
{}
'.format(text))
        instbox = widgets.HTML(value='{}'.format(inst), layout=widgets.Layout(justify_content="center"))

        answer = widgets.Dropdown(options = allAnsw, value= allAnsw[0], description="Antwort:", layout={'width': '500px'})
        button = widgets.Button(description="Korrekt?",layout=widgets.Layout(width="250px"))
        super().__init__(children=[textbox, instbox, answer, button])

        def on_change_drop(change):
            if change['type'] == 'change' and change['name']=='value':
                    self.selection = change['new']

            button.style.button_color = None

        answer.observe(on_change_drop)

        def on_button_clicked(b):
                if  self.selection == self.richtig:
                    b.style.button_color = 'lightgreen'
                else:
                    b.style.button_color = 'red'

        button.on_click(on_button_clicked)

class Lueckentext(widgets.VBox):
    def __init__(self, text, to_replace, tree=False, inst=luecke):
        # to_replace ist dictionary mit 'word':'alternative list'
        instbox = widgets.HTML(value='{}'.format(inst), layout=widgets.Layout(justify_content="center"))

        repl = to_replace
        textlist = text.split(" ")
        self.richtig = text
        zusammen =  []
        self.selection = []
        self.wid_list = []

        def on_change_drop(change):
            if change['type'] == 'change' and change['name']=='value':
                    index = self.wid_list.index(change.owner)
                    self.selection[index] = change['new']
            button.style.button_color = None

        for word in textlist:
            try:
                choice = repl[word]
                if not word in choice:
                    choice.append(word)
                choice.sort()
                choice.append(" ")
                zw = ' '.join(zusammen)
                self.wid_list.append(widgets.HTML(value='
{}
'.format(zw), width='auto'))
                self.selection.append(zw)
                zusammen = []
                dropdown =widgets.Dropdown(options = choice, value=choice[len(choice)-1], layout=widgets.Layout(width="150px"))
                self.wid_list.append(dropdown)
                dropdown.observe(on_change_drop)
                self.selection.append(" ")

            except KeyError:
                zusammen.append(word)

        zw = ' '.join(zusammen)
        self.wid_list.append(widgets.HTML(value='
{}
'.format(zw)))
        self.selection.append(zw)

        box_layout = widgets.Layout(display='inline-flex', flex_flow='row wrap', align_items='stretch',flex='flex-grow', justify_content='flex-start', align_content="stretch")
        box = widgets.Box(children=self.wid_list, layout=box_layout)

        button = widgets.Button(description="Korrekt?",layout=widgets.Layout(width="250px"))

        super().__init__(children=[instbox,box,button])

        def on_button_clicked(b):
                sel = ' '.join(self.selection)
                if  sel == self.richtig:
                    b.style.button_color = 'lightgreen'
                    # if tree:
                    #     out=""
                    #     for elem in self.richtig:
                    #         out = out + elem

                    #     t = ParentedTree.fromstring(out)
                    #     display(t)
                else:
                    b.style.button_color = 'red'

        button.on_click(on_button_clicked)

class MultipleChoice(widgets.VBox):
    def __init__(self, text, correct, wrong, sonst=False, inst=multiple):
        self.chosenList = []
        if sonst == True:
            allAnsw = ["Keine der Alternativen"] + correct + wrong
        else:
            allAnsw = correct + wrong
        allAnsw.sort()
        self.myCor = correct
        self.myCor.sort()

        aufgabenstellung = HTML(value='
{}
'.format(text))
        instbox = widgets.HTML(value='{}'.format(inst), layout=widgets.Layout(justify_content="center"))

        button = Button(description="Korrekt?",layout=widgets.Layout(width="250px"))

        def on_change_check(change):
            if change['type'] == 'change' and change['name']=='value':
                if change['new'] == True:
                    box = change['owner']
                    d = box.description
                    self.chosenList.append(d)
                else:
                    box = change['owner']
                    if box.description in self.chosenList:
                        self.chosenList.remove(box.description)
            button.style.button_color = None

        cb = []
        style = {'description_width': 'initial'}
        for answer in allAnsw:
            cb.append(Checkbox(value=False, description=answer, disabled=False, style=style, layout=Layout(width="600px")))
            cb[-1].observe(on_change_check)

        box = widgets.VBox(children=cb)

        super().__init__(children=[aufgabenstellung, instbox, box, button])

        def on_button_clicked(button):
            self.chosenList.sort()
            if self.chosenList == self.myCor:
                button.style.button_color = 'lightgreen'
            else:
                button.style.button_color = 'red'

        button.on_click(on_button_clicked)

class OpenTextfield(widgets.VBox):
    def __init__(self, text, initial, correct, inst=None, klammer=False):

        textfield = widgets.Textarea(value=initial, placeholder='Type something', description='', disabled=False, layout=Layout(width='500px'))
        # textfield.observe(on)
        textbox = widgets.HTML(value='
{}
'.format(text))
        instbox = widgets.HTML(value='{}'.format(inst))

        submit_button = widgets.Button(description="Korrekt?", layout=widgets.Layout(width="350px"))
        give_up_button = widgets.Button(description="Lösung zeigen", layout=widgets.Layout(width="150px"))
        button_box = widgets.HBox(children=[submit_button, give_up_button])

        super().__init__(children=[textbox, instbox, textfield, button_box])

        def on_ok_button_clicked(button):
            given_answer = textfield.value
            ans = given_answer.split()
            answer = ''.join(ans)
            corr = correct.split()
            newc = ''.join(corr)

            if answer == newc:
                button.style.button_color = 'lightgreen'
                button.description = "Richtig!"

            else:
                button.style.button_color = 'red'

                if klammer:
                    more = True
                    stack = []
                    not_open = 0
                    for i,c in enumerate(given_answer):
                        if c == '[':
                            stack.append(i)
                        elif c == ']' and stack:
                            stack.pop()
                        elif c == ']':
                            not_open += 1
                        elif c in ['(',')','{','}']:
                            button.description="Falsche Klammern!"
                            more = False
                            break

                    if stack and more:
                        more = False
                        button.description =str(len(stack))+" Klammern schließen!"

                    if not_open and more:
                        more = False
                        button.description = str(not_open)+" Klammern öffnen!"

                    if more:
                        button.description = "Noch eine Übereinstimmung mit Synatxbaum erreicht!"

        def on_show_solution_button_clicked(button):
            textfield.value = correct
            textfield.disabled = True
            submit_button.disabled = True
            give_up_button.disabled = True

        submit_button.on_click(on_ok_button_clicked)
        give_up_button.on_click(on_show_solution_button_clicked)


from termcolor import colored

def conv_to_kilos(miles):
    kilos = miles * 1.609344  # Conversion factor of 1.6xx kilos for every mile
    print(colored(f"\n\t\t***CONVERSION RESULT***\n", "green"))
    print("\t" + "-" * 40)
    print(
        f"\tYour distance driven in miles: {miles}\n"
        f"\tEquivalent number of kilometers: {kilos:.3f}"
    )
    print("\t" + "-" * 40)
    return kilos


from django.http import *
import json
from .models import *

def api_post(request, model_name):
    response_message = {}
    if "lists" == model_name:
        song_id = request.POST["song_id"]
        playlist_id = request.POST["playlist_id"]
        lists = PlaylistsSongs.objects.filter(song__id=song_id, playlist__id=playlist_id)
        if len(lists) == 0:
            print("EXISTING")
    return HttpResponse(json.dumps(response_message, indent=4), content_type="application/json")

def api_get(request):
    data = {"lists": {}, "all":[]}
    for s in Song.objects.all():
        data["all"].append({"title": s.title, "id": s.id})
    for p in Playlist.objects.all():
        playlist = {"name": p.name, "songs": []}
        for sp in PlaylistsSongs.objects.filter(playlist=p):
            playlist["songs"].append({"title": sp.song.title, "id": sp.song.id})
        data["lists"][p.id] = playlist
    return HttpResponse(json.dumps(data, indent=4), content_type="application/json")


"""
This model maps many users to a single company, and defines their role.
"""
from operator import itemgetter

from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from djchoices import ChoiceItem, DjangoChoices

from apps.user.models import User

from ..models import Entity

# permission roles
class Roles(DjangoChoices):
    OWNER = ChoiceItem('owner', order=1)
    EDITOR = ChoiceItem('editor', order=2)
    MEMBER = ChoiceItem('member', order=3)

    @classmethod
    def sort_roles(cls, member) -> int:
        choice = member.role
        return cls.get_choice(choice).order

class Member(models.Model):
    """
    A company has many members (users) with different permissions (roles).
    """

    # fields
    entity = models.ForeignKey(Entity, on_delete=models.CASCADE, related_name='members')
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='member')
    role = models.CharField(max_length=30, choices=Roles.choices)

    class Meta:
        unique_together = ('entity', 'user')

    def __str__(self):
        return "{0} ({1})".format(self.user.username, self.pk)

    @classmethod
    def sort_by_role(cls, member_list: []) -> []:
        """
        Accepts a list of members and sorts them by
        role
        """
        return sorted(member_list, key=Roles.sort_roles)

    @staticmethod
    def is_editor(user: User, entity: Entity) -> bool:
        """
        An editor is an EDITOR or an OWNER.
        An editor can edit a company.
        :param user: the user to check
        :param company: the company the user belongs to
        :return: True if the user is administrator+ else False
        """
        # user not logged in
        if not user.is_authenticated:
            return False

        try:
            member = Member.objects.filter(user=user, entity=entity)[0]
        except IndexError:
            return False

        if member.role in [Roles.EDITOR, Roles.OWNER]:
            return True
        return False

    @staticmethod
    def is_owner(user: User, entity: Entity) -> bool:
        # not a valid user
        if not user.is_authenticated:
            return False

        try:
            member = Member.objects.filter(user=user, entity=entity)[0]
        except IndexError:
            return False

        if member.role == Roles.OWNER:
            return True
        return False

    @staticmethod
    def get_members(entity: Entity) -> []:
        """
        :param role: Get members with the specific role
        :return: a list of Members (users)
        """

        members = Member.objects.filter(entity=entity).select_related('user')
        return members

    @staticmethod
    def is_editor_any(user: User) -> bool:

        # user is not logged in
        if not user.is_authenticated:
            return False

        try:
            Member.objects \
                .filter(user=user) \
                .filter(role=Roles.EDITOR)[0]
        except IndexError:
            return False
        return True

    @staticmethod
    def is_owner_any(user: User) -> bool:

        # user is not logged in
        if not user.is_authenticated:
            return False

        try:
            Member.objects \
                .filter(user=user) \
                .filter(role=Roles.OWNER)[0]
        except IndexError:
            return False
        return True

    @staticmethod
    def get_user_company(user: User):
        if not user.is_authenticated:
            return None

        entities = Member.objects \
            .filter(user=user) \
            .filter(role=Roles.EDITOR) \
            | Member.objects \
            .filter(user=user) \
            .filter(role=Roles.OWNER)

        for entity in entities:
            try:
                if entity.entity.company:
                    return entity.entity.company
            except ObjectDoesNotExist:
                pass
        return None

    @staticmethod
    def get_user_group(user: User):
        if not user.is_authenticated:
            return None

        entities = Member.objects \
            .filter(user=user) \
            .filter(role=Roles.EDITOR) \
            | Member.objects \
            .filter(user=user) \
            .filter(role=Roles.OWNER)

        for entity in entities:
            try:
                if entity.entity.group:
                    return entity.entity.group
            except ObjectDoesNotExist:
                pass
        return None