from datetime import datetime, timedelta, date
import json
import operator
from itertools import groupby
import logging

line_emoji_map = {"Red": "🔴", "Green": "🟢", "Blue": "🔵", "Orange": "🟠"}

with open("stations.json", "r") as data_file:
    stations = json.load(data_file)

def id_to_stop(line, id):
    """takes in a line and id
    returns the name of the stop the id is present in, if it exists
    """
    for s in stations[line]["stations"]:
        if str(id) in s["stops"]["0"] or str(id) in s["stops"]["1"]:
            return s["stop_name"]

def get_stop_pair(sz):
    """takes in a slow zone
    returns a formatted string with the line color emoji and two stop names
    """
    return (
        line_emoji_map[sz["color"]]
        + " "
        + id_to_stop(sz["color"], sz["fr_id"])
        + " → "
        + id_to_stop(sz["color"], sz["to_id"])
    )

def get_zone_date_length(sz):
    """takes in a slow zone
    returns the number of days the slow zone was active
    """
    d1 = datetime.strptime(sz["start"], "%Y-%m-%dT%H:%M:%S")
    d2 = datetime.strptime(sz["end"], "%Y-%m-%dT%H:%M:%S")
    return abs((d2 - d1).days) + 1

def format_line_slow_zone(sz):
    ret = ""
    ret += get_stop_pair(sz) + "\n"
    ret += "🗓 " + str(get_zone_date_length(sz)) + " days "
    ret += "⏳ " + str(round(sz["delay"], 1)) + "s "
    ret += "⬆ " + str(round(sz["delay"] / sz["baseline"] * 100, 2)) + "%\n"
    return ret

def format_new_line_slow_zone(sz):
    ret = ""
    ret += get_stop_pair(sz) + "\n"
    ret += "⏳ " + str(round(sz["delay"], 1)) + "s "
    ret += "⬆ " + str(round(sz["delay"] / sz["baseline"] * 100, 2)) + "%\n"
    return ret

def format_percent_change(latest, previous):
    ret = ""
    percent_change = (latest - previous) / previous * 100.0
    ret += "⬆ " if percent_change < 0 else "⬇ "
    ret += str(abs(round(percent_change, 2))) + " %\n"
    return ret

def format_updated_slow_zone_details(sz):
    ret = ""
    ret += get_stop_pair(sz) + "\n"
    ret += "⏳ " + str(round(sz["latest_delay"], 1)) + "s "
    ret += "-> " + str(round(sz["previous_delay"], 1)) + "s "
    ret += format_percent_change(sz["latest_delay"], sz["previous_delay"])
    return ret

def format_time(t):
    return datetime.strptime(t, "%Y-%m-%dT%H:%M:%S")

def chunks(lines, n):
    for i in range(0, len(lines), n):
        yield lines[i : i + n]

def generate_new_slow_zones_list(sz, date):
    today_slow_zones = filter(
        lambda s: format_time(s["start"]).ctime() == (date - timedelta(days=1)).ctime(),
        sz,
    )
    sorted_slow_zones = sorted(today_slow_zones, key=operator.itemgetter("color"))
    logging.debug(f"sorted_slow_zones: {sorted_slow_zones}")
    outputList = []
    for i, g in groupby(sorted_slow_zones, key=operator.itemgetter("color")):
        outputList.append(list(g))
    return outputList

def generate_grouped_slow_zone_list(sz, date):
    today_slow_zones = filter(
        lambda s: format_time(s["end"]).ctime() == (date - timedelta(days=1)).ctime(),
        sz,
    )
    sorted_slow_zones = sorted(today_slow_zones, key=operator.itemgetter("color"))
    logging.debug(f"sorted_slow_zones: {sorted_slow_zones}")
    outputList = []
    for i, g in groupby(sorted_slow_zones, key=operator.itemgetter("color")):
        outputList.append(list(g))
    return outputList

def get_change(current, previous):
    if current == previous:
        return 0
    try:
        return (abs(current - previous) / previous) * 100.0
    except ZeroDivisionError:
        return float("inf")

def generate_updated_slow_zones(sz, date):
    today_slow_zones = filter(
        lambda s: format_time(s["end"]).ctime() == (date - timedelta(days=1)).ctime(),
        sz,
    )
    sorted_slow_zones = sorted(today_slow_zones, key=operator.itemgetter("color"))

    significant_changed_slow_zones = filter(
        # Check for NaN in case of shuttling
        lambda s: s["latest_delay"] is not None and s["previous_delay"] is not None
        # Check for a >= 10% change
        and get_change(s["latest_delay"], s["previous_delay"]) >= 10
        # Check for 10 second minumum change
        and abs(s["latest_delay"] - s["previous_delay"]) >= 10,
        sorted_slow_zones,
    )

    return list(significant_changed_slow_zones)

def generate_post_text_map(g_sz):
    output_map = []
    for i, line in enumerate(g_sz):
        output_map.append("")
        output_map[i] += (
            line_emoji_map[line[0]["color"]] + " " + line[0]["color"] + " " + "Line\n---------------------\n"
        )
        for slow_zone in line:
            output_map[i] += format_line_slow_zone(slow_zone) + "\n\n"
        output_map[i] += "\n\n"

    # Filter out empty strings
    return filter(None, output_map)

def generate_data_dashboard_link(sz):
    """takes in a slow zone
    returns a data dashboard link to said slow zone
    """
    color = sz["color"].lower()
    stop1 = sz["fr_id"]
    stop2 = sz["to_id"]
    start = (datetime.strptime(sz["start"], "%Y-%m-%dT%H:%M:%S") - timedelta(days=14)).strftime("%Y-%m-%d")
    end = date.today().strftime("%Y-%m-%d")
    link = f"https://dashboard.transitmatters.org/{color}/trips/multi/?from={stop1}&to={stop2}&startDate={start}&endDate={end}"
    logging.debug(f"Generated Data Dashboard link: {link}")
    return link

def format_new_slow_zone(sz):
    output = ""
    output += "⚠ New Slow Zone ⚠\n"
    output += "---------------------\n"
    output += format_new_line_slow_zone(sz) + "\n"
    output += f"Check it out in our Data Dashboard: {generate_data_dashboard_link(sz)}"
    return output

def format_fixed_slow_zone(sz):
    output = ""
    output += "✅ Fixed Slow Zone 🎉\n"
    output += "---------------------\n"
    output += format_line_slow_zone(sz) + "\n"
    output += f"Check it out in our Data Dashboard: {generate_data_dashboard_link(sz)}"
    return output

def format_updated_slow_zone(sz):
    output = ""
    output += "🚨 Updated Slow Zone \n"
    output += "---------------------\n"
    output += format_updated_slow_zone_details(sz) + "\n"
    output += f"Check it out in our Data Dashboard: {generate_data_dashboard_link(sz)}"
    return output


from dataloader import read_bci_data, BCIDataset
from torchsummary import summary
from torch.utils.data import DataLoader

import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim

def get_activation_op(activation_type):
    if activation_type == 'ELU':
        return nn.ELU()
    elif activation_type == 'LeakyReLU':
        return nn.LeakyReLU()
    else:
        return nn.ReLU()

class DeepConvNet(nn.Module):
    def __init__(
        self,
        learning_rate=1e-2,
        epochs=300,
        activation_type='ELU'
    ):

        super(DeepConvNet, self).__init__()

        self.first_layer = nn.Sequential(
            nn.Conv2d(1, 25, kernel_size=(1, 5)),
            nn.Conv2d(25, 25, kernel_size=(2, 1)),
            nn.BatchNorm2d(25, eps=1e-05, momentum=0.1),
            get_activation_op(activation_type),
            nn.MaxPool2d(kernel_size=(1, 2)),
            nn.Dropout(p=0.5)
        )

        self.second_layer = nn.Sequential(
            nn.Conv2d(25, 50, kernel_size=(1, 5)),
            nn.BatchNorm2d(50, eps=1e-05, momentum=0.1),
            get_activation_op(activation_type),
            nn.MaxPool2d(kernel_size=(1, 2)),
            nn.Dropout(p=0.85)
        )

        self.third_layer = nn.Sequential(
            nn.Conv2d(50, 100, kernel_size=(1, 5)),
            nn.BatchNorm2d(100, eps=1e-05, momentum=0.1),
            get_activation_op(activation_type),
            nn.MaxPool2d(kernel_size=(1, 2)),
            nn.Dropout(p=0.85)
        )

        self.fourth_layer = nn.Sequential(
            nn.Conv2d(100, 200, kernel_size=(1, 5)),
            nn.BatchNorm2d(200, eps=1e-05, momentum=0.1),
            get_activation_op(activation_type),
            nn.MaxPool2d(kernel_size=(1, 2)),
            nn.Dropout(p=0.85)
        )

        self.dense = nn.Sequential(
            nn.Linear(in_features=8600, out_features=2)
        )

        self.flatten_size = 8600
        self.learning_rate = learning_rate
        self.epochs = epochs
        self.criterion = nn.CrossEntropyLoss()
        self.optimizer = optim.Adam(self.parameters(), learning_rate)

    def forward(self, inputs):

        x = inputs.view(-1, 1, 2, 750)
        x = self.first_layer(x)
        x = self.second_layer(x)
        x = self.third_layer(x)
        x = self.fourth_layer(x)
        x = self.dense(x.view(-1, self.flatten_size))

        return x

    def train(self, train_gen, test_gen):

        train_log, test_log = [], []
        for epoch in range(self.epochs):
            for inputs, labels in train_gen:
                self.optimizer.zero_grad()
                outputs = self.forward(inputs)
                loss = self.criterion(outputs, labels.long())
                loss.backward()
                self.optimizer.step()

            train_acc = self.test(train_gen)
            test_acc = self.test(test_gen)
            train_log.append((epoch, train_acc))
            test_log.append((epoch, test_acc))

            print('\nEpoch [%d] testing accuracy: %.2f%%' % (epoch, test_acc))
            print('Epoch [%d] training accuracy: %.2f%%' % (epoch, train_acc))

        print('\nFinished training!')
        print('Test accuracy: %.2f%%' % test_log[-1][1])

        return train_log, test_log

    """ Testing accuracy with specific data generator (train or test) """
    def test(self, gen):
        with torch.no_grad():
            corrects = 0
            n_test = 0
            for inputs, labels in gen:
                outputs = self.forward(inputs)
                corrects += torch.sum(torch.max(outputs, 1)[1] == labels).item()
                n_test += labels.size()[0]

        return corrects / n_test * 100

class EEGNet(nn.Module):
    def __init__(
        self,
        learning_rate=1e-2,
        epochs=300,
        activation_type='ELU'
    ):

        super(EEGNet, self).__init__()

        self.first_conv = nn.Sequential(
            nn.Conv2d(1, 16, kernel_size=(1, 51), stride=(1, 1), padding=(0, 25), bias=False),
            nn.BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        )

        self.depthwise_conv = nn.Sequential(
            nn.Conv2d(16, 32, kernel_size=(2, 1), stride=(1, 1), groups=16, bias=False),
            nn.BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True),
            get_activation_op(activation_type),
            nn.AvgPool2d(kernel_size=(1, 4), stride=(1, 4), padding=0),
            nn.Dropout(p=0.5)
        )

        self.separable_conv = nn.Sequential(
            nn.Conv2d(32, 32, kernel_size=(1, 15), stride=(1, 1), padding=(0, 7), bias=False),
            nn.BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True),
            get_activation_op(activation_type),
            nn.AvgPool2d(kernel_size=(1, 8), stride=(1, 8), padding=0),
            nn.Dropout(p=0.5)
        )

        self.classify = nn.Sequential(
            nn.Linear(in_features=736, out_features=2, bias=True)
        )

        self.learning_rate = learning_rate
        self.epochs = epochs
        self.criterion = nn.CrossEntropyLoss()
        self.optimizer = optim.Adam(self.parameters(), learning_rate)

    def count_parameters(self, model):
        return sum(p.numel() for p in model.parameters() if p.requires_grad)

    def forward(self, inputs):

        x = self.first_conv(inputs)
        x = self.depthwise_conv(x)
        x = self.separable_conv(x)
        x = self.classify(x.view(-1, 736))

        return x

    def train(self, train_gen, test_gen):

        train_log, test_log = [], []
        for epoch in range(self.epochs):
            for inputs, labels in train_gen:
                self.optimizer.zero_grad()
                outputs = self.forward(inputs)
                loss = self.criterion(outputs, labels.long())
                loss.backward()
                self.optimizer.step()

            train_acc = self.test(train_gen)
            test_acc = self.test(test_gen)
            print('\nEpoch [%d] testing accuracy: %.2f%%\n' % (epoch, test_acc))
            print('Epoch [%d] training accuracy: %.2f%%' % (epoch, train_acc))

            train_log.append((epoch, train_acc))
            test_log.append((epoch, test_acc))

        print('\nFinished training!')
        print('Test accuracy: %.2f%%' % test_log[-1][1])

        return train_log, test_log

    """ Testing accuracy with specific data generator (train or test) """
    def test(self, gen):
        with torch.no_grad():
            corrects = 0
            n_test = 0
            for inputs, labels in gen:
                outputs = self.forward(inputs)
                corrects += torch.sum(torch.max(outputs, 1)[1] == labels).item()
                n_test += labels.size()[0]

        return corrects / n_test * 100

""" Plot accuracy """
def plot_summary(data, name):

    plt.figure()
    for j in range(len(data)):
        label, config = data[j][0], data[j][1]
        xx, yy = [], []

        for i in range(len(config)):
            xx.append(config[i][0])
            yy.append(config[i][1])
        plt.plot(xx, yy, label=label)

    plt.xlabel('epochs')
    plt.ylabel('accuracy(%)')
    plt.legend()
    plt.savefig('%s.png' % name)

if __name__ == '__main__':

    """ Prepare environment """
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    print('Device: ', device)

    train_data, train_label, test_data, test_label = read_bci_data()
    train_set = BCIDataset(train_data, train_label)
    test_set = BCIDataset(test_data, test_label)

    train_gen = DataLoader(train_set, batch_size=32, shuffle=True)
    test_gen = DataLoader(test_set, batch_size=len(test_set), shuffle=True)
    activations = ['ELU', 'ReLU', 'LeakyReLU']
    logs = []

    """ DeepConvNet Experiments """
    for act_type in activations:
        # logs = []
        net = DeepConvNet(epochs=5000, learning_rate=0.0001, activation_type=act_type).cuda()
        train_log, test_log = net.train(train_gen, test_gen)
        logs.append(('Conv_%s_train' % act_type, train_log))
        logs.append(('Conv_%s_test' % act_type, test_log))
    plot_summary(logs, 'summary')

    """ EEGNet Experiments """
    for act_type in activations:
        # logs = []
        net = EEGNet(epochs=10000, learning_rate=0.0001, activation_type=act_type).cuda()
        train_log, test_log = net.train(train_gen, test_gen)
        logs.append(('EEG_%s_train' % act_type, train_log))
        logs.append(('EEG_%s_test' % act_type, test_log))
    plot_summary(logs, 'summary')

    for model in logs:
        print('%s: %.2f%%' % (model[0], model[1][-1][1]))
        print()


import json
import random

from field_capture import db
from field_capture.models import Room, Player
from .exceptions import (MissTokenError, TwiceRolledDiceError,
                         MissTurnLastPersonError, GameDoesNotStartError)

class RoomManager:
    def __init__(self, room_id=None):
        self.room = Room.query.filter_by(id=room_id).first()
        self.game_field = GameField(self.room)

    @staticmethod
    def get_room_exists(room_id):
        room = Room.query.filter_by(id=room_id).first()
        if room is None:
            return False
        return True

    @staticmethod
    def create_room(room_id, name, type, size):
        field = GameField.create_empty_field()
        field = json.dumps(field)
        room = Room(id=room_id, name=name, type=type, size=size,
                    status='waiting', global_score=340,
                    field=field, number_of_moves=0)
        db.session.add(room)
        db.session.commit()

    @staticmethod
    def get_public_rooms():
        public_rooms = Room.query.filter_by(type='public').all()
        return public_rooms

    @staticmethod
    def get_main_public_rooms_info_json():
        public_rooms = RoomManager.get_public_rooms()
        public_room_list = []
        for room in public_rooms:
            public_room_list.append({"id": room.id, "name": room.name,
                                     "size": room.size, "status": room.status,
                                     "players_amount": len(room.players)})
        return json.dumps(public_room_list)

    def get_json_players_id_with_colors(self):
        ids_and_colors = []
        for p in self.room.players:
            ids_and_colors.append({'color': p.order_of_turn, 'id': p.id})
        ids_and_colors = json.dumps(ids_and_colors)
        return ids_and_colors

    def is_full(self):
        if len(self.room.players) >= self.room.size:
            return True
        return False

    def is_playing(self):
        if self.room.status == 'playing':
            return True
        return False

    @staticmethod
    def get_rooms_list():
        rooms = Room.query.all()

    def are_players_ready(self):
        i = 0
        for player in self.room.players:
            if player.ready is True:
                i += 1

        if i == self.room.size:
            return True
        return False

    def get_engaged_place_number(self):
        return len(self.room.players)

    def get_number_of_moves(self):
        return self.room.number_of_moves

    def set_status(self, status):
        if (status == 'waiting') or (status == 'playing'):
            self.room.status = status
            db.session.commit()
            return True
        return False

    def randomly_set_token(self):
        p = random.randrange(1, self.room.size+1, 1)
        random_player_info = {}
        for player in self.room.players:
            if player.order_of_turn == p:
                player.token_presence = True
                random_player_info['id'] = player.id
                random_player_info['order_of_turn'] = p
                random_player_info['username'] = player.username
                db.session.commit()
                return random_player_info

    def start_game(self):
        self.set_status('playing')
        for player in self.room.players:
            player.playing = True
        db.session.commit()
        return True

    def finish_game(self):
        Player.query.filter_by(room_id=self.room.id, playing=False).delete()

        all_players_in_room = (Player.query
                               .filter_by(room_id=self.room.id)
                               .order_by(Player.order_of_turn).all())
        for player in all_players_in_room:
            player.surrender = False
            player.score = 0
            player.playing = False
            player.token_presence = False
            player.ready = False
            player.dice_has_rolled = False
            player.last_dice_values = json.dumps([0, 0])

        k = 1
        for player in all_players_in_room:
            player.order_of_turn = k
            k += 1

        field = GameField.create_empty_field()
        field = json.dumps(field)
        self.room.field = field
        self.room.status = 'waiting'
        self.room.global_score = 340

        # To avoide past game clock activity
        self.room.number_of_moves += 1

        db.session.commit()

    def delete_room(self):
        if len(self.room.players) == 0:
            db.session.delete(self.room)
            db.session.commit()

class PlayerManager:
    class Decorator:
        @classmethod
        def player_has_token(cls, func):
            def wrapper(self, *args):
                if self.player.token_presence is True:
                    return func(self, *args)
                raise MissTokenError("Not your turn!")
            return wrapper

        @classmethod
        def is_game_start(cls, func):
            def wrapper(self, *args):
                if self.room_manager.is_playing():
                    return func(self, *args)
                raise GameDoesNotStartError("Game doesn`t start yet!")
            return wrapper

        @classmethod
        def player_roll_dice_status(cls, rolled_dice):
            def wrap(func):
                def wrapped_f(self, *args):
                    if self.player.dice_has_rolled is rolled_dice:
                        return func(self, *args)
                    raise TwiceRolledDiceError("You already rolled dice")
                return wrapped_f
            return wrap

        @classmethod
        def is_player_win(cls, score_win=True):
            def wrap(func):
                def wrapped_f(self, *args):
                    func(self, *args)
                    if score_win:
                        global_score = self.room_manager.room.global_score
                        if (self.player.score > (global_score /
                                                 self.room_manager.room.size)):
                            return self.player
                    else:
                        i = 0
                        winner = None
                        for player in self.room_manager.room.players:
                            if (player.playing and (not player.surrender)):
                                i += 1
                                winner = player
                        if i == 1:
                            return winner
                    return False
                return wrapped_f
            return wrap

    def __init__(self, player_id, room_id):
        self.player = Player.query.filter_by(id=player_id,
                                             room_id=room_id).first()
        self.room_manager = RoomManager(room_id)

    @staticmethod
    def create_player(player_id, room_id, username):
        room_manager = RoomManager(room_id)
        engaged_place = len(room_manager.room.players)+1

        player = Player(id=player_id, room_id=room_id,
                        order_of_turn=engaged_place,
                        score=0, token_presence=False,
                        username=username, ready=False,
                        playing=False, surrender=False,
                        dice_has_rolled=False,
                        last_dice_values=json.dumps([0, 0]))

        db.session.add(player)
        db.session.commit()
        return player

    @staticmethod
    def get_player_in_room(player_id, room_id):
        player = Player.query.filter_by(id=player_id, room_id=room_id)
        return player

    @staticmethod
    def get_player_in_all_rooms(player_id):
        player_in_all_rooms = Player.query.filter_by(id=player_id).all()
        return player_in_all_rooms

    def get_active_players_in_room(self, room_id):
        active_players = (Player.query.
                          filter_by(surrender=False,
                                    playing=True, room_id=room_id).
                          order_by(Player.order_of_turn).all())
        return active_players

    @staticmethod
    def get_player_status(player_id, room_id=None):
        player_in_rooms = PlayerManager.get_player_in_all_rooms(player_id)
        for player in player_in_rooms:
            if player.playing is True:
                return 'playing'
            elif player.room.status == 'waiting':
                return 'waiting'
            elif room_id == player.room.id:
                return 'restore'
        return 'free'

    @staticmethod
    def player_wait_or_play(player_status):
        if player_status == 'waiting':
            return 'You already wait in other room'
        elif player_status == 'playing':
            return 'You already play'

    def change_ready_status(self):
        if not self.room_manager.is_playing():
            if self.player.ready is True:
                self.player.ready = False
            else:
                self.player.ready = True
            db.session.commit()
            if self.room_manager.are_players_ready():
                return True
            return False

    def change_score(self, points):
        self.player.score += points

    def set_playing(self, status):
        if type(status) == bool:
            self.player.playing = status
            return True
        return False

    def restore(self):
        self.player.playing = True
        db.session.commit()

    def get_next_player(self, active_players):
        order_of_turn = self.player.order_of_turn

        for player in active_players:
            if player.order_of_turn > order_of_turn:
                return player

        return active_players[0]

    @Decorator.player_has_token
    def miss_turn(self):
        self.room_manager.room.number_of_moves += 1
        number_of_moves = self.room_manager.room.number_of_moves

        self.player.token_presence = False
        self.player.dice_has_rolled = False
        self.player.last_dice_values = json.dumps([0, 0])

        room_id = self.room_manager.room.id

        active_players = self.get_active_players_in_room(room_id)

        if len(active_players) == 1:
            raise MissTurnLastPersonError()

        next_player = self.get_next_player(active_players)

        next_player.token_presence = True
        db.session.commit()

        return {'username': next_player.username,
                'order_of_turn': next_player.order_of_turn,
                'id': next_player.id,
                'number_of_moves': number_of_moves}

    @Decorator.player_roll_dice_status(False)
    @Decorator.player_has_token
    def roll_dice(self):
        width = random.randrange(1, 7)
        height = random.randrange(1, 7)
        self.player.dice_has_rolled = True

        self.player.last_dice_values = json.dumps([width, height])
        db.session.commit()

        return width, height

    @Decorator.is_player_win()
    @Decorator.player_roll_dice_status(True)
    @Decorator.player_has_token
    def set_figure(self, x, y):
        width, height = json.loads(self.player.last_dice_values)
        color = self.player.order_of_turn
        first_turn = False
        if self.player.score == 0:
            first_turn = True
        self.room_manager.game_field.is_possible_to_create_figure(x, y,
                                                                  width,
                                                                  height,
                                                                  color,
                                                                  first_turn)

        self.room_manager.game_field.create_figure(x, y, width, height, color)
        json_field = self.room_manager.game_field.get_json_field()
        self.room_manager.room.field = json_field
        self.change_score(width*height)
        db.session.commit()

    @Decorator.is_player_win(False)
    def exit(self):
        if self.player.playing is True:
            self.player.playing = False
        else:
            order_of_turn = self.player.order_of_turn
            db.session.delete(self.player)
            db.session.commit()
            for player in self.room_manager.room.players:
                if order_of_turn < player.order_of_turn:
                    player.order_of_turn -= 1

        db.session.commit()

        self.room_manager.delete_room()

    @Decorator.is_player_win(False)
    @Decorator.is_game_start
    def surrender(self):
        self.player.surrender = True
        db.session.commit()

class ScoreTable:
    @staticmethod
    def get_score_list(room_id):
        score_list = (Player.query.with_entities(Player.username,
                                                 Player.score,
                                                 Player.token_presence,
                                                 Player.order_of_turn,
                                                 Player.ready,
                                                 Player.surrender,
                                                 Player.playing)
                      .filter_by(room_id=room_id)
                      .order_by(Player.order_of_turn).all())

        leader_player = {"username": " ", "score": 0, "order_of_turn": 1}

        for player in score_list:
            if player.score >= leader_player['score']:
                leader_player['username'] = player.username
                leader_player['score'] = player.score
                leader_player['order_of_turn'] = player.order_of_turn

        score_dicts = []

        for player in score_list:
            score_dicts.append(player._asdict())

        return score_dicts, leader_player

class GameField:
    def __init__(self, room):
        self.room = room
        self.field = json.loads(room.field)

    @staticmethod
    def create_empty_field():
        field = []
        for i in range(0, 16):
            field.append([0 for i in range(0, 24)])
        return field

    def get_field(self):
        return self.field

    def get_json_field(self):
        return json.dumps(self.field)

    def _is_same_color_around(self, x, y, width, height, color):
        field_height = len(self.field)
        for temp_x in range(x, x+width):
            if y > 0:
                if self.field[y-1][temp_x] == color:
                    return True
            if y+height < field_height:
                if self.field[y+height][temp_x] == color:
                    return True

        field_width = len(self.field[0])
        for temp_y in range(y, y+height):
            if x > 0:
                if self.field[temp_y][x-1] == color:
                    return True
            if x+width < field_width:
                if self.field[temp_y][x+width] == color:
                    return True
        return False

    def _is_figure_out_of_field(self, x, y, width, height):
        if (x+width > len(self.field[0]) or
           (y+height > len(self.field) or
           (y < 0) or (x < 0))):
            return True

        return False

    def _is_figure_has_free_place(self, x, y, width, height):
        for temp_x in range(x, x+width):
            for temp_y in range(y, y+height):
                if self.field[temp_y][temp_x] != 0:
                    return False
        return True

    def is_possible_to_create_figure(self, x, y, width, height,
                                     color, first_turn):
        if self._is_figure_out_of_field(x, y, width, height):
            raise ValueError("Figure out of field")

        if first_turn is False:
            if not self._is_same_color_around(x, y, width, height, color):
                raise ValueError("You need build figure "
                                 "close to your previous figures")

        if not self._is_figure_has_free_place(x, y, width, height):
            raise ValueError("Figure has no free space")

        return True

    def create_figure(self, x, y, width, height, color):
        for temp_x in range(x, x+width):
            for temp_y in range(y, y+height):
                self.field[temp_y][temp_x] = color


from dash import Dash, dcc, html, Input, Output, callback
import dash_bootstrap_components as dbc

app = Dash(external_stylesheets=[dbc.themes.COSMO])
server=app.server
rxn_temp = dcc.Input(value='', type='number',placeholder='temperature',max=300)
my_output = html.Div()
vial_vol = dcc.Input(value='', type='number',placeholder='vial vol',min=1,max=50)
gas_mmol = dcc.Input(value='0',type='number',placeholder='mmol gas',min=0)
vol_solvent = dcc.Input(value='',type='number')
#need a way of error proofing vol_solvent < vial_vol (child dependency?)

def calculate_pressure(vial_vol,vol_solvent,rxn_temp,mmol_gas_generated):
    total_gas = float(vial_vol) - float(vol_solvent)
    mmol_gas = (total_gas/24.5) + float(mmol_gas_generated)
    temp_ideal_gas= float(rxn_temp) + 273
    print(mmol_gas)
    pressure = (mmol_gas/1000)*8.314*(temp_ideal_gas)/(total_gas/1E6)
    return round(pressure/100000, 2)

app.layout = html.Div([
    html.H1("Sealed vessel risk assesment tool"),
    html.Div([
        "Reaction temp: ",
        rxn_temp,
    ],style={'padding': 10, 'flex': 1}),
    html.Br(),
    html.Div(["Volume of vial (mL): ",vial_vol],style={'padding': 10, 'flex': 1}),
    html.Br(),
    html.Div(["mmol of gas generated in reaction: ",gas_mmol],style={'padding': 10, 'flex': 1}),
    html.Br(),
    html.Div(["total volume of solvent used (mL): ",vol_solvent],style={'padding': 10, 'flex': 1}),
    html.Br(),
    html.Div(),
    html.Button('Run', id='apply-button', n_clicks=0),
    my_output
])

if rxn_temp != None:
    @callback(
        Output(my_output, component_property='children'),
        [Input('apply-button','n_clicks')],
        Input(vial_vol, component_property='value'),
        Input(vol_solvent,component_property='value'),
        Input(rxn_temp,component_property='value'),
        Input(gas_mmol,component_property='value')
    )
    def update_output_div(n_clicks,vial_vol,vol_solvent,rxn_temp,gas_mmol):
        if n_clicks > 0:
            delta_vol = calculate_pressure(vial_vol,vol_solvent,rxn_temp,gas_mmol)
            n_clicks = 0
            if delta_vol < 7:
                return f'The reaction was conducted in vial of volume {vial_vol} mL, with total solvent volume of {vol_solvent} mL at a max reaction temperature of {rxn_temp}, this generates a pressure of {delta_vol} bar, assuming ideal gas and no vapor pressure'
            else:
                return f'pressure is {delta_vol} bar, recommended that the maximum operating pressure be no more than 100 psi (~7 bar). '
        else:
            return 'not calaculated yet'

if __name__ == '__main__':
    app.run_server(debug=True)


from enum import Enum
import logging
from typing import Dict

import numba
import numpy as np

from climate_indices import compute, eto, palmer, utils

# declare the names that should be included in the public API for this module
__all__ = ["pdsi", "percentage_of_normal", "pet", "scpdsi", "spei", "spi"]

# ------------------------------------------------------------------------------
class Distribution(Enum):
    """
    Enumeration type for distribution fittings used for SPI and SPEI.
    """

    pearson = "pearson"
    gamma = "gamma"

# ------------------------------------------------------------------------------
# Retrieve logger and set desired logging level
_logger = utils.get_logger(__name__, logging.DEBUG)

# ------------------------------------------------------------------------------
# valid upper and lower bounds for indices that are fitted/transformed to a distribution (SPI and SPEI)
_FITTED_INDEX_VALID_MIN = -3.09
_FITTED_INDEX_VALID_MAX = 3.09

def _norm_fitdict(params: Dict):
    """
    Compatibility shim. Convert old accepted parameter dictionaries
    into new, consistently keyed parameter dictionaries. If given
    a None object, None is returned.

    See https://github.com/monocongo/climate_indices/issues/449
    """
    if params is None:
        return params

    normed = {}
    for name, altname in _fit_altnames:
        val = params.get(name, None)
        if val is None:
            if altname not in params:
                continue
            _logger.warning("Using deprecated fitting parameter key %s."
                            " Use %s instead.",
                            altname, name)
            val = params[altname]
        normed[name] = val
    return normed

_fit_altnames = (("alpha", "alphas"), ("beta", "betas"),
                 ("skew", "skews"), ("scale", "scales"), ("loc", "locs"),
                 ("prob_zero", "probabilities_of_zero"))

# ------------------------------------------------------------------------------
@numba.jit
def spi(
        values: np.ndarray,
        scale: int,
        distribution: Distribution,
        data_start_year: int,
        calibration_year_initial: int,
        calibration_year_final: int,
        periodicity: compute.Periodicity,
        fitting_params: Dict = None,
) -> np.ndarray:
    """
    Computes SPI (Standardized Precipitation Index).

    :param values: 1-D numpy array of precipitation values, in any units,
        first value assumed to correspond to January of the initial year if
        the periodicity is monthly, or January 1st of the initial year if daily
    :param scale: number of time steps over which the values should be scaled
        before the index is computed
    :param distribution: distribution type to be used for the internal
        fitting/transform computation
    :param data_start_year: the initial year of the input precipitation dataset
    :param calibration_year_initial: initial year of the calibration period
    :param calibration_year_final: final year of the calibration period
    :param periodicity: the periodicity of the time series represented by the
        input data, valid/supported values are 'monthly' and 'daily'
        'monthly' indicates an array of monthly values, assumed to span full
         years, i.e. the first value corresponds to January of the initial year
         and any missing final months of the final year filled with NaN values,
         with size == # of years * 12
         'daily' indicates an array of full years of daily values with 366 days
         per year, as if each year were a leap year and any missing final months
         of the final year filled with NaN values, with array size == (# years * 366)
    :param fitting_params: optional dictionary of pre-computed distribution
        fitting parameters, if the distribution is gamma then this dict should
        contain two arrays, keyed as "alpha" and "beta", and if the
        distribution is Pearson then this dict should contain four arrays keyed
        as "prob_zero", "loc", "scale", and "skew".
    :return SPI values fitted to the gamma distribution at the specified time
        step scale, unitless
    :rtype: 1-D numpy.ndarray of floats of the same length as the input array
        of precipitation values
    """

    # we expect to operate upon a 1-D array, so if we've been passed a 2-D array
    # then we flatten it, otherwise raise an error
    shape = values.shape
    if len(shape) == 2:
        values = values.flatten()
    elif len(shape) != 1:
        message = f"Invalid shape of input array: {shape} -- only 1-D and 2-D arrays are supported"
        _logger.error(message)
        raise ValueError(message)

    # if we're passed all missing values then we can't compute
    # anything, so we return the same array of missing values
    if (np.ma.is_masked(values) and values.mask.all()) or np.all(np.isnan(values)):
        return values

    # clip any negative values to zero
    if np.amin(values) < 0.0:
        _logger.warning("Input contains negative values -- all negatives clipped to zero")
        values = np.clip(values, a_min=0.0, a_max=None)

    # remember the original length of the array, in order to facilitate
    # returning an array of the same size
    original_length = values.size

    # get a sliding sums array, with each time step's value scaled
    # by the specified number of time steps
    values = compute.sum_to_scale(values, scale)

    # reshape precipitation values to (years, 12) for monthly,
    # or to (years, 366) for daily
    if periodicity is compute.Periodicity.monthly:
        values = utils.reshape_to_2d(values, 12)
    elif periodicity is compute.Periodicity.daily:
        values = utils.reshape_to_2d(values, 366)
    else:
        raise ValueError(f"Invalid periodicity argument: {periodicity}")

    if distribution is Distribution.gamma:

        # get (optional) fitting parameters if provided
        if fitting_params is not None:
            alphas = fitting_params["alpha"]
            betas = fitting_params["beta"]
        else:
            alphas = None
            betas = None

        # fit the scaled values to a gamma distribution
        # and transform to corresponding normalized sigmas
        values = compute.transform_fitted_gamma(
            values,
            data_start_year,
            calibration_year_initial,
            calibration_year_final,
            periodicity,
            alphas,
            betas,
        )
    elif distribution is Distribution.pearson:

        # get (optional) fitting parameters if provided
        if fitting_params is not None:
            probabilities_of_zero = fitting_params["prob_zero"]
            locs = fitting_params["loc"]
            scales = fitting_params["scale"]
            skews = fitting_params["skew"]
        else:
            probabilities_of_zero = None
            locs = None
            scales = None
            skews = None

        # fit the scaled values to a Pearson Type III distribution
        # and transform to corresponding normalized sigmas
        values = compute.transform_fitted_pearson(
            values,
            data_start_year,
            calibration_year_initial,
            calibration_year_final,
            periodicity,
            probabilities_of_zero,
            locs,
            scales,
            skews,
        )

    else:

        message = f"Unsupported distribution argument: {distribution}"
        _logger.error(message)
        raise ValueError(message)

    # clip values to within the valid range, reshape the array back to 1-D
    values = np.clip(values, _FITTED_INDEX_VALID_MIN, _FITTED_INDEX_VALID_MAX).flatten()

    # return the original size array
    return values[0:original_length]

# ------------------------------------------------------------------------------
@numba.jit
def spei(
        precips_mm: np.ndarray,
        pet_mm: np.ndarray,
        scale: int,
        distribution: Distribution,
        periodicity: compute.Periodicity,
        data_start_year: int,
        calibration_year_initial: int,
        calibration_year_final: int,
        fitting_params: dict = None,
) -> np.ndarray:
    """
    Compute SPEI fitted to the gamma distribution.

    PET values are subtracted from the precipitation values to come up with an array
    of (P - PET) values, which is then scaled to the specified months scale and
    finally fitted/transformed to SPEI values corresponding to the input
    precipitation time series.

    :param precips_mm: an array of monthly total precipitation values,
        in millimeters, should be of the same size (and shape?) as the input PET array
    :param pet_mm: an array of monthly PET values, in millimeters,
        should be of the same size (and shape?) as the input precipitation array
    :param scale: the number of months over which the values should be scaled
        before computing the indicator
    :param distribution: distribution type to be used for the internal
        fitting/transform computation
    :param periodicity: the periodicity of the time series represented by the
        input data, valid/supported values are 'monthly' and 'daily'
        'monthly' indicates an array of monthly values, assumed to span full
         years, i.e. the first value corresponds to January of the initial year
         and any missing final months of the final year filled with NaN values,
         with size == # of years * 12
         'daily' indicates an array of full years of daily values with 366 days
         per year, as if each year were a leap year and any missing final months
         of the final year filled with NaN values, with array size == (# years * 366)
    :param data_start_year: the initial year of the input datasets (assumes that
        the two inputs cover the same period)
    :param calibration_year_initial: initial year of the calibration period
    :param calibration_year_final: final year of the calibration period
    :param fitting_params: optional dictionary of pre-computed distribution
        fitting parameters, if the distribution is gamma then this dict should
        contain two arrays, keyed as "alpha" and "beta", and if the
        distribution is Pearson then this dict should contain four arrays keyed
        as "prob_zero", "loc", "scale", and "skew"
        Older keys such as "alphas" and "probabilities_of_zero" are deprecated.
    :return: an array of SPEI values
    :rtype: numpy.ndarray of type float, of the same size and shape as the input
        PET and precipitation arrays
    """

    # Normalize fitting param keys
    fitting_params = _norm_fitdict(fitting_params)

    # if we're passed all missing values then we can't compute anything,
    # so we return the same array of missing values
    if (np.ma.is_masked(precips_mm) and precips_mm.mask.all()) \
            or np.all(np.isnan(precips_mm)):
        return precips_mm

    # validate that the two input arrays are compatible
    if precips_mm.size != pet_mm.size:
        message = "Incompatible precipitation and PET arrays"
        _logger.error(message)
        raise ValueError(message)

    # clip any negative values to zero
    if np.amin(precips_mm) < 0.0:
        _logger.warning("Input contains negative values -- all negatives clipped to zero")
        precips_mm = np.clip(precips_mm, a_min=0.0, a_max=None)

    # subtract the PET from precipitation, adding an offset
    # to ensure that all values are positive
    p_minus_pet = (precips_mm.flatten() - pet_mm.flatten()) + 1000.0

    # remember the original length of the input array, in order to facilitate
    # returning an array of the same size
    original_length = precips_mm.size

    # get a sliding sums array, with each element's value
    # scaled by the specified number of time steps
    scaled_values = compute.sum_to_scale(p_minus_pet, scale)

    if distribution is Distribution.gamma:

        # get (optional) fitting parameters if provided
        if fitting_params is not None:
            alphas = fitting_params["alpha"]
            betas = fitting_params["beta"]
        else:
            alphas = None
            betas = None

        # fit the scaled values to a gamma distribution and
        # transform to corresponding normalized sigmas
        transformed_fitted_values = \
            compute.transform_fitted_gamma(
                scaled_values,
                data_start_year,
                calibration_year_initial,
                calibration_year_final,
                periodicity,
                alphas,
                betas,
            )

    elif distribution is Distribution.pearson:

        # get (optional) fitting parameters if provided
        if fitting_params is not None:
            probabilities_of_zero = fitting_params["prob_zero"]
            locs = fitting_params["loc"]
            scales = fitting_params["scale"]
            skews = fitting_params["skew"]
        else:
            probabilities_of_zero = None
            locs = None
            scales = None
            skews = None

        # fit the scaled values to a Pearson Type III distribution
        # and transform to corresponding normalized sigmas
        transformed_fitted_values = \
            compute.transform_fitted_pearson(
                scaled_values,
                data_start_year,
                calibration_year_initial,
                calibration_year_final,
                periodicity,
                probabilities_of_zero,
                locs,
                scales,
                skews,
            )

    else:
        message = "Unsupported distribution argument: " + \
                  "{dist}".format(dist=distribution)
        _logger.error(message)
        raise ValueError(message)

    # clip values to within the valid range, reshape the array back to 1-D
    values = \
        np.clip(transformed_fitted_values,
                _FITTED_INDEX_VALID_MIN,
                _FITTED_INDEX_VALID_MAX).flatten()

    # return the original size array
    return values[0:original_length]

# ------------------------------------------------------------------------------
@numba.jit
def scpdsi(precip_time_series: np.ndarray,
           pet_time_series: np.ndarray,
           awc: float,
           data_start_year: int,
           calibration_start_year: int,
           calibration_end_year: int):
    """
    This function computes the self-calibrated Palmer Drought Severity Index
    (scPDSI), Palmer Drought Severity Index (PDSI), Palmer Hydrological Drought
    Index (PHDI), Palmer Modified Drought Index (PMDI), and Palmer Z-Index.

    :param precip_time_series: time series of precipitation values, in inches
    :param pet_time_series: time series of PET values, in inches
    :param awc: available water capacity (soil constant), in inches
    :param data_start_year: initial year of the input precipitation and PET datasets,
                            both of which are assumed to start in January of this year
    :param calibration_start_year: initial year of the calibration period
    :param calibration_end_year: final year of the calibration period
    :return: five numpy arrays containing SCPDSI, PDSI, PHDI, PMDI, and Z-Index values respectively
    """

    return palmer.scpdsi(precip_time_series,
                         pet_time_series,
                         awc,
                         data_start_year,
                         calibration_start_year,
                         calibration_end_year)

# ------------------------------------------------------------------------------
@numba.jit
def pdsi(precip_time_series: np.ndarray,
         pet_time_series: np.ndarray,
         awc: float,
         data_start_year: int,
         calibration_start_year: int,
         calibration_end_year: int):
    """
    This function computes the Palmer Drought Severity Index (PDSI), Palmer
    Hydrological Drought Index (PHDI), and Palmer Z-Index.

    :param precip_time_series: time series of monthly precipitation values, in inches
    :param pet_time_series: time series of monthly PET values, in inches
    :param awc: available water capacity (soil constant), in inches
    :param data_start_year: initial year of the input precipitation and PET datasets,
                            both of which are assumed to start in January of this year
    :param calibration_start_year: initial year of the calibration period
    :param calibration_end_year: final year of the calibration period
    :return: four numpy arrays containing PDSI, PHDI, PMDI, and Z-Index values respectively
    """

    return palmer.pdsi(precip_time_series,
                       pet_time_series,
                       awc,
                       data_start_year,
                       calibration_start_year,
                       calibration_end_year)

# ------------------------------------------------------------------------------
@numba.jit
def percentage_of_normal(values: np.ndarray,
                         scale: int,
                         data_start_year: int,
                         calibration_start_year: int,
                         calibration_end_year: int,
                         periodicity: compute.Periodicity) -> np.ndarray:
    """
    This function finds the percent of normal values (average of each calendar
    month or day over a specified calibration period of years) for a specified
    time steps scale. The normal precipitation for each calendar time step is
    computed for the specified time steps scale, and then each time step's
    scaled value is compared against the corresponding calendar time step's
    average to determine the percentage of normal. The period that defines the
    normal is described by the calibration start and end years arguments.
    The calibration period typically used for US climate monitoring is 1981-2010.

    :param values: 1-D numpy array of precipitation values, any length, initial
        value assumed to be January of the data start year (January 1st of the
        start year if daily periodicity), see the description of the
        *periodicity* argument below for further clarification
    :param scale: integer number of months over which the normal value is
        computed (eg 3-months, 6-months, etc.)
    :param data_start_year: the initial year of the input monthly values array
    :param calibration_start_year: the initial year of the calibration period
        over which the normal average for each calendar time step is computed
    :param calibration_end_year: the final year of the calibration period over
        which the normal average for each calendar time step is computed
    :param periodicity: the periodicity of the time series represented by the
        input data, valid/supported values are 'monthly' and 'daily'
        'monthly' indicates an array of monthly values, assumed to span full
         years, i.e. the first value corresponds to January of the initial year
         and any missing final months of the final year filled with NaN values,
         with size == # of years * 12
         'daily' indicates an array of full years of daily values with 366 days
         per year, as if each year were a leap year and any missing final months
         of the final year filled with NaN values, with array size == (# years * 366)
    :return: percent of normal precipitation values corresponding to the
        scaled precipitation values array
    :rtype: numpy.ndarray of type float
    """

    # validate the scale argument
    if (scale is None) or (scale < 1):
        message = f"Invalid scale argument: '{scale}'"
        _logger.error(message)
        raise ValueError(message)

    # if doing monthly then we'll use 12 periods, corresponding to calendar
    # months, if daily assume years w/366 days
    if periodicity is compute.Periodicity.monthly:
        periodicity = 12
    elif periodicity is compute.Periodicity.daily:
        periodicity = 366
    else:
        message = f"Invalid periodicity argument: '{periodicity}'"
        _logger.error(message)
        raise ValueError(message)

    # bypass processing if all values are masked
    if np.ma.is_masked(values) and values.mask.all():
        return values

    # make sure we've been provided with sane calibration limits
    if data_start_year > calibration_start_year:
        raise ValueError(
            "Invalid start year arguments (data and/or calibration): "
            "calibration start year is before the data start year",
        )
    if ((calibration_end_year - calibration_start_year + 1) * 12) > values.size:
        raise ValueError(
            "Invalid calibration period specified: total calibration years "
            "exceeds the actual number of years of data",
        )

    # get an array containing a sliding sum on the specified time step
    # scale -- i.e. if the scale is 3 then the first two elements will be
    # np.NaN, since we need 3 elements to get a sum, and then from the third
    # element to the end the values will equal the sum of the corresponding
    # time step plus the values of the two previous time steps
    scale_sums = compute.sum_to_scale(values, scale)

    # extract the timesteps over which we'll compute the normal
    # average for each time step of the year
    calibration_years = calibration_end_year - calibration_start_year + 1
    calibration_start_index = (calibration_start_year - data_start_year) * periodicity
    calibration_end_index = calibration_start_index + (calibration_years * periodicity)
    calibration_period_sums = scale_sums[calibration_start_index:calibration_end_index]

    # for each time step in the calibration period, get the average of
    # the scale sum for that calendar time step (i.e. average all January sums,
    # then all February sums, etc.)
    averages = np.full((periodicity,), np.nan)
    for i in range(periodicity):
        averages[i] = np.nanmean(calibration_period_sums[i::periodicity])

    # TODO replace the below loop with a vectorized implementation
    # for each time step of the scale_sums array find its corresponding
    # percentage of the time steps scale average for its respective calendar time step
    percentages_of_normal = np.full(scale_sums.shape, np.nan)
    for i in range(scale_sums.size):

        # make sure we don't have a zero divisor
        divisor = averages[i % periodicity]
        if divisor > 0.0:

            percentages_of_normal[i] = scale_sums[i] / divisor

    return percentages_of_normal

# ------------------------------------------------------------------------------
@numba.jit
def pet(temperature_celsius: np.ndarray,
        latitude_degrees: float,
        data_start_year: int) -> np.ndarray:
    """
    This function computes potential evapotranspiration (PET) using
    Thornthwaite's equation.

    :param temperature_celsius: an array of average temperature values,
        in degrees Celsius
    :param latitude_degrees: the latitude of the location, in degrees north,
        must be within range [-90.0 ... 90.0] (inclusive), otherwise a
        ValueError is raised
    :param data_start_year: the initial year of the input dataset
    :return: an array of PET values, of the same size and shape as the input
        temperature values array, in millimeters/time step
    :rtype: 1-D numpy.ndarray of floats
    """

    # make sure we're not dealing with all NaN values
    if np.ma.isMaskedArray(temperature_celsius) and (temperature_celsius.count() == 0):

        # we started with all NaNs for the temperature, so just return the same as PET
        return temperature_celsius

    # we were passed a vanilla Numpy array, look for indices where the value == NaN
    if np.all(np.isnan(temperature_celsius)):

        # we started with all NaNs for the temperature, so just return the same
        return temperature_celsius

    # If we've been passed an array of latitude values then just use
    # the first one -- useful when applying this function with xarray.GroupBy
    # or numpy.apply_along_axis() where we've had to duplicate values in a 3-D
    # array of latitudes in order to correspond with a 3-D array of temperatures.
    if isinstance(latitude_degrees, np.ndarray) and (latitude_degrees.size > 1):
        latitude_degrees = latitude_degrees.flat[0]

    # make sure we're not dealing with a NaN or out-of-range latitude value
    if (latitude_degrees is not None) and not np.isnan(latitude_degrees) and (-90.0 < latitude_degrees < 90.0):

        # compute and return the PET values using Thornthwaite's equation
        return eto.eto_thornthwaite(
            temperature_celsius,
            latitude_degrees,
            data_start_year,
        )

    message = (f"Invalid latitude value: {latitude_degrees}" +
               " (must be in degrees north, between -90.0 and " +
               "90.0 inclusive)")
    _logger.error(message)
    raise ValueError(message)

# ------------------------------------------------------------------------------
@numba.jit
def pci(rainfall_mm: np.ndarray) -> np.ndarray:
    """
    This function computes Precipitation Concentration Index(PCI, Oliver, 1980).

    :param rainfall_mm: an array of daily rainfall value in a year,
        in mm
    :return: PCI value for the year in aa numpy array
    :rtype: 1-D numpy.ndarray of float
    """

    # make sure we're not dealing with all NaN values
    if np.ma.isMaskedArray(rainfall_mm) and (rainfall_mm.count() == 0):

        # we started with all NaNs for the rainfall, so just return the same
        return rainfall_mm

    # we were passed a vanilla Numpy array, look for indices where the value == NaN
    if np.all(np.isnan(rainfall_mm)):

        # we started with all NaNs for the rainfall, so just return the same
        return rainfall_mm

    # make sure we're not dealing with a NaN or out-of-range or less than the expected rainfall value
    if len(rainfall_mm) == 366 and not sum(np.isnan(rainfall_mm)):
        m = [31, 29, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
        start = 0
        numerator = 0
        denominator = 0

        for month in range(12):
            numerator = numerator + (sum(rainfall_mm[start: m[month]]) ** 2)
            denominator = denominator + sum(rainfall_mm[start: m[month]])

            start = m[month]

        return np.array([(numerator/(denominator**2)) * 100])

    if len(rainfall_mm) == 365 and not sum(np.isnan(rainfall_mm)):
        m = [31, 28, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
        start = 0
        numerator = 0
        denominator = 0

        for month in range(12):
            numerator = numerator + (sum(rainfall_mm[start: m[month]]) ** 2)
            denominator = denominator + sum(rainfall_mm[start: m[month]])

            start = m[month]

        return np.array([(numerator/(denominator**2)) * 100])

    message = "NaN values in time-series or Total Number of days not in year "\
              "not available, total days should be 366 or 365"
    _logger.error(message)
    raise ValueError(message)


import requests
import logging
import urllib.parse
from config import APIKEY

def alert(url):
    logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(asctime)s - %(name)s - %(message)s')
    sendMsg(url)

def test():
    logging.basicConfig(level=logging.DEBUG, format='%(levelname)s - %(asctime)s - %(name)s - %(message)s')
    sendMsg('http://<test-parameter>')

def sendMsg(url):
    title = '[kWordSentry]'
    msg = "kWordSentry发现有关键词消失：{0}".format(url)
    msg = urllib.parse.quote_plus(msg)
    api = "https://sctapi.ftqq.com/{0}.send?title={1}&desp={2}".format(APIKEY, title, msg)
    try:
        r = requests.get(api)
    except Exception as e:
        logging.warning('Failed to connect to ServerChan: {0}'.format(e))
        return
    logging.info('Done.')

if __name__ == "__main__":
    print('TESTING...')
    test()


from typing import Any, Dict, List, Tuple, Union

import numpy as np
import torch
from torch import Tensor

from flash.core.data.base_viz import BaseVisualization
from flash.core.data.io.input import DataKeys
from flash.core.utilities.imports import _MATPLOTLIB_AVAILABLE, Image, requires
from flash.core.utilities.stages import RunningStage
from flash.image.segmentation.output import SegmentationLabelsOutput

if _MATPLOTLIB_AVAILABLE:
    import matplotlib.pyplot as plt
else:
    plt = None

class SegmentationMatplotlibVisualization(BaseVisualization):
    """Process and show the image batch and its associated label using matplotlib."""

    def __init__(self, labels_map: Dict[int, Tuple[int, int, int]]):
        super().__init__()

        self.max_cols: int = 4  # maximum number of columns we accept
        self.block_viz_window: bool = True  # parameter to allow user to block visualisation windows
        self.labels_map: Dict[int, Tuple[int, int, int]] = labels_map

    @staticmethod
    @requires("image")
    def _to_numpy(img: Union[Tensor, Image.Image]) -> np.ndarray:
        out: np.ndarray
        if isinstance(img, np.ndarray):
            out = img
        elif isinstance(img, Image.Image):
            out = np.array(img)
        elif isinstance(img, Tensor):
            out = img.squeeze(0).permute(1, 2, 0).cpu().numpy()
        else:
            raise TypeError(f"Unknown image type. Got: {type(img)}.")
        return out

    @requires("matplotlib")
    def _show_images_and_labels(
        self,
        data: List[Any],
        num_samples: int,
        title: str,
        limit_nb_samples: int = None,
        figsize: Tuple[int, int] = (6.4, 4.8),
    ):
        num_samples = max(1, min(num_samples, limit_nb_samples))

        # define the image grid
        cols: int = min(num_samples, self.max_cols)
        rows: int = num_samples // cols

        # create figure and set title
        fig, axs = plt.subplots(rows, cols, figsize=figsize)
        fig.suptitle(title)

        if not isinstance(axs, np.ndarray):
            axs = np.array(axs)
        axs = axs.flatten()

        for i, ax in enumerate(axs):
            # unpack images and labels
            sample = data[i]
            if isinstance(sample, dict):
                image = sample[DataKeys.INPUT]
                label = sample[DataKeys.TARGET]
            elif isinstance(sample, tuple):
                image = sample[0]
                label = sample[1]
            else:
                raise TypeError(f"Unknown data type. Got: {type(data)}.")
            # convert images and labels to numpy and stack horizontally
            image_vis: np.ndarray = self._to_numpy(image)
            label_tmp: Tensor = SegmentationLabelsOutput.labels_to_image(
                torch.as_tensor(label).squeeze(), self.labels_map
            )
            label_vis: np.ndarray = self._to_numpy(label_tmp)
            img_vis = np.hstack((image_vis, label_vis))
            # send to visualiser
            ax.imshow(img_vis)
            ax.axis("off")
        plt.show(block=self.block_viz_window)

    def show_load_sample(
        self,
        samples: List[Any],
        running_stage: RunningStage,
        limit_nb_samples: int,
        figsize: Tuple[int, int] = (6.4, 4.8),
    ):
        win_title: str = f"{running_stage} - show_load_sample"
        self._show_images_and_labels(samples, len(samples), win_title, limit_nb_samples, figsize)

    def show_per_sample_transform(
        self,
        samples: List[Any],
        running_stage: RunningStage,
        limit_nb_samples: int,
        figsize: Tuple[int, int] = (6.4, 4.8),
    ):
        win_title: str = f"{running_stage} - show_per_sample_transform"
        self._show_images_and_labels(samples, len(samples), win_title, limit_nb_samples, figsize)


from pathlib import Path

import pydicom as dicom
import numpy as np
import pandas as pd

import tensorly as tl
from tensorly.decomposition import parafac, tucker, partial_tucker
from joblib import Parallel, delayed
import argparse
import tensorflow as tf
from scipy.linalg import pinv, svd
from scipy.sparse.linalg import svds

def grid_to_slices(grid, slice_shape=(116, 116), n=100):
	'''Iterate over the sub-images in a pixel array.

	Args:
		grid (2D array):
			The raw pixel array from a dicom file.
			This array aranges the slices on a 2D grid.
		slice_shape (2D shape):
			The shape of the sub-images.
		n (positive int):
			The number of sub-images to yield.

	Yields (2D array):
		The sub-images having shape `slice_shape`.
	'''
	height, width = grid.shape
	h, w = slice_shape

	# Extract at most `n` slices from the pixel array
	for i in range(0, height, h):
		for j in range(0, width, w):
			if n > 0:
				s = grid[i:i+h, j:j+w]
				yield s
			n -= 1

	# If we've extracted less than `n`, yield zeros
	while n > 0:
		s = np.zeros(slice_shape)
		yield s
		n -= 1

def load_observation(obs_dir, slice_shape=(116, 116)):
	'''Load a single observation.

	Args:
		obs_dir (path):
			The path to the observation, a directory containing dicom files.
		slice_shape (2D shape):
			The shape of the individual images within a single pixel_array.
			See `grid_to_slices`.

	Returns (5D array):
		An array with the shape
		`(sequence length, features, depth, height, width)`
	'''
	obs_dir = Path(obs_dir)
	paths = sorted(path for path in obs_dir.glob('*.dcm'))
	try:
		dcs = (dicom.read_file(str(p)) for p in paths)
		grids = (dc.pixel_array for dc in dcs)
		slices = (grid_to_slices(g, slice_shape=slice_shape) for g in grids)
		stacks = (np.stack(s) for s in slices)
		obs = np.stack(stacks)
		obs = np.expand_dims(obs, 1)
		obs = obs.astype('float32')
		# print(obs.shape)
		return obs
	except (NotImplementedError, ValueError):
		return np.nan
def metadata(data_dir):
	'''Load the metadata for the dataset.

	A file `metadata.csv` should be stored in the same directory as the data.
	This file is cross-referenced with the actual data to only include metadata
	for the samples that actually exist locally.

	Args:
	data_dir (path):
	The root directory of the dataset.

	Returns (list of dict, one for each observation):
	path (path):
	The directoy containing the dicoms for the observation.
	label (bool):
	True if the observation is in the Parkinson's class.
	'''
	# The list of metadata dicts for each observation.
	ret = []

	# Things which might exist in the data_dir that are not data.
	ignore = ['.DS_Store', 'metadata.csv']

	# Open the metadata csv
	data_dir = Path(data_dir)
	meta = pd.read_csv(data_dir / 'metadata.csv')

	meta = meta.sort_values('Subject')
	meta = meta[['Subject', 'Group']]
	meta = meta.drop_duplicates()
	meta = meta.set_index('Subject')

	data_dir = Path(data_dir)
	for path in data_dir.glob('*'):

		if path.name in ignore: continue
		# print(path.name)
		subject = int(path.name)
		label = meta.loc[subject]['Group'] == 'PD'
		label = int(label)
		paths = Path(path).glob(f'DTI_gated/*/*')
		paths = (p for p in paths if p.name not in ignore)
		ret.extend({
		'path': p,
		'label': label,
		} for p in paths)

	# print(ret)
	return ret

def calculate_transition_models(data, q):
    frames = data.shape[0]
    vertical_slices = data.shape[1]
    height = data.shape[2]
    width = data.shape[3]

    state_transitions = np.zeros(q * q * vertical_slices)
    for slice_num in range(vertical_slices):
        slice_data = data[:,slice_num,:,:]
        Y = np.zeros((height * width, frames))
        for i in range(frames):
            frame = slice_data[i].reshape(height * width)
            Y[:,i] = frame

        U, E, V_t = svds(Y, k=q)
        V = V_t.T
        C = U[:,:q]
        Ehat = np.diag(E[:q])
        Vhat = V[:,:q]
        X = Ehat.dot(Vhat.T)

        x1 = np.zeros((q, frames - 1))
        x2 = np.zeros((q, frames - 1))
        for i in range(x1.shape[1]):
            for j in range(q):
                x1[j,i] = X[j,i]
                x2[j,i] = X[j,i + 1]

        x1_pinv = pinv(x1)
        A = x2.dot(x1_pinv)
        state_transition = A.reshape(q*q)

        start_index = q * q * slice_num
        end_index =  q * q * (slice_num+1)
        state_transitions[start_index:end_index] = state_transition
    return state_transitions

def get_lds_features(i, obs_dir, q, get_svd=False):
	X = load_observation(obs_dir)
	if type(X)==np.ndarray and X.shape[0]==65:
		X = np.reshape(X, (65,100,116,116))

		transition_features = calculate_transition_models(X, q)
		print('Completed line {}'.format(i))
		return transition_features
	else:
		print('Skipped line {}'.format(i))
		transition_features = np.array([])
		return transition_features

def get_decomposed_tensor(i, obs_dir, ranks):
	tf.enable_eager_execution()
	config = tf.ConfigProto()
	config.gpu_options.allow_growth = True
	sess = tf.Session(config=config)

	X = load_observation(obs_dir)

	if type(X)==np.ndarray and X.shape[0]==65:
		X = np.reshape(X, (65,100,116,116))
		X_tf = tf.convert_to_tensor(X, dtype=tf.float32)
		tl.set_backend('tensorflow')
		core, factors = tucker(X_tf, ranks=ranks, init='random', tol=10e-5, verbose=False)
		transition_features = core.numpy().flatten()
		line = transition_features
		out= line

		print('Completed line {}'.format(i))

	else:
		print('Skipped line {}'.format(i))

		out = []
	sess.close()
	return out
if __name__ == '__main__':
	parser = argparse.ArgumentParser()
	parser.add_argument('-d', '--data_dir', help='Enter data dir path', default='./data')
	parser.add_argument('-r','--ranks',type=int, nargs=4, help='Enter 4 vals for rank', default = [])
	parser.add_argument('-q', '--svd', type=int, help="Enter q val for svd", default=-1)
	args = parser.parse_args()
	data_dir=args.data_dir
	ranks =args.ranks
	q = args.svd

	td_frame = pd.DataFrame(metadata(data_dir))
	td_frame['features'] = [[] for _ in range(len(td_frame))]
	td_frame.features.astype('object')

	lds_frame = td_frame.copy()

	if ranks != []:
		td_frame['features'] = Parallel(n_jobs=-1, verbose=51)(delayed(get_decomposed_tensor)(i=i, obs_dir=x, ranks=ranks) for i, x in enumerate(td_frame['path']))
		td_frame.to_pickle(path=f'tensor_features_{ranks[0]}_{ranks[1]}_{ranks[2]}_{ranks[3]}.pkl')
	if q != -1:
		lds_frame['features'] = Parallel(n_jobs=-1, verbose=51)(delayed(get_lds_features)(i=i, obs_dir=x, q=q) for i, x in enumerate(lds_frame['path']))
		lds_frame.to_pickle(path=f'LDS_features_{q}.pkl')


try:
    import os
    import re
    import sys
    import requests
    import validators
except ModuleNotFoundError:
    print("[-] Requirements not satisfied!\n\trun 'pip install -r requirements.txt'\n\t\tOR\n\trun 'pip3 install -r requirements.txt'")
    quit()

def print_docs():
    docs = f"""
    Documentation:
        Base Usage: {"python" if os.name == "nt" else "python3"} {sys.argv[0]} [URL] [FLAGS]

        Flags:
            -a:
                Gets Phone Numbers & Email Adresses from the URL
            -e:
                Gets Only Email Adresses from the URL
            -p:
                Gets Only Phone Numbers from the URL
            -O [FILENAME]:
                Saves Scraped Data to a file.
    """
    print(docs)

def log(data, filename):
    with open(filename, "a") as f:
        for info in data:
            f.write(f"{info}\n")

def harvest_emails(data, saveEmails=False, filename=None):
    pattern = r"([\d\w\.]+@[\d\w\.\-]+\.\w+)"

    if re.search(pattern, data):
        emails = set(re.findall(pattern, data))
        for email in emails:
            print(f"[+] Email Found: {email}")
        print(f"[*] Found {len(emails)} emails\n")
        if saveEmails:
            log(emails, filename)
            print(f"[*] Saved Harvested Emails in '{filename}'\n")
    else:
        print("[-] Did not find any valid emails")

def harvest_phones(data, savePhones=False, filename=None):
    pattern = r"(\(?[0-9]{3}\)?(?:\-|\s|\.)?[0-9]{3}(?:\-|\.)[0-9]{4})"

    if re.search(pattern, data):
        nums = set(re.findall(pattern, data))
        for num in nums:
            print(f"[+] Phone Number Found: {num}")
        print(f"[*] Found {len(nums)} phone numbers\n")
        if savePhones:
            log(nums, filename)
            print(f"[*] Saved Harvested Phone Numbers in '{filename}'\n")
    else:
        print("[-] Did not find any valid phone numbers")

def harvest(data, save=False, filename=None):
    harvest_emails(data, save, filename)
    harvest_phones(data, save, filename)

def main():
    if len(sys.argv) >= 2:
        if "--help" in sys.argv:
            print_docs()
            sys.exit()
        url = sys.argv[1]
        isValid = validators.url(url)
        if isValid:
            try:
                r = requests.get(url)
            except:
                print("[-] The requested URL did not respond back")
                sys.exit()
            data = r.text
            if "-a" in sys.argv:
                if "-O" in sys.argv:
                    try:
                        filename = sys.argv[sys.argv.index("-O") + 1]
                    except IndexError:
                        print("[-] Please provide a valid filename")
                        sys.exit()
                    harvest(data, True, filename)
                else:
                    harvest(data)
            elif "-e" in sys.argv:
                if "-O" in sys.argv:
                    try:
                        filename = sys.argv[sys.argv.index("-O") + 1]
                    except IndexError:
                        print("[-] Please provide a valid filename")
                        sys.exit()
                    harvest_emails(data, True, filename)
                else:
                    harvest_emails(data)
            elif "-p" in sys.argv:
                if "-O" in sys.argv:
                    try:
                        filename = sys.argv[sys.argv.index("-O") + 1]
                    except IndexError:
                        print("[-] Please provide a valid filename")
                        sys.exit()
                    harvest_phones(data, True, filename)
                else:
                    harvest_phones(data)
            else:
                print(f"[-] Please provide a valid flag operation")
        else:
            print(f"[-] Invalid URL")
            sys.exit()
    else:
        print(f"[-] Invalid Usage:\n\tUsage: {'python' if os.name == 'nt' else 'python3'} {sys.argv[0]} [URL] [FLAGS]")
        print(f"\tTry '{'python' if os.name == 'nt' else 'python3'} {sys.argv[0]} --help' for help")
        sys.exit()

if __name__ == "__main__":
    main()


import matplotlib.pyplot as plt
import numpy as np
from matplotlib import gridspec

from odeModels.insuline.simulation1Day import simulation, pidC1

t, y = simulation([300, 300, 1400 - 300 - 300], [36.29, 86.88, 94.18], pidC1)
y = y + 10
step = 20
s = np.repeat(np.random.normal(y[::step, 0], 10), step)
minIndex = min(len(t), len(s))
t = t[:minIndex]
s = s[:minIndex]

fig = plt.figure(figsize=(8, 6))
gs = gridspec.GridSpec(1, 2, width_ratios=[12, 1])
ax = plt.subplot(gs[0])
bx = plt.subplot(gs[1])
ax.axhline(y=70, color='k', linestyle='-')
ax.plot(t, s, label='G(t)')
ax.fill_between(t, 70, where=s >= 70, facecolor='red', alpha=0.3, interpolate=True)
# x.fill_between(t, 55, where=s < 180, facecolor='green',alpha=1, interpolate=True)
ax.fill_between(t, 35, where=s >= 70, facecolor='red', alpha=1, interpolate=True)
bx.fill_between([0, 1], 0.95, facecolor='red', alpha=1)
bx.set_ylim([0, 1])
ax.set_ylim([30, 250])
ax.set_xlim([0, 1400])
ax.legend()
plt.savefig('GnoiseFree')
plt.show()


import os
import numpy as np
import matplotlib.pyplot as plt
import malmoenv
import argparse
from pathlib import Path
import time
from PIL import Image

from stable_baselines3.common import results_plotter
from stable_baselines3.common.monitor import Monitor
from stable_baselines3.common.results_plotter import load_results, ts2xy, plot_results
from stable_baselines3.common.noise import NormalActionNoise
from stable_baselines3.common.callbacks import BaseCallback
from stable_baselines3.common.env_checker import check_env

from stable_baselines3 import PPO

class SaveOnBestTrainingRewardCallback(BaseCallback):
    """
    Callback for saving a model (the check is done every ``check_freq`` steps)
    based on the training reward (in practice, we recommend using ``EvalCallback``).

    :param check_freq:
    :param log_dir: Path to the folder where the model will be saved.
      It must contains the file created by the ``Monitor`` wrapper.
    :param verbose: Verbosity level.
    """
    def __init__(self, check_freq: int, log_dir: str, verbose: int = 1):
        super(SaveOnBestTrainingRewardCallback, self).__init__(verbose)
        self.check_freq = check_freq
        self.log_dir = log_dir
        self.save_path = os.path.join(log_dir, 'best_model')
        self.best_mean_reward = -np.inf

    # def _init_callback(self) -> None:
    #     # # Create folder if needed
    #     # if self.save_path is not None:
    #     #     os.makedirs(self.save_path, exist_ok=True)

    def _on_step(self) -> bool:
        if self.n_calls % self.check_freq == 0:

          # Retrieve training reward
          x, y = ts2xy(load_results(self.log_dir), 'timesteps')
          if len(x) > 0:
              # Mean training reward over the last 100 episodes
              mean_reward = np.mean(y[-100:])
              if self.verbose > 0:
                print(f"Num timesteps: {self.num_timesteps}")
                print(f"Best mean reward: {self.best_mean_reward:.2f} - Last mean reward per episode: {mean_reward:.2f}")

              # New best model, you could save the agent here
              if mean_reward > self.best_mean_reward:
                  self.best_mean_reward = mean_reward
                  # Example for saving best model
                  if self.verbose > 0:
                    print(f"Saving new best model to {self.save_path}")
                  self.model.save(self.save_path)

        return True

log_dir = "tmp/"
os.makedirs(log_dir, exist_ok=True)

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='malmovnv test')
    parser.add_argument('--mission', type=str, default='missions/jumping.xml', help='the mission xml')
    parser.add_argument('--port', type=int, default=9000, help='the mission server port')
    parser.add_argument('--server', type=str, default='127.0.0.1', help='the mission server DNS or IP address')
    parser.add_argument('--port2', type=int, default=None, help="(Multi-agent) role N's mission port. Defaults to server port.")
    parser.add_argument('--server2', type=str, default=None, help="(Multi-agent) role N's server DNS or IP")
    parser.add_argument('--episodes', type=int, default=100, help='the number of resets to perform - default is 1')
    parser.add_argument('--episode', type=int, default=0, help='the start episode - default is 0')
    parser.add_argument('--role', type=int, default=0, help='the agent role - defaults to 0')
    parser.add_argument('--episodemaxsteps', type=int, default=100, help='max number of steps per episode')
    parser.add_argument('--saveimagesteps', type=int, default=0, help='save an image every N steps')
    parser.add_argument('--resync', type=int, default=0, help='exit and re-sync every N resets'
                                                              ' - default is 0 meaning never.')
    parser.add_argument('--experimentUniqueId', type=str, default='test1', help="the experiment's unique id.")
    args = parser.parse_args()
    if args.server2 is None:
        args.server2 = args.server

    xml = Path(args.mission).read_text()
    env = malmoenv.make()

    env.init(xml, args.port,
             server=args.server,
             server2=args.server2, port2=args.port2,
             role=args.role,
             exp_uid=args.experimentUniqueId,
             episode=args.episode, resync=args.resync)

    env = Monitor(env, log_dir)
    # print("checking env")
    check_env(env, True)
    s = SaveOnBestTrainingRewardCallback(2000, log_dir)
    # print("checked env")

    model = PPO("MlpPolicy", env, verbose=1, tensorboard_log="./ppo_test_tensorboard/")
    #model.load("tmp/best_model.zip")
    model.learn(total_timesteps=100000, callback=s, reset_num_timesteps=False)

    # print("trained and saved model")
    # for i in range(args.episodes):
    #     print("reset " + str(i))
    #     obs = env.reset()

    #     steps = 0
    #     done = False
    #     while not done and (args.episodemaxsteps <= 0 or steps < args.episodemaxsteps):
    #         # h, w, d = env.observation_space.shape
    #         # print(done)
    #         action, _states = model.predict(obs, deterministic=True)
    #         # action = env.action_space.sample()
    #         obs, reward, done, info = env.step(action)
    #         steps += 1
    #         # print("reward: " + str(reward))
    #         # print(obs)

    #         time.sleep(.05)

    env.close()


"""Additional project metadata"""
# pylint: disable=invalid-name

from importlib import metadata as _meta

try:
    distribution = _meta.distribution(__name__.split(".")[0])
except _meta.PackageNotFoundError:  # pragma: no cover
    raise RuntimeError("DoorPi was not properly installed") from None

project = "VoIP Door-Intercomstation with Raspberry Pi"

supporters = (
    "Phillip Munz <office@businessaccess.info>",
    "Hermann Dötsch <doorpi1@gmail.com>",
    "Dennis Häußler <haeusslerd@outlook.com>",
    "Hubert Nusser <hubsif@gmx.de>",
    "Michael Hauer <frrr@gmx.at>",
    "Andreas Schwarz <doorpi@schwarz-ketsch.de>",
    "Max Rößler <max_kr@gmx.de>",
    "missing someone? -> sorry -> mail me",
)

# created with: http://patorjk.com/software/taag/#p=display&f=Ogre&t=DoorPi
epilog = r"""
    ___                  ___ _
   /   \___   ___  _ __ / _ (_)  {project}
  / /\ / _ \ / _ \| '__/ /_)/ |  version:   {version}
 / /_// (_) | (_) | | / ___/| |  license:   {license}
/___,' \___/ \___/|_| \/    |_|  URL:       <{url}>

Author:     {author}
Supporter:  {supporters}
""".format(
    license=distribution.metadata["License"],
    project=distribution.metadata["Name"],
    version=distribution.metadata["Version"],
    author="{} <{}>".format(
        distribution.metadata["Author"], distribution.metadata["Author-email"]
    ),
    supporters="\n            ".join(supporters),
    url=distribution.metadata["Home-page"],
)


# 최소비용 구하기 211005
"""
입력
첫째 줄에 도시의 개수 N(1 ≤ N ≤ 1,000)
둘째 줄에는 버스의 개수 M(1 ≤ M ≤ 100,000)
셋째 줄부터 M+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스의 출발 도시의 번호가 주어진다. 그리고 그 다음에는 도착지의 도시 번호가 주어지고 또 그 버스 비용이 주어진다. 버스 비용은 0보다 크거나 같고, 100,000보다 작은 정수이다.
그리고 M+3째 줄에는 우리가 구하고자 하는 구간 출발점의 도시번호와 도착점의 도시번호가 주어진다. 출발점에서 도착점을 갈 수 있는 경우만 입력으로 주어진다.
출력
출발-도착까지 최소비용
* 알고리즘
최단거리 or 방문하지 않은 경우를 들른 뒤 최단거리 업데이트하기
## 시작도시, 끝도시, 비용 3 정보를 활용해서 딕셔너리 형태로 연결시킬까?
    # 1: [(2, 2), (3, 3), (4, 1), (5, 10)], 2: [(4, 2),], 3: [(5, 1)], 4: [(5, 3)]
    이 방법 자체는 틀리지 않았는데 defaultdict를 활용해서 만들자

# 답을 보고 작성하였으나 다익스트라 알고리즘과 queue를 활용한 방법은 그대로 익혀야겠다.
코드중에선 아래의 코드가 제일 직관적

"""
# 입력값 처리 -> 리스트 컴프리헨션으로 s,e,c 정보를 하나의 리스트로 담았다가 바로 정보에 대한 처리를 하는게 더 효율적이라 코드 변경했다.
import sys
import heapq
from collections import defaultdict
input = sys.stdin.readline
graph = defaultdict(list)
distances = [1e9] * (n +1)

n = int(input()) # 도시 개수
for i in range(int(input())): # 출발, 도착, 비용 목록
    s,e,c = map(int, input().split())
    graph[s].append((e, c))
start, end = map(int, input().split()) # start, end 도시

def dijkstra(start):
    queue = []
    heapq.heappush(queue, (0, start)) # queue에 처음 넣는 정보는 (자기 자신과의 거리 0, 시작위치)

    while queue:
        # start에서 최소의 distance를 기준으로 heappop 해준다.
        dist, now = heapq.heappop(queue)
        if distances[now] < dist: # 입력받은 distance 보다 현재 거리가 더 낮다면 그대로 다음 큐로
            continue
        for node in graph[now]: # 그래프에서 현 위치의 연결포인트들
            cost = dist + node[1] # [1] 번 인덱스는 cost를 의미한다 [0] 인덱스는 연결된 도착위치
            if distances[node[0]] > cost:
                # 만약 이번에 든 비용보다 기존의 비용이 더 크다면 업데이트
                distances[node[0]] = cost
                heapq.heappush(queue, (cost, node[0])) # 힙에 넣어준다. 다음 장소 탐색을 위해

dijkstra(start)
print(distances[end])


import base64
import hashlib
from django.http import HttpResponseRedirect
from django.shortcuts import render,redirect
from .models import Family
from django.views.decorators.csrf import csrf_exempt
from django.db.models import Q
from .forms import UploadFileForm
from django.core.files import File
from django.core.files.storage import FileSystemStorage

# Create your views here.
def main(request):
    return render(request, "morae/main.html")

def writing(request):
    return render(request, "morae/writing.html")

def details(request):
    addressValue = '모든'
    countryValue = '모든'
    context = {
        'address' : addressValue,
        'country' : countryValue,
        'family' : Family.objects.all()
    }

    if request.GET.get('address') != "":
        addressValue = request.GET.get('address')
        context = {
            'address' : addressValue,
            'country' : countryValue,
            'family' : Family.objects.filter(address__icontains = request.GET.get('address'))
        }

    if request.GET.get('country') != "":
        countryValue = request.GET.get('country')
        context = {
            'address' : addressValue,
            'country' : countryValue,
            'family' : Family.objects.filter(country__icontains = request.GET.get('country'))
        }

    if request.GET.get('country') != "" and request.GET.get('country') != "":
        context = {
            'address' : addressValue,
            'country' : countryValue,
            'family' : Family.objects.filter(country__icontains = request.GET.get('country') , address__icontains = request.GET.get('address'))
        }

    return render(request, "morae/details.html", context)

@csrf_exempt
def create(request):
    if request.method=="POST":
        kakao = request.POST.get('kakao')
        state= request.POST.get('state')
        address = request.POST.get('address')
        gmoney = request.POST.get('gmoney')
        kmoney = request.POST.get('kmoney')
        country = request.POST.get('country')
        detail = request.POST.get('detail')
        writepw = request.POST.get('writepw')

        tphoto = __create_file__(request.FILES['tphoto'])
        gphoto = __create_file__(request.FILES['gphoto'])

        Family.objects.create(state=state, address=address, gmoney=gmoney, kmoney=kmoney, country=country,
                              gphoto=gphoto, tphoto=tphoto, kakao=kakao, detail=detail, writepw=writepw)

        return redirect('/')

def __create_file__(myfile):
	fs = FileSystemStorage()
	filename = fs.save(myfile.name, myfile)
	uploaded_file_url = fs.url(filename)
	return myfile.name

@csrf_exempt
def delete(request):
    temppw = request.POST.get('userpw')
    tempkakao = request.POST.get('kakao2')

    realpw = Family.objects.filter(kakao=tempkakao, writepw = temppw)

    if realpw.exists():
        realpw.delete()

    return redirect('/')


from django.db import models
from multiselectfield import MultiSelectField
class EnquiryData(models.Model):
    name=models.CharField(max_length=100)
    email=models.EmailField(max_length=100)
    mobile=models.BigIntegerField()
    COURSES_CHOICES=(
        ('python','PYTHON'),
        ('django','DJANGO'),
        ('UI','UI'),
        ('Rest Api','REST API')
    )
    courses=MultiSelectField(max_length=100,choices=COURSES_CHOICES)
    TRSINERS_CHOICES=(
        ('narayana','NARAYANA'),
        ('Srinivas','SRINIVAS'),
        ('mohan reddy','MOHAN REDDY'),
        ('wilson','WILSON')
    )
    trainers=MultiSelectField(max_length=100,choices=TRSINERS_CHOICES)
    SHIFTS_CHOICES=(
        ('Morning','MORNING'),
        ('evening','EVENING'),
        ('night','NIGHT')
    )
    shifts=MultiSelectField(max_length=100,choices=SHIFTS_CHOICES)
    LOCATIONS_CHOICES=(
        ('ameerpet','AMEERPET'),
        ('madhapur','MADHAPUR'),
        ('KPHB','KPHB')
    )
    location=MultiSelectField(max_length=100,choices=LOCATIONS_CHOICES)
    start_date=models.DateField(max_length=100)
    gender=models.CharField(max_length=100)


import cv2
import mediapipe as mp
import numpy as np
import time

#ML solution provided by mediapipe for detecting face mesh
mp_face_mesh = mp.solutions.face_mesh

#need confidence score higher than 0.5 to detect face
#and mesh from frame to frame
face_mesh = mp_face_mesh.FaceMesh(min_detection_confidence=0.5,
                                  min_tracking_confidence=0.5)

mpFaceDetection = mp.solutions.face_detection
faceDetection = mpFaceDetection.FaceDetection(0.5)

#setting up thickness and circle radius of the mesh
#drawn on the detected face
mp_drawing = mp.solutions.drawing_utils
drwaing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)

# cap = cv2.VideoCapture("PilotData_DriverFatigue_GoPro.mp4")
cap = cv2.VideoCapture(0)

while cap.isOpened():
    success, image = cap.read()

    #to take time of how long the algorithm takes to work and to calculate
    #frames per second
    start = time.time()

    #the models we are paasing through mediapipe use RGB where openCV
    #reads in the images as RGB. Image is also flipped to maintain the
    #left and right head movement
    image = cv2.cvtColor(cv2.flip(image,1), cv2.COLOR_BGR2RGB)

    #we need the frames passed in the NN to be readable to improve performance
    image.flags.writeable = False

    #passing the frames to the face_mesh object created above, to get all the different landmarks in the face.
    #First it runs as a face detector. It then crops the face to input it to the NN.
    results = face_mesh.process(image)

    results_mark = faceDetection.process(image)
    bboxs = []

    #changing iamge to writable again to draw on the image
    image.flags.writeable = True

    #converting the image back to BGR
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

    img_h, img_w, img_c = image.shape
    face_3d = []
    face_2d = []

    #face detection
    if results_mark.detections:
	    for id, detection in enumerate(results_mark.detections):
	        bboxC = detection.location_data.relative_bounding_box
	        ih, iw, ic = image.shape
	        bbox = int(bboxC.xmin * iw), int(bboxC.ymin * ih), \
	               int(bboxC.width * iw), int(bboxC.height * ih)
	        cx, cy = bbox[0] + (bbox[2] // 2), \
	                 bbox[1] + (bbox[3] // 2)
	        bboxInfo = {"id": id, "bbox": bbox, "score": detection.score, "center": (cx, cy)}
	        bboxs.append(bboxInfo)
	        image = cv2.rectangle(image, bbox, (255, 0, 255), 2)

	        cv2.putText(image, f'{int(detection.score[0] * 100)}%',
	        	(bbox[0], bbox[1] - 20), cv2.FONT_HERSHEY_PLAIN,
	        	2, (255, 0, 255), 2)

    #pose detection
    if results.multi_face_landmarks:
        #if one person in the image, one result
        for face_landmark in results.multi_face_landmarks:

            #running through all the landmarks (nose, mouth ends, eye ends)
            for idx, lm in enumerate(face_landmark.landmark):

                if idx == 33 or idx == 263 or idx == 1 or idx == 61 or idx == 291 or idx == 199:
                    #separating out nose coordinates, used to draw a line from the nose as a guide to where
                    #the person is seeing
                    if idx ==1:
                        nose_2d = (lm.x * img_w, lm.y * img_h)
                        nose_3d = (lm.x * img_w, lm.y * img_h, lm.x * 3000)

                    #scaling the normalized x,y value
                    x,y = int(lm.x*img_w), int(lm.y*img_h)

                    face_2d.append([x,y])
                    face_3d.append([x, y, lm.z])

            face_2d = np.array(face_2d, dtype = np.float32)
            face_3d = np.array(face_3d, dtype = np.float32)

            #assumed intrinsic parameters of the camera
            focal_length = 1 * img_w
            cam_matrix = np.array([[focal_length, 0.0, img_h/2],
                                  [0.0, focal_length, img_w/2],
                                  [0.0, 0.0, 1.0]], dtype=np.float32)

            #distrotion parameters, camera assumed to be not distorted
            dist_matrix = np.zeros((4,1), dtype=np.float32)

            #Solving PnP:
            #rot_vec: how much is the point actually in the image
            #trans_vec: how much is the our point translated
            success, rot_vec, trans_vec = cv2.solvePnP(face_3d, face_2d, cam_matrix, dist_matrix)

            #Getting Rotational matrix from rot_vec
            rmat, jac = cv2.Rodrigues(rot_vec)

            #Getting angles
            angles, mtxR, mtxQ, Qx, Qy, Qz = cv2.RQDecomp3x3(rmat)

            #y rotation degrees to display on the image
            x = angles[0] * 360
            y = angles[1] * 360
            z = angles[2] * 360

            #for webcam: where the head is tilted
            if y<-10:
                text = "Looking left"
            elif y>10:
                text = "Looking right"
            elif x<-10:
                text = "Looking down"
            elif x>10:
                text = "Looking up"
            else:
                text = "Forward"

#             #for provided video: where the head is tilted
#             if y>0:
#                 text = "Looking left"
#             elif y<-8:
#                 text = "Looking right"
#             elif x<-10:
#                 text = "Looking down"
#             elif x>10:
#                 text = "Looking up"
#             else:
#                 text = "Forward"

            #to display the nose direction (where the face would be seeing)
            nose_3d_projection, jacobian = cv2.projectPoints(nose_3d, rot_vec, trans_vec, cam_matrix, dist_matrix)

            #nose tip coordinates
            p1 = (int(nose_2d[0]), int(nose_2d[1]))
            #scaled based how much is the face looking in either directions
            p2 = (int(nose_2d[0] +y*10), int(nose_2d[1] -x*10))

            cv2.line(image, p1, p2, (255,0,0) ,3)

            # Adding the text on the image
            cv2.putText(image, text, (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
            cv2.putText(image, "x: "+str(np.round(x,2)), (500, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
            cv2.putText(image, "y: "+str(np.round(y,2)), (500, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
            cv2.putText(image, "z: "+str(np.round(z,2)), (500, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)

        end = time.time()
        totalTime = end - start
        fps = 1/totalTime
        print("FPS: ", fps)
        cv2.putText(image, f'FPS: {int(fps)}', (20, 450), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

    cv2.imshow('Head Pose Estimation', image)

    if cv2.waitKey(5) & 0xFF == 27:
        break

cap.release()


from tkinter import *
import requests
from tkinter import messagebox
def clearAll() :
    fromField.delete(0, END)
    toField.delete(0, END)
    amountField.delete(0, END)
    finField.delete(0, END)
def checkError() :
    if (fromField.get() == "" or toField.get() == "" or amountField.get() == "") :
        messagebox.showerror("Input Error")
        clearAll()
        return
def prior():
    from_currency = (fromField.get())
    to_currency = (toField.get())
    amount = int(amountField.get())
    currency_conversion(from_currency,to_currency,amount)
def currency_conversion():
    from_currency = (fromField.get())
    to_currency = (toField.get())
    amount = int(amountField.get())
    response=requests.get(url)
    rate=response.json()['rates'][from_currency]
    amount_in_EUR=amount/rate
    amount=amount_in_EUR*(response.json()['rates'][to_currency])
    finField.insert(10, str(amount))
if __name__ == "__main__" :
    url = str.__add__('http://data.fixer.io/api/latest?access_key=', '7cdf410b0b856588f3c1f803302d1834')
    window = Tk()
    window.configure(background = "light green")
    window.title("Currency Convertor")
    window.geometry("525x300")
    dob = Label(window, text = "Enter Details", bg = "orange")
    from_label = Label(window, text = "from", bg = "turquoise")
    to_label = Label(window, text = "To", bg = "turquoise")
    amount_label = Label(window, text = "Amount", bg = "turquoise")
    convAmt = Label(window, text = "Converted Amount", bg = "turquoise")
    convert = Button(window, text = "Convert", fg = "white", bg = "black", command =currency_conversion)
    clearAllEntry = Button(window, text = "Clear All", fg = "white", bg = "black", command = clearAll)
    fromField= Entry(window)
    toField= Entry(window)
    amountField= Entry(window)
    finField=Entry(window)
    dob.grid(row = 0, column = 1)
    from_label.grid(row = 1, column = 0)
    fromField.grid(row = 1, column = 1)
    to_label.grid(row = 2, column = 0)
    toField.grid(row = 2, column = 1)
    amount_label.grid(row = 3, column = 0)
    amountField.grid(row = 3, column = 1)
    convert.grid(row = 4, column = 2)
    convAmt.grid(row = 9, column = 2)
    finField.grid(row = 10, column = 2)
    clearAllEntry.grid(row = 12, column = 2)
    window.mainloop()