"""
IDP 3 Group 9 - Tidal Lagoon Mathematical Model
Module: Main Program
Gianluca Cantone 2019-2020

Program Modules

 1. Main_Model.py - This file, runs simulation on the system to calculate how fast lagoon drains/fills and how much power will be generated.
 2. 3D_Model - 3D model of the bay (with visuals) plus integration function across whole model, function integrate and plot graph at all height levels.
 3. Grid_Element_Integration.py - Single element integration stand alone program (functions copied into 3D_Model.py)
 3. Optimizer - Runs simulator iterativly looking for best configuration

"""

#Libraries ====================================================================
import math as m
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import logging

#Global Variables =============================================================

Global_Time = [0]
Global_Volume = [0]
Global_Tide = [0]
Global_Head = [0]
Global_Head_Difference = [0]
Global_Velocity = [0]
Global_Discharge = [0]
Profile_List = []
Global_Head_Loss = [0]
Global_Power = [0]
Global_Power_Elec = []

Civil_Price = 143400000      #Good
Blade_Price = 1500000        #Good
Turbine_Price = 2000000      #(Just guide vanes atm)
Shaft_Price = 200000         #Good
Gearbox_Price = 134352       #Good
Generator_Price = 250000     #Good
Electrical_Price = 473329    #Good
Sluice_Price = 200000        #Good

Startup_Cost = 0
Payback_Time = 0

#Efficiency terms:

Eff_Turbine = 0.8
Eff_Gearbox = 0.9
Eff_Generator = 0.97

#Main Program =================================================================

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
log = logging.getLogger()
log.setLevel(logging.WARNING)

print("\n")
print("======================================================================")
print("IDP 3 Group 9: Tidal Lagoon Mathematical Model and Simulation Software")
print("======================================================================")
print("This software simulates the performance of our tidal lagoon system under different configurations and operating conditions. Type help() to see a list of commands and how to use them")

def Run_Simulation(**kwargs):

    #Parameters passed:

    Step_Size = kwargs["step"]
    Tidal_Function = kwargs["tidal_function"]
    Turbines = kwargs["turbines"]
    Turbine_Diameter = kwargs["diameter"]
    Sluices = kwargs["slucies"]
    Sluice_Size = kwargs["sluice_size"]
    Profile_Number = kwargs["profile"]
    Run_Time = kwargs["time"]
    Econ = kwargs["econ"]
    Output = kwargs["output"]
    Graphs = kwargs["graphs"]
    Graph_Head = kwargs["graph_head"]
    Graph_QV = kwargs["graph_QV"]
    Graph_P = kwargs["graph_P"]

    #Global Variables

    global Civil_Price, Blade_Price, Turbine_Price, Gearbox_Price, Generator_Price, Sluice_Price, Startup_Cost, Payback_Time
    global Eff_Turbine, Eff_Gearbox, Eff_Generator
    global Global_Time, Global_Volume, Global_Tide, Global_Head, Global_Head_Difference, Global_Velocity, Global_Discharge, Global_Head_Loss, Global_Power, Global_Power_Elec
    Global_Time = [0]
    Global_Volume = [0]
    Global_Tide = [0]
    Global_Head = [0]
    Global_Head_Difference = [0]
    Global_Velocity = [0]
    Global_Discharge = [0]
    Global_Head_Loss = [0]
    Global_Power = [0]
    Global_Power_Elec = []

    #Local Variables

    V_0 = 18891820
    Euler_Volume = [0]
    Euler_Volume_Tide = [0]
    Euler_Volume[0] = V_0
    Euler_Volume_Tide[0] = V_0
    Time = [0]
    M = 1453217
    G = 9.807
    rho = 1029
    Discharge_Coefficient= 0.8
    Sluicing_Discharge_Coefficient = 0.98
    Friction_Factor = 0.00035
    Draft_Length = 10
    Draft_Diameter = 10
    State = 0                   #0 = Waiting, 1 = filling sluice, 2 = draining generation, 3 = filling generation, 4 = draining sluice
    Current_Time = Step_Size
    Operational_Profile = Profile_List[Profile_Number-1]
    Profile_Stage = 0
    Total_Stages = len(Operational_Profile)
    Total_Mechanical_Energy = 0
    Total_Electrical_Energy = 0

    #Initial Calculations

    Area = np.pi*np.power((Turbine_Diameter/4),2)*Turbines
    Sluice_Area =  (np.pi*np.power((Turbine_Diameter/4),2)*Turbines)+(Sluice_Size*Sluices)

    '''
    Read operational algorithm profile, then switch between 5 different states, checking stopping conditions each time. Put Euler approximation in different function
    Operational states:
        -Filling_Sluice
        -Filling_Generation
        -Draining_Sluice
        -Draining_Generation
        -Transition (waiting)
    Save data to global arrays
    Plot graph using given parameters
    If value in square root turns negative, return an error. (This should only happen if sluice gates are not shut at the right time, it means the tide is higher than the lagoon)
    '''

    if Output == True:
        log.setLevel(logging.INFO)
    else:
        log.setLevel(logging.WARNING)

    print("\nRunning simulation...\n")

    State = Operational_Profile[0][0]
    Average_Head = 0
    AH_Count = 0
    Runtime = 0
    Flow_Rate_Average = 0
    Max_Head = []

    while Current_Time < Run_Time:

        if State == 0:

            log.info("In state 0: Waiting for tidal shift")

            while State == 0:

                if Current_Time > Run_Time:
                    State = 5
                    log.info("Runtime elapsed")
                elif Global_Tide[-1] < Operational_Profile[Profile_Stage][1]:
                    Profile_Stage = (Profile_Stage+1)%Total_Stages
                    State = Operational_Profile[Profile_Stage][0]
                else:
                    Global_Time.append(Current_Time)
                    Global_Tide.append(Evaluate_Tidal_Function(Tidal_Function, Current_Time))
                    Global_Volume.append(Global_Volume[-1])
                    Global_Head.append((Global_Volume[-1])/(M))
                    Global_Head_Difference.append(Global_Head[-1]-Global_Tide[-1])
                    Global_Velocity.append(0)
                    Global_Discharge.append(0)
                    Global_Head_Loss.append(0)
                    Global_Power.append(0)
                    Current_Time += Step_Size

        if State == 1:

            log.info("In state 1: Filling lagoon via sluicing")

            while State == 1:

                if Current_Time > Run_Time:
                    State = 5
                    log.info("Runtime elapsed")
                elif Global_Head[-1] > Operational_Profile[Profile_Stage][1]:
                    Profile_Stage = (Profile_Stage+1)%Total_Stages
                    State = Operational_Profile[Profile_Stage][0]
                else:

                    Global_Time.append(Current_Time)
                    Global_Tide.append(Evaluate_Tidal_Function(Tidal_Function, Current_Time))
                    #Global_Volume.append(Global_Volume[Current_Time-1]+Step_Size*Sluice_Area*Sluicing_Discharge_Coefficient*np.sqrt(2*G)*np.sqrt(Global_Tide[Current_Time]-((Global_Volume[Current_Time-1])/(M))-Pipe_Loss))

                    if (Global_Tide[-1])-((Global_Volume[-1])/(M)) < 0:
                        log.info("WARNING: Target value of " + str(Operational_Profile[Profile_Stage][1]) + "m in state 1 could not be met!")
                        Global_Volume.append(Global_Volume[-1])
                        Profile_Stage = (Profile_Stage+1)%Total_Stages
                        State = Operational_Profile[Profile_Stage][0]
                        Global_Velocity.append(Global_Velocity[-1])
                        Global_Discharge.append(Global_Discharge[-1])
                    else:
                        #Global_Volume.append(Global_Volume[-1]+Step_Size*Sluice_Area*Sluicing_Discharge_Coefficient*np.sqrt(2*G)*np.sqrt((Global_Tide[-1])-((Global_Volume[-1])/(M))-Pipe_Loss))
                        #Global_Velocity.append(np.sqrt(2*G)*np.sqrt((Global_Tide[-1])-((Global_Volume[-1])/(M))-Pipe_Loss))

                        Global_Velocity.append(np.sqrt(2*G*((Global_Tide[-1])-((Global_Volume[-1])/(M)))/(1+Friction_Factor*(Draft_Length/Draft_Diameter))))
                        Global_Volume.append(Global_Volume[-1]+Step_Size*Sluice_Area*Sluicing_Discharge_Coefficient*Global_Velocity[-1])

                        Global_Discharge.append(Global_Velocity[-1]*Sluice_Area*Sluicing_Discharge_Coefficient)

                    Global_Head.append((Global_Volume[-1])/(M))
                    Global_Head_Difference.append(Global_Head[-1]-Global_Tide[-1])
                    Global_Head_Loss.append(0)
                    Global_Power.append(0)
                    Current_Time += Step_Size
                    #print("the time is: " + str(Current_Time))

        if State == 2:

            log.info("In state 2: Draining lagoon via energy generation")

            while State == 2:

                if Current_Time > Run_Time:
                    State = 5
                    log.info("Runtime elapsed")
                elif Global_Head[-1] < Operational_Profile[Profile_Stage][1]:
                    Profile_Stage = (Profile_Stage+1)%Total_Stages
                    State = Operational_Profile[Profile_Stage][0]
                    #Current_Time = Run_Time+1
                else:

                    Global_Time.append(Current_Time)
                    Global_Tide.append(Evaluate_Tidal_Function(Tidal_Function, Current_Time))

                    if (Global_Volume[-1])/(M)-(Global_Tide[-1]) < 0:
                        log.info("WARNING: Target value of " + str(Operational_Profile[Profile_Stage][1]) + "m in state 2 could not be met!")
                        Global_Volume.append(Global_Volume[-1])
                        Profile_Stage = (Profile_Stage+1)%Total_Stages
                        State = Operational_Profile[Profile_Stage][0]
                        Global_Velocity.append(Global_Velocity[-1])
                        Global_Discharge.append(Global_Discharge[-1])
                        Global_Head_Loss.append(Global_Head_Loss[-1])
                        Global_Power.append(Global_Power[-1])
                    else:
                        #Global_Volume.append(Global_Volume[-1]-Step_Size*Area*Discharge_Coefficient*np.sqrt(2*G)*np.sqrt((Global_Volume[-1])/(M)-(Global_Tide[-1])-Turbine_Loss-Pipe_Loss))
                        #Global_Velocity.append(np.sqrt(2*G)*np.sqrt((Global_Volume[-1])/(M)-(Global_Tide[-1])-Turbine_Loss-Pipe_Loss))

                        Global_Velocity.append(np.sqrt((2*G*((Global_Volume[-1])/(M)-(Global_Tide[-1]))*(1-Eff_Turbine))/(1-Eff_Turbine*Friction_Factor*(Draft_Length/Draft_Diameter))))
                        Global_Volume.append(Global_Volume[-1]-Step_Size*Area*Discharge_Coefficient*Global_Velocity[-1])

                        Global_Discharge.append(Global_Velocity[-1]*Area*Discharge_Coefficient)
                        Global_Head_Loss.append(((Global_Volume[-1])/(M)-Global_Tide[-1]-(Friction_Factor*(Draft_Length/Draft_Diameter)*(Global_Velocity[-1]**2/(2*G)))))
                        Global_Power.append(rho*G*Global_Discharge[-1]*Global_Head_Loss[-1])

                    Global_Head.append((Global_Volume[-1])/(M))
                    Global_Head_Difference.append(Global_Head[-1]-Global_Tide[-1])
                    Current_Time += Step_Size

                    Average_Head += abs(Global_Head_Difference[-1])
                    Max_Head.append(abs(Global_Head_Difference[-1]))
                    AH_Count += 1
                    if Current_Time > 50000:
                        Runtime += 1*Step_Size
                    Flow_Rate_Average += Global_Discharge[-1]

        if State == 3:

            log.info("In state 3: Filling lagoon via energy generation")

            while State == 3:

                if Current_Time > Run_Time:
                    State = 5
                    log.info("Runtime elapsed")
                elif Global_Head[-1] > Operational_Profile[Profile_Stage][1]:
                    Profile_Stage = (Profile_Stage+1)%Total_Stages
                    State = Operational_Profile[Profile_Stage][0]
                else:

                    Global_Time.append(Current_Time)
                    Global_Tide.append(Evaluate_Tidal_Function(Tidal_Function, Current_Time))
                    #Global_Volume.append(Global_Volume[Current_Time-1]+Step_Size*Sluice_Area*Sluicing_Discharge_Coefficient*np.sqrt(2*G)*np.sqrt(Global_Tide[Current_Time]-((Global_Volume[Current_Time-1])/(M))-Pipe_Loss))

                    if (Global_Tide[-1])-((Global_Volume[-1])/(M)) < 0:
                        log.info("WARNING: Target value of " + str(Operational_Profile[Profile_Stage][1]) + "m in state 1 could not be met!")
                        Global_Volume.append(Global_Volume[-1])
                        Profile_Stage = (Profile_Stage+1)%Total_Stages
                        State = Operational_Profile[Profile_Stage][0]
                        Global_Velocity.append(Global_Velocity[-1])
                        Global_Discharge.append(Global_Discharge[-1])
                        Global_Head_Loss.append(Global_Head_Loss[-1])
                        Global_Power.append(Global_Power[-1])
                    else:
                        #Global_Volume.append(Global_Volume[-1]+Step_Size*Sluice_Area*Sluicing_Discharge_Coefficient*np.sqrt(2*G)*np.sqrt((Global_Tide[-1])-((Global_Volume[-1])/(M))-Pipe_Loss))
                        #Global_Velocity.append(np.sqrt(2*G)*np.sqrt((Global_Tide[-1])-((Global_Volume[-1])/(M))-Pipe_Loss))

                        Global_Velocity.append(np.sqrt((2*G*((Global_Tide[-1])-(Global_Volume[-1])/(M))*(1-Eff_Turbine))/(1-Eff_Turbine*Friction_Factor*(Draft_Length/Draft_Diameter))))
                        Global_Volume.append(Global_Volume[-1]+Step_Size*Area*Discharge_Coefficient*Global_Velocity[-1])

                        Global_Discharge.append(Global_Velocity[-1]*Area*Discharge_Coefficient)
                        Global_Head_Loss.append(Global_Tide[-1]-((Global_Volume[-1])/(M))-(Friction_Factor*(Draft_Length/Draft_Diameter)*(Global_Velocity[-1]**2/(2*G))))
                        Global_Power.append(rho*G*Global_Discharge[-1]*Global_Head_Loss[-1])

                    Global_Head.append((Global_Volume[-1])/(M))
                    Global_Head_Difference.append(Global_Head[-1]-Global_Tide[-1])
                    Current_Time += Step_Size

                    Average_Head += abs(Global_Head_Difference[-1])
                    Max_Head.append(abs(Global_Head_Difference[-1]))
                    AH_Count += 1
                    if Current_Time > 50000:
                        Runtime += 1*Step_Size
                    Flow_Rate_Average += Global_Discharge[-1]

        if State == 4:

            log.info("In state 4: Waiting for tidal shift")

            while State == 4:

                if Current_Time > Run_Time:
                    State = 5
                    log.info("Runtime elapsed")
                elif Global_Tide[-1] > Operational_Profile[Profile_Stage][1]:
                    Profile_Stage = (Profile_Stage+1)%Total_Stages
                    State = Operational_Profile[Profile_Stage][0]
                else:
                    Global_Time.append(Current_Time)
                    Global_Tide.append(Evaluate_Tidal_Function(Tidal_Function, Current_Time))
                    Global_Volume.append(Global_Volume[-1])
                    Global_Head.append((Global_Volume[-1])/(M))
                    Global_Head_Difference.append(Global_Head[-1]-Global_Tide[-1])
                    Global_Velocity.append(0)
                    Global_Discharge.append(0)
                    Global_Head_Loss.append(0)
                    Global_Power.append(0)
                    Current_Time += Step_Size

        log.info("Current time: " + str(Current_Time))

    Average_Head = (Average_Head/AH_Count)
    Flow_Rate_Average = (Flow_Rate_Average/AH_Count)

    #50000 start offset, run for 86400

    print("\nSimulation complete\n")
    print("Average head difference across turbines (m): " + str(Average_Head))
    print("Max head difference across turbines (m): " + str(max(Max_Head)))
    print("Run time (s): " + str(Runtime))
    print("Average discharge (m^3s^-1): " + str(Flow_Rate_Average))

    #Energy generation calculations
    print("\n================================================================")
    print("Running energy calculations...")

    for Power in Global_Power:

        if np.isnan(Power) == True:
            Power = 0

        Global_Power_Elec.append(Power*Eff_Gearbox*Eff_Generator)
        Total_Mechanical_Energy += int(Power*Step_Size)
        Total_Electrical_Energy += int(Global_Power_Elec[-1]*Step_Size)

    Energy_Lost = Total_Mechanical_Energy-Total_Electrical_Energy
    Efficiency = (Eff_Turbine*Eff_Gearbox*Eff_Generator)
    print("Total mechanical energy generated (J): " + str(Total_Mechanical_Energy))
    print("Energy lost (J): " + str(Energy_Lost))
    print("System efficiency: " + str(Efficiency))
    print("Total electrical energy generated (J): " + str(Total_Electrical_Energy))
    print("Total electrical energy generated (kWh): " + str(Total_Electrical_Energy/3.6e+6))
    #Print peak power and total energy
    #Print final power and energy and percentage lost

    if Econ == True:                                                            #Section for calculating economic assessment.

        print("\n================================================================")
        print("Running economic assessment of configuration...")

        Startup_Cost = Civil_Price+(Turbines*(Blade_Price+Shaft_Price+Turbine_Price+Gearbox_Price+(2*Sluice_Price)+Generator_Price+Electrical_Price))+(Sluices*Sluice_Price)
        Running_Costs_Day = 823 #a day
        Total_Running_Costs = Running_Costs_Day*(Run_Time/86400)
        Energy_Price = 92.5/1000 #per kWh
        Turnover = (Total_Electrical_Energy/(3.6e+6))*Energy_Price
        Gross_Profit = Turnover-Total_Running_Costs
        Net_Profit = Gross_Profit-Startup_Cost
        Payback_Time = Startup_Cost/Gross_Profit

        print("Startup costs: " + str(Startup_Cost))
        print("Running costs: " + str(Total_Running_Costs))
        print("Turnover: " + str(Turnover))
        print("Gross profit: " + str(Gross_Profit))
        print("Net profit: " + str(Net_Profit))
        print("Pay back time in years: " + str(Payback_Time))

        if Graphs == True:

            plt.figure(figsize=plt.figaspect(1)*2)
            ax = plt.axes()
            plt.title("Break-Even Analysis")
            ax.set_xlabel("Time (Years)")
            ax.set_ylabel("Equity (£)")

            Fixed_Costs_Plot = ax.plot([0,Payback_Time,2*Payback_Time], [Startup_Cost, Startup_Cost, Startup_Cost], "--", label="Fixed costs", color="red", linewidth=3)
            Total_Costs_Plot = ax.plot([0,Payback_Time,2*Payback_Time], [Startup_Cost, (Total_Running_Costs*Payback_Time + Startup_Cost), (Total_Running_Costs*Payback_Time*2 + Startup_Cost)], "--", label="Total costs", color="darkred", linewidth=3)
            Revenue_Plot = ax.plot([0,Payback_Time,2*Payback_Time], [0, (Turnover*Payback_Time), (Turnover*Payback_Time*2)], label="Revenue", color="blue", linewidth=3)

            Lines = Fixed_Costs_Plot+Total_Costs_Plot+Revenue_Plot
            Labels =[l.get_label() for l in Lines]
            ax.legend(Lines, Labels)
            plt.minorticks_on()
            ax.grid(which='major', color='black', linestyle='-', linewidth=1)
            ax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)

    #Take into account maintencance downtimes

    if Graphs == True:

        plt.figure(figsize=plt.figaspect(1)*2)

        ax = plt.axes()
        second_ax = ax.twinx()
        plt.title("Lagoon Volume, Lagoon Height and Tide Height Vs Time (Double Effect)")
        ax.set_xlabel("Time (s)")
        ax.set_ylabel("Volume (m^3)")
        second_ax.set_ylabel('Height (m)')
        Filling_Plot = ax.plot(Global_Time, Global_Volume, label="Lagoon Volume", color="deepskyblue", linewidth=3)
        Lagoon_Head_Plot = second_ax.plot(Global_Time, Global_Head, "--", label="Lagoon height", color="green", linewidth=2)
        Tide_Height_Plot = second_ax.plot(Global_Time, Global_Tide, "--", label="Tide height", color ="blue", linewidth=2)

#        plt.title("Lagoon Volume, Lagoon Height and Tide Height Vs Time (Double Effect)", fontsize='30', weight='bold')
#        ax.set_xlabel("Time (s)", fontsize='30', weight='bold')
#        ax.set_ylabel('Height (m)', fontsize='30', weight='bold')
#        Lagoon_Head_Plot = ax.plot(Global_Time, Global_Head, "--", label="Lagoon height", color="green", linewidth=2)
#        Tide_Height_Plot = ax.plot(Global_Time, Global_Tide, "--", label="Tide height", color ="blue", linewidth=2)
#        ax.set_ylabel('Height (m)')

        if Graph_Head == True:
            Head_Difference_Plot = second_ax.plot(Global_Time, Global_Head_Difference, "--", label="Head difference", color ="orange", linewidth=2)
            Lines = Filling_Plot+Lagoon_Head_Plot+Tide_Height_Plot+Head_Difference_Plot
        else:
            Lines = Filling_Plot+Lagoon_Head_Plot+Tide_Height_Plot
            #Lines = Lagoon_Head_Plot+Tide_Height_Plot

        Labels =[l.get_label() for l in Lines]
        ax.legend(Lines, Labels)

        #ax.legend(Lines, Labels, fontsize='30')

        plt.minorticks_on()
        ax.grid(which='major', color='black', linestyle='-', linewidth=1)
        ax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)
        ax.set_ylim(0,2e7)

#        for tick in ax.xaxis.get_major_ticks():
#                tick.label.set_fontsize(30)
#                tick.label.set_weight('bold')
#
#        for tick in ax.yaxis.get_major_ticks():
#                tick.label.set_fontsize(30)
#                tick.label.set_weight('bold')

        if Graph_QV == True:

            #Red graphs

            plt.figure(figsize=plt.figaspect(1)*2)
            QVax = plt.axes()
            second_QVax = QVax.twinx()
            plt.title("Velocity & Discharge Vs Time")
            QVax.set_xlabel("Time (s)")
            QVax.set_ylabel("Velocity (ms^-1)")
            second_QVax.set_ylabel('Discharge (m^3s^-1)')

            Velocity_Plot = QVax.plot(Global_Time, Global_Velocity, label="Flow velocity", color="red", linewidth=2)
            Discharge_Plot = second_QVax.plot(Global_Time, Global_Discharge, "--", label="Total discharge", color="maroon", linewidth=2)
            Lines = Velocity_Plot+Discharge_Plot
            Labels =[l.get_label() for l in Lines]
            QVax.legend(Lines, Labels)
            plt.minorticks_on()
            QVax.grid(which='major', color='black', linestyle='-', linewidth=1)
            QVax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)

        if Graph_P == True:

            plt.figure(figsize=plt.figaspect(1)*2)
            Pax = plt.axes()
            plt.title("Mechanical & Electrical Power Vs Time")
            Pax.set_xlabel("Time (s)")
            Pax.set_ylabel("Power (w)")

            Mech_Power_Plot = Pax.plot(Global_Time, Global_Power, label="Mechanical Power", color="gold", linewidth=2)
            Elec_Power_Plot = Pax.plot(Global_Time, Global_Power_Elec, "--", label="Electrical Power", color="y", linewidth=2)
            Lines = Mech_Power_Plot+Elec_Power_Plot
            Labels =[l.get_label() for l in Lines]
            Pax.legend(Lines, Labels)
            plt.minorticks_on()
            Pax.grid(which='major', color='black', linestyle='-', linewidth=1)
            Pax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)

#            plt.title("Mechanical & Electrical Power Vs Time", fontsize='30', weight='bold')
#            Pax.set_xlabel("Time (s)", fontsize='30', weight='bold')
#            Pax.set_ylabel("Power (w)", fontsize='30', weight='bold')
#            Pax.legend(Lines, Labels, fontsize='30')
#
#            for tick in Pax.xaxis.get_major_ticks():
#                tick.label.set_fontsize(30)
#                tick.label.set_weight('bold')
#
#            for tick in Pax.yaxis.get_major_ticks():
#                tick.label.set_fontsize(30)
#                tick.label.set_weight('bold')

def Evaluate_Tidal_Function(Type, Time):

    if Type == "sine":
        return (6.5*np.cos(2*np.pi*0.0000231*Time-np.pi)+6.5)

    elif Type == "sine_average":
        return (4*np.cos(2*np.pi*0.0000231*Time-np.pi)+6.5)
    elif Type == "Newport_1":
        Time += 14000
        return ( (3.678*np.cos(2*np.pi*(2.236e-5)*Time+(48.06*(np.pi/180)))) +
                 (1.4298*np.cos(2*np.pi*(2.239e-5)*Time-(130.6*(np.pi/180)))) +
                 (1.4986*np.cos(2*np.pi*(2.315e-5)*Time+(176.7*(np.pi/180)))) +
                 (0.7298*np.cos(2*np.pi*(2.194e-5)*Time-(53.75*(np.pi/180)))) + 6.278)

def Optimize(Item, Mode):

    global Global_Power, Profile_List, Eff_Turbine, Eff_Gearbox, Eff_Generator, Startup_Cost, Payback_Time

    if Item == "blade_size":

        Power = []
        Diameters = np.arange(1,12.5,0.5)
        Profile_List = []

        print("Calculating blade diameter optimization")

        if Mode == "single":
            Setup_Profile([[1,12],[0,5],[2,0]])
        elif Mode == "double":
            Setup_Profile([[3,14],[0,5],[2,0],[4,7]])

        for Turbine in np.arange(1,21,1):

            Power.append([])

            for Diameter in np.arange(1,12.5,0.5):

                Total_Mechanical_Energy = 0
                Run_Simulation(step=100, tidal_function="Newport_1", turbines=Turbine, diameter=Diameter, slucies=0, sluice_size=80, profile=1, time=60*60*24*365, econ=False, output=False, graphs=False, graph_head=False, graph_QV=False, graph_P=False)

                Cut_Count = 0

                for Energy in Global_Power:
                    Total_Mechanical_Energy += Energy*100

                Power[Turbine-1].append(Total_Mechanical_Energy)

        plt.figure(figsize=plt.figaspect(1)*2)
        ax = plt.axes()
        plt.title("Energy Output Vs Blade Diameter For Different Number of Turbines In Single Effect Mode (Newport Tidal Data)")
        ax.set_xlabel("Blade Diameter (m)")
        ax.set_ylabel("Energy Output (J)")

        Count = 0

        for i in Power:
            Temp = ax.plot(Diameters, Power[Count], label=("Turbines: " + str(Count+1)), linewidth=2)
            Count += 1

        #ax.legend("1 turbine","2 turbines","3 turbines","4 turbines","5 turbines","6 turbines","7 turbines","8 turbines","9 turbines","10 turbines","11 turbines","12 turbines","13 turbinse","14 turbines","15 turbines","16 turbines","17 turbines","18 turbines","19 turbines","20 turbines",)
        ax.legend()
        plt.minorticks_on()
        ax.grid(which='major', color='black', linestyle='-', linewidth=1)
        ax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)

    elif Item == "turbine_number":

        Power = []
        Turbines = np.arange(1,31,1)
        Diameters = np.arange(1,12.5,0.5)
        Profile_List = []

        print("Calculating turbine number optimization")

        if Mode == "single":
            Setup_Profile([[1,12],[0,5],[2,0]])
        elif Mode == "double":
            Setup_Profile([[3,14],[0,5],[2,0],[4,7]])

        Count = 0

        for Diameter in np.arange(1,12.5,0.5):

            Power.append([])
            Count += 1

            for Turbine in np.arange(1,31,1):

                Total_Mechanical_Energy = 0
                Run_Simulation(step=100, tidal_function="Newport_1", turbines=Turbine, diameter=Diameter, slucies=0, sluice_size=80, profile=1, time=60*60*24*365, econ=False, output=False, graphs=False, graph_head=False, graph_QV=False, graph_P=False)

                for Energy in Global_Power:
                    #Global_Power_Elec.append(i*Eff_Turbine*Eff_Gearbox*Eff_Generator)
                    Total_Mechanical_Energy += Energy*100

                Power[Count-1].append(Total_Mechanical_Energy)

        plt.figure(figsize=plt.figaspect(1)*2)
        ax = plt.axes()

        if Mode == "single":
            plt.title("Energy Output Vs Number of Turbines For Different Blade Diameters In Single Effect Mode (Newport Tidal Data)")
        elif Mode == "double":
            plt.title("Energy Output Vs Number of Turbines For Different Blade Diameters In Double Effect Mode (Newport Tidal Data)")

        ax.set_xlabel("Number of Turbines")
        ax.set_ylabel("Energy Output (J)")

        Count = 0

        for i in Power:
            Temp = ax.plot(Turbines, Power[Count], label=("Diameter: " + str(Diameters[Count])), linewidth=2)
            Count += 1

        #ax.legend("1 turbine","2 turbines","3 turbines","4 turbines","5 turbines","6 turbines","7 turbines","8 turbines","9 turbines","10 turbines","11 turbines","12 turbines","13 turbinse","14 turbines","15 turbines","16 turbines","17 turbines","18 turbines","19 turbines","20 turbines",)
        ax.legend()
        plt.minorticks_on()
        ax.grid(which='major', color='black', linestyle='-', linewidth=1)
        ax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)

    elif Item == "algorithm":

        Profile_List = []
        Power = []
        Triggers = np.arange(13,-1,-0.5)
        for i in range(len(Triggers)):
            Triggers[i] = (Triggers[i]/13)*100
        Count = 1
        Turbine_Count = 0

        for Turbines in np.arange(1,26,1):

            Power.append([])
            Profile_List = []
            Turbine_Count += 1
            Count = 1

            for i in np.arange(13,-1,-0.5):

                Total_Mechanical_Energy = 0
                Setup_Profile([[3,14],[0,i],[2,0],[4,0]])
                Run_Simulation(step=100, tidal_function="sine", turbines=Turbine_Count, diameter=5.87, slucies=0, sluice_size=80, profile=Count, time=150000, econ=False, output=False, graphs=False, graph_head=False, graph_QV=False, graph_P=False)

                for Energy in Global_Power:
                        #Global_Power_Elec.append(i*Eff_Turbine*Eff_Gearbox*Eff_Generator)
                        Total_Mechanical_Energy += Energy

                Power[Turbine_Count-1].append(Total_Mechanical_Energy)
                Count += 1

        plt.figure(figsize=plt.figaspect(1)*2)
        ax = plt.axes()
        plt.title("Energy Output Vs Sluice Gate Triggering Height (Double Effect)")
        ax.set_xlabel("Lagoon Triggering Height as (Percentage of Tidal Range)")
        ax.set_ylabel("Energy Output (J)")

        Count = 0

        for i in Power:
            Temp = ax.plot(Triggers, Power[Count], label=("Turbines: " + str(Count+1)), linewidth=2)
            Count += 1

        ax.legend()
        plt.minorticks_on()
        ax.grid(which='major', color='black', linestyle='-', linewidth=1)
        ax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)

    elif Item == "power":

        Power = [[],[]]
        Per_Turbine = []
        Turbines = np.arange(1,41,1)

        for Turbine in np.arange(1,41,1):

            Run_Simulation(step=100, tidal_function="Newport_1", turbines=Turbine, diameter=5.87, slucies=0, sluice_size=80, profile=1, time=60*60*24*365, econ=False, output=False, graphs=False, graph_head=False, graph_QV=False, graph_P=False)

            Max_Mechanical_Power = max(Global_Power)
            Max_Electrical_Power = (Max_Mechanical_Power*Eff_Gearbox*Eff_Generator)

            Power[0].append(Max_Mechanical_Power)
            Power[1].append(Max_Electrical_Power)
            Per_Turbine.append(Max_Electrical_Power/Turbine)

        plt.figure(figsize=plt.figaspect(1)*2)
        ax = plt.axes()
        plt.title("Max Power Output Vs Number of Turbines")
        ax.set_xlabel("Number of Turbines")
        ax.set_ylabel("Max Power Output (W)")

        Temp = ax.plot(Turbines, Power[0], label=("Mechanical Power"), color="gold", linewidth=2)
        Temp2 = ax.plot(Turbines, Power[1], "--", label=("Electrical Power"), color="y", linewidth=2)
        Temp2 = ax.plot(Turbines, Per_Turbine, "--", label=("Electrical Power Per Turbine"), color="red", linewidth=2)

        ax.legend()
        plt.minorticks_on()
        ax.grid(which='major', color='black', linestyle='-', linewidth=1)
        ax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)

    elif Item == "payback":

        Profile_List = []
        Turbines = np.arange(3,41,1)
        Startup_Costs = []
        Payback_Times = []

        if Mode == "single":
            Setup_Profile([[1,12],[0,5],[2,0]])
        elif Mode == "double":
            Setup_Profile([[3,14],[0,5],[2,0],[4,7]])

        for Turbine in Turbines:

            Run_Simulation(step=100, tidal_function="Newport_1", turbines=Turbine, diameter=5.87, slucies=0, sluice_size=80, profile=1, time=60*60*24*365, econ=True, output=False, graphs=False, graph_head=False, graph_QV=False, graph_P=False)
            Startup_Costs.append(Startup_Cost)
            Payback_Times.append(Payback_Time)

        plt.figure(figsize=plt.figaspect(1)*2)
        ax = plt.axes()
        plt.title("Number of Turbines Vs Payback Time")
        ax.set_xlabel("Number of Turbines")
        ax.set_ylabel("Payback Time (Years)")

        Temp = ax.plot(Turbines, Payback_Times, label=("Diameter: 5.87m"), color="blue", linewidth=2)

        ax.legend()
        plt.minorticks_on()
        ax.grid(which='major', color='black', linestyle='-', linewidth=1)
        ax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)

def Print_Costs():
    print("Here are some costs")

def Set_Costs(**kwargs):
    print("Setting costs")

def Heads_Graph():          #Shows a graph of lagoon height and tidal height against time parameter. Tidal height is built into program, volume comes from analyitic function call.

    Volume, Time = Analyitic_Simulation_Simple()
    print(Volume)
    Tide_Heights = [0]
    M = 1453217

    for i in range(len(Volume)):
        Volume[i] = Volume[i]/M
        Tide_Heights.append(5*np.sin(2*np.pi*0.0000463*(i+1)+1)+5)

    print("Time: " + str(len(Time)))
    print("Volume: " + str(len(Volume)))
    print("Tide height: " + str(len(Tide_Heights)))
    plt.figure(figsize=plt.figaspect(1)*2)
    ax = plt.axes() #proj_type = 'ortho'
    plt.title("Lagoon Elevation Head And Tidal Height")
    ax.set_xlabel("Time (s)")
    ax.set_ylabel("Head (m)")
    ax.plot(Time, Volume)
    Time.append(Time[len(Time)-1]+1)
    ax.plot(Time, Tide_Heights)
    plt.minorticks_on()

    ax.grid(which='major', color='black', linestyle='-', linewidth=1)
    ax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)

def Tidal_Function_Testing(Interval=86400):     #Shows a graph of a desired tidal function hardcoded in. The time interval is taken as the parameter.

    xlim = np.arange(0, 60* 60 *26, 60*60*2)
    Time = [0]
    Tide_Height = [0]
    FFT_Tidal_Function = [0]

    for i in range(Interval):

        Time.append(i)
        #Tide_Height.append(6*np.cos(2*np.pi*0.0000231*i-np.pi)+6)
        #FFT_Tidal_Function.append((3.68*np.sin(2*np.pi*(2.236e-5)*i*10))+(1.499*np.cos(2*np.pi*(2.315e-5)*i*10))+(0.73*np.cos(2*np.pi*(2.195e-5)*i*10))+(0.3581*np.sin(2*np.pi*(2.322e-5)*i*10)))
        FFT_Tidal_Function.append((3.678*np.cos(2*np.pi*(2.236e-5)*i+(48.06*(np.pi/180))))+(1.4298*np.cos(2*np.pi*(2.239e-5)*i-(130.6*(np.pi/180))))+(1.4986*
                                  np.cos(2*np.pi*(2.315e-5)*i+(176.7*(np.pi/180))))+(0.7298*np.cos(2*np.pi*(2.194e-5)*i-(53.75*(np.pi/180)))) + 6.278)

    plt.figure(figsize=plt.figaspect(1)*2)
    ax = plt.axes() #proj_type = 'ortho'
    plt.title("Contiuous Tidal Function Over 2019 (From Newport DFT)")
    ax.set_xlabel("Time (s)")
    ax.set_ylabel("Ocean Height (m)")
    ax.plot(Time, FFT_Tidal_Function)
    plt.minorticks_on()
    #plt.xticks(xlim, [str(n).zfill(2) + ':00' for n in np.arange(0, 26,2)])
    ax.grid(which='major', color='black', linestyle='-', linewidth=1)
    ax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)

def Analyitic_Simulation_Simple(Duration=21600):        #Calculates the volume and graphs it over a time parameter analyitically.

    M = 1453217
    G = 9.807
    V_0 = 18891820
    Area = (np.power(7.5,2)/4)*np.pi
    Time = [0]
    Volume = [0]
    Volume[0] = V_0
    Euler_Volume = [V_0]

    for i in range(Duration):
        Time.append(i)
        Volume.append(np.power(np.sqrt(V_0)-0.5*Area*i*np.sqrt((2*G)/(M)),2))
        Euler_Volume.append(Euler_Volume[-1]-(Area*np.sqrt(2*G*(Euler_Volume[-1]/M))))

    plt.figure(figsize=plt.figaspect(1)*2)
    ax = plt.axes() #proj_type = 'ortho'
    plt.title("Volume Vs Time")
    ax.set_xlabel("Time (s)")
    ax.set_ylabel("Volume (m^3)")
    ax.plot(Time, Volume)
    ax.plot(Time, Euler_Volume)
    ax.legend("Analytical", "Numerical")
    plt.minorticks_on()
    ax.grid(which='major', color='black', linestyle='-', linewidth=1)
    ax.grid(which='minor', color='black', linestyle='--', linewidth=0.5)

    return Volume, Time

def Setup_Profile(states=[]):

    #Format [[State number, triggering lagoon head], ...]
    print("Adding profile to list of saved profiles. This is profile number " + str(len(Profile_List)+1))
    Profile_List.append(states)

def help(*args):

    if len(args) == 0:

        print("Help:")
        print("\n1. To run a simulation, you must first setup an operating profile to tell the lagoon how it should operate. To learn how to do this, type help('profile'). Alternativly, you can use a defaul profile already built into the program. Profile 1 is a simple single effect profile, profile 2 is a simple double effect profile.")
        print("\n2. To learn how to run a simulation, type help('sim').")

    elif args[0] == "profile":

        print("Help profile:")
        print("\nThe lagoon can be in one of 5 states at any given time: \
State 0 - This is the waiting state, the lagoon is waiting for a head difference to build up before opening the sluice gates.\n\
State 1 - The lagoon is filling WITHOUT generating electricity. In this state, turbines are not moving, water is entering the lagoon through the turbine shafts and through sluice gates in the wall.\n\
State 2 - The lagoon is draining whilst generating electricity.\n\
State 3 - The lagoon is filling whilst generating electricity.\n\
State 4 - The lagoon is draining WITHOUT generating electricity.\n")
        print("The operational algorithm governs which states the lagoon switches between and when. The algorithm is built up of stages, each stage coresponds to one state and the condition that must be met to move to the next stage (state).\
              For example: Setup_Profile([[1,12],[0,5],[2,0]]) has 3 stages. The first stage says the simulation will start in state 1 and will transition to the next state when the water height reaches 12 m, the second state is state 0 and\
 the transition from that is when the water height is at 5 m. The final state is state 2, when the water level reaches 0 m then cycle will loop back to state 1. The water height is in reference to the lagoon or ocean height\
 depending on what makes sense for that particular state. Similarly, transitions are taken when the water height is above or below the triggering value, not equal and is again dependant on the particular state. As a safety\
 mechanism to ensure the simulation always runs, if a state transition isnt met and the tide changes direction, the state will switch despite the condition not being\
 met. Any profile can theoretically be constructed with any number of stages using this template. Built in profiles 1 and 2 are single and double effect modes respectivly.")

    elif args[0] == "sim":

        print("Help simulation:")
        print("\nTo run a simulation, run the following function call and changes the parameters as you desire: \n")
        print('Run_Simulation(step=100, tidal_function="Newport_1", turbines=25, diameter=5.88, slucies=0, sluice_size=80, profile=2, time=60*60*24*365, econ=True, output=False, graphs=True, graph_head=True, graph_QV=True, graph_P=True))')
        print("\nThe above command executes a simulation using our final lagoon configuration over the course of a year. Alternativly, you can change the tidal_function parameter to 'sine', the time (in seconds) to a diffrerent amount and any other numerical value to what you want to test. The final boolen parameters are used to toggle the display opf certain graphs as well as debug information to the console. Make sure you have setup a profile for your simulation run or use one of the two built in (profiles 1 or 2).")

    else:

        print("Help command unknown, are you sure you typed it correctly?")

Setup_Profile([[1,12],[0,5],[2,0]])
Setup_Profile([[3,14],[0,5],[2,0],[4,7]])


import os
import random
from collections import OrderedDict
from datetime import datetime

import numpy as np
import torch
from torchvision import transforms
from tqdm import tqdm

import utils
from BB.models.BB_Inception_V3 import get_model_isic
from Logger.logger_cubs import Logger_CUBS
from dataset.dataset_ham10k import load_ham_data

def train(args):
    device = utils.get_device()
    random.seed(args.seed)
    np.random.seed(args.seed)
    torch.manual_seed(args.seed)
    root = f"lr_{args.lr}_epochs_{args.epochs}_optim_{args.optim}"
    chk_pt_path = os.path.join(args.checkpoints, args.dataset, "BB", root, args.arch)
    output_path = os.path.join(args.output, args.dataset, "BB", root, args.arch)
    tb_logs_path = os.path.join(args.logs, args.dataset, "BB", f"{root}_{args.arch}")
    os.makedirs(chk_pt_path, exist_ok=True)
    os.makedirs(output_path, exist_ok=True)
    os.makedirs(tb_logs_path, exist_ok=True)

    print(f"output_path: {output_path}")
    os.makedirs(output_path, exist_ok=True)

    normalize = transforms.Normalize(
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225]
    )
    transform = transforms.Compose(
        [
            transforms.Resize(299),
            transforms.CenterCrop(299),
            transforms.ToTensor(),
            normalize
        ]
    )

    model, model_bottom, model_top = get_model_isic(args.bb_dir, args.model_name)
    train_loader, val_loader, idx_to_class = load_ham_data(args, transform, args.class_to_idx)
    criterion = torch.nn.CrossEntropyLoss()
    solver = None
    if args.optim == "SGD":
        solver = torch.optim.SGD(
            model.parameters(),
            lr=args.lr,
            momentum=args.momentum,
            weight_decay=args.weight_decay
        )
    elif args.optim == "Adam":
        solver = torch.optim.Adam(
            filter(lambda p: p.requires_grad, model.parameters()),
            lr=args.lr, weight_decay=args.weight_decay
        )

    final_parameters = OrderedDict(
        arch=[args.arch],
        dataset=[args.dataset],
        now=[datetime.today().strftime('%Y-%m-%d-%HH-%MM-%SS')]
    )
    run_id = utils.get_runs(final_parameters)[0]
    run_manager = Logger_CUBS(
        1, chk_pt_path, tb_logs_path, output_path, train_loader, val_loader, len(args.labels)
    )

    model = fit(
        args,
        model,
        criterion,
        solver,
        train_loader,
        val_loader,
        run_manager,
        run_id,
        device
    )

    test_and_save_model(
        model, train_loader, val_loader, idx_to_class, output_path, args.labels, device
    )

def fit(
        args,
        net,
        criterion,
        solver,
        train_loader,
        val_loader,
        run_manager,
        run_id,
        device
):
    run_manager.begin_run(run_id)
    for epoch in range(args.epochs):
        run_manager.begin_epoch()
        net.train()
        with tqdm(total=len(train_loader)) as t:
            for batch_id, (images, target) in enumerate(train_loader):
                solver.zero_grad()
                images = images.to(device)
                target = target.to(device)
                y_hat = net(images)
                y_hat = y_hat.logits
                train_loss = criterion(y_hat, target)
                train_loss.backward()
                solver.step()

                run_manager.track_train_loss(train_loss.item())
                run_manager.track_total_train_correct_per_epoch(y_hat, target)
                t.set_postfix(
                    epoch='{0}'.format(epoch),
                    training_loss='{:05.3f}'.format(run_manager.epoch_train_loss))
                t.update()

        net.eval()
        out_put_GT = torch.FloatTensor().cuda()
        out_put_predict_bb = torch.FloatTensor().cuda()
        with torch.no_grad():
            with tqdm(total=len(val_loader)) as t:
                for batch_id, (images, target) in enumerate(val_loader):
                    images = images.to(device)
                    target = target.to(device)
                    y_hat = net(images)
                    val_loss = criterion(y_hat, target)

                    run_manager.track_val_loss(val_loss.item())
                    run_manager.track_total_val_correct_per_epoch(y_hat, target)

                    out_put_predict_bb = torch.cat((out_put_predict_bb, y_hat), dim=0)
                    out_put_GT = torch.cat((out_put_GT, target), dim=0)
                    t.set_postfix(
                        epoch='{0}'.format(epoch),
                        validation_loss='{:05.3f}'.format(run_manager.epoch_val_loss))
                    t.update()

        run_manager.end_epoch(net)
        proba = torch.nn.Softmax(dim=1)(out_put_predict_bb)[:, 1]
        val_auroc, val_aurpc = utils.compute_AUC(out_put_GT, pred=proba)
        print(f"Epoch: [{epoch + 1}/{args.epochs}] "
              f"Train_loss: {round(run_manager.get_final_train_loss(), 4)} "
              f"Train_Accuracy: {round(run_manager.get_final_train_accuracy(), 4)} (%) "
              f"Val_loss: {round(run_manager.get_final_val_loss(), 4)} "
              f"Val_Accuracy: {round(run_manager.get_final_val_accuracy(), 4)} (%) "
              f"Best_Val_Accuracy: {round(run_manager.get_final_best_val_accuracy(), 4)} (%)  "
              f"Val_AUROC: {round(val_auroc, 4)} (%)  "
              f"Val_AURPC: {round(val_aurpc, 4)} (%)  "
              f"Epoch_Duration: {round(run_manager.get_epoch_duration(), 4)}")

    run_manager.end_run()

    return net

def test_and_save_model(
        model, train_loader, val_loader, idx_to_class, output_path, labels, device
):
    model = model.eval()
    print(f"Train dataset size: {len(train_loader.dataset)}")
    print(f"Val dataset size: {len(val_loader.dataset)}")
    print(idx_to_class)

    out_put_GT = torch.FloatTensor().cuda()
    out_put_predict_bb = torch.FloatTensor().cuda()
    with tqdm(total=len(val_loader)) as t:
        for batch in enumerate(val_loader):
            batch_idx, (images, target) = batch
            images = images.to(device)
            target = target.to(device)
            with torch.no_grad():
                y_hat_bb = model(images)

            out_put_predict_bb = torch.cat((out_put_predict_bb, y_hat_bb), dim=0)
            out_put_GT = torch.cat((out_put_GT, target), dim=0)

            t.set_postfix(iteration=f"{batch_idx}")
            t.update()

    proba = torch.nn.Softmax(dim=1)(out_put_predict_bb)[:, 1]
    val_auroc, val_aurpc = utils.compute_AUC(out_put_GT, pred=proba)

    out_put_GT_np = out_put_GT.cpu().numpy()
    y_hat_bb = out_put_predict_bb.cpu().argmax(dim=1)
    acc_bb = utils.cal_accuracy(out_put_GT_np, y_hat_bb)
    cls_report = utils.cal_classification_report(out_put_GT_np, y_hat_bb, labels)
    print(f"Accuracy of the network: {acc_bb * 100} (%)")
    print(f"Val AUROC of the network: {val_auroc} (0-1)")
    print(cls_report)

    np.save(os.path.join(output_path, f"out_put_GT_prune.npy"), out_put_GT_np)
    torch.save(out_put_predict_bb.cpu(), os.path.join(output_path, f"out_put_predict_logits_bb.pt"))
    torch.save(y_hat_bb, os.path.join(output_path, f"out_put_predict_bb.pt"))
    print(os.path.join(output_path, f"out_put_predict_bb.pt"))
    # np.save(os.path.join(output_path, f"out_put_predict_bb_prune.npy"), y_hat_bb)
    utils.dump_in_pickle(
        output_path=output_path, file_name="classification_report.pkl", stats_to_dump=cls_report
    )


import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import math, copy, time, pickle
from torch.autograd import Variable
from torch import optim

import random
import argparse
from statistics import mean
import math
import os

import Model_s
import Dataloader_elmo1
import optim_custorm
from allennlp.training.optimizers import DenseSparseAdam

from logger import Logger
from util import cal_f1
from util.preprocessing import perpareDataset
from config_uni import *

parser = argparse.ArgumentParser(description='multi_tagger')
parser.add_argument('--gpu', type=str, default='20', help='# of machine')
parser.add_argument('--mode', type=str, default='train', help='mode')
parser.add_argument('--optim', type=str, default='sgd', help='optim: adam, sgd')
parser.add_argument('--decay', type=str, default='normal', help='decay')
parser.add_argument('--dropout', type=float, default=0.3, help='dropout')
parser.add_argument('--weight', type=float, default=1, help='weight')
parser.add_argument('--momentum', type=float, default=.9, help='momentum')

args = parser.parse_args()

pkl_path = IO["pkl_path"]
model_path = IO["model_path"] + args.gpu

def extract_data(data_holder, name):
    data = [iter(item[name]) for item in data_holder]
    lens = [len(i) for i in data]
    return data, lens
def sample_idx(idx_list, count_list, lens_list):
    idx = random.choice(idx_list)
    count_list[idx] += 1
    if count_list[idx] == lens_list[idx]:
        idx_list.remove(idx)
    return idx, idx_list, count_list
def show_result(list1, list2, list3, list4, id2task, logger=None, step=None):
    for i, t in enumerate(zip(list1, list2, list3, list4)):
        print("%s prec: %f, rec: %f, F1: %f, acc: %f" %(id2task[i], t[0]*100, t[1]*100, t[2]*100, t[3]*100))
        #for idx, idc in enumerate(indicator):
        #    logger.scalar_summary(id2task[i]+"_"+idc, t[idx]*100, step+1)

def main():
    pkl_path = perpareDataset(embeddingsPath, datasets_config)

    data_holder, task2id, id2task, num_feat, num_voc, num_char, tgt_dict, embeddings = Dataloader_elmo1.multitask_dataloader(pkl_path, num_task=num_task, batch_size=BATCH_SIZE)
    para = model_para
    task2label = {"conll2000": "chunk", "unidep": "POS", "conll2003": "NER"}
    logger = Logger('./logs/'+str(args.gpu))
    para["id2task"] = id2task
    para["n_feats"] = num_feat
    para["n_vocs"] = num_voc
    para["n_tasks"] = num_task
    para["out_size"] = [len(tgt_dict[task2label[id2task[ids]]]) for ids in range(num_task)]
    para["n_chars"] = num_char
    model = Model_s.build_model_cnn(para)
    model.Word_embeddings.apply_weights(embeddings)

    params = list(filter(lambda p: p.requires_grad, model.parameters()))
    num_params = sum(p.numel() for p in model.parameters())
    print(model)
    def lr_decay(optimizer, epoch, decay_rate=0.05, init_lr=0.015):
        lr = init_lr/(1+decay_rate*epoch)
        for param_group in optimizer.param_groups:
            param_group['lr'] = lr
        return optimizer
    def exp_lr_decay(optimizer, epoch, decay_rate=0.05, init_lr=0.015):
        lr = init_lr * decay_rate ** epoch
        for param_group in optimizer.param_groups:
            param_group['lr'] = lr
        return optimizer

    if args.optim == "adam":
        model_optim = optim_custorm.adam(para["d_hid"], DenseSparseAdam(params, lr=0.0015, betas=(0.9, 0.98), eps=1e-9))
        args.decay = None
    elif args.optim == "sgd":
        model_optim = optim.SGD(params, lr=0.015, momentum=args.momentum, weight_decay=1e-8)
    if args.mode == "train":
        best_F1 = 0
        if not para["crf"]:
            calculate_loss = nn.NLLLoss()
        else:
            calculate_loss = None
        print("Start training...")
        print('-' * 60)
        KLLoss = None
        start_point = time.time()
        for epoch_idx in range(NUM_EPOCH):

            if args.optim == "sgd":
                if args.decay == "exp":
                    model_optim = exp_lr_decay(model_optim, epoch_idx)
                elif args.decay == "normal":
                    model_optim = lr_decay(model_optim, epoch_idx)
            Pre, Rec, F1, loss_list = run_epoch(model,
                                                data_holder,
                                                model_optim,
                                                calculate_loss,
                                                KLLoss,
                                                para,
                                                epoch_idx,
                                                id2task,
                                                logger)

            use_time = time.time() - start_point
            print("Time using: %f mins" %(use_time/60))
            if not best_F1 or best_F1 < F1:
                best_F1 = F1
                Model_s.save_model(model_path, model, para)
                print('*' * 60)
                print("Save model with average Pre: %f, Rec: %f, F1: %f on dev set." %(Pre, Rec, F1))
                save_idx = epoch_idx
                print('*' * 60)
        print("save model at epoch:", save_idx)
    elif args.mode == "finetune":
        para_path = os.path.join(path, 'para.pkl')
        with open(para_path, "wb") as f:
            para_save = pickle.load(f)
        model = Model_s.build_model(para_save)
        model = Model_s.read_model(model_path, model)

        params = list(filter(lambda p: p.requires_grad, model.parameters()))
        model_optim = optim_custorm.adam(para["d_hid"], 1, 800, torch.optim.SGD(params, lr=0.0, momentum=0.9))

    else:
        para_path = os.path.join(model_path, 'para.pkl')
        with open(para_path, "rb") as f:
            para_save = pickle.load(f)
        model = Model_s.build_model_cnn(para_save)
        model = Model_s.read_model(model_path, model)
        prec_list_test, rec_list_test, f1_list_test, acc_list_test = infer(model, data_holder, "test")

def wrap_variable(flag, *args):
    return [Variable(tensor, volatile=flag).cuda() if USE_CUDA else Variable(tensor) for tensor in args]

def update_log(model, logger, loss, step):
    # 1. Log scalar values (scalar summary)
    info = { 'loss': loss.data[0]}

    for tag, value in info.items():
        logger.scalar_summary(tag, value, step+1)

    # 2. Log values and gradients of the parameters (histogram summary)
    for tag, value in model.named_parameters():
        tag = tag.replace('.', '/')
        logger.histo_summary(tag, value.data.cpu().numpy(), step+1)
        logger.histo_summary(tag+'/grad', value.grad.data.cpu().numpy(), step+1)

def run_epoch(model,
              data_holder,
              model_optim,
              calculate_loss,
              KLLoss, para,
              epoch_idx,
              id2task,
              logger):####

    model.train()
    train_data, train_lens = extract_data(data_holder, "train")
    idx_list = [idx for idx in range(len(train_data))]
    count_list = [0 for i in range(len(train_data))]
    total_loss = 0
    loss_list = [0] * len(id2task)
    h_s_norm_list = []
    h_t_norm_list = [[] for i in range(len(id2task))]
    tmp = 0
    tmp_list = [0 for i in range(len(id2task))]
    start_time = time.time()

    for i in range(sum(train_lens)):
        idx, idx_list, count_list = sample_idx(idx_list, count_list, train_lens)
        model.zero_grad()
        src_seqs, src_masks, src_feats, tgt_seqs, tgt_masks, src_chars, _, src_tokens = next(train_data[idx])
        #src_seqs, src_masks, src_feats, tgt_seqs, tgt_masks, src_chars = wrap_variable(False, src_seqs, src_masks, src_feats, tgt_seqs, tgt_masks, src_chars)
        batch_size, seq_len = src_seqs.size()
        neglog, h_p, h_s = model(src_seqs, src_masks, src_feats, src_chars,
               tgt_seqs, tgt_masks, idx, src_tokens)

        if para["crf"]:
            loss = - neglog
        else:
            loss = calculate_loss(neglog.view(batch_size*seq_len, -1), tgt_seqs.view(batch_size*seq_len))*(batch_size*seq_len)/torch.sum(tgt_masks)
            l2_reg = None

        if id2task[idx] == "conll2003":
            loss = loss * args.weight

        loss_list[idx] += loss.detach().item()
        loss.backward()
        total_loss += loss.detach().item()
        torch.nn.utils.clip_grad_norm_(model.parameters(), CLIP)
        lr_now = model_optim.step()

        if i % PRINT_EVERY == 0 and i:
            using_time = time.time() - start_time
            print('| ep %2d | %4d/%5d btcs | ms/btc %4.4f | loss %5.7f |' %(epoch_idx+1, i, sum(train_lens), using_time * 1000 / (PRINT_EVERY*batch_size), total_loss/PRINT_EVERY))
            total_loss = 0

    logger.scalar_summary("h_s_norm", sum(h_s_norm_list)/len(h_s_norm_list) - tmp, epoch_idx+1)
    tmp = sum(h_s_norm_list)/len(h_s_norm_list)

    for idx in range(len(id2task)):
        logger.scalar_summary("h_t_norm"+str(idx), sum(h_t_norm_list[idx])/len(h_t_norm_list[idx]) - tmp_list[idx], epoch_idx+1)
        tmp_list[idx] = sum(h_t_norm_list[idx])/len(h_t_norm_list[idx])
    prec_list_dev, rec_list_dev, f1_list_dev, acc_list_dev = infer(model, data_holder, "dev")

    print('-' * 60)
    print("On dev set:")
    show_result(prec_list_dev, rec_list_dev, f1_list_dev, acc_list_dev, para["id2task"])

    return mean(prec_list_dev), mean(rec_list_dev), mean(f1_list_dev), loss_list

def infer(model, data_holder, name):
    model.eval()
    dev_data, _ = extract_data(data_holder, name)
    prf_list = []
    with torch.no_grad():
        for i, task in enumerate(dev_data):
            confusion_list = []
            for idx in range(len(task)):
                src_seqs, src_masks, src_feats, tgt_seqs, tgt_masks, src_chars, tgt_list, src_tokens = next(task)
                #src_seqs, src_masks, src_feats, tgt_seqs, tgt_masks, src_chars = wrap_variable(True, src_seqs, src_masks, src_feats, tgt_seqs, tgt_masks, src_chars)
                preds = model.predict(src_seqs, src_masks, src_feats, src_chars,tgt_seqs, tgt_masks, i, src_tokens)
                if CRF_FLAG:
                    prec_num, prec_den, rec_num, rec_den, correct_labels, num_label = cal_f1.evaluate_acc(tgt_list, preds)
                else:
                    prec_num, prec_den, rec_num, rec_den, correct_labels, num_label = cal_f1.evaluate_acc_(tgt_seqs, preds, src_masks)
                confusion_list += [prec_num, prec_den, rec_num, rec_den, correct_labels, num_label]

            prec, rec, f1, acc = cal_f1.eval_f1(sum(confusion_list[0::6]), sum(confusion_list[1::6]), sum(confusion_list[2::6]), sum(confusion_list[3::6]), sum(confusion_list[4::6]), sum(confusion_list[5::6]))
            prf_list += [prec, rec, f1, acc]
    return prf_list[0::4], prf_list[1::4], prf_list[2::4], prf_list[3::4]

if __name__ == '__main__':
    main()


from config import mail_user, mail_pwd, smtp_host, mail_to_list, mail_title
from ketang_login import Browser
from send_mail import SendEmail
from logger import Logger

logger = Logger('edu').logger
mail = SendEmail(smtp_host, mail_user, mail_pwd)
browser = Browser()

# def alert(msg):
#     logger.info(msg)
#     msg = str(msg)
#     mail.sendTxtMail(mail_to_list, mail_title, msg, logger)
#     return False

if __name__ == '__main__':
    url = 'https://edu.codeaha.com/school'
    if browser.web_test(url, logger, mail, mail_to_list, mail_title):
        print('测试通过')
    else:
        print('测试失败，请看日志')
    # 分割行
    logger.info('================================================')


import os
import time
import pickle

from operator import itemgetter

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import LabelEncoder
from sklearn.linear_model import SGDClassifier
from sklearn.metrics import classification_report as clsr
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cross_validation import train_test_split as tts

from NLTKPreprocessor import NLTKPreprocessor

def timeit(func):
    """
    Simple timing decorator
    """
    def wrapper(*args, **kwargs):
        start  = time.time()
        result = func(*args, **kwargs)
        delta  = time.time() - start
        return result, delta
    return wrapper

def identity(arg):
    """
    Simple identity function works as a passthrough.
    """
    return arg

@timeit
def train(X, y, classifier=SGDClassifier, outpath=None, verbose=True):
    """
    Builds a classifer for the given list of documents and targets in two
    stages: the first does a train/test split and prints a classifier report,
    the second rebuilds the model on the entire corpus and returns it for
    operationalization.

    X: a list or iterable of raw strings, each representing a document.
    y: a list or iterable of labels, which will be label encoded.

    Can specify the classifier to build with: if a class is specified then
    this will build the model with the Scikit-Learn defaults, if an instance
    is given, then it will be used directly in the build pipeline.

    If outpath is given, this function will write the model as a pickle.
    If verbose, this function will print out information to the command line.
    """

    @timeit
    def build(classifier, X, y=None):
        """
        Inner build function that builds a single model.
        """
        if isinstance(classifier, type):
            classifier = classifier()

        model = Pipeline([
            ('preprocessor', NLTKPreprocessor()),
            ('vectorizer', TfidfVectorizer(tokenizer=identity, preprocessor=None, lowercase=False)),
            ('classifier', classifier),
        ])

        model.fit(X, y)
        return model

    # Label encode the targets
    labels = LabelEncoder()
    y = labels.fit_transform(y)

    """
    # Begin evaluation
    if verbose: print("Building for evaluation")
    X_train, X_test, y_train, y_test = tts(X, y, test_size=0.2)
    model, secs = build(classifier, X_train, y_train)

    if verbose: print("Evaluation model fit in {:0.3f} seconds".format(secs))
    if verbose: print("Classification Report:\n")

    y_pred = model.predict(X_test)
    print(clsr(y_test, y_pred, target_names=labels.classes_))

    if verbose: print("Building complete model and saving ...")
    """

    model, secs = build(classifier, X, y)
    model.labels_ = labels

    if verbose: print("Complete model fit in {:0.3f} seconds".format(secs))

    if outpath:
        with open(outpath, 'wb') as f:
            pickle.dump(model, f)

            if verbose:print("Model written out to {}".format(outpath))

    return model

def predict(text,PATH):
    with open(PATH, 'rb') as f:
        model = pickle.load(f)

    yhat = model.predict([
        text
    ])
    return {
        "class":model.labels_.inverse_transform(yhat),
        "accuracy":1
    }
def show_most_informative_features(model, text=None, n=20):
    """
    Accepts a Pipeline with a classifer and a TfidfVectorizer and computes
    the n most informative features of the model. If text is given, then will
    compute the most informative features for classifying that text.

    Note that this function will only work on linear models with coefs_
    """
    # Extract the vectorizer and the classifier from the pipeline
    vectorizer = model.named_steps['vectorizer']
    classifier = model.named_steps['classifier']

    # Check to make sure that we can perform this computation
    if not hasattr(classifier, 'coef_'):
        raise TypeError(
            "Cannot compute most informative features on {} model.".format(
                classifier.__class__.__name__
            )
        )

    if text is not None:
        # Compute the coefficients for the text
        tvec = model.transform([text]).toarray()
    else:
        # Otherwise simply use the coefficients
        tvec = classifier.coef_

    # Zip the feature names with the coefs and sort
    coefs = sorted(
        zip(tvec[0], vectorizer.get_feature_names()),
        key=itemgetter(0), reverse=True
    )

    topn  = zip(coefs[:n], coefs[:-(n+1):-1])

    # Create the output string to return
    output = []

    # If text, add the predicted value to the output.
    if text is not None:
        output.append("\"{}\"".format(text))
        output.append("Classified as: {}".format(model.predict([text])))
        output.append("")

    # Create two columns with most negative and most positive features.
    for (cp, fnp), (cn, fnn) in topn:
        output.append(
            "{:0.4f}{: >15}    {:0.4f}{: >15}".format(cp, fnp, cn, fnn)
        )

    return "\n".join(output)

if __name__ == "__main__":
    PATH = "model.pickle"

    if not os.path.exists(PATH):
        # Time to build the model
        from nltk.corpus import movie_reviews as reviews

        X = ["hello friend",
             "haai",
             "hey",
             " ",
             "hii",
             "hey",
             ""]
        y = ["greeting",
             "greeting",
             "greeting",
             "fallback",
             "greeting",
             "greeting",
             "fallback"]

        model = train(X, y, outpath=PATH)

    else:
        with open(PATH, 'rb') as f:
            model = pickle.load(f)

    print(show_most_informative_features(model))

    with open(PATH, 'rb') as f:
        model = pickle.load(f)

    yhat = model.predict([
        "good morning"
    ])

    print(model.labels_.inverse_transform(yhat))


import numpy as np
import matplotlib.pyplot as plt

from identification.adaptive_lsm import lsm_linear_parametrization
from identification.identifier import Identifier
from model.model_obj_builder import create_model
from pid_reg.my_pid import PID

def get_obj_value(x, u, t):
    h = 0
    if t < 10:
        h = -5
    elif 10 <= t < 30:
        h = -10
    elif t >= 30:
        h = 0
    return x + u + h  # + np.random.uniform(-1, 1)
    # return x + u

def generator(t):
    y = 25
    if 5 <= t < 15:
        y = 80
    elif 15 <= t < 25:
        y = 30
    elif 25 <= t < 35:
        y = 60
    return y

def get_set_point(t):
    y = 20
    if 5 <= t < 15:
        y = 70
    elif 15 <= t < 25:
        y = 70 + 2 * (t - 15)
    elif 25 <= t < 35:
        y = 90 - 1.5 * (t - 25)
    return y

def test():
    t = 40
    f = 5  # 0.2 сек.

    # pid_reg = PID(0.8, 0.15, 0.1, f)
    pid = PID(1, 0.5, 0.1, f)
    pid.set_constraints(u1=-5, u2=15)

    x = []
    set_point = []
    y = []
    feedback_list = []

    t_i = 0.
    feedback = 0

    for i in range(t*f):
        x.append(t_i)
        set_point.append(generator(t_i))

        feedback_list.append(feedback)
        if i == 0:
            feedback = get_obj_value(feedback, 0, t_i)
        else:
            feedback = get_obj_value(feedback, y[-1], t_i)

        output = pid.update(set_point[i], feedback)

        y.append(output)
        t_i += 1 / f
    print(x)
    print(set_point)
    print(y)
    print(feedback_list)

    # time_sm = np.array(x)
    # time_smooth = np.linspace(time_sm.min(), time_sm.max(), num=1001, endpoint=True)
    # feedback_smooth = spline(x, feedback_list, time_smooth)

    # f2 = interp1d(x, feedback_list, kind='cubic')

    # plt.plot(time_smooth, feedback_smooth, label="2")
    # plt.plot(time_smooth, f2(time_smooth), label="1")

    plt.plot(x, y, label="Управление")
    plt.plot(x, set_point, '--', label="Уставка")
    plt.plot(x, feedback_list, label="Объект")
    plt.xlabel('time (s)')
    plt.ylabel('PID (PV)')
    plt.title('TEST PID')

    plt.grid(True)
    plt.legend()
    plt.show()

def test_asi():
    model = create_model("x(t-1)+u(t-1)+a0")
    print(model.model_expr)
    print(model.obj_expr)

    print(model.x_names)
    print(model.u_names)
    print(model.a_names)

    model.initialization(a0=0)
    print(model.value_x)
    print(model.value_u)
    print(model.value_a)

    f = 5
    t = 40
    t_i = 0.
    x = []
    set_point = []  # уставка
    obj_values = []  # значения объекта
    u_val = []  # значенея управления
    y = []
    a_val = np.zeros(f * t)  # значния коэффициента
    obj = 0
    u = 0
    model.number_averaged_values = 1
    identifier = Identifier(model=model, n=1, sigma=0.1)
    grad = []
    for i in range(t*f):
        x.append(t_i)
        set_point.append(generator(t_i))
        u_val.append(u)

        obj_values.append(obj)
        if i == 0:
            obj = get_obj_value(obj, 0, t_i)
        else:
            obj = get_obj_value(obj, u_val[-1], t_i)

        if i < identifier.n_0:
            y.append(model.func_model(*model.value_x, *model.value_u, *model.value_a))
            grad.append([func(*model.value_x, *model.value_u, *model.value_a) for func in model.grad])
            model.value_a = [a_val[i]]
            a_val[i + 1] = a_val[i]
        else:
            # Начинаем проводить корректировку параметров модели.
            if i == identifier.n_0:
                identifier.grad = grad
            y.append(model.func_model(*model.value_x, *model.value_u, *model.value_a))
            a_n = lsm_linear_parametrization(identifier, obj, 0.33, lbd=0.5)
            model.value_a = list(a_n)
            a_val[i] = a_n

        model.value_x = [obj]

        v = generator(t_i + 1 / f) - obj - a_val[i]

        if v < -10:
            v = -10
        elif v > 20:
            v = 20

        model.value_u = [v]
        u = v

        t_i += 1 / f

    print(u_val)
    print(y)
    print(obj_values)
    print(a_val)

    plt.plot(x, y, label="Модель")
    plt.plot(x, u_val, label="Управление")
    plt.plot(x, obj_values, label="Объект")
    plt.plot(x, set_point, '--', label="Уставка")
    plt.plot(x, a_val, label="a0")
    plt.grid(True)
    legend = plt.legend()
    legend.draggable(True)
    plt.show()

def main():
    # test()
    test_asi()
    # pid_reg = PID(1, 0.5, 0.1, 10)
    # print(pid_reg)

if __name__ == "__main__":
    main()


from django.shortcuts import render
from django.http import HttpResponse
from .models import *
from .forms import *

# Create your views here.

def join(request):
    if request.method == 'POST':
        form = JoinPlayer(request.POST)
        if form.is_valid():
            try:
                print(form.cleaned_data)
                form.save()
                return HttpResponse()
            except:
                form.add_error(None, 'Join player err')
        else:
            form = JoinPlayer()
    return HttpResponse()

def move(request, val):
    _v = val.split('-')
    print(_v)
    p = Player.objects.get(nickname=_v[0])
    p.xPos = (int)(_v[1])
    p.yPos = (int)(_v[2])
    p.save()
    return HttpResponse()

def getPlayers(request):
    return HttpResponse(list(Player.objects.all()))

def kill(request, nickname):
    Player.objects.filter(nickname=nickname).delete()
    return HttpResponse()


import streamlit as st
from textblob import TextBlob
import pandas as pd
import altair as alt
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
from PIL import Image

st.set_page_config(page_title='iPhone 4s REVIEWS SENTIMENT ANALYSIS', page_icon=':diamond_shape_with_a_dot_inside:')

image = Image.open('pic.png')
st.image(image)

def convert_to_df(sentiment):
	sentiment_dict = {'polarity':sentiment.polarity,'subjectivity':sentiment.subjectivity}
	sentiment_df = pd.DataFrame(sentiment_dict.items(),columns=['metric','value'])
	return sentiment_df

def analyze_token_sentiment(docx):
	analyzer = SentimentIntensityAnalyzer()
	pos_list = []
	neg_list = []
	neu_list = []
	for i in docx.split():
		res = analyzer.polarity_scores(i)['compound']
		if res > 0.1:
			pos_list.append(i)
			pos_list.append(res)

		elif res <= -0.1:
			neg_list.append(i)
			neg_list.append(res)
		else:
			neu_list.append(i)

	result = {'positives':pos_list,'negatives':neg_list,'neutral':neu_list}
	return result

def main():
	st.subheader(":iphone: iPhone 4s Review Sentiment Analysis")
	st.markdown(":wave: This is an app simple web app for sentiment analysis.")

	menu = ["Home","About"]
	choice = st.sidebar.selectbox("Menu",menu)

	if choice == "Home":
		st.subheader("Home")
		with st.form(key='nlpForm'):
			raw_text = st.text_area("Enter Text Here")
			submit_button = st.form_submit_button(label='Analyze')

		col1,col2 = st.columns(2)
		if submit_button:

			with col1:
				st.info("Results")
				sentiment = TextBlob(raw_text).sentiment
				st.write(sentiment)

				if sentiment.polarity > 0:
					st.markdown("Sentiment:: Positive :smiley: ")
				elif sentiment.polarity < 0:
					st.markdown("Sentiment:: Negative :rage: ")
				else:
					st.markdown("Sentiment:: Neutral ðŸ˜� ")

				result_df = convert_to_df(sentiment)
				st.dataframe(result_df)

				c = alt.Chart(result_df).mark_bar().encode(
					x='metric',
					y='value',
					color='metric')
				st.altair_chart(c,use_container_width=True)

			with col2:
				st.info("Token Sentiment")

				token_sentiments = analyze_token_sentiment(raw_text)
				st.write(token_sentiments)
	else:
		st.subheader("About")

if __name__ == '__main__':
	main()


# Заменям 'Report time' със 'Search time'
# Добавям бутон 'Search' в мода
# Добавям поле за въвеждане на брой WS цикли

import colorsys
from tkinter import *
from tkinter.ttk import *
import math
import datetime
import json

mode = ' '

window = Tk()
window.title("Battery Life Estimator ")
window.geometry("695x695")
window.configure(bg="gray")

sv1 = StringVar()
sv2 = StringVar()

sv3 = StringVar()
sv4 = StringVar()
sv5 = StringVar()
sv6 = StringVar()
sv7 = StringVar()
sv8 = StringVar()
sv9 = StringVar()
sv10= StringVar()

# trace wants a callback with nearly useless parameters, fixing with lambda.
sv1.trace('w', lambda nm, idx, mode, var=sv1: validate_float(var))
sv2.trace('w', lambda nm, idx, mode, var=sv2: validate_float(var))
sv3.trace('w', lambda nm, idx, mode, var=sv3: validate_float(var))
sv4.trace('w', lambda nm, idx, mode, var=sv4: validate_float(var))
sv5.trace('w', lambda nm, idx, mode, var=sv5: validate_float(var))
sv6.trace('w', lambda nm, idx, mode, var=sv6: validate_float(var))
sv7.trace('w', lambda nm, idx, mode, var=sv7: validate_float(var))
sv8.trace('w', lambda nm, idx, mode, var=sv8: validate_float(var))
sv9.trace('w', lambda nm, idx, mode, var=sv9: validate_float(var))
sv10.trace('w', lambda nm, idx, mode, var=sv10: validate_float(var))

txt_empty0 = Entry(window, width=0).grid(row=0, column=6)
txt_empty1 = Entry(window, width=0).grid(row=1, column=6)
txt_empty2 = Entry(window, width=1).grid(row=3, column=6)
txt_empty3 = Entry(window, width=1).grid(row=5, column=6)
txt_empty4 = Entry(window, width=1).grid(row=7, column=6)
txt_empty5 = Entry(window, width=1).grid(row=9, column=6)
txt_empty6 = Entry(window, width=1).grid(row=11, column=6)
txt_empty7 = Entry(window, width=1).grid(row=13, column=6)
txt_empty8 = Entry(window, width=1).grid(row=15, column=6)
txt_empty9 = Entry(window, width=1).grid(row=17, column=6)
txt_empty13 = Entry(window, width=1).grid(row=18, column=6)
txt_empty14 = Entry(window, width=1).grid(row=19, column=6)
txt_empty11 = Entry(window, width=1).grid(row=20, column=6)
txt_empty10 = Entry(window, width=1).grid(row=21, column=6)
txt_empty12 = Entry(window, width=1).grid(row=24, column=6)

lbl1 = Label(window, text="Battery capacity", font=("Arial Bold", 15))
lbl1.grid(column=0, row=0)
lbl3 = Label(window, text="Capacity *", font=("Arial Bold", 13))
lbl3.grid(column=0, row=2)
lbl5 = Label(window, text="Device", font=("Arial Bold", 15))
lbl5.grid(column=0, row=4)
lbl8 = Label(window, text="Search time ", font=("Arial Bold", 13))
lbl8.grid(column=0, row=8)
lbl9 = Label(window, text="Work time *", font=("Arial Bold", 13))
lbl9.grid(column=0, row=10)
lbl10 = Label(window, text="Sleep time *", font=("Arial Bold", 13))
lbl10.grid(column=0, row=12)
lbl13 = Label(window, text="Battery self discharge", font=("Arial Bold", 15))
lbl13.grid(column=0, row=14)
lbl15 = Label(window, text="self discharge", font=("Arial Bold", 13))
lbl15.grid(column=0, row=16)
lbl19 = Label(window, text="Cyclic mode *", font=("Arial Bold", 15))
lbl19.grid(column=0, row=18)
lbl21 = Label(window, text="Estimated battery lifetime", font=("Arial Bold", 15))
lbl21.grid(column=0, row=20)
lbl23 = Label(window, width=10, text="Lifetime", font=("Arial Bold", 13))
lbl23.grid(column=0, row=22)
lbl24 = Label(window, width=10, text="Iterations", font=("Arial Bold", 13))
lbl24.grid(column=0, row=23)

lbl_lifetime = Label(window, width=10, text=" ", font=("Arial Bold", 13))
lbl_lifetime.grid(column=1, row=22)
lbl_iterations = Label(window, width=10, text=" ", font=("Arial Bold", 13))
lbl_iterations.grid(column=1, row=23)

lbl61 = Label(window, text="Ah", font=("Arial Bold", 13))
lbl61.grid(column=3, row=2)
lbl62 = Label(window,width=10, text="Search cap", font=("Arial Bold", 13))
lbl62.grid(column=3, row=8)
lbl63 = Label(window, text="s", font=("Arial Bold", 13))
lbl63.grid(column=2, row=8)
lbl63 = Label(window, text="s", font=("Arial Bold", 13))
lbl63.grid(column=3, row=10)
lbl64 = Label(window, text="s", font=("Arial Bold", 13))
lbl64.grid(column=3, row=12)
lbl65 = Label(window, text="%", font=("Arial Bold", 13))
lbl65.grid(column=3, row=16)

lbl100 = Label(window, text="      ", width=12, font=("Arial Bold", 13))
lbl100.grid(column=2, row=18)

lbl101 = Label(window, text="mA", width=3, font=("Arial Bold", 13))
lbl101.grid(column=5, row=8)
lbl_wrc_cap = Label(window, text="work cap ", width=12, font=("Arial Bold", 13))
lbl_wrc_cap.grid(column=4, row=10)
lbl102 = Label(window, text="mA", width=3, font=("Arial Bold", 13))
lbl102.grid(column=5, row=10)
lbl_slp_cap = Label(window, text="sleep cap ", width=12, font=("Arial Bold", 13))
lbl_slp_cap.grid(column=4, row=12)
lbl103 = Label(window, text="mA", width=3, font=("Arial Bold", 13))
lbl103.grid(column=5, row=12)
lbl_ws_cycles = Label(window, width=13, text="WS Cycles", font=("Arial Bold", 13))
lbl_ws_cycles.grid(column=4, row=18)

txt_batt_cap = Entry(window, validate='key', textvariable=sv1, width=10)
txt_batt_cap.grid(column=2, row=2)
txt_src_time = Entry(window, validate='key', textvariable=sv2, width=10)
txt_src_time.grid(column=1, row=8)
txt_wrk_time = Entry(window, validate='key', textvariable=sv3, width=10)
txt_wrk_time.grid(column=2, row=10)
txt_slp_time = Entry(window, validate='key', textvariable=sv4, width=10)
txt_slp_time.grid(column=2, row=12)
txt_bsd = Entry(window, validate='key', textvariable=sv5, width=10)
txt_bsd.grid(column=2, row=16)
txt_rep_freq = Entry(window,validate='key', textvariable=sv9, width=10)
txt_rep_freq.grid(row=19, column=4)

txt_src_cap = Entry(window, validate='key', textvariable=sv10, width=10)
txt_src_cap.grid(column=4, row=8)

combo = Combobox(window)
combo['values'] = ("Findy Bike", "Findy Car", "Findy Raid", "Findy Pet", "Water Meter", "Test Device")
combo.current(0)
combo.grid(column=0, row=6)

flag = False  # flag_test_device
flag1 = False # Check Enter Valid data

def clicked_get_device():
    kind_of_device = combo.get()
    global class_device, txt_rep_cap, txt_wrk_cap, txt_slp_cap, flag
    if kind_of_device == "Test Device":
        flag = True
    if kind_of_device == "Test Device":

        txt_wrk_cap = Entry(window, validate='key', textvariable=sv7, width=10)
        txt_wrk_cap.grid(column=4, row=11)
        txt_slp_cap = Entry(window, validate='key', textvariable=sv8, width=10)
        txt_slp_cap.grid(column=4, row=13)

        lbl_wrc_cap.configure(text="work cap")
        lbl_slp_cap.configure(text="sleep cap")
    else:
        if flag:
            txt_rep_cap.delete(0, END)
            txt_wrk_cap.delete(0, END)
            txt_slp_cap.delete(0, END)

        device = combo.get()
        if device == "Findy Bike":
            class_device = FB
        elif device == "Findy Car":
            class_device = FC
        elif device == "Findy Pet":
            class_device = F5
        elif device == "Findy Raid":
            class_device = FR
        elif device == "Water Meter":
            class_device = WaterMeter

        lbl_wrc_cap.configure(text=class_device.AVG_CURRENT_CONSUMPTION_WORK)
        lbl_slp_cap.configure(text=class_device.AVG_CURRENT_CONSUMPTION_SLEEP)

def check_valid_data():
    global flag1

    if len(txt_batt_cap.get()) == 0:
        lbl_iterations.configure(text="batt cap")
    elif len(txt_wrk_time.get()) == 0:
        lbl_iterations.configure(text="work time")
    elif len(txt_slp_time.get()) == 0:
        lbl_iterations.configure(text="sleep time")
    elif mode == '' or mode == ' ':
        lbl_iterations.configure(text="")
        lbl_iterations.configure(text="mode")
    else:
        flag1 = True

    if not combo.get() == "Test Device":
        if flag1:
            print_result()
    else:
        clicked_get_device()
        if flag1:

            if len(txt_wrk_cap.get()) == 0:
                lbl_lifetime.configure(text="Enter")
                lbl_iterations.configure(text="work cap")
            elif len(txt_slp_cap.get()) == 0:
                lbl_lifetime.configure(text="Enter")
                lbl_iterations.configure(text="sleep cap")
            else:
                print_result()
    if flag1 == False:
        lbl_lifetime.configure(text="Enter")

    print(mode)

def print_result():
    result_dict = {}
    clicked_get_device()

    global class_device
    batt_capacity = float(txt_batt_cap.get())
    work_time = float(txt_wrk_time.get()) / 3600
    sleep_time = float(txt_slp_time.get()) / 3600

    if len(txt_bsd.get()) == 0:
        batt_self_discharge = 0
    else:
        batt_self_discharge = float(txt_bsd.get())

    if len(txt_src_time.get()) == 0:
        search_time = 0
    else:
        search_time = float(txt_src_time.get()) / 3600

    if len(txt_src_cap.get()) == 0:
        search_cap = 0
    else:
        search_cap = float(txt_src_cap.get())

    device = combo.get()
    type_report = ws_cycles()

    if device == "Findy Bike":
        class_device = FB
    elif device == "Findy Car":
        class_device = FC
    elif device == "Findy Pet":
        class_device = F5
    elif device == "Findy Raid":
        class_device = FR
    elif device == "Water Meter":
        class_device = WaterMeter
    else:
        class_device = TestDevice

    if class_device == TestDevice:
        report_cap = float(txt_rep_cap.get())  # *******
        work_cap = float(txt_wrk_cap.get())
        sleep_cap = float(txt_slp_cap.get())

        result_dict["report cap"] = report_cap # ********
        result_dict["work cap"] = work_cap
        result_dict["sleep cap"] = sleep_cap

        #dev = class_device(batt_capacity, report_time, report_cap, sleep_time,
                           #sleep_cap, work_time, work_cap, type_report, batt_self_discharge)

        dev = class_device(batt_capacity, search_time, search_cap, sleep_time,
                           sleep_cap, work_time, work_cap, type_report, batt_self_discharge)
    else:
        dev = class_device(batt_capacity, search_time, sleep_time, work_time, type_report, batt_self_discharge)

        #result_dict["report cap"] = dev.AVG_CURRENT_CONSUMPTION_REPORT
        result_dict["work cap"] = dev.AVG_CURRENT_CONSUMPTION_WORK
        result_dict["sleep cap"] = dev.AVG_CURRENT_CONSUMPTION_SLEEP

    lbl_lifetime.configure(text=dev.batt_life_time)
    lbl_iterations.configure(text=dev.num_iterations)

    result_dict["batt self discharge"] = batt_self_discharge
    result_dict["cycling mode"] = type_report
    result_dict["device"] = device
    result_dict["batt capacity"] = batt_capacity
    result_dict["report time"] = txt_rep_time.get()
    result_dict["work time"] = txt_wrk_time.get()
    result_dict["sleep time"] = txt_slp_time.get()
    result_dict["lifetime"] = dev.batt_life_time
    result_dict["iterations"] = dev.num_iterations

    return result_dict

def print_r():
    global mode
    if len(mode) < 10 and not mode[-1] == "R":
        mode += "R"
    lbl100.configure(text=mode)

def print_s():
    global mode
    if len(mode) < 10 and not mode[-1] == "S":
        mode += "S"
    lbl100.configure(text=mode)

def print_w():
    global mode
    if len(mode) < 10 and not mode[-1] == "W":
        mode += "W"
    lbl100.configure(text=mode)

def search():
    global mode
    if len(txt_src_time.get()) > 0 and len(txt_src_cap.get()) > 0:
        if len(mode) < 10 and not mode[-1] == "C":
            mode += "C"
    lbl100.configure(text=mode)

def clean():
    global mode
    if len(mode) > 1:
        mode = mode[:-1]
    lbl100.configure(text=mode)

def validate_float(var):
    new_value = var.get()
    try:
        new_value == '' or float(new_value)
        validate_float.old_value = new_value
    except:
        var.set(validate_float.old_value)

def save():
    if flag1:
        generate_report()

def nums_protocol():
    try:
        open('my.nums')
    except:
        data = '1'
        with open('my.nums', 'w') as file:
            file.writelines(data)

    with open('my.nums', 'r') as f:
        x = f.read()
    x += '1'
    with open('my.nums', 'w') as file:
        file.writelines(x)
    return len(x)

def generate_report():
    info = {}
    info = print_result()
    window2 = Tk()
    window2.title("Saved data")
    window2.geometry("400x500")

    info["number protocol"] = nums_protocol()
    date = datetime.datetime.now()
    date_format = date.strftime("%Y-%m-%d %H:%M:%S")
    info["date"] = date_format
    print(info)
    print()

    num_protocol = info["number protocol"]
    data = info["date"]
    device = info["device"]
    batt_capacity = info["batt capacity"]
    report_time = info["report time"]
    report_cap = info["report cap"]
    work_time = info["work time"]
    work_cap = info["work cap"]
    sleep_time = info["sleep time"]
    sleep_cap = info["sleep cap"]
    batt_self_discharge = info["batt self discharge"]
    cycling_mode = info["cycling mode"]
    batt_life_time = info["lifetime"]
    iterations = info["iterations"]

    lbl_protocol = Label(window2, text=f"Num Protocol:{num_protocol}"
                                       f"\n\n Data: {data}"
                                       f"\n\n Device: {device}"
                                       f"\n\n Batt Capacity: {batt_capacity} mA"
                                       f"\n\n Report Time: {report_time}s"
                                       f"\n\n Avg report cap: {report_cap}mA"
                                       f"\n\n Work Time: {work_time}s"
                                       f"\n\n Avg work cap: {work_cap}mA"
                                       f"\n\n Sleep Time: {sleep_time}s "
                                       f"\n\n Avg sleep cap: {sleep_cap}mA"
                                       f"\n\n Batt Self Discharge: {batt_self_discharge} %"
                                       f"\n\n Cycling Mode: {cycling_mode}"
                                       f"\n\n Batt Life Time: {batt_life_time}"
                                       f"\n\n Iterations: {iterations}").pack()

    try:
        data = json.load(open('my.protocol'))
    except:
        data = []
    data.append(info)
    with open('my.protocol', 'w') as file:
        json.dump(data, file, indent=3)

def ws_cycles():
    global new_mode
    if len(txt_rep_freq.get()) > 0:
        report_frequency = int(txt_rep_freq.get())

        if "WS" in mode and not report_frequency == 0:
            pos = mode.index("WS")
            # coun = mode.count("WS")
            # if coun > 1:
            #     report_frequency -= coun
            new_mode = mode[:pos] + ("WS" * report_frequency) + mode[pos + 2:]

        else:
            new_mode = mode

    else:
        report_frequency = 0
        new_mode = mode

    print(new_mode)
    return new_mode

btn_device = Button(window,width=14, text="Device consumption", command=clicked_get_device)
btn_device.grid(column=4, row=6)
btn_calculate = Button(window, width=14, text="        Calculate       ", command=check_valid_data)
btn_calculate.grid(column=4, row=22)
btn_save = Button(window, width=6, text="Save", command=save).grid(column=4, row=25)

btn_report = Button(window, text="Report", command=print_r)
btn_report.grid(column=1, row=19)
btn_work = Button(window, text="Work", command=print_w)
btn_work.grid(column=2, row=19)
btn_sleep = Button(window, text="Sleep", command=print_s)
btn_sleep.grid(column=3, row=19)
btn_clean = Button(window, text="X", command=clean)
btn_clean.grid(column=3, row=18)
btn_src = Button(window, text="Search", command=search)
btn_src.grid(column=1, row=18)

btn_ws = Button(window, text="WS Cycles", command=ws_cycles ).grid(column=4, row=20)

validate_float.old_value = ''
window.mainloop()


from aiogram import types

from data import texts
from data.config import YOUTUBE_USERNAME
from aiogram.dispatcher import FSMContext
from loader import dp
from states.States import Form
from re import search

from utils.db_api.mongo import users_db

check = r'[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+'
wallet = r'^0x[a-fA-F0-9]{40}$'

@dp.message_handler(text='⏩next', state=Form.GetYoutube)
async def get_link(msg: types.Message):
    await msg.answer(text=texts.text['youtube_text'].format(msg.from_user.first_name, YOUTUBE_USERNAME), disable_web_page_preview=True)
    await Form.CheckYoutube.set()

@dp.message_handler(state=Form.CheckYoutube)
async def check_link(msg: types.Message, state: FSMContext):
    youtube = msg.text
    if search(check, youtube):
        users_db.update_one({'telegram_id': msg.from_user.id}, {
            "$set":{
                'invite_youtube': youtube
            }
        }, upsert=True)

        walletcheck = users_db.find_one({'telegram_id': msg.from_user.id})
        try:
            print(walletcheck['wallet'])
            await msg.reply(text=texts.text['bye_text'])
            await state.finish()
        except:
            await msg.reply(text=texts.text['final_text'])
            await Form.GetWallet.set()
    else:
        await msg.reply(text=texts.text['youtube_repeat'])

@dp.message_handler(state=Form.GetWallet)
async def get_wallet(msg: types.Message, state: FSMContext):
    wallet_text = msg.text
    if search(wallet, wallet_text):
        users_db.update_one({'telegram_id': msg.from_user.id}, {
            "$set": {
                'wallet': wallet_text,
                'all_check': True
            }
        }, upsert=True)
        await msg.reply(text=texts.text['bye_text'])
        await state.finish()
    else:
        await msg.reply(text=texts.text['final_text'])


from django.urls import path
from . import views
from api.views import *
from django.conf import settings
from django.conf.urls.static import static

# from rest_framework_simplejwt.views import (
#     TokenObtainPairView,
#     TokenRefreshView,
# )

urlpatterns = [
    path('', views.index, name="index"),
    # GET API
    # Badge api

    path('search', UserSearchApi.as_view()),

    path('goal', GoalApi.as_view()),

    path('achievement', AchievementApi.as_view()),

    path('user', AllUserApi.as_view()),
    path('user/<uid>', UserApi.as_view()),

    path('user/<uid>/add', UserApi.as_view()),
    path('user/<uid>/update', UserApi.as_view()),

    path('user/<uid>/update', UserApi.as_view()),

    # Muscles
    path('muscles', MuscleApi.as_view()),
    path('muscles/<muscle_id>', MuscleApi.as_view()),

    path('muscles/<muscle_id>/exercise/<exercise_id>', ExerciseApi.as_view()),

    path('exercise', AllExerciseApi.as_view()),

    path('leaderboard',
         LeaderBoard.as_view()),

    path('group/create', GroupApi.as_view()),
    path('group/<id>/delete', GroupApi.as_view()),
    path('group', GroupApi.as_view())

]

urlpatterns = urlpatterns + \
    static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


from functools import cache
import requests
import arrow

@cache
def fetch_calendar():
    r = requests.get("https://github.com/totallynotmark6/cornell-college-block-schedule/raw/main/calendar.json")
    data =  r.json() # type: dict[str, dict]
    for key in data.keys():
        for block in data[key]['blocks']:
            block['start'] = arrow.get(block['start'])
            block['end'] = arrow.get(block['end'])
        for event in data[key]['events']:
            event['start'] = arrow.get(event['start'])
            event['end'] = arrow.get(event['end'])
    return data

def calendar_data(for_date: arrow.Arrow):
    calendar = fetch_calendar()
    school_year = None
    for key in calendar.keys():
        year_data = calendar[key]
        if year_data['blocks'][0]['start'] <= for_date <= year_data['blocks'][-1]['end']:
            school_year = key
            break
    if school_year is None:
        raise ValueError("Calendar data not found for date!")
    year_data = calendar[school_year]
    block = None
    for block_data in year_data['blocks']:
        if block_data['start'] <= for_date <= block_data['end']:
            block = block_data
            break
    event = None
    for event_data in year_data['events']:
        if event_data['start'] <= for_date <= event_data['end']:
            event = event_data
            break

    if block is None and event is not None:
        return {
            "block": None,
            "day": None,
            "week": None,
            "event": event,
            "school_year": school_year
        }
    elif block is None and event is None:
        # so if we're not in a block, and we're not in an offically defined break, we're in block break!
        # block break starts on the last day of the last block we were in, and ends on the first day of the next block
        # to get the last block we were in, we need to find the most recent block that ended before the date we're checking
        last_block = None
        for block_data in year_data['blocks']:
            if block_data['end'] < for_date:
                last_block = block_data

def is_summer():
    return False


from datetime import datetime
from django.test import TestCase, override_settings
from mock import Mock, patch

from codespeed.commits.git import getlogs
from codespeed.models import Project, Revision, Branch, Environment

@override_settings(ALLOW_ANONYMOUS_POST=True)
class GitTest(TestCase):
    def setUp(self):
        self.env = Environment.objects.create(name='env')
        self.project = Project.objects.create(name='project',
                                              repo_path='path',
                                              repo_type=Project.GIT)
        self.branch = Branch.objects.create(name='default',
                                            project_id=self.project.id)
        self.revision = Revision.objects.create(
            **{
               'commitid': 'id1',
               'date': datetime.now(),
               'project_id': self.project.id,
               'branch_id': self.branch.id,
            }
        )

    @patch("codespeed.commits.git.Popen")
    def test_git_output_parsing(self, popen):
        # given
        outputs = {
            "log": b"id\x00long_id\x001583489681\x00author\x00email\x00msg\x00\x1e",
            "tag": b'tag',
        }

        def side_effect(cmd, *args, **kwargs):
            ret = Mock()
            ret.returncode = 0
            git_command = cmd[1] if len(cmd) > 0 else None
            output = outputs.get(git_command, b'')
            ret.communicate.return_value = (output, b'')
            return ret

        popen.side_effect = side_effect

        # when
        # revision doesn't matter here, git commands are mocked
        logs = getlogs(self.revision, self.revision)

        # then
        expected = {
            'date': '2020-03-06 04:14:41',
            'message': 'msg',
            'commitid': 'long_id',
            'author': 'author',
            'author_email': 'email',
            'body': '',
            'short_commit_id': 'id',
            'tag': 'tag',
        }
        self.assertEquals([expected], logs)