import socket
from config import *
import threading
import sys
import sqlite3

class Server:
    def __init__(self):
        self.sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.addr=(ip,port)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.sock.bind(self.addr)
        self.conn=sqlite3.connect("./database/database.db", check_same_thread=False,timeout=100)
        self.chat_details = {}
        self.sock.listen()
        self.run()

    def send_message(self,message,client_sock):
        message=message+sep
        try:
            client_sock.send(bytes(message+'0'*(chunk_size-len(message)),encoding="utf-8"))

        except Exception as E:
            print("Exception in sending message")
            print(E)
            # self.sock.close()
            # self.conn.close()
            sys.exit(0)

    def receive_message(self,client_sock):
        # print(type(client_sock))
        try:
            return client_sock.recv(chunk_size).decode().split(sep)

        except Exception as E:
            print("Exception in recieving message")
            print(E)
            # self.sock.close()
            # self.conn.close()
            sys.exit(0)

    def run(self):
        try:
            while(True):
                # try:
                client_sock,client_addr=self.sock.accept()
                client_sock = client_sock
                threading.Thread(target=self.connect,args=(client_sock,)).start()
                # except socket.error:
                #     self.run()
                #     print("Got socket error")

        except Exception as e:
            print("An Exception occured")
            print(e)
            sys.exit(1)
        finally:
            self.sock.close()

    def connect(self,client_sock):
        reg_msg="Choose [1]Register or [2]Login\n Type the corresponding no."+sep
        client_sock.send(bytes(reg_msg+'0'*(chunk_size-len(reg_msg)),encoding="utf-8"))
        msg_rcvd=client_sock.recv(chunk_size).decode()
        msg_rcvd=int(msg_rcvd.split(sep)[0])
        self.next_steps(client_sock,msg_rcvd)
        # return msg_rcvd

    def next_steps(self,client_sock,msg_rcvd):
        registration= 0
        login_status = 0
        if(msg_rcvd == 1):
            while(True):
                if(self.register(client_sock)):
                    registration = 1
                    break

        if(registration == 1 or msg_rcvd==2):
            while(True):
                values = self.login(client_sock)
                if(values[0]):
                    login_status =1
                    break
        print("User Logged IN")

        self.functions(values[1],values[2],client_sock)
        self.sock.close()
        return

    def register(self,client_sock):
        val = (client_sock.recv(chunk_size).decode().split(sep))
        # print(val)
        usr,passwrd,name,typ,_= val
        try:
            cursor=self.conn.cursor()
            tmp=(usr,passwrd,name,int(typ),)
            cursor.execute('select * from user_password where user_name== ?',(usr,))
            res=cursor.fetchall()
            # print(res)
            if(len(res)!=0):
                self.send_message("not ok",client_sock)

                return False
            # print(tmp)
            cursor.execute('insert into user_password("user_name","passwrd","name","type") values(?,?,?,?)',tmp)
            self.conn.commit()
            self.send_message("ok",client_sock)
            cursor.close()
            return True
        except Exception as e:
            self.sock.close()
            print("exception in registering")
            print(e)
            sys.exit(0)

    def login(self,client_sock):
        try:
            usr,passwrd,_=client_sock.recv(chunk_size).decode().split(sep)
            cursor=self.conn.cursor()
            tmp=(usr,passwrd,)
            cursor.execute('select * from user_password where user_password.user_name==? and user_password.passwrd==?',tmp)
            res=cursor.fetchone()
            # print(res)
            if(len(res)==0):
                self.send_message("not ok",client_sock)
                return (False,1,usr)
            cursor.close()
            self.send_message("ok",client_sock)
            self.send_message(str(res[4]),client_sock)
            if(res[4]==1):

                return (True,1,usr)
            else:
                return (True,0,usr)
        except Exception as e:
            print("exception in login")
            print(e)
            sys.exit(0)

    def functions(self,check,user_name,client_sock):

        if(check==1):
            self.home_teacher(user_name,client_sock)
        else:
            self.home_student(user_name,client_sock)

    def view_post(self,client_sock,username,course_code):
        self.send_message("do you want to filter by keywords",client_sock)
        filter = int(self.receive_message(client_sock)[0])
        if(filter):
            self.send_message("please write keyword",client_sock)
            keyword = self.receive_message(client_sock)[0]
            try:
                cursor=self.conn.cursor()
                tmp=(course_code,keyword)
                cursor.execute('select * from posts where course_code == ? and keyword == ?',tmp)
                res=cursor.fetchall()
                string = "Course_Code   Post  Keyword TimePosted\n"
                for i in res:
                    string+= str(i[1])+"    " +i[2]+"   "+i[3]+"    "+i[4]+"\n"
                self.send_message(string,client_sock)
            except Exception as e:
                self.send_message("Failed to show posts",client_sock)
                print(e)
                cursor.close()
        else:
            try:
                cursor=self.conn.cursor()
                tmp=(course_code,)
                cursor.execute('select * from posts where course_code == ?',tmp)
                res=cursor.fetchall()
                string = "Course_Code   Post  Keyword TimePosted\n"
                for i in res:
                    string+= str(i[1])+"    " +i[2]+"   "+i[3]+"    "+i[4]+"\n"
                self.send_message(string,client_sock)
            except Exception as e:
                self.send_message("Failed to show posts",client_sock)
                print(e)
                cursor.close()

    def add_post(self,client_sock,user_name,course_code):

        self.send_message("please type post to add to classroom",client_sock)

        temp= self.receive_message(client_sock)
        post_details = temp[0]
        if(len(temp)>2):
            keyword = temp[1]
        else:
            keyword = None
        cursor=self.conn.cursor()
        try:
            tmp=(user_name,course_code,post_details,keyword,)
            cursor.execute('insert into posts("user_name","course_code","post_details","keyword", "add_time") values(?,?,?,?,datetime(\'now\', \'localtime\'))',tmp)
            self.conn.commit()
            self.send_message("ok",client_sock)

        except Exception as e:

            self.send_message("Failed to add posts",client_sock)
            print("Exception")
            print(e)

    def broadcast(self,message,client_sock,course_code,user_name):
        print("ALOINDAWOINEFAEORFNAERNFO")

        try:
            for i in self.chat_details[course_code]["clients"]:
                if(i!= client_sock):
                    self.send_message(message,i)
            self.chat_details[course_code]["messages"].append(user_name+"---->"+message)
        except Exception as e:
            print(e)
            print("ALOINDAWOINEFAEORFNAERNFO")

    def chat_session(self,user_name,course_code,client_sock):

        if course_code not in self.chat_details:
            self.chat_details[course_code] = {"clients":[],"messages":[]}

        self.chat_details[course_code]["clients"].append(client_sock)
        # print('asdasdasdasdasd')
        while(True):

            message = self.receive_message(client_sock)[0]
            # print('asdasdasdasdasd')
            print(message)
            if(message == "exit"):
                self.broadcast("i am closing chat session",client_sock,course_code,user_name)
                self.chat_details.pop(course_code)
                break
            elif(message == "showall"):
                final = ""
                message = self.chat_details[course_code]["messages"]
                for i in message:
                    final+=i+"\n"
                # print(final)
                self.send_message(final,client_sock)
            else:
                self.broadcast(message,client_sock,course_code,user_name)
        self.home_teacher(user_name,client_sock)

    def show_posts_and_courses(self,client_sock,user_name):

        self.send_message("Please enter Course Code to view course details",client_sock)
        course_code = int(self.receive_message(client_sock)[0])
        self.send_message("""please enter 1 to add post and 2 to view posts and 3 to start a chat session and 4 create GD and 5 for viewing GD
        and 6 for private_convo""",client_sock)
        value  = int(self.receive_message(client_sock)[0])
        if(value == 1):
            self.add_post(client_sock,user_name,course_code)
        elif(value ==2 ):
            self.view_post(client_sock,user_name,course_code)
        elif(value ==3):
            # print(3,'asdasdass')
            self.chat_session(user_name,course_code,client_sock)
        elif value==4:
            self.create_gd(course_code,client_sock,user_name)
        elif value==5:
            self.view_gd_teacher(course_code,client_sock,user_name)
        elif value==6:
            self.teacher_private_conv(user_name,client_sock,course_code)
        self.home_teacher(user_name,client_sock)
        return

    def teacher_private_conv(self,user_name,client_sock,course_code):
        cursor=self.conn.cursor()
        sql='select distinct student_name from private_conv where teacher_name==? and course_code==?'
        try:
            cursor.execute(sql,(user_name,course_code,))
            res=cursor.fetchall()
            if(len(res)!=0):
                string="choose private_conv with student from "
                for i in res:
                    string+=i[0]+" "
                string+="\n"
                self.send_message("ok",client_sock)
                self.send_message(string,client_sock)
            else:
                self.send_message("nok",client_sock)
                self.send_message("No student conversation",client_sock)
                self.home_teacher(user_name,client_sock)
                return
        except Exception as e:
            print(e)
            self.send_message("nok",client_sock)
            self.send_message("Not",client_sock)
            self.home_teacher(user_name,client_sock)
            return

        student_name=self.receive_message(client_sock)[0]
        self.send_message("Please select 1 to view message and 2 to send message",client_sock)
        choose=self.receive_message(client_sock)[0]
        # print(teacher_name,type(teacher_name))
        if choose=='1':
            try:
                cursor.execute("select * from private_conv where student_name==? and teacher_name == ? and course_code==?",(student_name,user_name,course_code,))
                res=cursor.fetchall()
                string="Message  time\n"
                for i in res:
                    string+=i[2]+" "+i[3]+"\n"
                self.send_message(string,client_sock)
            except Exception as e:
                self.send_message("No convo",client_sock)
                print(e)

        else:
            """insert name of sender"""
            try:
                msg=self.receive_message(client_sock)[0]
                msg=f"{user_name}--->"+msg
                cursor.execute("insert into private_conv values(?,?,?,datetime(\'now\', \'localtime\'),?)",(student_name,user_name,msg,course_code,))
                self.conn.commit()
                self.send_message("ok",client_sock)
            except Exception as e:
                self.send_message("No",client_sock)
                print(e)
        cursor.close()
        self.home_teacher(user_name,client_sock)

    def create_gd(self,course_code,client_sock,user_name):
        sql='insert into group_discussion("topic","course_code","user_name","message","post_time") values(?,?,?,?,datetime(\'now\', \'localtime\'))'
        temp= self.receive_message(client_sock)
        topic=temp[0]
        cursor=self.conn.cursor()
        try:
            cursor.execute("select * from group_discussion where course_code==? and topic==? and user_name==?",(course_code,topic,user_name,))
            res=cursor.fetchall()
            if len(res)>0:
                self.send_message("not ok",client_sock)
                self.send_message("Already available",client_sock)
                self.home_teacher(user_name,client_sock)
                return
            cursor.execute(sql,(topic,course_code,user_name,"Created GD",))
            self.conn.commit()
            self.send_message("ok",client_sock)
            self.send_message("Created GD",client_sock)
        except Exception as E:
            print(E)
            self.send_message("not ok",client_sock)
            self.send_message("Not created GD",client_sock)
        self.home_teacher(user_name,client_sock)

    def view_gd_teacher(self,course_code,client_sock,user_name):
        self.send_message("Please input topic",client_sock)
        topic=self.receive_message(client_sock)[0]
        cursor=self.conn.cursor()
        try:
            cursor.execute("select * from group_discussion where course_code==? and topic==? and user_name==?",(course_code,topic,user_name,))
            res=cursor.fetchall()
            if len(res)<=0:
                self.send_message("not",client_sock)
                self.home_teacher(user_name,client_sock)
                return
        except Exception as e:
            print(e)
            self.send_message("not",client_sock)
            self.home_teacher(user_name,client_sock)
            return

        self.send_message("ok",client_sock)
        self.send_message("input 1 for viewing messages in gd and 2 for adding messages to gd",client_sock)
        v=int(self.receive_message(client_sock)[0])
        sql1="select user_name,message,post_time from group_discussion where course_code == ? and topic == ?"
        sql2='insert into group_discussion("topic","course_code","user_name","message","post_time") values(?,?,?,?,datetime(\'now\', \'localtime\'))'
        if v==1:
            try:
                cursor.execute(sql1,(course_code,topic,))
                res=cursor.fetchall()
                string = "user_name  message  post_time\n"
                for i in res:
                    string+=i[0]+"  "+i[1]+"  "+i[2]+"\n"
                self.send_message(string,client_sock)

            except Exception as E:
                print(E)
                self.send_message("Not able to fetch",client_sock)

        elif v==2:
            self.send_message("please type message to add to gd",client_sock)
            temp= self.receive_message(client_sock)
            message = temp[0]
            try:
                cursor.execute(sql2,(topic,course_code,user_name,message,))
                self.conn.commit()
                self.send_message("ok",client_sock)
            except Exception as E:
                print(E)
                self.send_message("not ok",client_sock)

        cursor.close()
        self.home_teacher(user_name,client_sock)
        return

    def home_teacher(self,user_name,client_sock):
        self.send_message(f"Hi {user_name}\nPress 1 for showing existing courses\nPress 2 for creating new course \n press 3 for viewing course",client_sock)
        instruction=self.receive_message(client_sock)[0]
        print(instruction)
        if instruction=="1":
            self.view_courses_teacher(user_name,client_sock)
        elif instruction=="2":
            self.create_course(user_name,client_sock)
        elif instruction == "3":
            self.show_posts_and_courses(client_sock,user_name)
        else:
            self.home_teacher(user_name,client_sock)

    def view_courses_teacher(self,user_name,client_sock):

        try:
            cursor=self.conn.cursor()
            tmp=(user_name,)
            cursor.execute('select * from courses where courses.user_name== ?',tmp)
            res=cursor.fetchall()
            string = "Course_Code   Course_Name\n"

            for i in res:
                string+= str(i[0])+" " +i[1]+"\n"
            self.send_message(string,client_sock)

        except Exception as e:
            self.send_message("Failed to show courses",client_sock)
            print(e)
        cursor.close()
        self.home_teacher(user_name,client_sock)

    def create_course(self,user_name,client_sock):

        # message = "Please input course code and course name to create course "
        # self.send_message(message,client_sock)
        course_code,course_name,_ = self.receive_message(client_sock)
        cursor=self.conn.cursor()
        try:
            tmp=(user_name,course_code,course_name,)
            cursor.execute('insert into courses("user_name","course_code","course_name") values(?,?,?)',tmp)
            self.conn.commit()
            self.send_message("ok",client_sock)
        except Exception as e:
            self.send_message("Failed to create course",client_sock)
            print("Exception")
            print(e)
            # self.sock.close()
            # sys.exit(0)

        cursor.close()
        self.home_teacher(user_name,client_sock)

    def home_student(self,user_name,client_sock):

        self.send_message("""Hi, welcome to the classroom\nHere you can view and enroll for courses \n
        input 1 for showing existing courses \n
        input 2 for enrollig new course \n
        input 3  for viewing updates for registered courses\n
        input 4  for viewing details of a registered course\n
        """,client_sock)
        message = self.receive_message(client_sock)[0]
        message=int(message)
        if message==1:
            self.view_courses_student(user_name,client_sock)
        elif message==2:
            self.enroll_course(user_name,client_sock)
        elif message==3:
            self.view_updates(user_name,client_sock)
        elif message==4:
            self.check_course_reg(user_name,client_sock)
        else:
            self.home_student(user_name,client_sock)

    def check_course_reg(self,user_name,client_sock):
        self.send_message("please input the registered course's code",client_sock)
        course_code=self.receive_message(client_sock)[0]
        cursor=self.conn.cursor()
        sql1="select course_name from student_courses where user_name==? and course_code==?"
        try:
            cursor.execute(sql1,(user_name,course_code,))
            res=cursor.fetchone()
            if len(res):
                self.send_message("ok",client_sock)
                self.send_message(f"Welcome to Course {res[0]} with course_code {course_code}",client_sock)
                self.all_details(user_name,client_sock,course_code)
            else:
                self.send_message("not ok",client_sock)
                self.send_message("Input correct code",client_sock)
                self.home_student(user_name,client_sock)

        except Exception as e:
            print(e)
        cursor.close()
        self.home_student(user_name,client_sock)

    def all_details(self,user_name,client_sock,course_code):

        self.send_message("please select 1 for seeing posts and 2 for GD and 3 for chat session and  4 for private conversation with teacher",client_sock)
        value = self.receive_message(client_sock)[0]
        if(int(value)==1):
            sql1="select keyword,post_details,add_time from posts where  course_code == ?  order by add_time desc"
            # conn=se
            try:
                cursor=self.conn.cursor()
                cursor.execute(sql1,(course_code,))
                res=cursor.fetchall()
                string ="keyword  post_details  add_time\n"
                for i in res:
                    # print(i)
                    string+="  ".join([j for j in i]) +"\n"
                self.send_message(string,client_sock)

            except Exception as e:
                self.send_message("Not able to see post")
                print(e)

        elif int(value)==2:
            self.view_gd(course_code,client_sock,user_name)
        elif int(value)==3:
            self.chat_session_student(user_name,course_code,client_sock)
        elif int(value)==4:
            self.private_conv(user_name,client_sock,course_code)
        cursor.close()
        self.home_student(user_name,client_sock)

    def private_conv(self,user_name,client_sock,course_code):
        cursor=self.conn.cursor()
        try:
            cursor.execute("select user_name from courses where course_code==?",(course_code,))
            res=cursor.fetchone()
            teacher_name=res[0]
        except:
            self.send_message("Not able to fetch teacher's name",client_sock)
            self.home_student(user_name,client_sock)
            return
        self.send_message("Please select 1 to view message and 2 to send message",client_sock)
        choose=self.receive_message(client_sock)[0]
        # print(teacher_name,type(teacher_name))
        if choose=='1':
            try:
                cursor.execute("select * from private_conv where student_name==? and teacher_name == ? and course_code==?",(user_name,teacher_name,course_code,))
                res=cursor.fetchall()
                string="Message  time\n"
                for i in res:
                    string+=i[2]+" "+i[3]+"\n"
                self.send_message(string,client_sock)
            except Exception as e:
                self.send_message("No convo",client_sock)
                print(e)

        else:
            """insert name of sender"""
            try:
                msg=self.receive_message(client_sock)[0]
                msg=f"{user_name}--->"+msg
                cursor.execute("insert into private_conv values(?,?,?,datetime(\'now\', \'localtime\'),?)",(user_name,teacher_name,msg,course_code,))
                self.conn.commit()
                self.send_message("ok",client_sock)
            except Exception as e:
                self.send_message("No",client_sock)
                print(e)
        cursor.close()
        self.home_student(user_name,client_sock)

    def view_gd(self,course_code,client_sock,user_name):
        self.send_message("Please input topic",client_sock)
        topic=self.receive_message(client_sock)[0]
        cursor=self.conn.cursor()
        try:
            cursor.execute("select * from group_discussion where course_code==? and topic==?",(course_code,topic,))
            res=cursor.fetchall()
            if len(res)<=0:
                self.send_message("not",client_sock)
                self.home_student(user_name,client_sock)
                return
        except Exception as e:
            print(e)
            self.send_message("not",client_sock)
            self.home_student(user_name,client_sock)
            return

        self.send_message("ok",client_sock)
        self.send_message("input 1 for viewing messages in gd and 2 for adding messages to gd",client_sock)
        v=int(self.receive_message(client_sock)[0])
        sql1="select user_name,message,post_time from group_discussion where course_code == ? and topic == ?"
        sql2='insert into group_discussion("topic","course_code","user_name","message","post_time") values(?,?,?,?,datetime(\'now\', \'localtime\'))'
        if v==1:
            try:
                cursor.execute(sql1,(course_code,topic,))
                res=cursor.fetchall()
                string = "user_name  message  post_time\n"
                for i in res:
                    string+=i[0]+"  "+i[1]+"  "+i[2]+"\n"
                self.send_message(string,client_sock)

            except Exception as E:
                print(E)
                self.send_message("Not able to fetch",client_sock)

        elif v==2:
            self.send_message("please type message to add to gd",client_sock)
            temp= self.receive_message(client_sock)
            message = temp[0]
            try:
                cursor.execute(sql2,(topic,course_code,user_name,message,))
                self.conn.commit()
                self.send_message("ok",client_sock)
            except Exception as E:
                print(E)
                self.send_message("not ok",client_sock)

        cursor.close()
        self.home_student(user_name,client_sock)
        return

    def chat_session_student(self,user_name,course_code,client_sock):
        course_code=int(course_code)
        print("ahbdjsfkn")

        if course_code not in self.chat_details:
            self.send_message("nok",client_sock)
            self.send_message("No active chat session",client_sock)
        else:
            self.send_message("ok",client_sock)
            self.send_message("inside chat session",client_sock)
            self.chat_details[course_code]["clients"].append(client_sock)
            self.broadcast("i joined the chat session",client_sock,course_code,user_name)
            while(True):
                print('1')
                message = self.receive_message(client_sock)[0]

                if(message == "exit"):
                    self.broadcast("i am leaving the session",client_sock,course_code,user_name)
                    self.chat_details[course_code]["clients"].remove(client_sock)
                elif(message == "showall"):
                    final = ""
                    message = self.chat_details[course_code]["messages"]
                    for i in message:
                        final+=i+"\n"
                    self.send_message(final,client_sock)
                else:
                    self.broadcast(message,client_sock,course_code,user_name)
        self.home_student(user_name,client_sock)

    def view_updates(self,user_name,client_sock):

        sql="select * from posts where (select course_code from student_courses where user_name==?) order by add_time desc limit 10"
        cursor=self.conn.cursor()
        try:
            tmp=(user_name,)
            cursor.execute(sql,tmp)
            res=cursor.fetchall()
            print(res)
            string="course_code post_details keyword add_time \n"
            for i in res:
                string+= str(i[1])+" "+i[2]+i[3]+i[4]+"\n"
            self.send_message(string,client_sock)
        except Exception as e:
            print(e)
            self.send_message("Failed to show courses",client_sock)
        cursor.close()
        self.home_student(user_name,client_sock)

    def view_courses_student(self, user_name,client_sock):
        try:
            cursor=self.conn.cursor()
            tmp=(user_name,)
            cursor.execute('select * from student_courses where user_name== ?',tmp)
            res=cursor.fetchall()
            string="course_code course_name\n"
            for i in res:
                string+= str(i[1])+" "+i[2]+"\n"
            self.send_message(string,client_sock)
        except:
            self.send_message("Failed to show courses",client_sock)
        cursor.close()
        self.home_student(user_name,client_sock)

    def enroll_course(self,user_name,client_sock):
        self.send_message("please input course code to register",client_sock)
        course_code = int(self.receive_message(client_sock)[0])

        cursor=self.conn.cursor()
        tmp=(course_code,)
        cursor.execute('select * from courses where course_code== ?',tmp)

        course_details=cursor.fetchall()

        if len(course_details)==0:
            self.send_message("not ok",client_sock)
            self.send_message("No Course Available",client_sock)
            return self.home_student(user_name,client_sock)

        teacher = course_details[0][2]
        course_name = course_details[0][1]
        course_code= course_details[0][0]
        try:
            tmp=(user_name,course_code,course_name,)
            cursor.execute('insert into student_courses("user_name","course_code","course_name") values(?,?,?)',tmp)
            self.conn.commit()
            self.send_message("ok",client_sock)
            self.send_message("succefully enrolled to the course",client_sock)
        except Exception as e :
            print(e)
            self.send_message("not ok",client_sock)
            self.send_message("Failed to enroll to the courses",client_sock)
        cursor.close()
        self.home_student(user_name,client_sock)

        # cursor=self.conn.cursor()
        # try:
        #     tmp=(username)
        #     cursor.executemany(f'select * from student_courses where user_name== ?',tmp)
        #     res=cursor.fetchall()
        #     self.send_message("".join(res),client_sock)
        # except:
        #     self.send_message("Failed to show courses",client_sock)

        # pass

    # def back_to_home_student(self,username):
    #     pass
    # def back_to_home_teacher(self,username):
    #     pass

    # def add_post(self,username,post,course_code):

    #     pass

    # def view_post_student(self,username,course_code):
    #     pass

    # def create_assignment(self, user_name,course_code):

    #     pass

        # self.functions(values[1],values[2])

    # def connect(self,client_sock):
        # if(first):
        #     msg_rcvd=self.cons_msg(client_sock)

        # if(msg_rcvd==1):
        #     if self.register(client_sock):
        #         check  = self.login(client_sock)
        #         if(check[0]==True):
        #             self.functions(client_sock,check[1],check[2])
        #         else:
        #             self.connect(client_sock,first=0)
        #     else:
        #         self.connect(client_sock,first=0)
        # elif msg_rcvd==2:
        #     if(self.login(client_sock)):
        #         self.functions(client_sock)
        #     else:
        #         self.connect(client_sock,first=0,msg_rcvd=2)
        # return

    # def add_course(self,client_sock):
    #     post,username =client_sock.recv(chunk_size).decode().split(sep)

    # def add_post(self,client_sock):
    #     post,username =client_sock.recv(chunk_size).decode().split(sep)

    #     try:
    #         cursor=self.conn.cursor()

    #     except :
    #         pass
    #     pass

if __name__ == "__main__":
    s=Server()


# 이항분류는 뭐야? 시그모이드
# diabetes 데이터로 이항분류(sigmoid)와 다항분류(softmax)처리

from keras.models import Sequential
from keras.layers import Dense
import numpy as np

dataset=np.loadtxt("../testdata/diabetes.csv", delimiter=',')
# print(dataset[:1])
# print(dataset.shape) #(759, 9)
# print(dataset[:,-1])  #label 값 확인해보니, 0,1로 되어있다. 손을 댈 필요가 읎어요

from sklearn.model_selection import train_test_split
# 0~8열, 9열
x_train, x_test, y_train, y_test=train_test_split(dataset[:,0:8], dataset[:,-1], test_size=0.3, random_state=123)
print(x_train.shape, x_test.shape, y_train.shape, y_test.shape) #(531, 8) (228, 8) (531,) (228,)

#===============================================================================
# # 이항분류 sigmoid / binary_crossentropy / 이항분류는 label을 0~1 사이의 값
#===============================================================================
model=Sequential()
model.add(Dense(units=64, input_dim=8, activation='relu'))
model.add(Dense(units=32, activation='relu'))
model.add(Dense(units=1, activation='sigmoid'))

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 이항분류일 때 loss는 binary_crossentropy를 쓴다.
model.fit(x_train, y_train, epochs=100, batch_size=32, validation_split=0.2, verbose=0)
scores=model.evaluate(x_test, y_test) #마지막값만 볼까?
print('%s : %.2f'%(model.metrics_names[0], scores[0]))
print('%s : %.2f'%(model.metrics_names[1], scores[1]))

# 예측값 구하기
# print(x_train[0])
new_data=[[-0.0588235,0.20603,0.,0.,0.,-0.105812,-0.910333,-0.433333]]
pred=model.predict(new_data, batch_size=32, verbose=0)
print('sigmoid 예측결과: ',pred)
print('sigmoid 예측결과: ',np.where(pred>0.5,1,0))
# [[0.4277862]] 0.5를 기준으로 0과1을 분류

#===============================================================================
# # 다항분류 #softmax /categorical_crossentropy'
# 레이블의 개수만큼 확률값으로 나온다.
# 다항분류일떄는 반드시 레이블에 대해 onehot처리를 해줘야한다.
#===============================================================================
# 다항분류 softmax
print(y_train[:3]) # onehot처리가 필요
from keras.utils import to_categorical #sklearn 에 onehot 사용가능

y_train=to_categorical(y_train)
y_test=to_categorical(y_test)
print(y_train[:3])

model2=Sequential()
model2.add(Dense(units=64, input_dim=8, activation='relu'))
model2.add(Dense(units=32, activation='relu'))
model2.add(Dense(units=2, activation='softmax'))
# label의 category 수 만큼 결과는 확률 값으로 출력된다

model2.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 이항분류일 때 loss는 binary_crossentropy를 쓴다.
model2.fit(x_train, y_train, epochs=100, batch_size=32, validation_split=0.2, verbose=0)
scores2=model2.evaluate(x_test, y_test) #마지막값만 볼까?
print('%s : %.2f'%(model2.metrics_names[0], scores2[0]))
print('%s : %.2f'%(model2.metrics_names[1], scores2[1]))

new_data2=[[-0.0588235,0.20603,0.,0.,0.,-0.105812,-0.910333,-0.433333]]
pred2=model2.predict(new_data2, batch_size=32, verbose=0)
print('softmax 예측결과: ',pred2)
#softmax 예측결과:  [[0.6369634  0.36303654]]
# 확률값이 가장 큰 지점의 index를 분류 결과로 취한다.
print('softmax 예측결과: ',np.argmax(pred2))


import torch
import json

def tokensByDevice(device, token, isBase, debug=False, showTokens=False):
    if debug and showTokens:
        print(
            f'{"Tokenising prompt" if isBase else "Inferencing"} using ({device})... Tokens:',
            token,
        )
    elif debug:
        print(f'{"Tokenising prompt" if isBase else "Inferencing"} using ({device})...')
    if device == "cuda":
        if isBase:
            result = torch.as_tensor([token]).cuda()
        else:
            result = torch.as_tensor([[token]], device="cuda")
    elif device == "cpu-gptq":
        if isBase:
            result = token
        else:
            result = torch.as_tensor([[token]])
    elif device == "cpu":
        if isBase:
            result = torch.as_tensor([token])
        else:
            result = torch.as_tensor([[token]])
    if debug:
        print("tokenised result", result)
    return result

class bcolors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKCYAN = '\033[96m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'

bc = bcolors.OKGREEN
tc = bcolors.WARNING
th = bcolors.OKBLUE
vs = "│"

def printDictAsTable(dict, title="Table output:", headers = ["Key", "Value"]):
    table = ""
    maxKeyWidth = max(len(str(key)) for key in dict.keys())
    maxValueWidth = max(len(str(value)) for value in dict.values())
    if maxValueWidth < 8:
        maxValueWidth = 8
    tableHeaders = bc + vs + " " + th +  "   ".join(cell.ljust(maxKeyWidth if cell == headers[0] else maxValueWidth) for cell in headers) + bc + vs + "\n"
    for key, value in dict.items():
        table += bc + vs + " " + tc + f" {vs} ".join(str(cell).ljust(maxKeyWidth if cell == key else maxValueWidth) for cell in [key, value]) + bc + vs + "\n"
    tableWidth = maxKeyWidth + maxValueWidth + 4
    border = ""
    spacer = ""
    for i in range(tableWidth):
        spacer += " "
        border += "─"
    table = bc + "┌" + border + "┐\n" + vs + tc + (" " + title).ljust(tableWidth) + bc + vs + "\n" + vs + spacer + vs + "\n" + tableHeaders + table + "└" + border + "┘" + bcolors.ENDC
    print(table)

def processConvo(convo, humanRole, aiRole):
    try:
        convHistoryList = json.loads(convo)
    except json.JSONDecodeError:
        return "Invalid conversationHistory JSON"

    if not isinstance(convHistoryList, list):
        return 'conversationHistory JSON structure must be an array of arrays, eg. [["Human","How do magnets work?"],["AI","They just do"]]'

    convHistoryPyList = []

    for item in convHistoryList:
        if not isinstance(item, list) or len(item) != 2:
            return 'Each conversationHistory subarray must contain exactly 2 values - role and message, eg. [["Human","How do magnets work?"],["AI","They just do"]]'

        role, message = item
        if role not in ('Human', 'AI'):
            return "conversationHistory roles must only be either 'Human' or 'AI'."

        role = humanRole if role == 'Human' else aiRole
        convHistoryPyList.append((role, message))

    return convHistoryPyList

# def logToCsv():
#     try:
#         now = datetime.now()
#         today = now.strftime("%Y-%m-%d")
#         with open(f'conversations-{today}.csv', 'a', newline='', encoding='utf-8-sig') as file:
#             writer = csv.writer(file)
#             writer.writerow([user,'Human',inputString])
#             writer.writerow([user,'Assistant',conversation.messages[-1][-1]])
#         pass
#     except Exception as e:
#         print(f"Error occurred in CSV: {e}")


import os
from PIL import Image
from typing import List

# There are 10,000 punks
punks_count: range = range(10_000)

# Dimensions of one punk is 24x24
punks_sprite_width: int = 24
punks_sprite_height: int = 24

# Dimensions of spritesheet is 100x100
punks_spritesheet_width: range = range(100)
punks_spritesheet_height: range = range(100)

# Load the punk image as an object
punks_spritesheet: Image = Image.open("punks.png")

# Individual punk sprites as images
punks_sprites: List = list(punks_count)

# Iterate over sprites and populate `punks_sprites`
for x in punks_spritesheet_width:
    for y in punks_spritesheet_height:
        # Exact pixel coordinates for the specific sprite
        left: int = x * punks_sprite_width
        upper: int = y * punks_sprite_height
        right: int = left + punks_sprite_width
        lower: int = upper + punks_sprite_height

        # Calculate the current index of the spritesheet
        index = (x * len(punks_spritesheet_width)) + y

        # Populate the sprite by cropping at the pixels from above
        punks_sprites[index] = punks_spritesheet.crop((left, upper, right, lower))

# Create the output directory
if not os.path.exists("punks"):
    os.makedirs("punks")

# Save punks to PNG files
for sprite_index in range(len(punks_sprites)):
    try:
        outpath = f"punks/punk{sprite_index}.png"
        punks_sprites[sprite_index].save(outpath, "PNG")
        print(f"Saved Punk #{sprite_index} to {outpath}")
    except AttributeError:
        print(f"Error at punk index {sprite_index}.")
        exit(0)


import heapq
from typing import List

# 다시 풀어보기

class Solution:
    def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:
        graph = {}
        for time in times:
            if time[0] not in graph:
                graph[time[0]] = {}
            graph[time[0]][time[1]] = time[2]

        nodes = {x: float('inf') for x in range(1, n + 1)}
        nodes[k] = 0  # Start Node is k

        queue = []
        heapq.heapify(queue)
        heapq.heappush(queue, [k, nodes[k]])  # [destination, distance]

        while queue:
            current_destination, current_distance = heapq.heappop(queue)

            if nodes[current_destination] < current_distance:  # 이미 최적 경로를 발견한 경우 진행할 필요 없음
                continue

            if current_destination in graph:
                for new_destination, new_distance in graph[current_destination].items():
                    distance = current_distance + new_distance
                    if distance < nodes[new_destination]:  # 기존에 알고있던 거리보다 짧은 경우에 수정 작업 진행
                        nodes[new_destination] = distance
                        heapq.heappush(queue, [new_destination, distance])

        max_distance = 0
        for item in nodes:
            if nodes[item] == float('inf'):
                return -1
            max_distance = max(max_distance, nodes[item])

        return max_distance

if __name__ == "__main__":
    result_ = []
    times = [[2, 1, 1], [2, 3, 1], [3, 4, 1]]
    n = 4
    k = 2
    result = 2
    answer = Solution().networkDelayTime(times, n, k)
    print(f'answer : {answer} | result {result}')

    times = [[1, 2, 1]]
    n = 2
    k = 1
    result = 1
    answer = Solution().networkDelayTime(times, n, k)
    print(f'answer : {answer} | result {result}')

    times = [[1, 2, 1]]
    n = 2
    k = 2
    result = -1
    answer = Solution().networkDelayTime(times, n, k)
    print(f'answer : {answer} | result {result}')


from typing import List

from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session

from . import crud, models, schemas
from .database import SessionLocal, engine

models.Base.metadata.create_all(bind=engine)

app = FastAPI()

# Dependency
def get_db():
    try:
        db = SessionLocal()
        yield db
    finally:
        db.close()

users_micro = [
{
"referral_link": "test1",
"credits": 10,
"administrator": 1,
"referraled_id": None,
"id": 1
},
{
"referral_link": "test2",
"credits": 20,
"administrator": 0,
"referraled_id": 1,
"id": 2
},
{
"referral_link": "test3",
"credits": 0,
"administrator": 0,
"referraled_id": None,
"id": 42
},
{
"referral_link": "test4",
"credits": 20,
"administrator": 0,
"referraled_id": None,
"id": 43
},
{
"referral_link": "test5",
"credits": 20,
"administrator": 0,
"referraled_id": None,
"id": 44
}
]

def get_user(user_id, db):
    db_user = crud.get_user(db=db, user_id=user_id)
    if db_user is None:
        db_user = [x for x in users_micro if x['id'] == user_id][0]
        print(db_user)
        if db_user is None:
            raise HTTPException(status_code=404, detail="User not found")
        crud.create_user(db=db, user_id=db_user['id'], administrator=db_user['administrator'],
                         referraled_id=db_user['referraled_id'])
    db_user = crud.get_user(db=db, user_id=user_id)
    return db_user

@app.get("/plans")
def read_plans(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    plans = crud.get_plans(db, skip=skip, limit=limit)
    return plans

@app.post("/plans", response_model=schemas.Plan)
def create_plan(plan: schemas.PlanBase, db: Session = Depends(get_db)):
    db_plan = crud.get_plan_by_name(db, plan_name=plan.name)
    if db_plan:
        raise HTTPException(status_code=400, detail="Plan already exists")
    return crud.create_plan(db=db, plan=plan)

@app.get("/plans/name/{plan_name}", response_model=schemas.Plan)
def get_plan_by_name(plan_name: str, db: Session = Depends(get_db)):
    db_plan = crud.get_plan_by_name(db, plan_name=plan_name)
    if db_plan is None:
        raise HTTPException(status_code=400, detail="Plan doesn't exists")
    return db_plan

@app.get("/plans/id/{plan_id}", response_model=schemas.Plan)
def get_plan_by_name(plan_id: int, db: Session = Depends(get_db)):
    db_plan = crud.get_plan_by_id(db, plan_id=plan_id)
    if db_plan is None:
        raise HTTPException(status_code=400, detail="Plan doesn't exists")
    return db_plan

@app.get("/users/", response_model=List[schemas.User])
def get_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    users = crud.get_users(db, skip=skip, limit=limit)
    return users

@app.get("/users/{user_id}", response_model=schemas.User)
def get_user(user_id: int, db: Session = Depends(get_db)):
    db_user = crud.get_user(db, user_id=user_id)
    if db_user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return db_user

@app.get("/vouchers", response_model=List[schemas.Voucher])
def get_vouchers(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    vouchers = crud.get_vouchers(db, skip=skip, limit=limit)
    return vouchers

@app.post("/vouchers", response_model=schemas.Voucher)
def create_voucher(voucher: schemas.VoucherBase, db: Session = Depends(get_db)):
    return crud.create_voucher(db=db, voucher=voucher)

@app.post("/vouchers/{num_codes}", response_model=List[schemas.Voucher])
def create_vouchers(num_codes: int, voucher: schemas.VoucherBase, db: Session = Depends(get_db)):
    vouchers = []
    for i in range(num_codes):
        vouchers.append(crud.create_voucher(db=db, voucher=voucher))
    return vouchers

@app.post("/{user_id}/{voucher_code}")
def add_credits_to_user(user_id: int, voucher_code: str, db: Session = Depends(get_db)):
    db_voucher = crud.verify_voucher(db=db, voucher_code=voucher_code)
    if db_voucher is None:
        raise HTTPException(status_code=404, detail="Voucher not found")
    if db_voucher.is_used == 1:
        raise HTTPException(status_code=404, detail="Voucher is already used")
    db_user = get_user(user_id, db)
    crud.add_credits_to_user(db=db, user_id=user_id, credits=db_voucher.amount)
    crud.used_voucher(db=db, voucher_code=voucher_code, user_id=user_id)
    return {"name": "successful"}

@app.get("/referral_link/{user_id}")
def get_referral(user_id: int, db: Session = Depends(get_db)):
    db_user = get_user(user_id, db)
    return db_user.referral_link

@app.post("/promo_codes", response_model=schemas.PromoCode)
def create_promo_code(promo_code: schemas.PromoCodeBase, db: Session = Depends(get_db)):
    return crud.create_promo_code(db=db, promo_code=promo_code)

@app.post("/promo_codes/{nums}", response_model=List[schemas.PromoCode])
def create_promo_codes(nums: int, promo_code: schemas.PromoCodeBase, db: Session = Depends(get_db)):
    promo_codes = []
    print(nums)
    print(10)
    for i in range(nums):
        promo_codes.append(crud.create_promo_code(db=db, promo_code=promo_code))
    return promo_codes

@app.post("/promo_codes10", response_model=List[schemas.PromoCode])
def create_promo_codes(promo_code: schemas.PromoCodeBase, db: Session = Depends(get_db)):
    promo_codes = []
    # print(num)
    for i in range(10):
        promo_codes.append(crud.create_promo_code(db=db, promo_code=promo_code))
    return promo_codes

@app.get("/promo_codes", response_model=List[schemas.PromoCode])
def get_promo_codes(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    return crud.get_promo_codes(db=db, skip=skip, limit=limit)

@app.get("/promo_codes/id/{promo_code_id}", response_model=schemas.PromoCode)
def get_promo_code_by_id(promo_code_id: int, db: Session = Depends(get_db)):
    db_promo_code = crud.get_promo_code_by_id(db=db, promo_code_id=promo_code_id)
    if db_promo_code is None:
        raise HTTPException(status_code=400, detail="Promocode doesn't exists")
    return db_promo_code

@app.get("/promo_codes/name/{promo_code_code}", response_model=schemas.PromoCode)
def get_promo_code_by_code(promo_code_code: str, db: Session = Depends(get_db)):
    db_promo_code = crud.get_promo_code_by_code(db=db, promo_code_code=promo_code_code)
    if db_promo_code is None:
        raise HTTPException(status_code=400, detail="Promocode doesn't exists")
    return db_promo_code

@app.post("/successfulpayment")
def successful_payment(successful: schemas.SuccessfulPayment, db: Session = Depends(get_db)):
    db_user = get_user(successful.user_id, db)
    if(db_user.referraled_id != None):
        db_referraled_user = crud.get_user(db, db_user.referraled_id)
        crud.add_credits_to_user(db, db_referraled_user.id, successful.amount * 0.1)
    crud.add_credits_to_user(db, db_user.id, successful.credits * (-1))
    db_promo_code = crud.get_promo_code_by_code(db, successful.promo_code)
    if(db_promo_code != None):
        crud.used_promo_code(db, db_promo_code.code, db_user.id)
    return {"successful": "payment"}


# --coding: utf8--

import os
import sys

PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

def path(*a):
    return os.path.join(PROJECT_ROOT, *a)

# This trick allows to import apps without that prefixes
sys.path.insert(0, path('apps'))
sys.path.insert(0, path('lib'))
sys.path.insert(1, path('.'))

TEST = 'test' in sys.argv

ALLOWED_HOSTS = ['rpe.io']

ADMINS = [
    ('Alex Morozov', 'inductor2000@mail.ru')
]

TIME_ZONE = 'Europe/Moscow'
LANGUAGE_CODE = 'ru-ru'
USE_I18N = True
USE_L10N = True
USE_TZ = True

MEDIA_URL = '/media/'
STATIC_URL = '/static/'

MEDIA_ROOT = path('../media')
STATIC_ROOT = path('../../static')

STATICFILES_DIRS = (
    path('static'),
)

STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

ROOT_URLCONF = 'rpenglish.urls'
WSGI_APPLICATION = 'rpenglish.wsgi.application'

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_extensions',
    'djangobower',
    'hamlpy',
    'pipeline',
    'lib',
    'adminsortable',
    'scripts',
    'plays',
)

HAML_LOADERS = (
    ('django.template.loaders.cached.Loader', (
        'hamlpy.template.loaders.HamlPyFilesystemLoader',
        'hamlpy.template.loaders.HamlPyAppDirectoriesLoader',
    )),
)

COMMON_TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
)

TEMPLATE_LOADERS = HAML_LOADERS + COMMON_TEMPLATE_LOADERS
TEMPLATE_DIRS = (
    path('templates'),
)

from django.conf.global_settings import TEMPLATE_CONTEXT_PROCESSORS as TCP  # NOQA

TEMPLATE_CONTEXT_PROCESSORS = TCP + (
    'django.core.context_processors.request',
)

STATICFILES_FINDERS += (
    'djangobower.finders.BowerFinder',
)

BOWER_COMPONENTS_ROOT = path('static')
BOWER_INSTALLED_APPS = (
    'bootstrap#3.3.6',
)

PIPELINE_COMPILERS = (
    'lib.sass.SASSCCompiler',
    'pipeline.compilers.coffee.CoffeeScriptCompiler',
)

def sass_load_paths(*apps):
    """
    A helper to make cross-imports of scss files possible
    """
    return ' '.join('--include-path %s/static' % path(app)
                    for app in apps)

# The paths to scss directories with files which should have a possibility of
# importing each other.
PIPELINE_SASSC_ARGUMENTS = sass_load_paths('', )

PIPELINE_CSS_COMPRESSOR = None
PIPELINE_JS_COMPRESSOR = None
PIPELINE_DISABLE_WRAPPER = True

PIPELINE_CSS = {
    'base': {
        'source_filenames': [
            'bower_components/bootstrap/dist/css/bootstrap.css',
            'bower_components/bootstrap/dist/css/bootstrap-theme.css',
        ],
        'output_filename': 'css/base.css'
    }
}

STATICFILES_STORAGE = 'pipeline.storage.PipelineStorage'
STATICFILES_FINDERS += (
    'pipeline.finders.PipelineFinder',
)

# Параметры для удобного запуска ./manage.py shell_plus --notebook
IPYTHON_ARGUMENTS = [
    '--ext', 'django_extensions.management.notebook_extension',
]

NOTEBOOK_ARGUMENTS = [
    '--ip=0.0.0.0',
    '--no-browser',
]

STUDENT_COLORS = ['#ff0000', '#00ff00', '#0000ff', ]
PLAY_STEPS = ['review_source', 'direct_translation', 'reverse_translation',
              'role_play', ]


from typing import List
from abc import abstractmethod
from typing import List, Set, Mapping
from index.structure import TermOccurrence
import math
from enum import Enum

class IndexPreComputedVals:
    def __init__(self, index):
        self.index = index
        self.document_norm, self.doc_count = self.precompute_vals()

    def precompute_vals(self):
        """
        Inicializa os atributos por meio do indice (idx):
            doc_count: o numero de documentos que o indice possui
            document_norm: A norma por documento (cada termo é presentado pelo seu peso (tfxidf))
        """
        document_norm = {}
        doc_count = self.index.document_count
        for key, entry in self.index.dic_index.items():
            occ_list = self.index.get_occurrence_list(key)
            for occ in occ_list:
                # print(occ)
                tfxidf = (
                    VectorRankingModel.tf_idf(
                        self.index.document_count, occ.term_freq, len(occ_list)
                    )
                    ** 2
                )
                document_norm[occ.doc_id] = (
                    document_norm[occ.doc_id] + tfxidf
                    if occ.doc_id in document_norm
                    else tfxidf
                )
        for doc in document_norm.keys():
            document_norm[doc] = document_norm[doc] ** 0.5
        return document_norm, doc_count

class RankingModel:
    @abstractmethod
    def get_ordered_docs(
        self,
        query: Mapping[str, TermOccurrence],
        docs_occur_per_term: Mapping[str, List[TermOccurrence]],
    ) -> (List[int], Mapping[int, float]):
        raise NotImplementedError(
            "Voce deve criar uma subclasse e a mesma deve sobrepor este método"
        )

    def rank_document_ids(self, documents_weight):
        doc_ids = list(documents_weight.keys())
        doc_ids.sort(key=lambda x: -documents_weight[x])
        return doc_ids

class OPERATOR(Enum):
    AND = 1
    OR = 2

# Atividade 1
class BooleanRankingModel(RankingModel):
    def __init__(self, operator: OPERATOR):
        self.operator = operator

    def intersection_all(
        self, map_lst_occurrences: Mapping[str, List[TermOccurrence]]
    ) -> List[int]:
        print(f"map_occ: {map_lst_occurrences}")
        if not map_lst_occurrences:
            return []
        first_term = list(map_lst_occurrences)[0]
        first_list = map_lst_occurrences[first_term]
        list_id = []
        for term_occ in first_list:
            list_id.append(term_occ.doc_id)
        set_ids = set(list_id)
        for term, lst_occurrences in map_lst_occurrences.items():
            # print(lst_occurrences)
            occ_list = []
            for term_occ in lst_occurrences:
                occ_list.append(term_occ.doc_id)
            set_ids = set_ids.intersection(set(occ_list))
        print(f"interseção: {set_ids}")
        return list(set_ids)

    def union_all(
        self, map_lst_occurrences: Mapping[str, List[TermOccurrence]]
    ) -> List[int]:
        print(f"map_occ: {map_lst_occurrences}")
        if not map_lst_occurrences:
            return []
        first_term = list(map_lst_occurrences)[0]
        first_list = map_lst_occurrences[first_term]
        list_id = []
        for term_occ in first_list:
            list_id.append(term_occ.doc_id)
        set_ids = set(list_id)
        for term, lst_occurrences in map_lst_occurrences.items():
            occ_list = []
            for term_occ in lst_occurrences:
                occ_list.append(term_occ.doc_id)
            set_ids = set_ids.union(set(occ_list))
        print(f"união: {set_ids}")
        return list(set_ids)

    def get_ordered_docs(
        self,
        query: Mapping[str, TermOccurrence],
        map_lst_occurrences: Mapping[str, List[TermOccurrence]],
    ) -> (List[int], Mapping[int, float]):
        """Considere que map_lst_occurrences possui as ocorrencias apenas dos termos que existem na consulta"""
        if self.operator == OPERATOR.AND:
            return self.intersection_all(map_lst_occurrences), None
        else:
            return self.union_all(map_lst_occurrences), None

# Atividade 2
class VectorRankingModel(RankingModel):
    def __init__(self, idx_pre_comp_vals: IndexPreComputedVals):
        self.idx_pre_comp_vals = idx_pre_comp_vals

    @staticmethod
    def tf(freq_term: int) -> float:
        return 1 + math.log2(freq_term) if freq_term > 0 else 0.0

    @staticmethod
    def idf(doc_count: int, num_docs_with_term: int) -> float:
        return math.log2(doc_count / num_docs_with_term)

    @staticmethod
    def tf_idf(doc_count: int, freq_term: int, num_docs_with_term) -> float:
        tf = VectorRankingModel.tf(freq_term)
        idf = VectorRankingModel.idf(doc_count, num_docs_with_term)
        # print(f"TF:{tf} IDF:{idf} n_i: {num_docs_with_term} N: {doc_count}")
        return tf * idf

    def get_ordered_docs(
        self,
        query: Mapping[str, TermOccurrence],
        docs_occur_per_term: Mapping[str, List[TermOccurrence]],
    ) -> (List[int], Mapping[int, float]):
        documents_weight = {}

        for term, occ_list in docs_occur_per_term.items():
            if term not in query:
                continue
            wquery = self.tf_idf(
                self.idx_pre_comp_vals.doc_count, query[term].term_freq, len(occ_list)
            )
            for occ in occ_list:
                wdoc = self.tf_idf(
                    self.idx_pre_comp_vals.doc_count, occ.term_freq, len(occ_list)
                )
                documents_weight[occ.doc_id] = (
                    wdoc * wquery / self.idx_pre_comp_vals.document_norm[occ.doc_id]
                    if occ.doc_id not in documents_weight
                    else documents_weight[occ.doc_id]
                    + wdoc * wquery / self.idx_pre_comp_vals.document_norm[occ.doc_id]
                )
        # for key, value in documents_weight.items():
        #     documents_weight[key] = value / self.idx_pre_comp_vals.document_norm[key]
        return self.rank_document_ids(documents_weight), documents_weight


from __future__ import annotations

import types
import typing
from imaplib import IMAP4
from imaplib import IMAP4_SSL

from ._config import Configuration
from ._console import console
from ._types import MailBoxesType

class Client:
    """An encapsulation of an IMAP Client.  This client is underpinned by
    either a IMAP4 client if port 143 is provided otherwise it utilises an
    IMAP4_SSL Client.

    :param host: The imap server host.
    :param port: The imap server port.
    """

    def __init__(self, config: Configuration) -> None:
        self.config = config
        self._delegate = self.initialise_client()

    def initialise_client(self) -> typing.Union[IMAP4, IMAP4_SSL]:
        """Initialise the underlying wrapped client."""
        client_cls = IMAP4_SSL if self.config.ssl else IMAP4
        try:
            client = client_cls(**self.config.client_args)
            client.login(self.config.user, self.config.password)
            return client
        except ConnectionError:
            console.print_exception()
            raise
        except IMAP4.error:
            console.print_exception()
            raise

    @property
    def total_mailboxes(self) -> int:
        """Retrieve the total number of mail boxes in the server."""
        return 10

    @property
    def total_mails(self) -> int:
        """Retrieve the total number of emails in all mailboxes."""
        return 1337

    def retrieve_mailboxes(self, directory: str = '""', pattern: str = "*") -> MailBoxesType:
        """Retrieves all the mailboxes available in the server.

        :param directory: The directory to recurse, top-level by default.
        :param pattern: Pattern to apply to names, matches anything by default.
        """
        return self._delegate.list()

    def quota(self, root: str) -> typing.Tuple[str, typing.List[typing.Any]]:
        """Retrieve the roots resource usage and limits."""
        return self._delegate.getquota(root)

    def close(self) -> None:
        """Close the underlying imap client."""
        self._delegate.close()

    def __enter__(self) -> Client:
        return self

    def __exit__(
        self,
        exc_type: typing.Optional[typing.Type[BaseException]] = None,
        exc_val: typing.Optional[BaseException] = None,
        exc_tb: typing.Optional[types.TracebackType] = None,
    ) -> None:
        """Allow the client to be used as a context manager itself."""
        self._delegate.__exit__(exc_type, exc_val, exc_tb)


import pygame

pygame.init()

screen = pygame.display.set_mode((800, 600))
done = 0
x = 20
y = 20
is_blue = True
clock = pygame.time.Clock()

while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = 1
        if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
            is_blue = not is_blue
            print('hi')

    if is_blue:
        color = (0, 128, 255)
    else:
        color = (255, 100, 0)
    pressed = pygame.key.get_pressed()
    screen.fill((0, 0, 0))
    if pressed[pygame.K_UP]:
        y -= 3
        print('hi')
    if pressed[pygame.K_DOWN]:
        y += 3
    if pressed[pygame.K_LEFT]:
        x -= 3
    if pressed[pygame.K_RIGHT]:
        x += 3
    pygame.draw.rect(screen, color, pygame.Rect(x, y, 60, 60))
    pygame.display.flip()
    clock.tick(60)


#!/usr/bin/env python3
"""Allows us to define custom handlers for using frames from the Arduino.
The method capture_frames will take care of retrieving the frames.
Simply create a serial connection, then define a handler and plug it in. It will be called with the frame data, frame_count, and config.
The handler should return a boolean indicating whether or not to continue capturing frames.
If it returns False, then we will tell the transmitter not to send any more frames.
    Note that there may still be some frames to give to the handler before the capture is finished
    if the transmitter does not stop right away.
If it returns True, it is up to capture_frames whether or not to continue.

You may optionally define a start_handler which is run before capturing.
It will be called with config.

You may optionally define an end_handler which is run after capturing is completed.
It will be called with frame_count, bad_checksum_count, and config.

@precondition: TRANSMIT_MODE 1
               DEBUG_MODE 0
(these are #defined in the transmitter/Arduino code, make sure their values are correct)
"""

__author__ = 'Joseph Rubin'

import sys
import serial
from serial import SerialException
from serial.serialutil import Timeout
import struct

from const import *
from frame import *
from util import *
from config import Config
from calibration_generated import *

NAME = 'delete_me'

OUTPUT_DIRECTORY_ROOT = 'raw/'
OUTPUT_SUBDIRECTORY = 'capture{}/'

# Fields that we write to the csv file.
HEADERS = ('time', 'gyroX', 'gyroY', 'gyroZ', 'acclX', 'acclY', 'acclZ', 'button')

# How long to capture for (in seconds). This value is only relevant when invoking this file directly (using _main).
# But if we are, for example, capturing from the GUI, this value will be ignored.
DURATION_SECONDS = 5

def _main():
    con = make_con(resetting=True)
    try:
        con.open()
    except SerialException:
        raise Exception('Please make sure the device is plugged in\n           and no program is using it!')
    wait_for_sig_ready(con)

    # Start automatically by sending a SIG_REQUEST.
    con.write(SIG_REQUEST)
    do_writing_capture(con, duration_seconds=DURATION_SECONDS)
    con.close()

def make_con(*, resetting=True, timeout=None):
    """Returns a closed but configured serial connection to the Arduino.

    If resetting is True, the connection will be a resetting connection,
    meaning that it will reset the Arduino when it is opened.

    After opening it you should check con.is_open for success,
    and retrieve con.name to see what was actually opened.

    Understand that if the con is resetting, the connection may finish opening before the Arduino completely resets,
    so don't send anything right away because the Arduino will not receive it! Wait for SIG_READY instead.
    """
    # We use a timeout because it is essentially the only way to recover
    # when the board is disconnected in the middle of a capture.
    con = serial.Serial(timeout=timeout)
    con.port = get_arduino_port()
    con.baudrate = TRANSMITTER_BAUD_RATE
    con.dtr = resetting
    con.terminated = False
    return con

def wait_for_sig_ready(con, *, sig_max_offset=400):
    """Consume bytes in the connection until SIG_READY is encountered (also consumes the signal itself)."""
    until_ready = con.read_until(SIG_READY, sig_max_offset)
    if not until_ready.endswith(SIG_READY):
        raise SerialException('Arduino never sent SIG_READY. Aborting.')

def wait_for_sig_head(con, *, sig_max_offset=30):
    """Consume bytes in the connection until SIG_HEAD is encountered (also consumes the signal itself)."""
    error_message = 'Transmitter never sent SIG_HEAD. Aborting.'
    read_count = 0
    while True:
        b = con.read(1)
        read_count += 1

        if b:
            if b == SIG_HEAD:
                return True
            elif b == SIG_DENIED:
                # debug
                print('Found SIG_DENIED instead')
                return False
        else:
            raise SerialException(error_message)

        if read_count > sig_max_offset:
            raise SerialException(error_message)

def capture_frames(con, handler, start_handler=None, end_handler=None):
    if not wait_for_sig_head(con):
        raise RequestDeniedException()

    # Now we would like to read the configuration data that was sent. See usage of struct.unpack later for more information on the module.
    capture_rate, gyro_scale, accl_scale = struct.unpack('<HHB', con.read(5))
    config = Config(capture_rate=capture_rate, gyro_scale=gyro_scale, accl_scale=accl_scale)

    if start_handler is not None:
        start_handler(config)

    frame_count = 0
    bad_checksum_count = 0
    do_capture = True
    while do_capture:
        raw_frame, frame = read_frame(con)
        frame_count += 1

        # debug
        if frame_count == 1:
            print('$')

        # Resolve the checksum as XOR of every raw byte.
        checksum_resolution = 0
        for byte in raw_frame:
            checksum_resolution ^= byte
        # Since we XOR'd with the Arduino-calculated checksum,
        # we should get zero if the data was successfully received.
        if checksum_resolution != 0:
            print('$ Bad checksum: ' + str(checksum_resolution) + ' - skip!')
            bad_checksum_count += 1
            continue

        # Check end flag to tell us when to stop.
        if frame.flag.end:
            # debug
            print('$ Encountered stop flag.')
            do_capture = False
            # The contents of the frame that has the end flag are to be ignored.
            continue

        # The handler itself can tell us when it's no longer interested in more frames.
        # We don't simply end the capture, we just send a SIG_ENOUGH and let the transmitter
        # send the end frame by itself. This means that until the transmitter decides to stop
        # there may be more frames to give to the handler.
        if not handler(frame, frame_count, config):
            con.write(SIG_ENOUGH)
            con.flush()

    # The optional end_handler is called at the end of capturing.
    if end_handler is not None:
        end_handler(frame_count, bad_checksum_count, config)

    # Notice how we don't close the connection here.
    # It's up to the user to handle the connection,
    # and it's possible to capture more than once before closing it.

def read_frame(con):
    # We read 16 bytes at a time, because that's the size of the struct that the arduino is sending.
    # 2 bytes  (unsigned) time
    # 12 bytes   (signed) reading
    # 1 byte   (unsigned) flags
    # 1 byte   (unsigned) checksum (XOR)
    frame_size = 16
    raw_frame = con.read(frame_size)
    if len(raw_frame) < frame_size:
        raise SerialException('Timeout occurred. Perhaps the board was disconnected.')

    # Struct (module) lets us restore data from structures automatically using a format string to tell it the struct members.
    # The result is put into a tuple.
    # Arduino is little endian, so we use '<'.
    # B = uint8 | H = uint16 | h = int16.
    unpacked_frame = struct.unpack('<HhhhhhhBB', raw_frame)

    flag_byte = unpacked_frame[7]
    flag_dict = {
        # If transmission is over.
        'end': get_bit(flag_byte, 0),
        # What sensor this frame is from. We combine two bits (two's place and unit's place).
        'sensor': (2 * get_bit(flag_byte, 2)) + get_bit(flag_byte, 1),
        # If the button was pressed during this frame.
        'button': get_bit(flag_byte, 7)
    }

    flag = Flag(**flag_dict)
    reading = Reading(*[unpacked_frame[i] for i in range(1, 7)])
    frame = Frame(time=unpacked_frame[0], reading=reading, flag=flag)

    return raw_frame, frame

# Below is a handler configuration that is used to capture data and save to a file.

def do_writing_capture(con: serial.Serial, enable_trailer: bool=True, duration_seconds=None):
    """Capture data from the transmitter and save it to a file. (This does not send a SIG_REQUEST itself.)"""
    # With a duration of None, we will never terminate on our own (we continue until the transmitter sends a frame with the end flag set).

    # Check to make sure that our serial con is good.
    if not con.is_open:
        raise SerialException('Serial did not open.')

    # Since that was successful, we should find a free output path,
    # but don't create it yet in case something goes wrong before we start capturing data.
    output_path = None
    capture_number = -1
    while output_path is None or os.path.isdir(output_path):
        capture_number += 1
        output_path = OUTPUT_DIRECTORY_ROOT + OUTPUT_SUBDIRECTORY.format(str(capture_number).zfill(5))

    # We need to pass the output_path to our handler so we use a wrapper function.
    capture_frames(con, *writing_capture_handler_wrapper(output_path, duration_seconds))

    # Trailer (see the spec under communications protocol for details).
    if enable_trailer:
        while True:
            # noinspection PyArgumentList
            line = con.readline().decode('ascii', errors='ignore')
            if line == '.\r\n' or line == '.\n':
                break
            sys.stdout.write(line)

    # debug
    print('$ End of capture.')

    return output_path

def writing_capture_handler_wrapper(output_path, duration_seconds=None):
    # With a duration of None, we will never terminate on our own (we continue until the transmitter sends a frame with the end flag set).

    # This wrapper function allows us. to return a custom version of our custom handler. (we are defining a closure)
    # That is, the following code will run just once.

    # Prepare our output files.
    # Even though we are capturing 'raw' data, we are still scaling and calibrating it.
    output_filename_tongue = output_path + 'tongue.csv'
    output_filename_throat = output_path + 'throat.csv'
    # But we also keep a copy of the data that is not at all scaled or calibrated (currently we have no use for this).
    output_filename_tongue_no_modification = output_path + 'tongue_unscaled_uncalibrated.csv'
    output_filename_throat_no_modification = output_path + 'throat_unscaled_uncalibrated.csv'

    output_tongue = None
    output_throat = None
    output_tongue_no_modification = None
    output_throat_no_modification = None

    def setup_files(config):
        global output_tongue, output_throat, output_tongue_no_modification, output_throat_no_modification

        os.makedirs(output_path)

        output_tongue = open(output_filename_tongue, 'w')
        output_throat = open(output_filename_throat, 'w')
        output_tongue_no_modification = open(output_filename_tongue_no_modification, 'w')
        output_throat_no_modification = open(output_filename_throat_no_modification, 'w')

        # Write the csv headers.
        output_tongue.write(format_csv(HEADERS) + '\n')
        output_throat.write(format_csv(HEADERS) + '\n')
        output_tongue_no_modification.write(format_csv(HEADERS) + '\n')
        output_throat_no_modification.write(format_csv(HEADERS) + '\n')

        # Save config data as empty files (these are not currently used).
        open(output_path + 'capture_rate_' + str(config.capture_rate), 'w').close()
        open(output_path + 'gyro_scale_' + str(config.gyro_scale), 'w').close()
        open(output_path + 'accl_scale_' + str(config.accl_scale), 'w').close()

        # debug
        #print('$ Writing:', output_filename_tongue)
        #print('$ Writing:', output_filename_throat)

    def write_frames(frame, frame_count, config):
        global output_tongue, output_throat, output_tongue_no_modification, output_throat_no_modification

        flag = frame.flag
        reading = frame.reading

        # Make a raw output string, with no calibration or scaling applied.
        output_string_raw = format_csv((
            frame.time,
            reading.gyro_x, reading.gyro_y, reading.gyro_z,
            reading.accl_x, reading.accl_y, reading.accl_z,
            flag.button
        )) + '\n'

        # Our sensor_id flag tells us which calibration numbers to use.
        if flag.sensor == TONGUE_SENSOR_ID:
            reading.gyro_x -= calib.tongue.gyro.x
            reading.gyro_y -= calib.tongue.gyro.y
            reading.gyro_z -= calib.tongue.gyro.z

            reading.accl_x -= calib.tongue.accl.x
            reading.accl_y -= calib.tongue.accl.y
            reading.accl_z -= calib.tongue.accl.z
        elif flag.sensor == THROAT_SENSOR_ID:
            reading.gyro_x -= calib.throat.gyro.x
            reading.gyro_y -= calib.throat.gyro.y
            reading.gyro_z -= calib.throat.gyro.z

            reading.accl_x -= calib.throat.accl.x
            reading.accl_y -= calib.throat.accl.y
            reading.accl_z -= calib.throat.accl.z

        # Now we can scale the numbers from raw values to dps (for gyro) or gs (for accl).
        reading.gyro_x = calculate_dps(reading.gyro_x, config.gyro_scale)
        reading.gyro_y = calculate_dps(reading.gyro_y, config.gyro_scale)
        reading.gyro_z = calculate_dps(reading.gyro_z, config.gyro_scale)

        reading.accl_x = calculate_gs(reading.accl_x, config.accl_scale)
        reading.accl_y = calculate_gs(reading.accl_y, config.accl_scale)
        reading.accl_z = calculate_gs(reading.accl_z, config.accl_scale)

        # Prepare a line of csv output.
        output_string = format_csv((
            frame.time,
            reading.gyro_x, reading.gyro_y, reading.gyro_z,
            reading.accl_x, reading.accl_y, reading.accl_z,
            flag.button
        )) + '\n'

        # Our sensor_id flag tells us where to output.
        if flag.sensor == TONGUE_SENSOR_ID:
            output_tongue.write(output_string)
            output_tongue_no_modification.write(output_string_raw)
        elif flag.sensor == THROAT_SENSOR_ID:
            output_throat.write(output_string)
            output_throat_no_modification.write(output_string_raw)

        # We divide by two because we are capturing from two sensors.
        return duration_seconds is None or (frame_count / config.capture_rate / 2 < duration_seconds)

    def close_files(frame_count, bad_checksum_count, _config):
        global output_tongue, output_throat, output_tongue_no_modification, output_throat_no_modification

        output_tongue.close()
        output_throat.close()
        output_tongue_no_modification.close()
        output_throat_no_modification.close()

        # Write the name file.
        name_file = open(output_path + 'name.txt', 'w')
        name_file.write(NAME)
        name_file.close()

        # debug
        print('$ Captured', frame_count, 'frames.')
        print('$ Found', bad_checksum_count, 'bad checksums.')

    return write_frames, setup_files, close_files

def get_bit(number, index):
    """Get the index'th bit of a number."""
    return (number >> index) & 1

def format_csv(tup):
    """Join a sequence of values together with commas in between."""
    return ','.join([str(val) for val in tup])

if __name__ == '__main__':
    _main()


import torchsort
from .pytorch_ops import soft_rank, soft_sort
from .gumbel_ops import my_gumbel_sinkhorn, my_matching
import torch
import torch.nn as nn
import torch.nn.functional as F

class importance_generator_sr(nn.Module):
    def __init__(self, structure=None, wn_flag=True):
        super(importance_generator_sr, self).__init__()

        self.bn1 = nn.LayerNorm([256])

        self.structure = structure

        self.Bi_GRU = nn.GRU(64, 128,bidirectional=True)

        self.h0 = torch.zeros(2,1,128)
        self.inputs = nn.Parameter(torch.Tensor(len(structure),1, 64))
        nn.init.orthogonal_(self.inputs)
        self.inputs.requires_grad=False

        if wn_flag:
            self.linear_list = [weight_norm(nn.Linear(256, structure[i], bias=False)) for i in range(len(structure))]
        else:
            self.linear_list = [nn.Linear(256, structure[i], bias=False,) for i
                                in range(len(structure))]

        self.mh_fc = torch.nn.ModuleList(self.linear_list)

    def forward(self, k_masks):
        if self.bn1.weight.is_cuda:
            self.inputs = self.inputs.cuda()
            self.h0 = self.h0.cuda()
        outputs, hn = self.Bi_GRU(self.inputs, self.h0)
        outputs = [F.relu(self.bn1(outputs[i,:])) for i in  range(len(self.structure))]

        outputs = [self.mh_fc[i](outputs[i]) for i in range(len(self.mh_fc))]

        ori_masks = []
        for i in range(len(outputs)):
            # g = sample_gumbel(outputs[i].size())
            # # print(g.size())
            # if outputs[i].is_cuda:
            #     g = g.cuda()
            # current_out = torch.exp(outputs[i])
            current_out = torch.sigmoid(outputs[i])
            # print(c_mask)
            if self.training:
                # print(current_out)
                _, index = torch.sort(current_out, descending=True)

                c_mask = k_masks[i].gather(0, index.squeeze().argsort())

                # if current_out.size(1) == 16:
                #     s = 0.02
                # elif current_out.size(1) == 32:
                #     s = 0.01
                # elif current_out.size(1) == 64:
                #     s = 0.001
                soft_r = torchsort.soft_rank(current_out.cpu(), regularization_strength=0.005)
                # print(current_out)
                # print(soft_r)
                # soft_r = c_mask.size(0) - soft_r
                soft_mask = soft_indexing(c_mask, soft_r.squeeze().cuda()-1.0)
                # print(soft_mask)
                c_mask = soft_mask.gather(0, index.squeeze().argsort())
            else:
                _, index = torch.sort(current_out, descending=True)
                c_mask = k_masks[i].gather(0, index.squeeze().argsort())

                soft_r = torchsort.soft_rank(current_out.cpu(), regularization_strength=1e-5)

                soft_mask = soft_indexing(c_mask, soft_r.squeeze().cuda()-1.0, hard=True)

                c_mask = soft_mask.gather(0, index.squeeze().argsort())
                # print(c_mask)
            # print(c_mask)
            ori_masks.append(c_mask)
        # out = torch.cat(outputs, dim=1)

        return ori_masks

def soft_indexing(x, index, hard=False):
    # soft_x = torch.zeros(x.size())
    x_size = x.size(0)
    # if x.is_cuda:
    #     soft_x = x.cuda()
    # print(x.size())
    # print(index.size())
    soft_list = []
    # print(index)
    # print(x)
    for i in range(x.size(0)):

        x_range = torch.arange(x_size)
        i_lt = torch.LongTensor([i])
        if x.is_cuda:
            x_range = x_range.cuda()
            i_lt = i_lt.cuda()

        c_idx = torch.index_select(index, 0, i_lt)
        num = torch.exp(-(x_range-c_idx).pow(2))

        den = num.sum().detach()
        # print(num/den)
        c_v = ((num/den)*x).sum()
        soft_list.append(c_v)
        # if i == 0:
        #     soft_x = c_v
        # else:
        #     soft_x = torch.cat([soft_x, c_v])
    soft_x = torch.stack(soft_list)

    if hard:
        out_hard = torch.zeros(soft_x.size())
        out_hard[soft_x >= 0.5] = 1
        out_hard[soft_x < 0.5] = 0
        if soft_x.is_cuda:
            out_hard = out_hard.cuda()
        soft_x = (soft_x - out_hard).detach() + soft_x
    # print(num)
    # print(den)
    # print(soft_list[0])
    # print(soft_x)
    return soft_x

class permutation_generator(nn.Module):
    def __init__(self, structure=None,):
        super(permutation_generator, self).__init__()
        self.structure = structure
        # torch.eye(self.structure[i])
        # torch.randn(self.structure[i], self.structure[i]).abs()
        params_list = [nn.Parameter(torch.randn(self.structure[i], self.structure[i]).abs()) for i in range(len(self.structure))]
        self.params = nn.ParameterList(params_list)

    def forward(self, k_masks):
        perm_mats = self.actual_permutation()
        # print(perm_mats[0].size())
        # print(k_masks[0].size())
        p_kmasks = [torch.matmul(perm_mats[i], k_masks[i]) for i in range(len(self.structure))]
        # print(p_kmasks[0].size())
        return p_kmasks

    def projection(self):
        for i in range(len(self.structure)):
            # c_perm

            c_perm = F.relu(self.params[i].data)
            c_perm = c_perm / c_perm.sum(0, keepdim=True).detach()
            c_perm = c_perm / c_perm.sum(1, keepdim=True).detach()
            self.params[i].data = c_perm

    def actual_permutation(self):
        self.actual_perms = []
        for i in range(len(self.structure)):
            if not self.training:
                c_perm = self.params[i]
                # c_perm = torch.round(c_perm)
                max_idx = torch.argmax(c_perm, 0, keepdim=True)
                one_hot = torch.FloatTensor(c_perm.shape)
                one_hot.zero_()
                if max_idx.is_cuda:
                    one_hot = one_hot.cuda()

                one_hot.scatter_(0, max_idx, 1)

                c_perm = one_hot
            else:
                c_perm = self.params[i]

            self.actual_perms.append(c_perm)
        return self.actual_perms

    def regularization(self):
        losses = []
        for i in range(len(self.structure)):
            c_perm = self.params[i]
            c_loss = c_perm.abs().sum(0) - (c_perm.pow(2).sum(0)).sqrt()
            c_loss = c_loss.sum()
            r_loss = c_perm.abs().sum(1) - (c_perm.pow(2).sum(1)).sqrt()
            r_loss = r_loss.sum()

            losses.append(c_loss+r_loss)

        return torch.sum(torch.stack(losses))

class gumbel_matching(nn.Module):
    def __init__(self, structure=None,):
        super(gumbel_matching, self).__init__()
        self.structure = structure
        # torch.eye(self.structure[i])
        # torch.randn(self.structure[i], self.structure[i]).abs()
        params_list = [nn.Parameter(3*torch.eye(self.structure[i])) for i in range(len(self.structure))]
        self.params = nn.ParameterList(params_list)

    def forward(self, k_masks):
        perm_mats = self.actual_permutation()
        # print(perm_mats[0].size())
        # print(k_masks[0].size())
        p_kmasks = [torch.matmul(perm_mats[i], k_masks[i]) for i in range(len(self.structure))]
        # print(p_kmasks[0].size())
        return p_kmasks

    def actual_permutation(self):
        self.actual_perms = []
        for i in range(len(self.structure)):

            log_alpha = self.params[i]

            soft_perms_inf, log_alpha_w_noise = my_gumbel_sinkhorn(log_alpha.unsqueeze(0))
            if not self.training:
                # log_alpha_w_noise_flat = torch.transpose(log_alpha_w_noise, 0, 1)
                # n_numbers = log_alpha_w_noise.size(-1)
                # log_alpha_w_noise_flat = log_alpha_w_noise.view(-1, n_numbers, n_numbers)
                #
                # hard_perms_inf = my_matching(log_alpha_w_noise_flat)
                # perm = hard_perms_inf.float()
                # if log_alpha_w_noise.is_cuda:
                #     perm = perm.cuda()
                c_perm = self.params[i]
                # c_perm = torch.round(c_perm)
                max_idx = torch.argmax(c_perm, 0, keepdim=True)
                one_hot = torch.FloatTensor(c_perm.shape)
                one_hot.zero_()
                if max_idx.is_cuda:
                    one_hot = one_hot.cuda()

                one_hot.scatter_(0, max_idx, 1)

                perm = one_hot
            else:
                perm = soft_perms_inf

            self.actual_perms.append(perm.squeeze())

        return self.actual_perms


import douiftelegrambot
import json
from pathlib import Path

TOKEN_FILEPATH = f"{Path(__file__).parent}/token.json"

def main() -> None:
    print("Start System.")
    token = read_token()
    main_system = douiftelegrambot.MainSystem(token)
    print("Initial finish.")
    main_system.start_system()

def read_token() -> dict:
    result = json.load(open(TOKEN_FILEPATH, mode="r", encoding="utf8"))
    return result

if __name__ == "__main__":
    main()


from typing import Tuple
from turtle import Turtle, RawTurtle

def draw_pixel(turtle: Turtle | RawTurtle, pixel: Tuple[int, int], color="black"):
    turtle.penup()
    turtle.goto(pixel)
    turtle.color(color)
    turtle.pendown()
    turtle.dot(5)
    return turtle


"""Documentazione ufficiale Flask>> https://flask.palletsprojects.com/en/2.2.x/quickstart/"""
from flask import Flask, request

app = Flask(__name__)

#DECORETOR: prende in input la funzione somma, esegue codice prima e dopo essa. il valore di ritorno è un'altra funzione
@app.route('/', methods=['GET'])
def somma():
    try:
        a = int(request.args.get('a'))
        b = int(request.args.get('b'))
        c = a + b
        return str(c)
    except:
        return "Errore!!"
    """return "
Faccio la somma
"""

if __name__ == '__main__':
    app.run()

# RICHIESTA HTTP = http://localhost:5000/?a=2&b=7      --> 9            è andata a buon fine la richiesta
# RICHIESTA HTTP = http://localhost:5000/?a=2&b=fhbsd  --> Errore!!     non è andata a buon fine la richiesta


import datetime
import dataclasses
import warnings
from collections import OrderedDict
from typing import Optional, List, Iterator

from huawei_lte_api.ApiGroup import ApiGroup
from huawei_lte_api.Session import GetResponseType, SetResponseType
from huawei_lte_api.enums.sms import BoxTypeEnum, TextModeEnum, SaveModeEnum, SendTypeEnum, PriorityEnum, TypeEnum, StatusEnum, SortTypeEnum
from huawei_lte_api.Tools import Tools

@dataclasses.dataclass
class Message:
    index: int  # Index in API
    status: StatusEnum  # Status of SMS
    phone: str  # Phone number of sender
    content: str  # Text content of SMS
    date_time: datetime.datetime  # Datetime of SMS send/receive
    sca: Optional[str]  # Message center number in INTL format eg. +420603052000
    save_type: SaveModeEnum  # How to save received SMS in device
    priority: PriorityEnum  # What priority this SMS have
    type: TypeEnum  # Type of SMS
    text_mode: TextModeEnum = TextModeEnum.SEVEN_BIT  # Type of encoding of SMS

    @classmethod
    def from_dict(cls, data: dict) -> 'Message':
        date_time = data.get('Date')
        return Message(
            index=int(data.get('Index', 0)),
            status=StatusEnum(int(data.get('Smstat', 0))),
            phone=data.get('Phone', ''),
            content=data.get('Content', ''),
            date_time=Tools.string_to_datetime(date_time) if date_time else datetime.datetime.now(),
            sca=data.get('Sca'),
            save_type=SaveModeEnum(int(data.get('SaveType', 0))),
            priority=PriorityEnum(int(data.get('Priority', 0))),
            type=TypeEnum(int(data.get('SmsType', 0))),
        )

    def to_dict(self) -> dict:
        return {
            'Index': str(self.index),
            'Smstat': str(self.status.value),
            'Phone': self.phone,
            'Content': self.content,
            'Date': Tools.datetime_to_string(self.date_time),
            'Sca': self.sca,
            'SaveType': str(self.save_type.value),
            'Priority': str(self.priority.value),
            'SmsType': str(self.type.value)
        }

class Sms(ApiGroup):
    def get_cbsnewslist(self) -> GetResponseType:
        return self._session.get('sms/get-cbsnewslist')

    def sms_count(self) -> GetResponseType:
        return self._session.get('sms/sms-count')

    def splitinfo_sms(self) -> GetResponseType:
        return self._session.get('sms/splitinfo-sms')

    def sms_feature_switch(self) -> GetResponseType:
        return self._session.get('sms/sms-feature-switch')

    def send_status(self) -> GetResponseType:
        return self._session.get('sms/send-status')

    def get_sms_list(self,
                     page: int = 1,
                     box_type: BoxTypeEnum = BoxTypeEnum.LOCAL_INBOX,
                     read_count: int = 20,
                     sort_type: SortTypeEnum = SortTypeEnum.DATE,
                     ascending: bool = False,
                     unread_preferred: bool = False
                     ) -> GetResponseType:

        if isinstance(sort_type, int):
            warnings.warn(
                "get_sms_list: Passing int into sort_type is deprecated and will be removed in next minor version! Please use enums.sms.SortTypeEnum instead!.",
                DeprecationWarning
            )
            sort_type = SortTypeEnum(sort_type)

        # Note: at least the B525s-23a is order sensitive
        return Tools.enforce_list_response(self._session.post_get('sms/sms-list', OrderedDict((
            ('PageIndex', page),
            ('ReadCount', read_count),
            ('BoxType', box_type.value),
            ('SortType', sort_type.value),
            ('Ascending', 1 if ascending else 0),
            ('UnreadPreferred', 1 if unread_preferred else 0),
        ))), 'Message', 'Messages')

    def delete_sms(self, sms_id: int) -> SetResponseType:
        """
        Delete single SMS by its ID
        :param sms_id: Id of SMS you wish to delete
        :return: dict
        """
        return self._session.post_set('sms/delete-sms', {'Index': sms_id})

    def backup_sim(self, from_date: datetime.datetime, is_move: bool = False) -> SetResponseType:
        return self._session.post_set('sms/backup-sim', OrderedDict((
            ('IsMove', int(is_move)),
            ('Date', Tools.datetime_to_string(from_date))
        )))

    def set_read(self, sms_id: int) -> SetResponseType:
        return self._session.post_set('sms/set-read', {
            'Index': sms_id
        })

    def save_sms(self,
                 phone_numbers: List[str],
                 message: str,
                 sms_index: int = -1,
                 sca: Optional[str] = None,
                 text_mode: TextModeEnum = TextModeEnum.SEVEN_BIT,
                 from_date: Optional[datetime.datetime] = None,
                 ) -> SetResponseType:
        """

        :param phone_numbers:
        :param message:
        :param sms_index:
        :param sca: (Optional) Message center number in INTL format eg. +420603052000
        :param text_mode:
        :param from_date:
        :return:
        """

        if from_date is None:
            from_date = datetime.datetime.utcnow()

        return self._session.post_set('sms/save-sms', OrderedDict((
            ('Index', sms_index),
            ('Phones', {'Phone': phone_numbers}),
            ('Sca', sca),
            ('Content', message),
            ('Length', len(message)),
            ('Reserved', text_mode.value),
            ('Date', Tools.datetime_to_string(from_date))
        )))

    def send_sms(self,
                 phone_numbers: List[str],
                 message: str,
                 sms_index: int = -1,
                 sca: Optional[str] = None,
                 text_mode: TextModeEnum = TextModeEnum.SEVEN_BIT,
                 from_date: Optional[datetime.datetime] = None,
                 ) -> SetResponseType:
        """

        :param phone_numbers:
        :param message:
        :param sms_index:
        :param sca: (Optional) Message center number in INTL format eg. +420603052000
        :param text_mode:
        :param from_date:
        :return:
        """
        if from_date is None:
            from_date = datetime.datetime.utcnow()
        return self._session.post_set('sms/send-sms', OrderedDict((
            ('Index', sms_index),
            ('Phones', {'Phone': phone_numbers}),
            ('Sca', sca),
            ('Content', message),
            ('Length', len(message)),
            ('Reserved', text_mode.value),
            ('Date', Tools.datetime_to_string(from_date))
        )))

    def cancel_send(self) -> SetResponseType:
        return self._session.post_set('sms/cancel-send', 1)

    def config(self) -> GetResponseType:
        return self._session.get('sms/config')

    def set_config(self,
                   sca: str,
                   save_mode: SaveModeEnum = SaveModeEnum.LOCAL,
                   validity: int = 10752,
                   use_s_report: bool = False,
                   send_type: SendTypeEnum = SendTypeEnum.SEND,
                   priority: PriorityEnum = PriorityEnum.NORMAL
                   ) -> SetResponseType:
        """
        Sets default SMS send config
        :param sca: Message center number in INTL format eg. +420603052000
        :param save_mode:
        :param validity: validity in seconds?
        :param use_s_report:
        :param send_type:
        :param priority:
        :return:
        """
        return self._session.post_set('sms/config', OrderedDict((
            ('SaveMode', save_mode.value),
            ('Validity', validity),
            ('Sca', sca),
            ('UseSReport', use_s_report),
            ('SendType', send_type.value),
            ('Priority', priority.value)
        )))

    def sms_count_contact(self) -> GetResponseType:
        return self._session.get('sms/sms-count-contact')

    def sms_list_contact(self,
                         pageindex: int = 1,
                         readcount: int = 20,
                         ) -> GetResponseType:
        return self._session.post_get('sms/sms-list-contact', {
            'pageindex': pageindex,
            'readcount': readcount,
        })

    def get_sms_list_pdu(self,
                         page: int = 1,
                         box_type: BoxTypeEnum = BoxTypeEnum.LOCAL_INBOX,
                         read_count: int = 20,
                         ) -> GetResponseType:
        """
        Return SMS in PDU format
        :param page: page number
        :param box_type: box type
        :param read_count: items per page
        :return:
        """
        return self._session.post_get('sms/sms-list-pdu', {
            'PageIndex': page,
            'ReadCount': read_count,
            'BoxType': box_type.value,
        })

    def split_sms(self) -> GetResponseType:
        """
        Endpoint found by reverse engineering B310s-22 firmware, unknown usage
        :return:
        """
        return self._session.get('sms/split-sms')

    def send_sms_pdu(self,
                     pdu: str,
                     length: int,
                     sms_index: int = -1,
                     sca: Optional[str] = None,
                     validity: int = 10752,
                     status_report: bool = False,
                     save_mode: SaveModeEnum = SaveModeEnum.LOCAL,
                     send_type: SendTypeEnum = SendTypeEnum.SEND
                     ) -> SetResponseType:
        """
        Sends PDU SMS, this is not implemented on my router so not tested
        :param pdu: PDU to send e.g. 001100098121436587F900000B05E8329BFD06
        :param length: !TODO Length of PDU not an actual len() but some magic calculation I will not research right now
        :param sms_index: Index of sms in router default -1
        :param sca: # Message center number in INTL format e.g. +420603052000
        :param validity: validity in seconds?
        :param status_report: Require status report for sms (received or not)
        :param save_mode: SaveModeEnum
        :param send_type: SendTypeEnum
        :return:
        """

        return self._session.post_set('sms/send-sms-pdu', {
            'Index': sms_index,
            'PDU': pdu,
            'Length': length,
            'SaveMode': save_mode.value,
            'Validity': validity,  # validity in seconds?
            'Sca': sca,  # Message center number in INTL format eg. +420603052000
            'UseSReport': 1 if status_report else 0,  # Report SMS received
            'SendType': send_type.value
        })

    def recover_sms(self) -> GetResponseType:
        """
        Endpoint found by reverse engineering B310s-22 firmware, unknown usage
        :return:
        """
        return self._session.get('sms/recover-sms')

    def copy_sms(self) -> GetResponseType:
        """
        Endpoint found by reverse engineering B310s-22 firmware, unknown usage
        :return:
        """
        return self._session.get('sms/copy-sms')

    def move_sms(self) -> GetResponseType:
        """
        Endpoint found by reverse engineering B310s-22 firmware, unknown usage
        :return:
        """
        return self._session.get('sms/move-sms')

    def get_messages(self,
                     page: int = 1,
                     box_type: BoxTypeEnum = BoxTypeEnum.LOCAL_INBOX,
                     read_count: Optional[int] = None,
                     sort_type: SortTypeEnum = SortTypeEnum.DATE,
                     ascending: bool = False,
                     unread_preferred: bool = False
                     ) -> Iterator[Message]:

        # No read count, return all, API does not provide a way to return all so we need to do it our self
        if read_count is None:
            read_count = 20  # Read by chunks of 20
            page = 1  # Start page has to be always 1

        while True:
            now = datetime.datetime.now()
            sms_list_tmp = self.get_sms_list(page, box_type, read_count, sort_type, ascending, unread_preferred)
            if int(sms_list_tmp.get('Count', 0)) == 0:
                break

            # get messages
            for message_raw in sms_list_tmp.get('Messages', {}).get('Message', []):
                message = Message.from_dict(message_raw)

                # Check if message is possibly multipart,
                # if it is ignore it if is younger than 60 seconds
                # This way we provide the router with enough time to receive all possible parts and do correct rebuild
                if message.type == TypeEnum.MULTIPART and message.date_time + datetime.timedelta(seconds=10) > now:
                    continue

                yield message

            page += 1