import pandas as pd
import numpy as np
import math
import json

# list of relevant aa indeces as per the paper
aaindex_list = [pd.read_excel("AZAE970101.xlsx", index_col="Unnamed: 0"), pd.read_excel("BENS940104.xlsx", index_col="Unnamed: 0"), pd.read_excel("BETM990101.xlsx", index_col="Unnamed: 0"), pd.read_excel("BONM030103.xlsx", index_col="Unnamed: 0"), pd.read_excel("BONM030104.xlsx", index_col="Unnamed: 0"),
                pd.read_excel("KOLA920101.xlsx", index_col="Unnamed: 0"), pd.read_excel("LUTR910108.xlsx", index_col="Unnamed: 0"), pd.read_excel("MUET010101.xlsx", index_col="Unnamed: 0"), pd.read_excel("TANS760101.xlsx", index_col="Unnamed: 0"), pd.read_excel("ZHAC000106.xlsx", index_col="Unnamed: 0")]

# load positions that have been pre-determined using the logistic regression
pos = []
with open("positions.json", "r") as file:
    pos = json.load(file)

intervals = []
for position in pos:
    if len(intervals) == 0:
        intervals.append([position, 1])
    else:
        if intervals[-1:][0][0] == (position - 1):
            intervals[-1:][0][1] += 1
        else:
            intervals.append([position, 1])

# intervals in format
# (start_pos, length)

def encode(shape, serum_seq, antigen_seq):
    global aaindex_list
    # shape in
    # (number of amino acids, number of aaindex)

    global pos
    answer = np.zeros((len(pos), 10))

    i = 0
    for position in pos:
        if serum_seq[position] != antigen_seq[position]:
            try:
                for j in range(len(aaindex_list)):
                    df = aaindex_list[j]
                    letter_val = df.loc[serum_seq[position], antigen_seq[position]]
                    if math.isnan(letter_val):
                        letter_val = -(df.loc[antigen_seq[position], serum_seq[position]])
                    #print(answer.shape, i, j)
                    answer[i][j] = letter_val
            except:
                pass
        i = i + 1

    return answer

sequence_dict = {}
def construct_x(serum, sequences, distances, len_sequence):
    # takes in the serum name and all antigen sequences
    # also: the csv containing all antigenic distances,
    # start position, length of sequence,
    # important aaindex for use by aaindex_encode,
    # and the list of all important positions
    global aaindex_list
    global pos
    global sequence_dict
    num_pos = len(pos)

    df = pd.read_csv(distances, index_col = 0)
    x = np.zeros((df.shape[0], num_pos, len(aaindex_list)))
    # SHAPE: (num_antigens, 566, 10)
    # where 566 is the standardized length of all the aligned sequences
    # and 10 is the # of aaindeces being examined

    sep = "/"
    with open(sequences, "r") as seq_file:

        content = seq_file.read().split(">")
        serum_sequence = ""

        if len(sequence_dict.keys())==0:
            for line in content:
                entries = line.split("\n")
                try:
                    name = entries[0].split("/")[0]
                    sequence = "".join(entries[1:])
                    if name not in sequence_dict.keys():
                        sequence_dict[name] = sequence
                except Exception as e:
                    print(e)

        serum_sequence = sequence_dict[serum]

        i = 0
        keys_list = list(sequence_dict.keys())
        keys_list.sort()
        for key in keys_list:
            if key in df.index.values:
                try:
                    substring = np.zeros((len_sequence, 10))
                    substring = np.array(encode(substring.shape, serum_sequence, sequence_dict[key]))
                    x[i] = substring
                    i += 1

                except ValueError as e:
                    print(e)

    return x

def scale(vector):
    min = vector[0]
    max = vector[0]
    for val in vector:
        if val < min:
            min = val
        if val > max:
            max = val

    for i in range(len(vector)):
        vector[i] = (vector[i] - min) / (max - min)
    return vector

def construct_y(serum, distances):
    # y values are the distances between each antigen-serum pair

    df = pd.read_csv(distances)
    y = np.zeros(df.shape[0], )
    for antigen_index in range(y.shape[0]):
        #y_val = df.iloc[antigen_index].loc[serum]
        # if y_val > 4:
        #     y[antigen_index] = 1
        # else:
        #     y[antigen_index] = 0
        y[antigen_index] = df.iloc[antigen_index].loc[serum]
    return y


import os
import pathlib
import unittest

# We need to make this below so that the manager.py finds the config.ini
os.environ['GRID2VIZ_ROOT'] = os.path.join(
            pathlib.Path(__file__).parent.absolute(), 'data')

agents_path = os.path.join(
            pathlib.Path(__file__).parent.absolute(), 'data', 'agents')

config_str = f'[DEFAULT]\nagents_dir={agents_path}\nn_cores=2'
config_file_path = os.path.join(os.environ['GRID2VIZ_ROOT'], 'config.ini')

with open(config_file_path, 'w') as f:
    f.write(config_str)

from grid2op.Backend import PandaPowerBackend
from grid2op.Episode.EpisodeData import EpisodeData
from grid2op.Parameters import Parameters

from grid2viz.src.kpi.EpisodeAnalytics import EpisodeAnalytics
from grid2viz.src.manager import make_network

class TestGenerateAgent(unittest.TestCase):
    def setUp(self):
        self.case = "rte_case14_realistic"

        self.backend = PandaPowerBackend()
        self.param = Parameters()

        self.agents_path = agents_path
        self.agent_name = 'redispatching-baseline'
        self.scenario_name = '000'

    def test_plot(self):
        self.episode_data = EpisodeData.from_disk(
            os.path.join(self.agents_path, self.agent_name), self.scenario_name
        )
        self.episode_analytics = EpisodeAnalytics(
            self.episode_data, self.scenario_name, self.agent_name
        )
        self.episode_analytics.decorate(self.episode_data)

        make_network(self.episode_analytics).plot_obs(self.episode_analytics.observations[0])


#############################################################################
# compiler_wrapper.py
#
# Authors: Anurag
#
# Provides a wrapper interface for the compiler process
# This includes programming language detection
#############################################################################

# ocr_wrapper
# Author: Anurag
# Return value: string of data
# Parameters:
#	1. compiler_input - text input
#	2. language - programming language
# This function is a wrapper for the entire Compiler Process

import subprocess
import os
import re
import docker

def compiler_wrapper(compiler_input, language):
	# Jenkins testing
	#return None
	# End Jenkins testing
	container = None
	line_numbers = []
	if language == "C":
		dockerCommand = ["/bin/sh", "-c", ("printf '" + compiler_input + "' > compile.c && gcc compile.c && ./a.out")]
		client = docker.from_env()
		error_r = False
		try:
			container = client.containers.run("frolvlad/alpine-gcc", command=dockerCommand, privileged=True, mem_limit="50m", cpuset_cpus="0")
		except docker.errors.ContainerError as e:
			error_r = True
			out_text = str(e.stderr.decode())
		if not error_r:
			out_text = str(container.decode())
		out_text = out_text.replace("./compile_c.sh: 2: ./a.out: not found\n", "")
		matches = re.findall("compile\.c:[0-9]+:[0-9]+:", out_text)
		for match in matches:
			temp_m = match.replace("compile.c:", "")
			temp_m = temp_m.split(':')
			if int(temp_m[0]) not in line_numbers:
				line_numbers += [int(temp_m[0])]
		return out_text, line_numbers
	elif language == "C++":
		dockerCommand = ["/bin/sh", "-c", ("printf '" + compiler_input + "' > compile.cpp && g++ compile.cpp && ./a.out")]
		client = docker.from_env()
		error_r = False
		out_text = ""
		try:
			container = client.containers.run("frolvlad/alpine-gxx", command=dockerCommand, privileged=True, mem_limit="50m", cpuset_cpus="0")
		except docker.errors.ContainerError as e:
			error_r = True
			out_text = str(e.stderr.decode())
		if not error_r:
			out_text = str(container.decode())
		out_text = out_text.replace("./compile_c.sh: 2: ./a.out: not found\n", "")
		matches = re.findall("compile\.cpp:[0-9]+:[0-9]+:", out_text)
		for match in matches:
			temp_m = match.replace("compile.cpp:", "")
			temp_m = temp_m.split(':')
			if int(temp_m[0]) not in line_numbers:
				line_numbers += [int(temp_m[0])]
		return out_text, line_numbers
	elif language == "C#":
		dockerCommand = ["/bin/sh", "-c", ("printf '" + compiler_input + "' > compile.cs && mcs compile.cs && mono compile.exe")]
		client = docker.from_env()
		error_r = False
		out_text = ""
		try:
			#container = client.containers.run("frolvlad/alpine-mono", command=dockerCommand, privileged=True)
			container = client.containers.run("mono", command=dockerCommand, privileged=True, mem_limit="50m", cpuset_cpus="0")
		except docker.errors.ContainerError as e:
			error_r = True
			out_text = str(e.stderr.decode())
		if not error_r:
			out_text = str(container.decode())
			out_text = out_text.replace("Microsoft (R) Visual C# Compiler version 3.6.0-4.20224.5 (ec77c100)\nCopyright (C) Microsoft Corporation. All rights reserved.\n\n", "")
		matches = re.findall("compile\.cs\([0-9]+,[0-9]+\):", out_text)
		for match in matches:
			temp_m = match.replace("compile.cs(", "")
			temp_m = temp_m.split(',')
			if int(temp_m[0]) not in line_numbers:
				line_numbers += [int(temp_m[0])]
		return out_text, line_numbers
	elif language.upper() == "JAVA":
		temp = compiler_input
		classn = ""
		if len(re.findall("class .* {", temp)) > 1:
			# Multiple classes
			pass
		else:
			# Only 1 class
			classn = re.findall("class .*? {", temp)
			if classn == []:
				return ("Unable to locate class in program. Please review your code.", [1])
			else:
				classn = classn[0]
			classn = classn.split(" ")[1]
		dockerCommand = ["/bin/sh", "-c", ("printf '" + compiler_input + "' > compile.java && javac compile.java && java " + classn)]
		client = docker.from_env()
		error_r = False
		out_text = ""
		try:
			container = client.containers.run("openjdk:11", command=dockerCommand, privileged=True, mem_limit="50m", cpuset_cpus="0")
		except docker.errors.ContainerError as e:
			error_r = True
			out_text = str(e.stderr.decode())
		if not error_r:
			out_text = str(container.decode())
		matches = re.findall("compile\.java:[0-9]+:", out_text)
		for match in matches:
			temp_m = match.replace("compile.java:", "")
			temp_m = temp_m.split(':')
			if int(temp_m[0]) not in line_numbers:
				line_numbers += [int(temp_m[0])]
		return out_text, line_numbers
	else:
		"Language Not Supported"
	return None, None

def main():
	# Testing function for pipeline
	print(compiler_wrapper('#include<stdio.h>\n\nint main(int argc, char *argv[]) {\nprintf ("Hello World") ;\nreturn 0;\n\n}', "C"))
	#print(compiler_wrapper('#include<stdio.h>\n#include<stdlib.h>\nint main(int argc, char *argv[]) {\nchar * a = malloc(sizeof(char) * 52000000);\na[51999999]="\0";\nprintf("%s\n", a);\nreturn 0;\n\n}', "C"))
	#print(compiler_wrapper("#include <iostream>\nint main() {\nstd::cout << \"Hello World\";\nreturn 0;\n}", "C++"))
	#print(compiler_wrapper("namespace HelloWorld {\nclass Hello {\n static void Main(string[] args) {\nSystem.Console.WriteLine(\"Hello World\");\n}\n}\n}", "C#"))
	print(compiler_wrapper("class BC { public void e() { } } class HelloWorld {\npwublic static void main(String[] args) {\nSystem.out.println(\"Hello World\");\n}\n}", "Java"))

if __name__ == "__main__":
	main()


from django.db import models
from django.db.models.fields import DateTimeField
from django.contrib.auth.models import User # importando o models User do django
from datetime import datetime

# Create your models here.

class Evento(models.Model):
    titulo = models.CharField(max_length=100)
    descricao = models.TextField(blank=True, null=True)
    data_evento = models.DateTimeField(verbose_name='DATA DO EVENTO')
    data_criacao = models.DateTimeField(verbose_name='DATA DE CRIAÇÃO', auto_now=True)
    usuario = models.ForeignKey(User, on_delete=models.CASCADE) # chave estrangeira

    # Especificando o nome da tabela
    class Meta:
        db_table = 'evento'

    # Altera como será mostrado no Painel
    # def __str__(self):
    #     return self.titulo

    # Formatando a data
    def get_data_evento(self):
        return self.data_evento.strftime('%d/%m/%Y %H:%M')

    def get_data_input_evento(self):
        return self.data_evento.strftime('%Y-%m-%dT%H:%M')

    def get_data_evento_atrasado(self):
        if self.data_evento < datetime.now():
            return True
        else:
            return False


import pytest
import os

def main():
    testdir = str(os.getcwd() + '\\tests\\')
    print(testdir)

    args = [testdir,
            '--verbose',  # detailed view
            '--capture=sys',
            '-s',  # print stdout
            '-v']  # verbose
    pytest.main(args=args)

if __name__ == '__main__':
    main()


# -*- coding: utf-8 -*-
"""
Created on Sun Nov  3 17:11:42 2019

@author: jacqu

Parses graphs and counts nucleotides with problems for angles computation

Stores a dict of info for each graph

"""

import numpy as np
import pickle
import os
import networkx as nx
import sys
import argparse

if __name__ == "__main__":
    script_dir = os.path.dirname(os.path.realpath(__file__))
    sys.path.append(os.path.join(script_dir, '..'))

    from data_processing.graph_utils import *
    from data_processing.angles import base_angles
    from data_processing.rna_classes import *
    from data_processing.utils import *

    parser = argparse.ArgumentParser()

    parser.add_argument('-i', '--graphs_dir', help="path to directory containing 'rna_classes' nx graphs ",
                        type=str, default="C:/Users/jacqu/Documents/MegaSync Downloads/RNA_graphs")
    parser.add_argument('-c', "--cutoff", help="Max number of train samples. Set to -1 for all graphs in dir",
                        type=int, default=4)

    parser.add_argument('-o', '--write_dir', help="path to directory to write preprocessed graphs ",
                        type=str, default="../data/chunks")

    parser.add_argument('-d', "--debug", help="debug",
                        type=bool, default=True)

     # =======

    args=parser.parse_args()

    # Hyperparams
    gr_dir = args.graphs_dir
    annot_dir = args.write_dir

    angles = ['alpha', 'beta', 'gamma', 'chi', 'delta', 'gly_base']

    print(f'Calculating {len(angles)} angles for each nt.')
    print(f'Graphs with node features will be saved to {annot_dir}')

    cpt, bads =0,0

    parse_dict = {}

    for pdb_id in os.listdir(gr_dir):

        if(cpt<args.cutoff):
            cpt+=1
            print(f'Reading {pdb_id}')

            # Add key in parse dict to store information about this graph
            parse_dict[pdb_id]={}
            lonely_nts=[]
            weird_nt_type = 0
            no_prev = 0
            no_next=0
            no_prev_next = 0
            atoms_error = 0
            perfect = 0

            # Dict for new node attributes
            node_attrs = {}
            for a in angles:
                node_attrs[a]={} #dict of dicts to store angle values for each node

            nt_a, nt_u, nt_g, nt_c = {},{},{},{}

            # Load graph
            g = pickle.load(open(os.path.join(gr_dir,pdb_id), 'rb'))

            # 1/ Remove dangling nodes from graph

            nodes =g.nodes(data=True)
            N = g.number_of_nodes()
            parse_dict[pdb_id]['num_nodes_init']=N

            # Clean edges
            remove_self_edges(g) # Get rid of self edges (not sure its right?)
            g=nx.to_undirected(g)
            g= dangle_trim(g)
            N1 = g.number_of_nodes()
            if(N1==0):
                continue # empty graph, do not process and do not save

            # ================ Nucleotides parsing ===================
            for n, data in g.nodes(data=True):
                nucleotide = data['nucleotide']
                pdb_pos = int(nucleotide.pdb_pos)

                # Count context nodes
                nbr_neigh = len(g[n])

                if(nbr_neigh==0):
                    lonely_nts.append(n)
                else:

                    # Prev and next nucleotides
                    prev_nt = find_nucleotide(g,n[0], pdb_pos-1)
                    next_nt = find_nucleotide(g,n[0], pdb_pos+1)
                    if (prev_nt==None and next_nt==None):
                        no_prev_next +=1
                    elif prev_nt==None:
                        no_prev +=1
                    elif next_nt==None:
                        no_next +=1

                    # Nucleotide identity
                    n_type = nucleotide.nt
                    nt_a[n] = float(n_type=='A')
                    nt_u[n] = float(n_type=='U')
                    nt_g[n] = float(n_type=='G')
                    nt_c[n] = float(n_type=='C')
                    if(n_type not in {'A','U','G','C'}):
                        weird_nt_type +=1

                    # Angles :
                    angles = base_angles(nucleotide, prev_nt, next_nt)
                    nonzero = np.count_nonzero(angles)
                    if(nonzero<5): # one angle that doesnot require prev or next failed
                        atoms_error +=1
                    if(nonzero==8):
                        perfect +=1

                    # Store in node attributes dict
                    #TODO
                    #node_attrs['chi'][n]=angles[-2]

            # ==========================================================

            # add info to dict
            if args.debug:
                parse_dict[pdb_id]['lonely nts']=len(lonely_nts)
                parse_dict[pdb_id]['no prev']=no_prev
                parse_dict[pdb_id]['no next']=no_next
                parse_dict[pdb_id]['no prev/next']=no_prev_next
                parse_dict[pdb_id]['atoms pb']=atoms_error
                parse_dict[pdb_id]['weird_nt_type']=weird_nt_type
                parse_dict[pdb_id]['perfect']=perfect

                continue

            # ========= Create features and check all angles !=0 =============

            # Remove lonely nucleotides
            G = g.copy()
            G.remove_nodes_from(lonely_nts)

            # check all nodes have at least one neighbor:

            nbr_neigh = [len(G[n]) for n in G.nodes()]
            m = min(nbr_neigh)
            if(m==0): # Do not save this graph : one node is lonely .
                print('Lonely node(s). passing')
                bads+=1
                continue

            N1 = G.number_of_nodes()
            if(N1<=4): # Not enough nodes, do not process and do not save
                print('less than 4 nodes. passing')
                bads+=1
                continue # empty graph, do not process and do not save

            # Add node feature to all nodes
            for a in angles:
                assert(len(node_attrs[a]) == G.number_of_nodes())
                nx.set_node_attributes(G, node_attrs[a], a)
            # Nucleotide types
            nx.set_node_attributes(G, nt_a, 'A')
            nx.set_node_attributes(G, nt_u, 'U')
            nx.set_node_attributes(G, nt_g, 'G')
            nx.set_node_attributes(G, nt_c, 'C')

            # Save
            cpt+=1
            with open(os.path.join(annot_dir,pdb_id),'wb') as f:
                pickle.dump(G, f)

    # Save debug dict
    if(args.debug):
        print('writing pdb parse dict')
        with open('pdb_parsing_dict.pickle','wb') as f:
                pickle.dump(parse_dict, f)

    print(f'wrote {cpt} preprocessed graphs to {annot_dir}')
    print(f'removed {bads} too small graphs')


from twisted.internet import reactor, endpoints, protocol
from game.server.server import \
    Server, \
    MatchManager, \
    PublicProtocol, \
    WebSocketServer
from autobahn.twisted.websocket import WebSocketServerFactory
from game.server.match import Match

tcpPort = 1025
websocketPort = 1026
serverAddress = "192.168.2.20"

manager = MatchManager(Match)
endpoints.serverFromString(reactor, "tcp:{0}".format(tcpPort)).listen(Server(manager))

webSocketServer = WebSocketServer(u"ws://{0}:{1}".format(serverAddress, websocketPort), manager)
reactor.listenTCP(1026, webSocketServer)
print("Server listening on address {0} for websocket on port {1} and tcp on port {2}".format(serverAddress, tcpPort, websocketPort))
reactor.run()


from typing import List

def partition(arr:List[int], l:int, r:int) -> int:
    """_summary_

    Args:
        arr (List[int]): _description_
        l (int): _description_
        r (int): _description_

    Returns:
        int: _description_
    """
    left,right =l,r
    if l!=r and l < r:
        pivot = arr[l]
        left = left+1

        while left <= right:
            if arr[right] < pivot and arr[left] > pivot:
                arr[left], arr[right] = arr[right], arr[left]
            if not arr[left] > pivot:
                left+=1
            if not arr[right] < pivot:
                right-=1
    arr[l],arr[right] = arr[right], arr[l]
    return right

def quick_sort(arr:List[int], left:int ,right:int) -> None:
    if left < right:
        pi:int = partition(arr, left, right)
        quick_sort(arr, left, pi-1)
        quick_sort(arr,pi+1, right)

array: List[int] = [ 1, 7, 8, 9, 1, 2]
quick_sort(array, 0, len(array) - 1)
assert array[0] == 1
assert array[-1] == 9
#2.12 µs ± 4.6 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)


config = {
    "medicine": True,
    "medicine_name": "My_Medicine",
    "time_slots": [
        {
            "name": "Morning",
            "start": "07:00",
            "end": "08:00"
        },
        {
            "name": "Evening",
            "start": "18:00",
            "end": "22:00"
        }
    ],
    "reminders": True,
    "reminder_time": 600,
    "accel_min_change": 0.2,  # The min change in g on any axis to indicate that medicine is being taken
    "ignore_time": 30,        # Movement within this interval will not set another alert
    "data_send_delay": 1
}

import sys
import os.path
import time
from cbcommslib import CbApp, CbClient
from cbutils import betweenTimes, hourMin2Epoch, nicetime
from cbconfig import *
import json
from twisted.internet import reactor

CONFIG_FILE                       = CB_CONFIG_DIR + "medicine_monitor.config"
CID                               = "CID164"  # Client ID

class Medicine():
    def __init__(self, bridge_id, name):
        self.bridge_id = bridge_id
        self.name = name
        self.s = []
        self.waiting = False
        self.starting = 0
        self.lastValues = [0.0, 0.0, 0.0]
        self.lastTime = 0
        self.taken = False
        self.lastReminderTime = 0
        reactor.callLater(1, self.monitor)

    def setIDs(self, bridge_id, idToName):
        self.idToName = idToName
        self.bridge_id = bridge_id

    def onChange(self, timeStamp, values):
        #self.cbLog("debug", "onChange. values: " + str(values))
        try:
            if self.starting < 3:
                self.lastValues = values
                self.starting += 1
                return
            moved = False
            for v in range(0,3):
                if abs(values[v] - self.lastValues[v]) > config["accel_min_change"]:
                    moved = True
            if moved:
                if timeStamp - self.lastTime > config["ignore_time"]:
                    self.lastTime = timeStamp
                    self.taken = True
            self.lastValues = values
        except Exception as ex:
            self.cbLog("warning", "medicine onChange encountered problems. Exception: " + str(type(ex)) + str(ex.args))

    def monitor(self):
        # Called every 5 seconds
        #self.cbLog("debug", "monitor. taken: " + str(self.taken))
        try:
            if self.taken:
                self.taken = False
                inSlot = False
                for s in config["time_slots"]:
                    if betweenTimes(self.lastTime, s["start"], s["end"]):
                        inSlot = True
                        break
                joinedName = self.name.replace(" ", "_")
                if inSlot:
                    values = {
                        "name": self.bridge_id + "/" + joinedName + "/" + "in_slot",
                        "points": [[int(self.lastTime*1000), 1]]
                    }
                else:
                    values = {
                        "name": self.bridge_id + "/" + joinedName + "/" + "out_slot",
                        "points": [[int(self.lastTime*1000), 1]]
                    }
                self.storeValues(values)
        except Exception as ex:
            self.cbLog("warning", "medicine monitor encountered problems in taken. Exception: " + str(type(ex)) + str(ex.args))
        try:
            if config["reminders"]:
                now = time.time()
                if now - self.lastReminderTime > config["reminder_time"] + 10:
                    for s in config["time_slots"]:
                        if betweenTimes(now, s["start"], s["end"]):
                            if not betweenTimes(self.lastTime, s["start"], s["end"]):
                                epochEnd = hourMin2Epoch(s["end"])
                                if epochEnd - now < config["reminder_time"]:
                                    self.cbLog("debug", "monitor. start: " + str(s["start"]) + ", end: " + str(s["end"]))
                                    msg = {"m": "alert",
                                           "a": "Remember to take your " + self.name + " by " + nicetime(epochEnd)[:5],
                                           "t": now
                                          }
                                    self.client.send(msg)
                                    self.cbLog("debug", "msg send to client: " + str(json.dumps(msg, indent=4)))
                                    self.lastReminderTime = now
                                    break
        except Exception as ex:
            self.cbLog("warning", "medicine monitor encountered problems in reminders. Exception: " + str(type(ex)) + str(ex.args))
        reactor.callLater(5, self.monitor)

    def sendValues(self):
        msg = {"m": "data",
               "d": self.s
               }
        self.cbLog("debug", "sendValues. Sending: " + str(json.dumps(msg, indent=4)))
        self.client.send(msg)
        self.s = []
        self.waiting = False

    def storeValues(self, values):
        self.s.append(values)
        if not self.waiting:
            self.waiting = True
            reactor.callLater(config["data_send_delay"], self.sendValues)

class App(CbApp):
    def __init__(self, argv):
        self.appClass = "monitor"
        self.state = "stopped"
        self.status = "ok"
        self.devices = []
        self.idToName = {}
        self.medicine = {}
        self.gotSensor = []
        #CbApp.__init__ MUST be called
        CbApp.__init__(self, argv)

    def setState(self, action):
        if action == "clear_error":
            self.state = "running"
        else:
            self.state = action
        msg = {"id": self.id,
               "status": "state",
               "state": self.state}
        self.sendManagerMessage(msg)

    def onConcMessage(self, message):
        #self.cbLog("debug", "onConcMessage, message: " + str(json.dumps(message, indent=4)))
        if "status" in message:
            if message["status"] == "ready":
                # Do this after we have established communications with the concentrator
                msg = {
                    "m": "req_config",
                    "d": self.id
                }
                self.client.send(msg)
        self.client.receive(message)

    def onClientMessage(self, message):
        self.cbLog("debug", "onClientMessage, message: " + str(json.dumps(message, indent=4)))
        global config
        if "config" in message:
            if "warning" in message["config"]:
                self.cbLog("warning", "onClientMessage: " + str(json.dumps(message["config"], indent=4)))
            else:
                try:
                    newConfig = message["config"]
                    copyConfig = config.copy()
                    copyConfig.update(newConfig)
                    if copyConfig != config or not os.path.isfile(CONFIG_FILE):
                        self.cbLog("debug", "onClientMessage. Updating config from client message")
                        config = copyConfig.copy()
                        with open(CONFIG_FILE, 'w') as f:
                            json.dump(config, f)
                        self.cbLog("info", "Config updated")
                        self.readLocalConfig()
                        # With a new config, send init message to all connected adaptors
                        for i in self.adtInstances:
                            init = {
                                "id": self.id,
                                "appClass": self.appClass,
                                "request": "init"
                            }
                            self.sendMessage(init, i)
                except Exception as ex:
                    self.cbLog("warning", "onClientMessage, could not write to file. Type: " + str(type(ex)) + ", exception: " +  str(ex.args))

    def onAdaptorData(self, message):
        #self.cbLog("debug", "onAdaptorData, message: " + str(json.dumps(message, indent=4)))
        try:
            if message["characteristic"] == "acceleration":
                self.medicine[message["id"]].onChange(message["timeStamp"], [message["data"]["x"], message["data"]["y"], message["data"]["z"]])
        except Exception as ex:
            self.cbLog("warning", "onAdaptorData problem. Exception: " + str(type(ex)) + str(ex.args))

    def onAdaptorService(self, message):
        #self.cbLog("debug", "onAdaptorService, message: " + str(json.dumps(message, indent=4)))
        try:
            if self.state == "starting":
                self.setState("running")
            serviceReq = []
            for p in message["service"]:
                if p["characteristic"] == "acceleration":
                    if self.medicine[message["id"]]:
                        self.medicine[message["id"]].gotSensor = True
                    else:
                        self.gotSensor.append(message["id"])
                    serviceReq.append({"characteristic": "acceleration", "interval": 2})
            msg = {"id": self.id,
                   "request": "service",
                   "service": serviceReq}
            self.sendMessage(msg, message["id"])
            self.cbLog("debug", "onAdaptorService, response: " + str(json.dumps(msg, indent=4)))
        except Exception as ex:
            self.cbLog("warning", "onAdaptorServices problem. Exception: " + str(type(ex)) + str(ex.args))

    def readLocalConfig(self):
        global config
        try:
            with open(CONFIG_FILE, 'r') as f:
                newConfig = json.load(f)
                self.cbLog("debug", "Read local config")
                config.update(newConfig)
        except Exception as ex:
            self.cbLog("warning", "Local config does not exist or file is corrupt. Exception: " + str(type(ex)) + str(ex.args))
        self.cbLog("debug", "Config: " + str(json.dumps(config, indent=4)))

    def onConfigureMessage(self, managerConfig):
        self.readLocalConfig()
        idToName2 = {}
        for adaptor in managerConfig["adaptors"]:
            adtID = adaptor["id"]
            if adtID not in self.devices:
                # Because managerConfigure may be re-called if devices are added
                name = adaptor["name"]
                friendly_name = adaptor["friendly_name"]
                self.cbLog("debug", "managerConfigure app. Adaptor id: " +  adtID + " name: " + name + " friendly_name: " + friendly_name)
                idToName2[adtID] = friendly_name
                self.idToName[adtID] = friendly_name.replace(" ", "_")
                self.devices.append(adtID)
        self.client = CbClient(self.id, CID, 5)
        self.client.onClientMessage = self.onClientMessage
        self.client.sendMessage = self.sendMessage
        self.client.cbLog = self.cbLog
        self.client.loadSaved()
        for a in idToName2:
            self.medicine[a] = Medicine(self.bridge_id, idToName2[a])
            self.medicine[a].cbLog = self.cbLog
            self.medicine[a].client = self.client
            self.medicine[a].setIDs(self.bridge_id, self.idToName)
            if a in self.gotSensor:  # Caters for adaptors responding before the manager
                self.medicine[a].gotSensor = True
                self.gotSensor.remove(a)

        self.setState("starting")

if __name__ == '__main__':
    App(sys.argv)


import requests

from bs4 import BeautifulSoup

def print_hyperlink(act, link):
    r = requests.get(link)
    if r.status_code != 200:
        return print("connection failed")
    soup = BeautifulSoup(r.content, 'html.parser')
    hrefs = soup.find_all('a')
    print(hrefs[act-1].get("href"))

print_hyperlink(int(input()), input())


#
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F
from torch.utils.data import DataLoader, ConcatDataset
from torch.utils.tensorboard import SummaryWriter
from torch.optim.lr_scheduler import StepLR, CosineAnnealingLR, ExponentialLR

#
from torchvision import transforms

#
from torch_ema import ExponentialMovingAverage

#
import numpy as np

#
from sklearn.model_selection import train_test_split

#
from ptflops import get_model_complexity_info

#
from tqdm import tqdm

#
import argparse

#
import os
import shutil

#
import models
from data.dataset import OrchidDataSet
from config import DefualtConfig
from utils import get_confidence_score
from utils import mixup_data, mixup_criterion
from utils import rand_bbox
from utils.self_supervised import get_pseudo_labels
from optim.scheduler import GradualWarmupScheduler

###################################################################################

config = DefualtConfig()
device = torch.device(f'cuda:{config.use_gpu_index}' if torch.cuda.is_available() else'cpu') if config.use_gpu_index != -1 else torch.device('cpu')

def main(logdir):

    # Step 1 : prepare logging writer
    writer = SummaryWriter(log_dir=logdir)

    # Step 2 :
    print(config.model_name)
    model = getattr(models, config.model_name)(config)
    if config.load_model:
        model.load_state_dict(torch.load(config.model_path))
    model.to(device)

    #
    # Metrics : FLOPs, Params
    # resize = (224, 224) if config.model_name != 'ConvNeXt' else (384, 384)
    resize = (config.resize, config.resize)
    macs, params = get_model_complexity_info(model, (3, resize[0], resize[1]), as_strings=True, print_per_layer_stat=False, verbose=False)
    print('pthflops : ')
    print('{:<30}  {:<8}'.format('Computational complexity: ', macs))
    print('{:<30}  {:<8}'.format('Number of parameters: ', params))

    # Step 3 : DataSets}
    # Data Augumentation
    transform_set = [
        transforms.RandomResizedCrop((resize[0])),
        # transforms.ColorJitter(brightness=0.5),
        transforms.RandomHorizontalFlip(p=0.5),
        transforms.RandomRotation(degrees=15),
        # transforms.RandAugment()
        # transforms.AutoAugment(transforms.AutoAugmentPolicy.IMAGENET)
    ]

    transform_set = transforms.Compose([

        # # Reorder transform randomly
        transforms.RandomOrder(transform_set),

        # Resize the image into a fixed shape
        transforms.Resize(resize),

        # ToTensor() should be the last one of the transforms.
        transforms.ToTensor(),
        # transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),

        #
        transforms.RandomErasing()
    ])
    ds = OrchidDataSet(config.trainset_path, transform_set=transform_set)
    ds_unlabeled = None
    if config.do_semi:
        ds_unlabeled = OrchidDataSet(config.unlabeledset_path, transform_set=transform_set)

    # Step 3
    # Deal with imbalance dataset
    #   For the classification task, we use cross-entropy as the measurement of performance.
    #   Since the wafer dataset is serverly imbalance, we add class weight to make it classifier better
    class_weights = [1 - (ds.targets.count(c))/len(ds) for c in range(config.num_classes)]
    class_weights = torch.FloatTensor(class_weights).to(device)
    criterion = nn.CrossEntropyLoss(weight=class_weights, label_smoothing=0.1)
    # criterion = LabelSmoothingCrossEntropy()

    optimizer = torch.optim.AdamW(model.parameters(), lr=config.lr, weight_decay=1e-8)
    # optimizer = torch.optim.Adam(model.parameters(), lr=config.lr)
    # optimizer = torch.optim.SGD(model.parameters(), lr=config.lr, momentum=0.9)

    ema = ExponentialMovingAverage(model.parameters(), decay=0.995)
    if config.load_model:
        ema = ema.load_state_dict(torch.load(config.ema_path))

    # scheduler_warmup is chained with schduler_steplr
    # scheduler_steplr = StepLR(optimizer, step_size=10, gamma=0.1)
    scheduler_steplr = CosineAnnealingLR(optimizer, T_max=config.cosine_tmax)
    # scheduler_steplr = CosineAnnealingLR(optimizer, T_max=config.num_epochs - config.lr_warmup_epoch + 1)
    # scheduler_steplr = ExponentialLR(optimizer, gamma=0.9)
    # if config.lr_warmup_epoch > 0:
    scheduler_warmup = GradualWarmupScheduler(optimizer, multiplier=1, total_epoch=config.lr_warmup_epoch, after_scheduler=scheduler_steplr)

    # Step 4
    # train_loader, valid_loader = get_loader(ds)
    ds_train, ds_valid = get_train_valid_ds(ds)

    # Step 5
    history = {'train_acc' : [], 'train_loss' : [], 'valid_acc' : [], 'valid_loss' : []}
    best_epoch, best_epoch_ema, best_loss, best_acc_ema, best_acc = 0, 0, 1e100, 0, 0
    nonImprove_epochs = 0

    # this zero gradient update is needed to avoid a warning message, issue #8.
    optimizer.zero_grad()
    optimizer.step()

    #
    # assert not(config.do_cutMix and config.do_MixUp), "Only support one of the mix-based augmentation"

    for epoch in range(config.start_epoch, config.start_epoch + config.num_epochs):

        print('=' * 150)

        #
        # if config.lr_warmup_epoch > 0:
        scheduler_warmup.step(epoch + 1)

        writer.add_scalar("lr", optimizer.param_groups[0]["lr"], epoch)
        print(f'Epoch {epoch}, LR = {optimizer.param_groups[0]["lr"]}')

        #
        collator = torch.utils.data.dataloader.default_collate
        # if config.do_cutMix:
        #     collator = CutMixCollator(config.cutMix_alpha)

        train_loader = DataLoader(ds_train, batch_size=config.batch_size, shuffle=True, collate_fn=collator, num_workers=config.num_workers, pin_memory=True)
        # if epoch == 35:
        #     torch.save(model.state_dict(), f'{config.model_path[:-4]}_normal.pth')
        #     torch.save(ema.state_dict(), f'{config.ema_path[:-4]}_normal.pth')

        if epoch >= config.semi_start_epoch and config.do_semi:
            # Obtain pseudo-labels for unlabeled data using trained model.
            print(f"[ Train | Start pseudo labeling]")
            pseudo_set = get_pseudo_labels(model, ds_unlabeled)

            if pseudo_set != None:
                # Construct a new dataset and a data loader for training.
                # This is used in semi-supervised learning only.
                concat_dataset = ConcatDataset([ds_train, pseudo_set])
                train_loader = DataLoader(concat_dataset, batch_size=config.batch_size, shuffle=True, collate_fn=collator, num_workers=config.num_workers, pin_memory=True)

        valid_loader = DataLoader(ds_valid, batch_size=config.batch_size, shuffle=False, num_workers=config.num_workers, pin_memory=True)

        #
        train_criterion = criterion
        # if config.do_cutMix:
        #     train_criterion = CutMixCriterion(reduction='mean', label_smoothing=0.1)

        train_acc, train_loss = train(model, train_loader, train_criterion, optimizer, ema)
        print(f"[ Train | {epoch + 1:03d}/{config.num_epochs:03d} ] loss = {train_loss:.5f}, acc = {train_acc:.5f}")

        #
        valid_acc, valid_loss = valid(model, valid_loader, criterion, None)
        print(f"[ Valid | {epoch + 1:03d}/{config.num_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f}")

        #
        valid_acc_ema, valid_loss_ema = valid(model, valid_loader, criterion, ema)
        print(f"[ Valid | {epoch + 1:03d}/{config.num_epochs:03d} ] loss = {valid_loss_ema:.5f}, acc = {valid_acc_ema:.5f} (EMA)")

        # Append the training statstics into history
        history['train_acc'].append(train_acc)
        history['valid_acc'].append(valid_acc)
        history['train_loss'].append(train_loss)
        history['valid_loss'].append(valid_loss)

        # Tensorboard Visualization
        writer.add_scalar("Train/train_acc", train_acc, epoch)
        writer.add_scalar("Valid/valid_acc", valid_acc, epoch)
        writer.add_scalar("Valid/valid_acc_ema", valid_acc_ema, epoch)
        writer.add_scalar("Train/train_loss", train_loss, epoch)
        writer.add_scalar("Valid/valid_loss", valid_loss, epoch)
        writer.add_scalar("Valid/valid_loss_ema", valid_loss_ema, epoch)

        #
        if valid_acc > best_acc:
            best_acc = valid_acc
            torch.save(model.state_dict(), f'{logdir}/{config.model_path}')
            torch.save(ema.state_dict(), f'{logdir}/{config.ema_path}')
            get_confidence_score(model, loader=valid_loader, use_gpu_index=config.use_gpu_index, batch_size=config.batch_size, outpu_file_path=f'{logdir}/prediction-Confidence-best.csv')
            print(f'Saving model with acc {valid_acc:.4f} and loss {valid_loss:.4f}')

        # EarlyStop
        # if the model improves, save a checkpoint at this epoch
        if valid_acc_ema > best_acc_ema:
            best_loss = valid_loss_ema
            best_acc_ema = valid_acc_ema
            best_epoch = epoch
            torch.save(model.state_dict(), f'{logdir}/ema_{config.model_path}')
            torch.save(ema.state_dict(), f'{logdir}/ema_{config.ema_path}')
            get_confidence_score(model, loader=valid_loader, use_gpu_index=config.use_gpu_index, batch_size=config.batch_size, outpu_file_path=f'{logdir}/prediction-Confidence-best-ema.csv')
            print(f'Saving model with acc {valid_acc_ema:.4f} and loss {valid_loss_ema:.4f} (EMA)')
            nonImprove_epochs = 0
        else:
            nonImprove_epochs += 1

        # Stop training if your model stops improving for "config['early_stop']" epochs.
        if nonImprove_epochs >= config.earlyStop_interval:
            break

    torch.save(model.state_dict(), f'{logdir}/last_{config.model_path}')
    torch.save(ema.state_dict(), f'{logdir}/last_{config.ema_path}')
    print(f'Best epoch: {best_epoch} with acc {best_acc:.4f}')
    print(f'Best epoch: {best_epoch_ema} with acc {best_acc_ema:.4f} (EMA)')

    writer.flush()
    writer.close()

    # Step 6 : Explanation & Visualization
    get_confidence_score(model, loader=valid_loader, use_gpu_index=config.use_gpu_index, batch_size=config.batch_size, outpu_file_path=f'{logdir}/last-prediction-Confidence.csv')

###################################################################################

def get_train_valid_ds(ds):

    # Split the train/test with each class should appear on both train/test dataset
    valid_split = config.train_valid_split

    indices = list(range(len(ds)))  # indices of the dataset
    train_indices, valid_indices = train_test_split(indices, test_size=valid_split, stratify=ds.targets, random_state=42)

    # Creating sub dataset from valid indices
    # Do not shuffle valid dataset, let the image in order
    valid_indices.sort()
    ds_valid = torch.utils.data.Subset(ds, valid_indices)

    ds_train = torch.utils.data.Subset(ds, train_indices)

    return ds_train, ds_valid

def get_loader(ds):

    # Split the train/test with each class should appear on both train/test dataset
    valid_split = config.train_valid_split

    indices = list(range(len(ds)))  # indices of the dataset
    train_indices, valid_indices = train_test_split(
        indices, test_size=valid_split, stratify=ds.targets)

    # Creating sub dataset from valid indices
    # Do not shuffle valid dataset, let the image in order
    valid_indices.sort()
    ds_valid = torch.utils.data.Subset(ds, valid_indices)

    # Creating PT data samplers and loaders:
    train_sampler = torch.utils.data.SubsetRandomSampler(train_indices)

    # Construct data loaders.
    train_loader = DataLoader(
        ds, batch_size=config.batch_size, sampler=train_sampler, num_workers=config.num_workers, pin_memory=True)
    valid_loader = DataLoader(
        ds_valid, batch_size=config.batch_size, shuffle=False, num_workers=config.num_workers, pin_memory=True)

    return train_loader, valid_loader

def train(model, train_loader, criterion, optimizer, ema):

    # ---------- Training ----------
    # Make sure the model is in train mode before training.
    model.train()

    losses, accs = [], []

    # Iterate the training set by batches.
    for batch in tqdm(train_loader):

        # A batch consists of image data and corresponding labels.
        # imgs = (batch_size, 3, 224, 224)
        # labels = (batch_size)
        imgs, labels = batch
        imgs = imgs.to(device)

        target_a, target_b, lam = None, None, None
        # do_mix = False

        r = np.random.rand(1)
        do_mix = True if r < config.mix_prob else False

        r_mix_method = np.random.rand(1)
        mix_method = 'cutmix'
        if config.do_cutMix and config.do_MixUp:
            mix_method = 'cutmix' if r_mix_method < 0.5 else 'mixup'
        elif config.do_cutMix:
            mix_method = 'cutmix'
        elif config.do_MixUp:
            mix_method = 'mixup'

        if config.do_cutMix and mix_method == 'cutmix':
            if config.beta > 0 and do_mix:
                # generate mixed sample
                do_mix = True
                lam = np.random.beta(config.beta, config.beta)
                rand_index = torch.randperm(imgs.size()[0]).cuda()
                target_a = labels
                target_b = labels[rand_index]
                bbx1, bby1, bbx2, bby2 = rand_bbox(imgs.size(), lam)
                imgs[:, :, bbx1:bbx2, bby1:bby2] = imgs[rand_index, :, bbx1:bbx2, bby1:bby2]
                # adjust lambda to exactly match pixel ratio
                lam = 1 - ((bbx2 - bbx1) * (bby2 - bby1) / (imgs.size()[-1] * imgs.size()[-2]))
        else:
            labels = labels.to(device)

        if config.do_MixUp and do_mix and mix_method == 'mixup':
            labels = labels.to(device)
            imgs, targets_a, targets_b, lam = mixup_data(imgs, labels, alpha=0.2, use_cuda=torch.cuda.is_available())
            imgs, targets_a, targets_b = map(Variable, (imgs, targets_a, targets_b))

        # Forward the data. (Make sure data and model are on the same device.)
        logits = model(imgs.to(device))

        # Calculate the cross-entropy loss.
        # We don't need to apply softmax before computing cross-entropy as it is done automatically.
        # loss = criterion(logits, labels.to(device))

        loss = None
        if (config.do_cutMix or config.do_MixUp) and do_mix:
            if config.do_MixUp and mix_method == 'mixup':
                loss = mixup_criterion(criterion, logits, targets_a, targets_b, lam)
            elif config.do_cutMix and mix_method == 'cutmix':
                # loss = criterion(logits, labels)
                target_a = target_a.to(device)
                target_b = target_b.to(device)
                # lam = lam.to(device)
                loss = criterion(logits, target_a) * lam + criterion(logits, target_b) * (1. - lam)
        else:
            loss = criterion(logits, labels.to(device))

        # Gradients stored in the parameters in the previous step should be cleared out first.
        optimizer.zero_grad()

        # Compute the gradients for parameters.
        loss.backward()

        # # STN : Allow transformes to do like translation, cropping, isotropic scaling but rotation
        # #           , with a intention to let STN learns where to focus on instead of how to transform the image.
        # # Below is what matrix should look like :
        # #    [ x_ratio, 0 ] [offset_X]
        # #    [ 0, y_ratio ] [offset_y]
        # if config.model_name == "STN_ViT":
        #     model.fc_loc[-1].weight.grad[1].zero_()
        #     model.fc_loc[-1].weight.grad[3].zero_()

        # Update the parameters with computed gradients.
        optimizer.step()
        ema.update()

        # # Clip the gradient norms for stable training.
        # grad_norm = nn.utils.clip_grad_norm_(model.parameters(), max_norm=10)

        # Compute the accuracy for current batch.
        # acc = torch.tensor([0])
        # if not config.do_cutMix:
        acc = (logits.argmax(dim=-1) == labels.to(device)).float().mean()
        accs.append(acc.item())
        losses.append(loss.item())

    return np.mean(accs), np.mean(losses)

def valid(model, valid_loader, criterion, ema=None):
    # ---------- Validation ----------
    # Make sure the model is in eval mode so that some modules like dropout are disabled and work normally.
    model.eval()

    accs, losses = [], []

    if ema is not None:

        with ema.average_parameters():

            # Iterate the validation set by batches.
            for batch in tqdm(valid_loader):

                # A batch consists of image data and corresponding labels.
                imgs, labels = batch

                # We don't need gradient in validation.
                # Using torch.no_grad() accelerates the forward process.
                with torch.no_grad():
                    logits = model(imgs.to(device))

                # We can still compute the loss (but not the gradient).
                losses.append(criterion(logits, labels.to(device)).item())

                # Compute the accuracy for current batch.
                accs.append((logits.argmax(dim=-1) == labels.to(device)).float().mean().item())

    else:

        # Iterate the validation set by batches.
        for batch in tqdm(valid_loader):

            # A batch consists of image data and corresponding labels.
            imgs, labels = batch

            # We don't need gradient in validation.
            # Using torch.no_grad() accelerates the forward process.
            with torch.no_grad():
                logits = model(imgs.to(device))

            # We can still compute the loss (but not the gradient).
            losses.append(criterion(logits, labels.to(device)).item())

            # Compute the accuracy for current batch.
            accs.append((logits.argmax(dim=-1) == labels.to(device)).float().mean().item())

    return np.mean(accs), np.mean(losses)

###################################################################################

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='AICUP - Orchid Classifier')

    # parser.add_argument('--lr', default=2e-5, type=float,
    #                     help='Base learning rate')
    # parser.add_argument('--bs', default=32, type=int, help='Batch size')
    # parser.add_argument('--e', default=50, type=int, help='Numbers of epoch')
    # parser.add_argument('--v', default=50, type=int, help='Experiment version')
    # parser.add_argument('--device', default=-1, type=int,
    #                     help='GPU index, -1 for cpu')
    parser.add_argument('--logdir', default='model', type=str, required=True,
                                help='The folder to store the training stats of current model')

    args = parser.parse_args()

    #
    assert not os.path.isdir(os.path.join(os.getcwd(), args.logdir)), "Already has a folder with the same name"
    os.mkdir(args.logdir)

    shutil.copy('./config.py', f'{args.logdir}/config.py')

    main(args.logdir)

###################################################################################