from calc import * #Importo todas as funções de calc.py

teste,b = 0,0 #Valor inicial das variáveis de controle
i = str(input("\nInforme o numero do IPV4: "))
#--------------- Inicio da verificação de validade da entrada ------#
while teste != 1:
  lista = separar(i) #Recebe o ip como lista de inteiros
  j = verificar(i,lista) #Verifica se a variável (i) atende os limites
  if j == 1: #A variavel (j) é o retorno da função verificar(), se b for 1 é porque (i) está invalida nesse caso o programa aciona a condicional
    print("\nInformação invalida")
    i = str(input("\nInforme o numero do IPV4 Novamente: "))
  else:
    teste = 1
    a = coletar(i,lista) #A variável (a) recebe o número do ipv4 em binário
#--------------- Fim da verificação de válidade da entrada --------#
teste,j = 0,0 #Reinicia os valores das variáveis de controle

m = str(input("\nInforme o numero da Máscara de sub-rede: "))
#--------------- Inicio da verificação de validade da entrada ------#
while teste != 1:
  lista = separar(m)
  j = verificar(m,lista)
  if j == 1:
    print("\nInformação invalida")
    m = str(input("\nInforme o numero da Máscara de sub-rede: "))
  else:
    teste = 1
    b = coletar(m,lista) #A variável (b) recebe o número da mascara de sub-rede em binário
#--------------- Fim da verificação de válidade da entrada --------#

c,d = broad(b,a) #A variavel (c) tem como valor o broadcast em binario, a variavel (d) tem como valor o broadcast
f,g = end(b,a) #A variavel (f) tem como valor o Endereço da subrede em binario, a variavel (g) tem como valor o Endereço da subrede.

print('\nEndereço IP em binário: ',a)
print('\nMáscara de sub-rede em binário: ',b)
print("\nEndereço da sub-rede =",g)
print("\nEndereço de Broadcast =",d)
print("\nEndereço da sub-rede em binário =",f)
print("\nEndereço de Broadcast em binário =",c)


from pyDes import *;

def ceaserCipher(text):
	arr = list(text)
	for i in range(len(arr)):
		if(ord(arr[i]) >= 97 and ord(arr[i])<=122):
			arr[i] = chr((((ord(arr[i])-97)+3)%26)+97)
		elif(ord(arr[i]) >= 65 and ord(arr[i])<=90):
			arr[i] = chr((((ord(arr[i])-65)+3)%26)+65)
		elif(ord(arr[i]) >= 48 and ord(arr[i])<=57):
			arr[i] = chr((((ord(arr[i])-48)+3)%10)+48)
	return str(arr)

def dataEncryptionStandards(data,key):
	d = des(key,CBC,b"\0\0\0\0\0\0\0\0",pad = None,padmode = PAD_PKCS5)
	cipher = d.encrypt(data)
	return cipher,d.decrypt(cipher)

print("1 : Ceaser Encryption")
print("2 : DES")
choice = int(input("Enter your choice"))
if choice==1:
	print("Enter the text for encryption is small case or capital case")
	text = input()
	print("Original Text is : ",text)
	print("Encrpted Text Is  : ",end = " ")
	print(ceaserCipher(text))
elif choice==2:
	data = str(input("Enter data for encryption"))
	key = str(input("Enter key for encryption 8 bytes only"))
	print("Original Text is : ",data)
	print("Key is   : ",key)
	print("Encrypted Text is : ",end = " ")
	cipher,original = dataEncryptionStandards(data,key)
	print(cipher)
	print("Decrypted text is : ")
	print(original)

'''
Class initialization
--------------------
pyDes.des(key, [mode], [IV], [pad], [padmode])
pyDes.triple_des(key, [mode], [IV], [pad], [padmode])

key     -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes
	   for Triple DES
mode    -> Optional argument for encryption type, can be either
	   pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining)
IV      -> Optional Initial Value bytes, must be supplied if using CBC mode.
	   Length must be 8 bytes.
pad     -> Optional argument, set the pad character (PAD_NORMAL) to use during
	   all encrypt/decrpt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5)
	   to use during all encrypt/decrypt operations done with this instance.

I recommend to use PAD_PKCS5 padding, as then you never need to worry about any
padding issues, as the padding can be removed unambiguously upon decrypting
data that was encrypted using PAD_PKCS5 padmode.

'''


"""
作者：张景润
学号：1172510217
"""
import csv
import random
import numpy as np

Num = 10  # 聚类的类别数目
Width = 784  # 样本的特征维度
Data_Path = './ClusterSamples.csv'  # 样本所在的文件
Ground_Path = './SampleLabels.csv'  # 真实聚类结果文件
Label_Path = './ClusterLabels.csv'  # 样本聚类标签输出文件

def read_data():
    global Width
    with open(Data_Path, 'r') as f:
        data_lst = []
        reader = csv.reader(f)
        for lst in reader:
            data_lst.append([int(item) for item in lst])
        Width = len(data_lst[0])
        return data_lst

def init_cluster_center(data_lst, init=1):  # 初始化聚类中心
    if init == 0:  # 随机初始化聚类中心
        center_lst = random.sample(data_lst, Num)
    elif init == 1:  # 选取批次距离尽可能远的Num个点
        center_lst = [get_center(data_lst)]  # 第一个样本点为所有样本点的质心
        while len(center_lst) < Num:
            dis, new_center = 0, []
            for data in data_lst:
                new_dis = min([cal_dis(data, center) for center in center_lst])
                if new_dis >= dis:
                    dis = new_dis
                    new_center = data
            center_lst.append(new_center)  # 选取距离已有中心点的最近距离最大的点作为新的中心点
    else:  # 待开发
        center_lst = init_cluster_center(data_lst, 0)
    return center_lst

def cal_dis(data1, data2):  # 计算两个样本的中心
    return sum([(item1 - item2) ** 2 for item1, item2 in zip(data1, data2)])

def get_center(data_lst):  # 获取data_lst的中心
    center = [0] * Width
    for data in data_lst:
        for idx, item in enumerate(data):
            center[idx] += item
    center = [float(item) / len(data_lst) for item in center]
    return center

def cluster(data_lst, center_lst):
    label_lst = [-1] * len(data_lst)
    flag, iter_count = True, 0
    while flag:
        flag = False
        for idx, data in enumerate(data_lst):  # 更新每一个聚类的标签
            dis_lst = [cal_dis(data, data2) for data2 in center_lst]
            min_idx = dis_lst.index(min(dis_lst))  # 找到与当前样本距离最近的聚类中心
            if min_idx != label_lst[idx]:
                label_lst[idx] = min_idx
                flag = True
        for idx in range(Num):  # 更新聚类中心
            tmp_lst = list(map(lambda item: item[1], filter(lambda item: item[0] == idx, zip(label_lst, data_lst))))
            center_lst[idx] = get_center(tmp_lst)
        iter_count += 1
        print('当前是第%d次迭代' % iter_count)
    return label_lst

def write_data(label_lst):  # 将聚类结果写入文件
    with open(Label_Path, 'w', encoding='utf-8') as f:
        f.write('\n'.join(map(str, label_lst)))

def evaluate():  # 查看聚类结果与标准答案比较
    with open(Ground_Path, 'r', encoding='utf-8') as f0, open(Label_Path, 'r', encoding='utf-8') as f1:
        ground_lst, predict_lst = [], []
        cluster_lst = [[0 for _ in range(Num)] for _ in range(Num)]
        for line in f0:
            line = line.strip('\n')
            if line:
                ground_lst.append(int(line))
        for line in f1:
            line = line.strip('\n')
            if line:
                predict_lst.append(int(line))
        for ground, predict in zip(ground_lst, predict_lst):
            cluster_lst[ground][predict] += 1
        for idx, lst in enumerate(cluster_lst):
            print('聚类%d：%s' % (idx, '\t'.join(map(str, lst))))
        res = np.array(cluster_lst)  # debug模式下以表格形式查看res
        print(res)

def main():
    data_lst = read_data()
    center_lst = init_cluster_center(data_lst)
    label_lst = cluster(data_lst, center_lst)
    write_data(label_lst)
    # evaluate()

if __name__ == '__main__':
    main()


import xarray as xr

import seaborn as sns

import warnings

def rates_to_df(arr):
    """
    Convert DataArray of rates to DataFrame

    Parameters
    ----------
    arr : xr.DataArray
        DataArray containing the rates of neurons across time

    Returns
    -------
    pd.DataFrame with neurons as index and time points as columns
    """
    non_time_dims = [dim for dim in arr.dims if dim != 'time']
    assert len(non_time_dims) == 1
    non_time_dim = non_time_dims[0]

    return (arr.to_dataframe('name')
               .reset_index()
               .pivot(index = non_time_dim, columns = 'time', values = 'name'))

def plot_rasterlike_rates(ax, rates, normalize=True, cbar_ax=None, cmap=None):
    """
    Plot rates of a trial similarly to a rasterplot

    Parameters
    ----------
    ax : plt.Axes
        Axes to plot on
    rates : pd.DataFrame
        rates to plot
        neuron x time
        convert DataArray to DataFrame by rates_to_df
    normalize : bool (default True)
        normalize every neuron's max. firing rate to 1
    cbar_ax : plt.Axes (default None)
        Axes to plot the colorbar on
        if None, don't plot a colorbar
    cmap : string or colormap
        colormap to use for the rates
    """
    if normalize:
        rates /= rates.max('time')

    if cbar_ax is not None:
        cbar = True
    else:
        cbar = False

    if cmap is None:
        cmap = sns.color_palette('Wistia', desat = 0.6)

    if isinstance(rates, xr.DataArray):
        plot_rates = rates_to_df(rates)
    else:
        plot_rates = rates
        warning.warn('would be nice to get an indexed DataArray')

    sns.heatmap(plot_rates, ax = ax, cbar = cbar, cbar_ax = cbar_ax,
                rasterized = True,
                cbar_kws = {'label' : 'rates'},
                cmap = cmap)


from flask import (
    Blueprint, flash, g, redirect, render_template, request, url_for, Flask, send_from_directory, current_app,session
)
from werkzeug.exceptions import abort

from flask_login import login_required, current_user
from asc.schema import Flight, SchemaError, Aircraft, Pilot, Slot
from sqlalchemy import func, or_, and_
import datetime
from sqlalchemy import text as sqltext
from sqlalchemy.sql import func, select
# WTForms
from flask_wtf import FlaskForm
from wtforms import Form, StringField, PasswordField, validators, SubmitField, SelectField, BooleanField, RadioField, \
    TextAreaField, TimeField
from wtforms.fields import EmailField, IntegerField, DateField, TimeField, DateTimeField, DecimalField
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo, Length

from asc.wtforms_ext import MatButtonField, TextButtonField
from decimal import *
from asc.common import *

try:
    # app = Flask(__name__)
    # from asc import create_app
    # app = create_app()
    app = current_app
except Exception as e:
    print("Failed to load app in flights.py: {}".format(e))

bp = Blueprint('flights', __name__, url_prefix='/flights')
applog = app.logger

constDAYDATE = 'daydate'
constDAYVIEW = 'dayview'

# TODO: Dayend to roll log if empty
# TODO: purge downloads folder
# TODO: ARE you sure for flights where either a start/tug down or landed is already recorded and the user presses that button.

class NewDayForm(FlaskForm):
    newdate = DateField('Date',
                     description="Today's Date",
                     default=datetime.date.today()
                     # validators=[validators.DataRequired()]
                    )
    instructor = StringField('Instructor',
                             description = "Instructor Name",
                             render_kw={'list': "instructors"})
    towpilot = StringField('Tow Pilot',
                             description = "Tow Pilot Name",
                             render_kw={'list': "towies"})
    dutypilot = StringField('Duty Pilot',
                             description = "Duty Pilot Name",
                             render_kw={'list': "dutypilots"})

class FlightForm(FlaskForm):
    id = IntegerField('ID',
                      description='Primary Key')
    pic = StringField('PIC',
                      [validators.DataRequired(message='You cannot have a blank code')],
                      description="Pilot in charge",
                      render_kw={'list': "activepilots"})
    p2 = StringField('P2',
                     description="Passenger or Student",
                     render_kw={'list': "activepilots"})
    flt_date = DateField('Date',
                         description='Flight Date',
                         render_kw={'class': 'mobile_port_supress mobile_land_supress'})
    ac_regn = StringField('Regn',
                          [validators.DataRequired(message='You cannot have a blank code')],
                          description='Last 3 characters of AC Registration',
                          render_kw={'list': "acregns"})
    takeoff = TimeField('Takeoff',
                        [validators.Optional()],
                        description="Takeoff Time",
                        render_kw={'step': "60"})
    tug_down = TimeField('Tug Down',
                         [validators.Optional()],
                         description="Time the Tow plane landed",
                         render_kw={'step': "60"})
    release_height = IntegerField('Release Height',
                                  [validators.Optional()],
                                  description='Release height in feet',
                                  render_kw={'Step': '500'})
    landed = TimeField('Landed',
                       [validators.Optional()],
                       description="Landing Time",
                       render_kw={'step': "60"})
    tow_pilot = StringField('Tow Pilot',
                            description="Tow Pilot",
                            render_kw={'list': "towies"})
    tug_regn = StringField('Tug Regn',
                           description="Tug Registration - Last Three Characters",
                           render_kw={'list': "towregnlist"})
    btnsubmit = MatButtonField('done',
                            id='matdonebtn',
                            icon='done',
                            help="Confirm all Changes")
    cancel = MatButtonField('cancel',
                            id='matcancelbtn',
                            icon='cancel',
                            help="Press to exit and make no changes")  # , render_kw={'formnovalidate':''})
    payment = MatButtonField('payment', id='matpaymentbtn', icon='attach_money',
                             help="Maintain cost and payment")  # , render_kw={'formnovalidate':''})
    note = MatButtonField('note', id='matnotebtn', icon='description',
                          help="Add a note to the flight")
    delete = MatButtonField('delete', id='matdeletebtn',
                            icon='delete',
                            help='Press to delete this record',
                            render_kw={'onclick': 'return ConfirmDelete()'})

class PaymentForm(FlaskForm):
    payer = StringField('Payer',
                        description='Person paying for this flight',
                        render_kw={'list': "pilots"})
    tow_charge = DecimalField('Launch',
                              description="Launch Charge",
                              places=2,
                              rounding=None)
    glider_charge = DecimalField('Glider',
                                 description="Glider Charge",
                                 places=2)
    other_charge = DecimalField('Other',
                                description="Other eg. Trial Flight Charge",
                                places=2)
    payment_note = StringField('Payment',
                               description = "Payment Method",
                               render_kw={'list': "paymentmethods"})
    paid = BooleanField('Paid',
                        description="Checked indicates this flight has been paid")
    btnsubmit = MatButtonField('done',
                            id='matdonebtn',
                            icon='done',
                            help="Confirm all Changes")
    cancel = MatButtonField('cancel',
                            id='matcancelbtn',
                            icon='cancel',
                            help="Press to exit and make no changes")  # , render_kw={'formnovalidate':''})
    # 22 Jan 23:  Removed the delete function
    # We don't want users to be able to delete a record on the payment screen
    # delete = MatButtonField('delete',
    #                         id='matdeletebtn',
    #                         icon='delete',
    #                         help='Press to delete this record',
    #                         render_kw={'onclick': 'return ConfirmDelete()'})
    calc = TextButtonField('Transactions',
                           id="calcbtn",
                           text='Calc',
                           help="Reset default charges")

class NoteForm(FlaskForm):
    flt_date = DateField('Date',
                         description='Flight Date',
                         )
    general_note = TextAreaField('Note',
                               description = "Any Notes for Lionel or yourself",
                                render_kw={'rows':'8'})
    btnsubmit = MatButtonField('done',
                            id='matdonebtn',
                            icon='done',
                            help="Confirm all Changes")
    cancel = MatButtonField('cancel',
                            id='matcancelbtn',
                            icon='cancel',
                            help="Press to exit and make no changes")  # , render_kw={'formnovalidate':''})
    delete = MatButtonField('delete', id='matdeletebtn',
                            icon='delete',
                            help='Press to delete this record',
                            render_kw={'onclick': 'return ConfirmDelete()'})

# TODO: Test Error Handling in Production environment.

@app.before_request
def before_request():
    applog.debug("In before_request")

@bp.route('/daysummary')
@login_required
def daysummary():
    # TODO: Add paging
    sql = sqltext("""
    SELECT
        flt_date,
        count(*) movements,
        max(id) last_id
        FROM flights
        GROUP BY flt_date
        ORDER BY flt_date desc
        """)
    sql = sql.columns(flt_date=db.Date)
    app.logger.info("Main Page accessed")
    try:
        summary = db.engine.execute(sql).fetchall()
        return render_template('flights/daysummary.html', summary=summary)
    except Exception as e:
        flash("An error occurred: {}".format(e))
        app.logger.info("An error occurred: {}".format(e))
        # return e
        abort(404, e)

@bp.route('/newday', methods=['GET','POST'])
@login_required
def newday():
    """
    Start a new day.  Get the roster details and so on.
    """
    if request.method == 'GET':
        instlist = [r.fullname for r in db.session.query(Pilot).filter(Pilot.instructor == True).all()]
        towielist = [r.fullname for r in db.session.query(Pilot).filter(Pilot.towpilot == True).all()]
        dplist = [r.fullname for r in db.session.query(Pilot).filter(Pilot.towpilot == False).filter(Pilot.instructor == False).all()]
        newdate = datetime.date.today()
        thisform = NewDayForm()
        todayroster = Roster.query.filter(Roster.roster_date >= newdate).first()
        if todayroster is None:
            thisform.newdate.data = datetime.date.today()
        else:
            thisform.newdate.data = todayroster.roster_date
            thisform.instructor.data = todayroster.roster_inst
            thisform.towpilot.data = todayroster.roster_tp
            thisform.dutypilot.data = todayroster.roster_dp
    if request.method == 'POST' :
        thisform = NewDayForm(request.form)
        # Add the note here
        notetext = "Instructor:{}, Tow Pilot:{}, Duty Pilot:{}".format(thisform.instructor.data,
                                                                       thisform.towpilot.data,
                                                                       thisform.dutypilot.data)
        daynote = Flight(flt_date=thisform.newdate.data,
                         linetype='NT',
                         general_note=notetext)
        db.session.add(daynote)
        db.session.commit()
        # print("post note: {} / {}".format(thisform.newdate.data,thisform.instructor.data))
        return redirect(url_for('flights.daysheet', date=thisform.newdate.data.strftime('%Y-%m-%d')))
    # if not post....
    return render_template('flights/newday.html', form=thisform, instlist=instlist, towielist=towielist,
                           dplist=dplist)

@bp.route('/daysheet/<date>')
@login_required
def daysheet(date):
    """
    Display flights on a day
    :param date: String.  Can be one of:
        "TODAY" - obvious
        "SESSION" - a date stored in the session cookie "DAYDATE"
        a string in dd-mmm-yyyy format
    :return:
    """
    if date == 'TODAY':
        thisdate = date.today()
    elif date == 'SESSION':
        thisdate = datetime.datetime.strptime(session[constDAYDATE],'%Y-%m-%d').date()
    else:
        thisdate = datetime.datetime.strptime(date, '%Y-%m-%d').date()
    # Now remember it:
    session[constDAYDATE] = datetime.datetime.strftime(thisdate, '%Y-%m-%d')
    # Deal with the view
    if constDAYVIEW in session:
        view = session[constDAYVIEW]
    else:
        view = 'ALL'
    if view is None:
        view = 'ALL'
    if view == 'AIRBORNE':
        thisset = db.session.query(Flight).filter(Flight.linetype == 'FL').filter(Flight.flt_date == thisdate).filter(Flight.takeoff != None).filter(Flight.landed == None).all()
        title = "Airborne " + thisdate.strftime("%a %d %b")
    elif view == 'WAITING':
        thisset = db.session.query(Flight).filter(Flight.linetype == 'FL').filter(Flight.flt_date == thisdate).filter(Flight.takeoff == None).all()
        title = "Gridded " + thisdate.strftime("%a %d %b")
    elif view == 'LANDED':
        thisset = db.session.query(Flight).filter(Flight.linetype == 'FL').filter(Flight.flt_date == thisdate).filter(Flight.landed != None).all()
        title = "Landed " +  thisdate.strftime("%a %d %b")
    elif view == 'UNPAID':
        thisset = db.session.query(Flight).filter(Flight.linetype == 'FL').filter(Flight.flt_date == thisdate).filter(Flight.landed != None).filter(or_(Flight.payment_note == None, Flight.payment_note =='')).all()
        title = "Unpaid"
    else:
        thisset = db.session.query(Flight).filter(Flight.flt_date == thisdate).all()
        title  = "All Flights " + thisdate.strftime("%a %d %b")
    # see if it is possible to record tug down with one button.
    launched = db.session.query(Flight).filter(Flight.linetype == 'FL').filter(Flight.flt_date == thisdate).filter(Flight.takeoff != None).filter(Flight.tug_down == None).all()
    current_glider_under_tow = ''
    id_of_towed_aircraft = 0
    count_of_towed_aircraft = 0
    for l in launched:
        # check if slef launch
        thisaircraft = db.session.query(Aircraft).filter(Aircraft.regn == l.ac_regn).first()
        if thisaircraft is not None and thisaircraft.default_launch != constTOW_FOR_SELF_LAUNCH:
            id_of_towed_aircraft = l.id
            current_glider_under_tow = l.ac_regn
            count_of_towed_aircraft += 1
    if count_of_towed_aircraft > 1:
        flash("Seems we have > 1 aircraft under tow!!")
    try:
        return render_template('flights/daysheet.html', title=title,  flights=thisset ,
                               towcount=count_of_towed_aircraft,
                               towedregn=current_glider_under_tow,
                               towedid=id_of_towed_aircraft
                               )
    except Exception as e:
        flash("An error occurred: {}".format(e))
        app.logger.info("An error occurred: {}".format(e))
        return render_template('flights/daysummary.html')

@bp.route('/changeview', methods=['GET', 'POST'])
@login_required
def changeview():
    if request.method == 'GET':
        return render_template('flights/changeview.html')
    if request.method == 'POST':
        session[constDAYVIEW] = request.form['view']
    return redirect(url_for('flights.daysheet', date='SESSION'))

@bp.route('/changeflight/<id>', methods=['GET', 'POST'])
@login_required
def changeflight(id):
    # --------------------------------------------------------------------------------------------
    # Preparation - get default values, poplulate lists
    # --------------------------------------------------------------------------------------------
    thisrec = Flight.query.get(id)
    # if thisrec is None then we will be adding
    if thisrec is None:
        thisrec = Flight()
        thisrec.flt_date = db.session.query(func.max(Flight.flt_date).label("maxdate")).scalar()
        lastflt = db.session.query(Flight).filter(Flight.linetype == 'FL').order_by(Flight.id.desc()).first()
        # Determine the TUG:
        if lastflt is not None:
            thisrec.tow_pilot = lastflt.tow_pilot
            applog.debug("New rec tow pilot set to {} at point A. LAst flit id is {}".format(lastflt.tow_pilot, lastflt.id))
            thisrec.tug_regn = lastflt.tug_regn
        # on a mobile platform the flight date is always the current date.  On windows or linux it will be prompted.
        # is todays date equal to the flight date?
        applog.info("User Agent:{}".format(request.user_agent.platform))
        if request.user_agent.platform is not None and request.user_agent.platform not in ['windows', 'linux']:
            thisrec.flt_date = datetime.date.today()
    else:
        lastdate = thisrec.flt_date
    #
    thisform = FlightForm(obj=thisrec, name='Flight Maintenance')
    if thisform.cancel.data:
        return redirect(url_for('flights.daysheet', date=thisrec.flt_date.strftime('%Y-%m-%d')))
    # Pilot list
    # Populate the choices list for pilots and
    sql = sqltext("""
          select pic
          from flights
          where flt_date > :date
          and pic <> ''
          and linetype = 'FL'
          union
          select p2
          from flights
          where flt_date > :date
          and p2 <> ''
          and linetype = 'FL'
          union
          select fullname
          from pilots
          """)
    if thisrec.flt_date is not None:
        activepilotdate = thisrec.flt_date - datetime.timedelta(days=180)
    else:
        activepilotdate = datetime.date.today()
    pilotlist = [r[0] for r in db.engine.execute(sql, date=activepilotdate).fetchall()]
    # A/C List
    sql = sqltext("""
        select distinct ac_regn
        from flights
        where flt_date > :date
        and linetype = 'FL'
       union
        select regn
        from aircraft
        order by ac_regn
        """)
    if thisrec.flt_date is not None:
        activeacdate = thisrec.flt_date - datetime.timedelta(days=90)
    else:
        activeacdate = datetime.date.today()
    acregnlist = [r[0] for r in db.engine.execute(sql, date=activeacdate).fetchall()]
    # Add the Tug only if it not already there..(i.e. used in last 90 days) ...
    if constREGN_FOR_TUG_ONLY not in acregnlist:
        acregnlist.append(constREGN_FOR_TUG_ONLY)
    # Towie list
    towielist = [r.fullname for r in db.session.query(Pilot).filter(Pilot.towpilot == True).all()]
    sql = sqltext("""
        select distinct regn
        from aircraft
        where launch = 1
    """)
    towregnlist = [r.regn for r in db.engine.execute(sql).fetchall()]
    towregnlist.append(constTOW_FOR_SELF_LAUNCH)

    # --------------------------------------------------------------------------------------------
    # Processing
    # --------------------------------------------------------------------------------------------
    if request.method == 'POST' and thisform.validate():
        # Provided the field names are the same, this function updates all the fields
        # on the table.  If there are any that are different then each field needs to be
        # assigned manually.
        try:
            thisform.populate_obj(thisrec)
            if thisrec.id is None:
                # thisrec.pic_gnz_no = thisrec.get_pic_gnz_no()
                # thisrec.p2_gnz_no = thisrec.get_p2_gnz_no()
                db.session.add(thisrec)
                applog.info('New Flight added: {}'.format(thisrec.id))
            elif thisform.delete.data:
                db.session.delete(thisrec)
                applog.info('Flight {} deleted'.format(id))
            else:
                # if this a tug only flight make sure that tug down and landed are the same
                # thisrec.pic_gnz_no = thisrec.get_pic_gnz_no()
                # thisrec.p2_gnz_no = thisrec.get_p2_gnz_no()
                if thisrec.ac_regn == constREGN_FOR_TUG_ONLY or thisrec.tug_regn == constTOW_FOR_SELF_LAUNCH:
                    if thisrec.tug_down == None and thisrec.landed != None:
                        thisrec.tug_down = thisrec.landed
                    elif thisrec.tug_down != None and thisrec.landed == None:
                        thisrec.landed = thisrec.tug_down
                applog.info('Flight {} changed'.format(id))
            if thisrec.tug_regn != constTOW_FOR_SELF_LAUNCH and thisrec.ac_regn != constREGN_FOR_TUG_ONLY:
                addupdslot('DEFAULT','LASTTOWIE',thisrec.tow_pilot)
                addupdslot('DEFAULT','LASTTUG',thisrec.tug_regn)
            applog.debug('Just before commit: towie is {}'.format(thisrec.tow_pilot))
            db.session.commit()
            if thisform.note.data:
                return redirect(url_for('flights.flightnote', id=thisrec.id))
            if thisform.payment.data:
                return redirect(url_for('flights.payment', id=thisrec.id))
        except SchemaError as e:
            flash(e)
            return render_template('flights/changeflight.html', form=thisform, pilots=pilotlist, ac=acregnlist, towielist=towielist)
        return redirect(url_for('flights.daysheet', date=thisrec.flt_date.strftime('%Y-%m-%d')))
    applog.debug('About to render changefligt at b with id {}'.format(thisform.id.data))
    return render_template('flights/changeflight.html', form=thisform, pilots=pilotlist, ac=acregnlist, towielist=towielist, towregnlist=towregnlist)

# NOTELINE is a line of type NT that is NOT a flight!
@bp.route('/linenote/<id>', methods=['GET', 'POST'])
@login_required
def linenote(id):
    if constDAYDATE in session:
        thisdate = datetime.datetime.strptime(session[constDAYDATE], '%Y-%m-%d').date()
    else:
        thisdate = datetime.date.today()
    thisrec = Flight.query.get(id)
    if thisrec is None:
        thisrec = Flight()
        thisrec.flt_date = db.session.query(func.max(Flight.flt_date).label("maxdate")).scalar()
        # lastflt = db.session.query(Flight).order_by(Flight.id.desc()).first()
        thisrec.linetype = 'NT'
    thisform = NoteForm(obj=thisrec, name='Notes for Flight')
    if request.method == 'GET':
        # if thisrec is None then we will be adding
        return render_template('flights/changenote.html', form=thisform, line=thisrec)
    if request.method == 'POST' and thisform.validate():
        # Provided the field names are the same, this function updates all the fields
        # on the table.  If there are any that are different then each field needs to be
        # assigned manually.
        try:
            thisform.populate_obj(thisrec)
            if thisrec.id is None:
                db.session.add(thisrec)
                applog.info('New Note added: {}'.format(thisrec.id))
            elif thisform.delete.data:
                db.session.delete(thisrec)
                applog.info('Note {} deleted'.format(id))
            else:
                applog.info('Note {} note details updated'.format(id))
            db.session.commit()
            return redirect(url_for('flights.daysheet', date=thisdate.strftime('%Y-%m-%d')))
        except SchemaError as e:
            flash(e)
            return render_template('flights/changenote.html', form=thisform)
        except Exception as e:
            flash(e)
            return render_template('flights/changenote.html', form=thisform)
        return redirect(url_for('flights.daysheet', date=thisdate.strftime('%Y-%m-%d')))
    return redirect(url_for('flights.daysheet', date=thisdate.strftime('%Y-%m-%d')))

@bp.route('/flightnote/<id>', methods=['GET', 'POST'])
@login_required
def flightnote(id):
    if constDAYDATE in session:
        thisdate = datetime.datetime.strptime(session[constDAYDATE], '%Y-%m-%d').date()
    else:
        thisdate = datetime.date.today()
    thisrec = Flight.query.get(id)
    if thisrec is None:
        flash("Unable to locate record")
        return redirect(url_for('flights.daysheet', date=thisdate.strftime('%Y-%m-%d')))
    # otherwise we have a good record
    thisform = NoteForm(obj=thisrec, name='Notes for Flight')
    if request.method == 'GET':
        return render_template('flights/changenote.html', form=thisform, line=thisrec)
    if request.method == 'POST' and thisform.validate():
        # Provided the field names are the same, this function updates all the fields
        # on the table.  If there are any that are different then each field needs to be
        # assigned manually.
        try:
            thisform.populate_obj(thisrec)
            if thisrec.id is None:
                flash("Unable to locate record")
                return redirect(url_for('flights.daysheet', date=thisdate.strftime('%Y-%m-%d')))
            else:
                applog.info('Flight {} note details updated'.format(id))
            db.session.commit()
        except SchemaError as e:
            flash(e)
            return render_template('flights/changenote.html', form=thisform)
        return redirect(url_for('flights.daysheet', date=thisdate.strftime('%Y-%m-%d')))
    return redirect(url_for('flights.daysheet', date=thisdate.strftime('%Y-%m-%d')))

@bp.route('/tuglanded/<id>')
@login_required
def tuglanded(id):
    if constDAYDATE in session:
        thisdate = datetime.datetime.strptime(session[constDAYDATE], '%Y-%m-%d').date()
    else:
        thisdate = datetime.date.today()
    try:
        thisflight = Flight.query.get(id)
        if thisflight.linetype != 'FL':
            flash("This line is not a flight")
            return redirect(url_for('flights.daysheet', date=thisdate.strftime('%Y-%m-%d')))
        down = datetime.datetime.now()
        # now round to the nearest minute
        if down.second > 30:
            # remove the seconds
            down -= datetime.timedelta(seconds=down.second, microseconds=down.microsecond)
            # Add one minute
            down += datetime.timedelta(minutes=1)
        else:
            # jsut remove the seconds
            down -= datetime.timedelta(seconds = down.second, microseconds=down.microsecond)
        thisflight.tug_down = down.time()
        if thisflight.ac_regn == constREGN_FOR_TUG_ONLY or thisflight.tug_regn == constTOW_FOR_SELF_LAUNCH:
            if thisflight.tug_down == None and thisflight.landed != None:
                thisflight.tug_down = thisflight.landed
            elif thisflight.tug_down != None and thisflight.landed == None:
                thisflight.landed = thisflight.tug_down
        db.session.commit()
    except Exception as e:
        applog.debug("Error updating tug down :{}".format(e))
        flash("Unable to update.  Record via Flight id")
    return redirect(url_for('flights.daysheet', date=thisdate.strftime('%Y-%m-%d')))

@bp.route('/payment/<id>', methods=['GET', 'POST'])
@login_required
def payment(id):
    thisrec = Flight.query.get(id)
    # if thisrec is None then we have an error
    if thisrec is None:
        flash("Cannot locate flight")
        return redirect(url_for('flights.daysummary'))
    thisform = PaymentForm(obj=thisrec, name='Payment Maintenance')
    if thisform.cancel.data:
        return redirect(url_for('flights.changeflight', id=id))
    if thisform.calc.data:
        if not calc_charges(id):
            flash("There was a problem setting the default charges")
        return redirect(url_for('flights.payment', id=id))
    if request.method == 'POST' and thisform.validate():
        # Provided the field names are the same, this function updates all the fields
        # on the table.  If there are any that are different then each field needs to be
        # assigned manually.
        try:
            thisform.populate_obj(thisrec)
            if thisrec.id is None:
                db.session.add(thisrec)
                applog.info('New payment details Added')
            # 22 Jan 23 - do not allow delete on the payment form.
            # elif thisform.delete.data:
            #     db.session.delete(thisrec)
            #     applog.info('Flight {} payment details deleted'.format(id))
            else:
                applog.info('Flight {} payment details updated'.format(id))
            db.session.commit()
        except SchemaError as e:
            flash(e)
            return render_template('flights/payment.html', form=thisform, glider_time = thisrec.glider_mins())
        return redirect(url_for('flights.daysheet', date=thisrec.flt_date.strftime('%Y-%m-%d')))
    # if we are here then we are getting the data for display
    sql = sqltext("""
             select fullname
             from pilots
             """)
    pilotlist = [r[0] for r in db.engine.execute(sql).fetchall()]
    return render_template('flights/payment.html', form=thisform,pilots=pilotlist)

def calc_charges(id):
    """
    For a given id calculate the charges and update the record.
    :param id: The ID of the record
    :return: Boolean to say if all was ok or not
    """
    try:
        thisrec = Flight.query.get(id)
        if thisrec is None:
            return False
        # Launch fee
        tug = Aircraft.query.filter_by(regn=thisrec.tug_regn).first()
        if tug is None and thisrec.tug_regn != constTOW_FOR_SELF_LAUNCH:
            return False
        if thisrec.ac_regn == constREGN_FOR_TUG_ONLY:
            if thisrec.tow_mins() <= 0:
                flash('Insufficient flight data to calculate default charges')
                return False
        elif thisrec.tug_regn == constTOW_FOR_SELF_LAUNCH:
            pass # there are no checks.
        else:  # Not tug only.
            if thisrec.tow_mins is None \
                or (thisrec.release_height is None and thisrec.tug_regn != constTOW_FOR_SELF_LAUNCH) \
                or thisrec.glider_mins() == 0:
                flash('Insufficient flight data to calculate default charges')
                return False
            if thisrec.tow_mins() <= 0 or thisrec.glider_mins() <= 0:
                flash('Incorrect flight data.  Cannot calculate default charges')
                return False
        # Flight fee
        thisac = Aircraft.query.filter_by(regn=thisrec.ac_regn).first()
        if thisac is None:
            # this could be valid.  Could be a visiting a/c
            thisrec.glider_charge = 0
        # BIG IF-ELIF that determins the rates
        # Start with trial flights
        if thisrec.p2.upper() == constTRIAL_FLIGHT_CUST:
            SlotForRate = Slot.query.filter_by(slot_type='DEFAULT').filter_by(slot_key='TRIALFLIGHTPRICE').first()
            if SlotForRate is None:
                thisrec.tow_charge = 100
            else:
                thisrec.tow_charge = SlotForRate.slot_data
            thisrec.payer = thisrec.p2
        # REGN_FOR_TUG_ONLY appears in the Glider Regn field.
        elif thisrec.ac_regn == constREGN_FOR_TUG_ONLY:
            thisrec.glider_charge = 0
            thisrec.tow_charge = Decimal(thisrec.tow_mins() * (tug.rate_per_hour_tug_only / 60))
            thisrec.payer = thisrec.pic_rec().fullname
            # Nothing else to do if the tug is flown on it's own.
        elif thisrec.tug_regn == constTOW_FOR_SELF_LAUNCH:
            thisrec.tow_charge = 0
            thisrec.glider_charge = Decimal(thisrec.glider_mins()) * (thisac.rate_per_hour / 60)
            if thisrec.p2 is not None:
                payer = Pilot.query.filter_by(fullname=thisrec.p2).first()
            if payer is None:
                payer = Pilot.query.filter_by(fullname=thisrec.pic).first()
            if payer is not None:
                if payer.bscheme and thisac.bscheme:
                    thisrec.glider_charge = 0
            if payer is not None:
                thisrec.payer = payer.fullname
        else: # Normal Launch
            # Calculate the tow / launch costs
            thisrec.tow_charge = 0
            if tug.rate_per_height != 0:
                thisrec.tow_charge += Decimal((thisrec.release_height / tug.per_height_basis) * tug.rate_per_height)
            if tug.rate_per_hour != 0:
                thisrec.tow_charge += Decimal(thisrec.tow_mins() * (tug.rate_per_hour / 60))
            if tug.flat_charge_per_launch != 0:
                thisrec.tow_charge += tug.flat_charge_per_launch
            # Calculate the glider charge
            thisrec.glider_charge = Decimal(thisrec.glider_mins()) * (thisac.rate_per_hour / 60)
            if thisrec.p2 is not None:
                payer = Pilot.query.filter_by(fullname=thisrec.p2).first()
            if payer is None:
                payer = Pilot.query.filter_by(fullname=thisrec.pic).first()
            if payer is not None:
                if payer.bscheme and thisac.bscheme:
                    thisrec.glider_charge = 0
            if payer is not None:
                thisrec.payer = payer.fullname
        db.session.commit()
    except Exception as e:
        flash(str(e))
        return False
    return True


import re
import datetime
import win10toast
import threading

def find_closest_match(pattern, text):
    # Find all matches of the pattern in the text
    matches = re.findall(pattern, text)

    # Get the current date and time
    now = datetime.datetime.now()

    # Set the minimum difference to the maximum possible value
    min_difference = datetime.timedelta.max

    # Set the closest match to None
    closest_match = None

    # Iterate over the matches
    for match in matches:
        # Parse the date and time from the match
        date = datetime.datetime.strptime(match, '%Y-%m-%d %H:%M:%S')

        # Calculate the difference between the current date and time and the match
        difference = abs(date - now)

        # If the difference is smaller than the minimum difference, set the closest match to the current match
        if difference < min_difference:
            min_difference = difference
            closest_match = match

    # Return the closest match
    return closest_match

def extract_command(file):
    # Read the Markdown file
    with open(file, 'r') as f:
        text = f.read()

    # Extract the command from the text
    pattern = r'\*\*(.+?)\*\*'
    match = find_closest_match(pattern, text)

    if match:
        # Split the command into time and message
        time, message = match.group(1).split(';')

        # Strip leading and trailing whitespace from the time and message
        time = time.strip()
        message = message.strip()

        return time, message

    return None, None

def launch_alarm(file):
    # Create a ToastNotifier object
    toaster = win10toast.ToastNotifier()

    # Extract the command from the Markdown file
    time, message = extract_command(file)

    if time and message:
        # Parse the time and display a notification
        alarm_time = datetime.datetime.strptime(time, '%Y-%m-%d %H:%M:%S')
        toaster.show_toast("Alarm", message, alarm_time)
    else:
        print("Command not found")

def run_alarm_thread(file):
    # Create a ToastNotifier object
    toaster = win10toast.ToastNotifier()

    # Extract the command from the Markdown file
    time, message = extract_command(file)

    # Set the current alarm to None
    current_alarm = None

    # Run the loop indefinitely
    while True:
        # If the current alarm is None or has already gone off, extract the next command from the file
        if not current_alarm or current_alarm < datetime.datetime.now():
            time, message = extract_command(file)
            current_alarm = datetime.datetime.strptime(time, '%Y-%m-%d %H:%M:%S')

        # Calculate the time until the next alarm
        time_until_alarm = current_alarm - datetime.datetime.now()

        # Sleep for the time until the next alarm
        time.sleep(time_until_alarm.total_seconds())

        # Display the notification
        toaster.show_toast("Alarm", message, current_alarm)
def run_alarm(file):
    # Create a ToastNotifier object
    toaster = win10toast.ToastNotifier()

    # Extract the command from the Markdown file
    time, message = extract_command(file)

    # Set the current alarm to None
    current_alarm = None

    # Run the loop indefinitely
    while True:
        # If the current alarm is None or has already gone off, extract the next command from the file
        if not current_alarm or current_alarm < datetime.datetime.now():
            time, message = extract_command(file)
            current_alarm = datetime.datetime.strptime(time, '%Y-%m-%d %H:%M:%S')

        # Calculate the time until the next alarm
        time_until_alarm = current_alarm - datetime.datetime.now()

        # Sleep for the time until the next alarm
        time.sleep(time_until_alarm.total_seconds())

        # Display the notification
        toaster.show_toast("Alarm", message, current_alarm)

# Run the alarm
#run_alarm('alarm.md')

# Create a thread to run the alarm in the background
#alarm_thread = threading.Thread(target=run_alarm_thread, args=('alarm.md',))

# Start the thread
#alarm_thread.start()

#launch_alarm('alarm.md')

# example of md file
#**2022-01-01 00:00:00; Wake up!**
#It's time to start your day.


from pycp2k.inputsection import InputSection
from ._coord7 import _coord7
from ._velocity8 import _velocity8

class _nose6(InputSection):
    def __init__(self):
        InputSection.__init__(self)
        self.Nnos = None
        self.COORD = _coord7()
        self.VELOCITY = _velocity8()
        self._name = "NOSE"
        self._keywords = {'Nnos': 'NNOS'}
        self._subsections = {'VELOCITY': 'VELOCITY', 'COORD': 'COORD'}


import math
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
from distutils.version import LooseVersion
from timm.models.layers import trunc_normal_

from vqfr.archs.vqganv1_arch import GeneralizedQuantizer, ResnetBlock, UpDownSample, VQGANDecoder, VQGANEncoder
from vqfr.ops.dcn import ModulatedDeformConvPack, modulated_deform_conv
from vqfr.utils import get_root_logger
from vqfr.utils.registry import ARCH_REGISTRY

class DCNv2Pack(ModulatedDeformConvPack):
    """Modulated deformable conv for deformable alignment.
    Different from the official DCNv2Pack, which generates offsets and masks
    from the preceding features, this DCNv2Pack takes another different
    features to generate offsets and masks.
    Ref:
        Delving Deep into Deformable Alignment in Video Super-Resolution.
    """

    def forward(self, x, feat):
        out = self.conv_offset(feat)
        o1, o2, mask = torch.chunk(out, 3, dim=1)
        offset = torch.cat((o1, o2), dim=1)
        mask = torch.sigmoid(mask)

        offset_absmean = torch.mean(torch.abs(offset))
        if offset_absmean > 50:
            logger = get_root_logger()
            logger.warning(f'Offset abs mean is {offset_absmean}, larger than 50.')

        if LooseVersion(torchvision.__version__) >= LooseVersion('0.9.0'):
            return torchvision.ops.deform_conv2d(x, offset, self.weight, self.bias, self.stride, self.padding,
                                                 self.dilation, mask)
        else:
            return modulated_deform_conv(x, offset, mask, self.weight, self.bias, self.stride, self.padding,
                                         self.dilation, self.groups, self.deformable_groups)

class TextureWarpingModule(nn.Module):

    def __init__(self, channel, cond_channels, cond_downscale_rate, deformable_groups, previous_offset_channel=0):
        super(TextureWarpingModule, self).__init__()
        self.downsample = UpDownSample(
            in_channels=cond_channels, out_channels=cond_channels, scale_factor=cond_downscale_rate, direction='down')
        self.offset_conv1 = nn.Sequential(
            nn.Conv2d(channel + cond_channels, channel, kernel_size=1),
            nn.GroupNorm(num_groups=32, num_channels=channel, eps=1e-6, affine=True), nn.SiLU(inplace=True),
            nn.Conv2d(channel, channel, groups=channel, kernel_size=7, padding=3),
            nn.GroupNorm(num_groups=32, num_channels=channel, eps=1e-6, affine=True), nn.SiLU(inplace=True),
            nn.Conv2d(channel, channel, kernel_size=1))

        self.offset_conv2 = nn.Sequential(
            nn.Conv2d(channel + previous_offset_channel, channel, 3, 1, 1),
            nn.GroupNorm(num_groups=32, num_channels=channel, eps=1e-6, affine=True), nn.SiLU(inplace=True))
        self.dcn = DCNv2Pack(channel, channel, 3, padding=1, deformable_groups=deformable_groups)

    def forward(self, x_main, prior, previous_offset=None):
        prior = self.downsample(prior)
        offset = self.offset_conv1(torch.cat([prior, x_main], dim=1))
        if previous_offset is None:
            offset = self.offset_conv2(offset)
        else:
            offset = self.offset_conv2(torch.cat([offset, previous_offset], dim=1))

        warp_feat = self.dcn(x_main, offset)
        return warp_feat, offset

class MainDecoder(nn.Module):

    def __init__(self, base_channels, resolution_scale_rates, channel_multipliers, align_opt, align_from_patch):
        super(MainDecoder, self).__init__()
        self.log_size = int(math.log(align_from_patch, 2))

        self.channel_dict = {}
        self.resolution_scalerate_dict = {}

        resolution_scale_rates = resolution_scale_rates[::-1]

        for idx, scale in enumerate(range(self.log_size + 1)):
            self.channel_dict['Level_%d' % 2**scale] = channel_multipliers[idx] * base_channels
            self.resolution_scalerate_dict['Level_%d' % 2**scale] = resolution_scale_rates[idx]

        self.decoder_dict = nn.ModuleDict()
        self.pre_upsample_dict = nn.ModuleDict()

        for scale in range(self.log_size - 1, -1, -1):
            in_channel = self.channel_dict['Level_%d' %
                                           2**scale] if scale == self.log_size else self.channel_dict['Level_%d' %
                                                                                2**(scale + 1)]
            stage_channel = self.channel_dict['Level_%d' % 2**scale]
            upsample_rate = self.resolution_scalerate_dict['Level_%d' % 2**scale]

            self.decoder_dict['Level_%d' % 2**scale] = ResnetBlock(2 * stage_channel, stage_channel)
            self.pre_upsample_dict['Level_%d' % 2**scale] = UpDownSample(
                in_channel, stage_channel, upsample_rate, direction='up')

        self.align_func_dict = nn.ModuleDict()
        if align_opt is not None:
            for level_name, level_cfg in align_opt.items():
                level_idx = int(math.log(int(level_name.split('_')[1]), 2))
                previous_offset_channel = 0 if (
                    2**level_idx) == align_from_patch else self.channel_dict['Level_%d' % (2**(level_idx + 1))]
                channel = self.channel_dict[level_name]
                level_cfg['channel'] = channel
                level_cfg['previous_offset_channel'] = previous_offset_channel
                self.align_func_dict[level_name] = TextureWarpingModule(**level_cfg)

        self.conv_out_1 = nn.Sequential(
            nn.GroupNorm(num_groups=32, num_channels=self.channel_dict['Level_%d' % 2**0], eps=1e-6, affine=True),
            nn.SiLU(inplace=True))
        self.conv_out = nn.Sequential(
            nn.Conv2d(self.channel_dict['Level_%d' % 2**0] + 3, 3, kernel_size=3, stride=1, padding=1))

    def forward(self, dec_res_dict, prior, x_lq):

        x, offset = self.align_func_dict['Level_%d' % 2**self.log_size](dec_res_dict['Level_%d' % 2**self.log_size],
                                                                        prior)

        for scale in range(self.log_size - 1, -1, -1):
            x = self.pre_upsample_dict['Level_%d' % 2**scale](x)
            upsample_offset = F.interpolate(offset, scale_factor=2, align_corners=False, mode='bilinear') * 2
            warp_feat, offset = self.align_func_dict['Level_%d' % 2**scale](
                dec_res_dict['Level_%d' % 2**scale], prior, previous_offset=upsample_offset)
            x = self.decoder_dict['Level_%d' % 2**scale](torch.cat([x, warp_feat], dim=1))
        x = self.conv_out_1(x)
        x = self.conv_out(torch.cat([x, x_lq], dim=1))
        return x

class InpFeatConv(nn.Module):

    def __init__(self, in_dim, out_dim):
        super(InpFeatConv, self).__init__()
        self.conv = nn.Conv2d(in_dim, out_dim, 3, padding=1)

    def forward(self, x):
        x = self.conv(x)
        return x

@ARCH_REGISTRY.register()
class VQFRv1(nn.Module):

    def __init__(self,
                 base_channels,
                 proj_patch_size,
                 resolution_scale_rates,
                 channel_multipliers,
                 encoder_num_blocks,
                 decoder_num_blocks,
                 quant_level,
                 inpfeat_extraction_opt,
                 align_opt,
                 align_from_patch,
                 quantizer_opt,
                 fix_keys=[]):
        super().__init__()

        self.inpfeat_extraction = InpFeatConv(**inpfeat_extraction_opt)

        self.encoder = VQGANEncoder(
            base_channels=base_channels,
            proj_patch_size=proj_patch_size,
            resolution_scale_rates=resolution_scale_rates,
            channel_multipliers=channel_multipliers,
            encoder_num_blocks=encoder_num_blocks,
            quant_level=quant_level)

        self.decoder = VQGANDecoder(
            base_channels=base_channels,
            proj_patch_size=proj_patch_size,
            resolution_scale_rates=resolution_scale_rates,
            channel_multipliers=channel_multipliers,
            decoder_num_blocks=decoder_num_blocks)

        self.main_decoder = MainDecoder(
            base_channels=base_channels,
            resolution_scale_rates=resolution_scale_rates,
            channel_multipliers=channel_multipliers,
            align_opt=align_opt,
            align_from_patch=align_from_patch)

        self.quantizer = GeneralizedQuantizer(quantizer_opt)

        self.apply(self._init_weights)

        for k, v in self.named_parameters():
            for fix_k in fix_keys:
                if fix_k in k:
                    v.requires_grad = False

    def _init_weights(self, m):
        if isinstance(m, nn.Linear):
            trunc_normal_(m.weight, std=.02)
            if isinstance(m, nn.Linear) and m.bias is not None:
                nn.init.constant_(m.bias, 0)
        elif isinstance(m, (nn.LayerNorm, nn.GroupNorm)):
            nn.init.constant_(m.bias, 0)
            nn.init.constant_(m.weight, 1.0)
        elif isinstance(m, nn.Conv2d):
            trunc_normal_(m.weight, std=.02)
            if m.bias is not None:
                nn.init.constant_(m.bias, 0)

    def quant(self, enc_dict, iters=-1):
        quant_dict, emb_loss, info_dict = self.quantizer(enc_dict, iters=iters)
        return quant_dict, emb_loss, info_dict

    def encode(self, x):
        enc_dict = self.encoder(x)
        return enc_dict

    def decode(self, quant_dict):
        dec, feat_dict = self.decoder(quant_dict, return_feat=True)
        return dec, feat_dict

    def main_decode(self, dec_dict, prior, x_lq):
        dec = self.main_decoder(dec_dict, prior, x_lq)
        return dec

    def get_last_layer(self):
        return self.decoder.conv_out[-1].weight

    def get_main_last_layer(self):
        return self.main_decoder.conv_out[-1].weight

    def forward(self, x_lq, iters=-1, return_keys=('dec'), fidelity_ratio=None):

        inpfeat = self.inpfeat_extraction(x_lq)

        res = {}

        enc_dict = self.encode(x_lq)
        quant_dict, _, feat_dict = self.quant(enc_dict, iters=iters)

        if 'feat_dict' in return_keys:
            res['feat_dict'] = feat_dict

        if 'dec' in return_keys:
            dec, dec_feat_dict = self.decode(quant_dict)
            main_dec = self.main_decode(dec_feat_dict, inpfeat, x_lq)
            res['dec'] = dec
            res['main_dec'] = main_dec

        return res


from algo.cnn import CNN

import os
from pathlib import Path
import torch
import torch.nn as nn
import torch.optim as optim
from torch import autograd
import numpy as np

class Discriminator(nn.Module):  # Discriminator Network

    # initializer
    def __init__(self, device, lr, state_dim, num_actions, wasserstein, grad_penalty, branched=False):
        super(Discriminator, self).__init__()
        self.lr = lr
        self.state_dim = state_dim
        self.num_actions = num_actions
        self.device = device
        self.branched = branched

        self.grad_penalty = grad_penalty
        self.wasserstein = wasserstein
        self.bce_loss = nn.BCEWithLogitsLoss()

        self.cnn = CNN(n_channels=9)
        if self.branched:
            self.disc = nn.Linear(512+1+num_actions, 3)
        else:
            self.disc = nn.Linear(512+3+1+num_actions, 1)

        self.optimizer = optim.AdamW(self.parameters(), lr=lr, weight_decay=0.1)
        self.to(self.device)

        checkpoint_dir = os.path.join(Path(__file__).parent.parent, 'checkpoints')
        if not os.path.exists(checkpoint_dir):
            os.makedirs(checkpoint_dir)
        self.checkpoint_file = os.path.join(checkpoint_dir, f'bc_gail_disc')

    # forward propagation
    def forward(self, state, command, speed, action):
        cnn_out = self.cnn(state)
        if self.branched:
            disc_inp = torch.cat([cnn_out, speed, action], dim=1)
            preds = self.disc(disc_inp)
            pred = command[:, 0:1] * preds[:, 0:1] + \
                   command[:, 1:2] * preds[:, 1:2] + \
                   command[:, 2:3] * preds[:, 2:3]
        else:
            disc_inp = torch.cat([cnn_out, command, speed, action], dim=1)
            pred = self.disc(disc_inp)
        return pred

    # gradient penalty (only works with single-branched architecture)
    def compute_grad_pen(self, expert_states, expert_commands, expert_speeds, expert_actions,
                         agent_states, agent_commands, agent_speeds, agent_actions, lambda_=10):

        alpha = torch.rand(expert_states.size(0), 1)

        exp_cnn_out = self.cnn(expert_states)
        agt_cnn_out = self.cnn(agent_states)
        expert_data = torch.cat([exp_cnn_out, expert_commands, expert_speeds, expert_actions], dim=1)
        policy_data = torch.cat([agt_cnn_out, agent_commands, agent_speeds, agent_actions], dim=1)
        alpha = alpha.expand_as(expert_data).to(expert_data.device)
        mixup_data = alpha * expert_data + (1 - alpha) * policy_data
        disc = self.disc(mixup_data)

        ones = torch.ones(disc.size()).to(self.device)
        grad = autograd.grad(outputs=disc, inputs=mixup_data, grad_outputs=ones,
                             create_graph=True, retain_graph=True, only_inputs=True)[0]

        grad_pen = lambda_ * (grad.norm(2, dim=1) - 1).pow(2).mean()
        return grad_pen

    def learn(self, disc_batch_size, expert_states, expert_commands, expert_speeds, expert_actions,
              agent_states, agent_commands, agent_speeds, agent_actions):

        n_states = agent_states.shape[0]

        # sample expert data
        expert_states_sample, expert_commands_sample, expert_speeds_sample, expert_actions_sample = sample_expert(
            expert_states, expert_commands, expert_speeds, expert_actions, n_states)

        # creating tensors from expert states and actions
        expert_states_sample = torch.from_numpy(expert_states_sample)
        expert_commands_sample = torch.from_numpy(expert_commands_sample)
        expert_speeds_sample = torch.from_numpy(expert_speeds_sample)
        expert_actions_sample = torch.from_numpy(expert_actions_sample)

        # creating mini-batches
        batch_starts = np.arange(0, n_states, disc_batch_size)

        mean_real_loss = 0
        mean_fake_loss = 0
        mean_raw_loss = 0
        mean_loss = 0

        for b in batch_starts:
            expert_states_batch = expert_states_sample[b: b + disc_batch_size].to(self.device)
            expert_commands_batch = expert_commands_sample[b: b + disc_batch_size].to(self.device)
            expert_speeds_batch = expert_speeds_sample[b: b + disc_batch_size].to(self.device)
            expert_actions_batch = expert_actions_sample[b: b + disc_batch_size].to(self.device)
            agent_states_batch = agent_states[b: b + disc_batch_size].to(self.device)
            agent_commands_batch = agent_commands[b: b + disc_batch_size].to(self.device)
            agent_speeds_batch = agent_speeds[b: b + disc_batch_size].to(self.device)
            agent_actions_batch = agent_actions[b: b + disc_batch_size].to(self.device)

            # calculating discriminator prediction
            agent_preds = torch.squeeze(
                self.forward(agent_states_batch, agent_commands_batch,
                             agent_speeds_batch, agent_actions_batch), dim=-1)

            expert_preds = torch.squeeze(
                self.forward(expert_states_batch, expert_commands_batch,
                             expert_speeds_batch, expert_actions_batch), dim=-1)

            if self.wasserstein:
                raw_disc_loss = -(expert_preds.mean() - agent_preds.mean())
            else:

                # real and fake labels (1s and 0s)
                real_labels = torch.ones(expert_states_batch.shape[0]).to(self.device)
                fake_labels = torch.zeros(agent_states_batch.shape[0]).to(self.device)

                # calculating the loss
                loss_real = self.bce_loss(expert_preds, real_labels)
                loss_fake = self.bce_loss(agent_preds, fake_labels)
                raw_disc_loss = loss_real + loss_fake

            if self.grad_penalty:
                grad_pen = self.compute_grad_pen(
                    expert_states_batch, expert_commands_batch, expert_speeds_batch, expert_actions_batch,
                    agent_states_batch, agent_speeds_batch, agent_commands_batch, agent_actions_batch)
                disc_loss = raw_disc_loss + grad_pen
            else:
                disc_loss = raw_disc_loss

            # backpropagation and optimization
            self.optimizer.zero_grad()
            disc_loss.backward()
            self.optimizer.step()

            mean_real_loss += loss_real.item() if not self.wasserstein else 0.0
            mean_fake_loss += loss_fake.item() if not self.wasserstein else 0.0
            mean_raw_loss += raw_disc_loss.item()
            mean_loss += disc_loss.item()

        mean_real_loss /= len(batch_starts)
        mean_fake_loss /= len(batch_starts)
        mean_raw_loss /= len(batch_starts)
        mean_loss /= len(batch_starts)

        return mean_real_loss, mean_fake_loss, mean_raw_loss, mean_loss

    # generate rewards
    def generate_rewards(self, agent_states, agent_commands, agent_speeds, agent_actions):
        with torch.no_grad():
            probs = torch.sigmoid(self.forward(agent_states, agent_commands, agent_speeds, agent_actions))
            clamped_probs = torch.clamp(probs, min=0.01, max=0.99)
            rewards = - torch.log(1 - clamped_probs)
        return rewards.squeeze()

    def save_models(self):
        torch.save(self.state_dict(), self.checkpoint_file)

    def load_models(self):
        self.load_state_dict(torch.load(self.checkpoint_file, map_location=self.device))

def sample_expert(expert_states, expert_commands, expert_speeds, expert_actions, num):
    indices = np.random.choice(np.arange(expert_states.shape[0]), num, replace=False)
    return expert_states[indices], expert_commands[indices], expert_speeds, expert_actions[indices]


#!/usr/bin/env python3
import socket
import numpy as np
import json
import matplotlib.pyplot as plt
import matplotlib.animation as animation

HOST = '<IP>'  # IP address
PORT = 65431  # Port to listen on (use ports > 1023)
DATA_SIZE = 50 # number of data to be displayed in the graph simultaneously, you can change it to any number you want

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen()
    print('Server is listening...')
    conn, addr = s.accept()
    with conn:
        print('Connected by', addr)

        # set up the figure
        fig = plt.figure(figsize=plt.figaspect(0.5))

        # create 3D subplots for gyro and accelerometer data
        ax_gyro = fig.add_subplot(1, 2, 1, projection='3d')
        ax_acce = fig.add_subplot(1, 2, 2, projection='3d')

        # initialize empty lists for storing data
        gyro_x = [0]
        gyro_y = [0]
        gyro_z = [0]
        acce_x = [0]
        acce_y = [0]
        acce_z = [0]

        # define the animation function
        def animate(frame):
            message = "server requesting data"
            conn.sendall(message.encode('utf-8'))
            data = conn.recv(1024).decode('utf-8')
            print('Received from socket server : ', data)
            # print data
            json_data = json.loads(data)
            # json_formatted_str = json.dumps(json_data, indent=2)
            # print(json_formatted_str)
            # append data to list
            gyro_x.append(json_data["Gyro"]["x"])
            gyro_y.append(json_data["Gyro"]["y"])
            gyro_z.append(json_data["Gyro"]["z"])
            acce_x.append(json_data["Acce"]["x"])
            acce_y.append(json_data["Acce"]["y"])
            acce_z.append(json_data["Acce"]["z"])

            # Limit the data lists to a maximum length of 100
            if len(gyro_x) > DATA_SIZE:
                gyro_x.pop(0)
                gyro_y.pop(0)
                gyro_z.pop(0)
                acce_x.pop(0)
                acce_y.pop(0)
                acce_z.pop(0)
            # time.sleep(3)
            ax_gyro.clear()
            ax_gyro.plot(gyro_x, gyro_y, gyro_z, label="Gyro", color="blue")
            ax_gyro.legend()
            ax_acce.clear()
            ax_acce.plot(acce_x, acce_y, acce_z, label="Acce", color="red")
            ax_acce.legend()

        # create a FuncAnimation object and display the plot
        ani = animation.FuncAnimation(fig, animate, frames=range(0, DATA_SIZE), interval=200)
        plt.show()


from pathlib import Path

import torch
import numpy as np
import pandas as pd
from torch.utils.data import Dataset

class PPFDataset(Dataset):
    def __init__(self, dataset_dir, num_images=None):
        assert Path(f'data/{dataset_dir}/labels.csv').is_file()
        df = pd.read_csv(f'data/{dataset_dir}/labels.csv')

        if num_images == None:
            num_images = len(df)
        else:
            assert num_images < len(df)
        num_patches = pd.Series(df['num_patches'][:num_images], dtype='int32')
        filename = pd.Series(df['filename'][:num_images])

        self.filenames = [
            f'{fname}_{i:03d}.npy'
            for fname, imax in zip(filename, num_patches)
            for i in range(imax)
        ]

        assert Path(f'data/{dataset_dir}/image').is_dir()
        self.ds_dir = f'data/{dataset_dir}'

        self.dataset_size = len(self.filenames)

    def __getitem__(self, idx):
        assert Path(f'{self.ds_dir}/image/{self.filenames[idx]}').is_file()

        with open(f'{self.ds_dir}/image/{self.filenames[idx]}', 'rb') as file:
            img_ppf = np.load(file)

        return img_ppf

    def __len__(self):
        return self.dataset_size

class PPFPairDataset(Dataset):
    def __init__(self, dataset_dir, num_images=None):
        assert Path(f'data/{dataset_dir}/labels.csv').is_file()
        df = pd.read_csv(f'data/{dataset_dir}/labels.csv')

        if num_images == None:
            num_images = len(df)
        else:
            assert num_images < len(df)
        num_patches = pd.Series(df['num_patches'][:num_images], dtype='int32')
        filename = pd.Series(df['filename'][:num_images])

        self.filenames = [
            f'{fname}_{i:03d}.npy'
            for fname, imax in zip(filename, num_patches)
            for i in range(imax)
        ]

        assert Path(f'data/{dataset_dir}/image').is_dir()
        assert Path(f'data/{dataset_dir}/model').is_dir()
        self.ds_dir = f'data/{dataset_dir}'

        self.dataset_size = len(self.filenames)

    def __getitem__(self, idx):
        assert Path(f'{self.ds_dir}/image/{self.filenames[idx]}').is_file()
        with open(f'{self.ds_dir}/image/{self.filenames[idx]}', 'rb') as file:
            img_ppf = np.load(file)

        assert Path(f'{self.ds_dir}/model/{self.filenames[idx]}').is_file()
        with open(f'{self.ds_dir}/model/{self.filenames[idx]}', 'rb') as file:
            md_ppf = np.load(file)

        return img_ppf, md_ppf

    def __len__(self):
        return self.dataset_size

if __name__ == '__main__':
    from statistics import mean, median, stdev
    from torch.utils.data import DataLoader
    from tqdm import tqdm
    from configs import DEVICE

    train_ds = PPFPairDataset('ycbv_obj_000001_train', 50)
    train_dl = DataLoader(train_ds, batch_size=1)
    print(f'Training set size: {train_ds.__len__()}')

    x, y = next(iter(train_dl))
    print(x[0, :5, :], y[0, :5, :], sep='\n')
    print(x.size(), y.size())

    loss_func = torch.nn.CosineSimilarity(dim=0)
    loss = loss_func(x, y)
    print(loss.size(), loss, sep='\n')

    # losses = [
    #     torch.sum(loss_func(x.to(DEVICE), y.to(DEVICE))).item()
    #     for x, y in tqdm(train_dl)
    # ]
    # print(f'Mean: {mean(losses):.3f} / Median: {median(losses):.3f} / Stdev: {stdev(losses):.3f}')


import orjson
from pydantic import parse_raw_as
from redis.asyncio import Redis, ConnectionError

from core.helpers.utils import orjson_dumps
from db.storage.cache.base import AbstractAsyncCacheStorage, IndexModelType

class RedisAsyncCacheStorage(AbstractAsyncCacheStorage):
    """Redis is a repository class of Pydantic models that uses or json for serialization/deserialization."""

    def __init__(self, client: Redis):
        self._storage: Redis
        super().__init__(client)

    async def ping(self) -> bool:
        result = True
        try:
            # ping is unsafe
            self._storage and await self._storage.ping()
        except ConnectionError:
            result = False

        return result

    async def set(self, key: str, value: IndexModelType, ttl: int):
        await self._storage.set(key, value.json(encoder=orjson_dumps, by_alias=True), ex=ttl)

    async def get(self, key: str, model_type: type[IndexModelType]) -> IndexModelType | None:
        value = await self._storage.get(key)

        if value is None:
            return None

        return parse_raw_as(model_type, value, json_loads=orjson.loads)


"""
fc_model.py

Written by Emily Holmes during a LAAS-CNRS internship, 2023
Simple feed-forward fully connected model for SSL estimations
"""

# Imports
import tensorflow as tf
import numpy as np
import os
import json

# Parameters
DATASET_FOLDER = '../dataset/SSLR'
TRAIN_FOLDER = 'lsp_train_106'
TEST_FOLDER = 'lsp_test_106'
LABEL_FOLDER = 'label'
ALT_GCC_FOLDER = 'gcc'

AUDIO_FOLDER = 'audio'
GT_FOLDER = 'gt_frame/'

FEATURES_FOLDER = 'features'
PREDICT_FOLDER = 'predict'

ACTIVE_FOLDER = TEST_FOLDER

GENERATE_MODEL = 1
GENERATE_DATASET = 1
MODEL_FIT = 1
PREDICT = 1
PREDIT_DETEC = 0

'''
Custom metric function

Compares the L2 norm of the prediction and ground truth.
Returns 100% if 100% of predicted values are within a 20deg margin
'''
def is_within_20deg(y_true, y_pred):
    delta_theta = tf.abs(tf.subtract(y_true[0], y_pred[0]))
    return tf.reduce_mean(tf.cast(tf.less_equal(delta_theta, 20.0/360), tf.float32))

'''
Custom learning rate

Returns a slower learning rate as epoches go on, allows for better convergence of the results
'''
def lr_scheduler(epoch, lr):
    if epoch > 1 & (epoch % 5) == 0:
        lr = 0.01/(2*(epoch-1))
        return lr
    return lr

if GENERATE_MODEL:
    '''
    Model generation - Predicts the azimuth
    Model is sequential (one input, one output)

    Input: the 51 center delays ([-25,25]) for the generalised cross-correlation (GCC-PHAT)
    between 4 microphones (totalling 6 different combinations)

    Structure:
    Fully connected layers with tanh activation, ending with a sigmoid activation

    Returns: a prediction between 0 (0°) and 1 (360°)
    '''
    model = tf.keras.Sequential([
            tf.keras.layers.Flatten(input_shape=(51,6)),
            tf.keras.layers.Dense(150, activation = 'tanh'),
            tf.keras.layers.BatchNormalization(),
            tf.keras.layers.Dense(150, activation = 'tanh'),
            tf.keras.layers.BatchNormalization(),
            tf.keras.layers.Dense(51, activation = 'tanh'),
            tf.keras.layers.BatchNormalization(),
            tf.keras.layers.Dense(1, activation = 'sigmoid'),

    ])

    model.summary()
    model.compile(optimizer=tf.optimizers.Adam(learning_rate=0.01),
                loss=tf.keras.losses.MeanAbsoluteError(),
                metrics=['accuracy', is_within_20deg])

    if PREDIT_DETEC:
        '''
        Model generation - Detect audio activity
        Model is sequential (one input, one output)

        Input: the 51 center delays ([-25,25]) for the generalised cross-correlation (GCC-PHAT)
        between 4 microphones (totalling 6 different combinations)

        Structure:
        Fully connected layers with tanh activation, ending with a sigmoid activation

        Returns: a prediction between 0 (0% chance of an audio activity) to 1 (100% chance of an audio activity)
        '''
        model_detec = tf.keras.Sequential([
                tf.keras.layers.Flatten(input_shape=(51,6)),
                tf.keras.layers.Dense(300, activation = 'tanh'),
                tf.keras.layers.BatchNormalization(),
                tf.keras.layers.Dense(1, activation = 'sigmoid'),
        ])

        model_detec.summary()
        model_detec.compile(optimizer=tf.optimizers.Adam(learning_rate=0.01, epsilon = 0.1),
                    loss=tf.keras.losses.BinaryCrossentropy(),
                    metrics=['accuracy'])

if GENERATE_DATASET:

    # yield an input frame (51,6) and the ground truth azimuth
    def generator_doa():
        for filecount, filename in enumerate(os.listdir(os.path.join(DATASET_FOLDER, FEATURES_FOLDER, ALT_GCC_FOLDER, TRAIN_FOLDER))):
            cur_file = os.path.join(DATASET_FOLDER, FEATURES_FOLDER, ALT_GCC_FOLDER, TRAIN_FOLDER, filename)
            cur_label = os.path.join(DATASET_FOLDER, FEATURES_FOLDER, LABEL_FOLDER, TRAIN_FOLDER, filename.split('.')[0] + '.w8192_o4096.csv')
            file = np.genfromtxt(cur_file, delimiter=',')
            label = np.genfromtxt(cur_label, delimiter=',')
            for index in range(label.shape[0]):
                line = np.reshape(file, (file.shape[0], 51, 6))[index]
                yield line, label[index][1]

    def generator_doa_val():
        for filecount, filename in enumerate(os.listdir(os.path.join(DATASET_FOLDER, FEATURES_FOLDER, ALT_GCC_FOLDER, TEST_FOLDER))):
            cur_file = os.path.join(DATASET_FOLDER, FEATURES_FOLDER, ALT_GCC_FOLDER, TEST_FOLDER, filename)
            cur_label = os.path.join(DATASET_FOLDER, FEATURES_FOLDER, LABEL_FOLDER, TEST_FOLDER, filename.split('.')[0] + '.w8192_o4096.csv')
            file = np.genfromtxt(cur_file, delimiter=',')
            label = np.genfromtxt(cur_label, delimiter=',')
            for index in range(label.shape[0]):
                line = np.reshape(file, (file.shape[0], 51, 6))[index]
                yield line, label[index][1]

    # create a dataset from the generator function above
    ds = tf.data.Dataset.from_generator(
        generator_doa,
        output_signature=(
        tf.TensorSpec(shape=(51, 6), dtype=tf.float32),
        tf.TensorSpec(shape=(), dtype=tf.float32))
        )
    ds = ds.batch(256)
    ds = ds.repeat(10)
    ds = ds.shuffle(3, reshuffle_each_iteration=True)

    # create a dataset from the generator function above
    ds_val = tf.data.Dataset.from_generator(
        generator_doa_val,
        output_signature=(
        tf.TensorSpec(shape=(51, 6), dtype=tf.float32),
        tf.TensorSpec(shape=(), dtype=tf.float32))
        )
    ds_val = ds_val.batch(256)

    if PREDIT_DETEC:
        # yield an input frame (51,6) and the ground truth activation
        def generator_detec():
            for filecount, filename in enumerate(os.listdir(os.path.join(DATASET_FOLDER, FEATURES_FOLDER, ALT_GCC_FOLDER, TRAIN_FOLDER))):
                cur_file = os.path.join(DATASET_FOLDER, FEATURES_FOLDER, ALT_GCC_FOLDER, TRAIN_FOLDER, filename)
                cur_label = os.path.join(DATASET_FOLDER, FEATURES_FOLDER, LABEL_FOLDER, TRAIN_FOLDER, filename.split('.')[0] + '.w8192_o4096.csv')
                file = np.genfromtxt(cur_file, delimiter=',')
                label = np.genfromtxt(cur_label, delimiter=',')
                for index in range(label.shape[0]):
                    line = np.reshape(file, (file.shape[0], 51, 6))[index]
                    yield line, int(label[index][0])

        ds_detec = tf.data.Dataset.from_generator(
            generator_detec,
            output_signature=(
            tf.TensorSpec(shape=(51, 6), dtype=tf.float32),
            tf.TensorSpec(shape=(), dtype=tf.int8))
            )
        ds_detec = ds_detec.batch(256)
        ds_detec = ds_detec.repeat(10)
        ds_detec = ds_detec.shuffle(3, reshuffle_each_iteration=True)

'''
Train the model

Frames in the training dataset = 183059
Frames in the testing dataset = 102775
Steps per epoch = samples / batch_size
'''
if MODEL_FIT:
    epoches = 15
    callbacks = [tf.keras.callbacks.LearningRateScheduler(lr_scheduler, verbose=0)]

    model.fit(ds, epochs=epoches, batch_size=64, verbose=2, steps_per_epoch=400, callbacks = callbacks) #, validation_data=ds_val)

    weightList = []
    biasList = []
    for i in range(1,len(model.layers)):
        weights = model.layers[i].get_weights()[0]
        weightList.append((weights.T).tolist())
        bias = [[float(b)] for b in model.layers[i].get_weights()[1]]
        biasList.append(bias)

    data = {"weights": weightList,"biases":biasList}
    f = open('weightsandbiases.txt', "w")
    json.dump(data, f)
    f.close()

    if PREDIT_DETEC:
        model.fit(ds, epochs=epoches, batch_size=64, verbose=2, steps_per_epoch=450, callbacks = callbacks)

'''
Predict and output prediction for comparison

Prediction is done on only one file for quick computation and comparison
'''
if PREDICT:
    print("Predicting...")
    sample = os.path.join(DATASET_FOLDER, FEATURES_FOLDER, ALT_GCC_FOLDER, TRAIN_FOLDER, "ssl-data_2017-05-13-15-25-43_0.csv")
    x_pred = np.genfromtxt(sample, delimiter=',', skip_header=0, dtype=float)
    x_pred = np.reshape(x_pred, (x_pred.shape[0], 51,6))

    pred = model.predict(x_pred, verbose=2, steps=2)
    dump_file = os.path.join(DATASET_FOLDER, PREDICT_FOLDER, "pred_doa.csv")
    np.savetxt(dump_file, pred, delimiter = ",")

    if PREDIT_DETEC:
        pred_detec = model_detec.predict(x_pred, verbose=2, steps=2)
        dump_file_detec = os.path.join(DATASET_FOLDER, PREDICT_FOLDER, "pred_detec.csv")
        np.savetxt(dump_file_detec, pred_detec, delimiter = ",")


# Databricks notebook source
from IPython.display import display
from datetime import datetime
import ipywidgets as widgets
from scenario_class import Scenario
from databricks.sdk.runtime import spark
import pandas as pd
import numpy as np

scenario = Scenario()

# COMMAND ----------

optimisation = scenario.optimisation.df
assumption = scenario.assumption.df.set_index('period_end').sort_index()

PC_VSC_flow = optimisation['PC_VSC_flow']
PC_solar_production = optimisation['PC_solar_production']
operational_capacity = assumption.loc[assumption['identifier'] == 'PC_battery_operational_capacity']

BESS_df = pd.DataFrame()
num_inverters = 1554

BESS_df['Demand/Inverter'] = PC_VSC_flow.div(num_inverters)
BESS_df['PV Production/Inverter'] = PC_solar_production.div(num_inverters)
BESS_df['BESS Size'] = 20

# bess_index = []
# oc_index = operational_capacity.index
# for index in BESS_df.index:
#     i = index.strftime("%Y-%m-%d")
#     if i in oc_index:
#         BESS_df['BESS Size'].loc[index] = operational_capacity['numeric_value'].loc[i]

BESS_df['BESS Size'] = BESS_df['BESS Size']
BESS_df['Net Flow of Energy'] = BESS_df['PV Production/Inverter'] - BESS_df['Demand/Inverter']

charge_conditions = [BESS_df['Net Flow of Energy'] >= 0, BESS_df['Net Flow of Energy'] < 0]
charge_choices = ['Charge', 'Discharge']
BESS_df['Charge/Discharge'] = np.select(charge_conditions, charge_choices)

bess_states = []
spill = []
bess_state = 20
bess_effiency = 0.95
inverter_effiency = 0.95
bess_power = 2
current_spill = 0
for row in BESS_df.iterrows():
    net_flow = row[1][3]
    if bess_state + net_flow >= 0 and bess_state + net_flow <= bess_size:
        if net_flow > bess_power:
            current_spill += net_flow - bess_power
            bess_state += bess_power*bess_effiency
        else:
            bess_state += net_flow*bess_effiency
        bess_states.append(bess_state)
    spill.append(current_spill)

BESS_df['BESS State of Charge'] = b_size
BESS_df['Spill'] = spill
BESS_df

# COMMAND ----------

BESS_df.tail(30)

# COMMAND ----------

BESS_df.plot(y='Spill', use_index=True)

# COMMAND ----------

solcast_query = "select * from sandbox.pv_data.solcast__18_201125_133_408077"
df_solcast = spark.sql(solcast_query).toPandas()
optimisation = scenario.optimisation.df

num_rows = len(optimisation.index)
df_solcast = df_solcast.iloc[:num_rows]
df_solcast = df_solcast.drop(columns=['PeriodEnd', 'PeriodStart'])
optimisation = optimisation.reset_index(drop=True)
df = optimisation.join(df_solcast, how='outer')
df


from django.test import TestCase
from .models import Watering
from datetime import timedelta
from django.utils import timezone
from .api import viewsets
from django.urls import reverse

class WateringLogic(TestCase):

    def test_check_soil_needs_watering_time(self):
        watering, next_interval = viewsets.check_last_watering_interval()
        print("next_interval", next_interval)
        self.assertEqual(watering, True)

    def test_post_dry_humidity_to_api(self):
        url = reverse('jardim_api:humidity')
        data = {'humidity': 900}
        response = self.client.post(url, data=data)
        watering = response.json()['watering']
        duration = response.json()['duration']
        print(response.status_code)
        print("watering", watering)
        print("duration", duration)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(watering, True)
        self.assertGreater(duration, 0)

    def test_watering_under_interval(self):
        watering = Watering()
        watering.time_stamp = timezone.now()
        watering.save()
        url = reverse('jardim_api:humidity')
        data = {'humidity': 900}
        response = self.client.post(url, data=data)
        watering = response.json()['watering']
        duration = response.json()['duration']
        print(response.status_code)
        print("watering", watering)
        print("duration", duration)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(watering, False)
        self.assertEqual(duration, 0)

    def test_watering_above_interval(self):
        watering = Watering()
        time = timezone.now() - timedelta(minutes=21)
        watering.time_stamp = time
        watering.save()
        url = reverse('jardim_api:humidity')
        data = {'humidity': 900}
        response = self.client.post(url, data=data)
        watering = response.json()['watering']
        duration = response.json()['duration']
        print(response.status_code)
        print("watering", watering)
        print("duration", duration)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(watering, True)
        self.assertGreater(duration, 0)


from random import uniform, randint

from Ant import Ant

class Colony:
    def __init__(self, params):
        self.__params = params
        self.__population = []
        self.__pheromoneMatrix = [[0.0 for _ in range(self.__params['noNodes'])] for _ in range(self.__params['noNodes'])]

    @property
    def population(self):
        return self.__population

    @property
    def pheromoneMatrix(self):
        return self.__pheromoneMatrix

    def initialisation(self):
        self.__population = []
        for _ in range(self.__params['colSize']):
            a = Ant(self.__params)
            self.__population.append(a)

    def constructSolution(self):
        for i in range(1, self.__params['noNodes']):
            for k in range(self.__params['colSize']):
                q = uniform(0, 1)
                if q <= self.__params['q0']:
                    neighbour = self.selectMostAtractive(k)
                else:
                    neighbour = self.selectMostAtractiveByWheel(k)
                # adding node to solution
                newSolution = self.__population[k].solution + [neighbour]
                self.__population[k].solution = newSolution
                # local pheromone update
                pos1 = self.population[k].solution[-2]
                pos2 = self.population[k].solution[-1]
                self.pheromoneMatrix[pos1 - 1][pos2 - 1] = (1.0 - self.__params['ro']) * self.pheromoneMatrix[pos1 - 1][pos2 - 1] + self.__params['ro'] * self.__params['initPheromone']
                self.pheromoneMatrix[pos2 - 1][pos1 - 1] = (1.0 - self.__params['ro']) * self.pheromoneMatrix[pos2 - 1][pos1 - 1] + self.__params['ro'] * self.__params['initPheromone']
                if i == self.__params['noNodes'] - 1:
                    first = self.population[k].solution[0]
                    last = self.population[k].solution[-1]
                    self.pheromoneMatrix[first - 1][last - 1] = (1.0 - self.__params['ro']) * self.pheromoneMatrix[first - 1][last - 1] + self.__params['ro'] * self.__params['initPheromone']
                    self.pheromoneMatrix[last - 1][first - 1] = (1.0 - self.__params['ro']) * self.pheromoneMatrix[last - 1][first - 1] + self.__params['ro'] * self.__params['initPheromone']

        for i in range(self.__params['colSize']):
            self.population[i].distance = self.calculateDistance(self.population[i].solution)

    def selectMostAtractive(self, k):
        neighbours = self.getUnvisitedNeighbours(k)
        node = self.population[k].solution[-1]
        argMax = 0.0
        chosen = neighbours[0]
        for i in neighbours:
            pheromonePerEdge = (self.pheromoneMatrix[i - 1][node - 1] ** self.__params['alpha']) * ((1.0 / self.__params['matrix'][i - 1][node - 1]) ** self.__params['beta'])
            if pheromonePerEdge > argMax:
                argMax = pheromonePerEdge
                chosen = i
        return chosen

    def selectMostAtractiveByWheel(self, k):
        neighbours = self.getUnvisitedNeighbours(k)
        node = self.population[k].solution[-1]
        sum = 0.0
        for i in neighbours:
            sum += (self.pheromoneMatrix[i - 1][node - 1] ** self.__params['alpha']) * ((1.0 / self.__params['matrix'][i - 1][node - 1]) ** self.__params['beta'])
        pick = uniform(0, sum)
        current = 0.0
        for i in neighbours:
            current += (self.pheromoneMatrix[i - 1][node - 1] ** self.__params['alpha']) * ((1.0 / self.__params['matrix'][i - 1][node - 1]) ** self.__params['beta'])
            if current > pick:
                return i
        return neighbours[0]

    def getUnvisitedNeighbours(self, k):
        node = self.population[k].solution[-1]
        neighbours = []
        for i in range(self.__params['noNodes']):
            if self.__params['matrix'][node - 1][i] != 0 and i + 1 not in self.__population[k].solution:
                neighbours.append(i + 1)
        return neighbours

    def pheromoneUpdate(self):
        ant = self.bestSolution().solution
        for i in range(len(ant) - 1):
            self.pheromoneMatrix[ant[i] - 1][ant[i + 1] - 1] = (1.0 - self.__params['ro']) * self.pheromoneMatrix[ant[i] - 1][ant[i + 1] - 1] + self.__params['ro'] * (1.0 / self.calculateDistance(self.bestSolution().solution))
            self.pheromoneMatrix[ant[i + 1] - 1][ant[i] - 1] = (1.0 - self.__params['ro']) * self.pheromoneMatrix[ant[i + 1] - 1][ant[i] - 1] + self.__params['ro'] * (1.0 / self.calculateDistance(self.bestSolution().solution))
        self.pheromoneMatrix[ant[0] - 1][ant[-1] - 1] = (1.0 - self.__params['ro']) * self.pheromoneMatrix[ant[0] - 1][ant[-1] - 1] + self.__params['ro'] * (1.0 / self.calculateDistance(self.bestSolution().solution))
        self.pheromoneMatrix[ant[-1] - 1][ant[0] - 1] = (1.0 - self.__params['ro']) * self.pheromoneMatrix[ant[-1] - 1][ant[0] - 1] + self.__params['ro'] * (1.0 / self.calculateDistance(self.bestSolution().solution))

    def pheromoneUpdateForEach(self, x):
        ant = x.solution
        for i in range(len(ant) - 1):
            self.pheromoneMatrix[ant[i] - 1][ant[i + 1] - 1] = (1.0 - self.__params['ro']) * self.pheromoneMatrix[ant[i] - 1][ant[i + 1] - 1] + self.__params['ro'] * (1.0 / self.calculateDistance(ant))
            self.pheromoneMatrix[ant[i + 1] - 1][ant[i] - 1] = (1.0 - self.__params['ro']) * self.pheromoneMatrix[ant[i + 1] - 1][ant[i] - 1] + self.__params['ro'] * (1.0 / self.calculateDistance(ant))

    def bestSolution(self):
        minim = self.population[0].distance
        index = 0
        for i in range(1, self.__params['colSize']):
            if self.population[i].distance < minim:
                minim = self.population[i].distance
                index = i
        return self.population[index]

    def calculateDistance(self, solution):
        distance = 0
        for i in range(len(solution) - 1):
            distance += self.__params['matrix'][solution[i] - 1][solution[i + 1] - 1]
        distance += self.__params['matrix'][solution[-1] - 1][solution[0] - 1]
        return distance

    def dynamic(self):
        for _ in range(self.__params['noChanges']):
            maxValue = self.__params['maxValue']
            minValue = self.__params['minValue']
            value = randint(minValue, maxValue)
            node1 = 0
            node2 = 0
            while node1 == node2:
                node1 = randint(0, self.__params['noNodes'] - 1)
                node2 = randint(0, self.__params['noNodes'] - 1)
            self.__params['matrix'][node1][node2] = value
            self.__params['matrix'][node2][node1] = value

            print("New cost of edge [" + str(node1) + "," + str(node2) + "] is: " + str(value))


"""
This is the sample Python file that we want to automate in batch mode
"""
import pandas as pd

def main():
    #Create a dummy pandas dataframe
    dummy_df=pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})
    #Write the dataframe to a csv
    dummy_df.to_csv('sample_dummy_file_1.csv')
    print('Automating the boring work makes our lives so much better!')

if __name__== "__main__":
  main()


"""
CH23. 85_Fibonacci_Number.py
"""

import collections
import enum
import heapq
import functools
import itertools
import re
import sys
import math
import bisect

# 입력 1
input1 = 5

# 출력
output1 = 5

# 입력 2
input2 = 10

# 출력 2
output2 = 55

# --------------------------------------------------
def fib(N):
    if N <= 1:
        return N
    return fib(N-1) + fib(N-2)

print(fib(input1))
print(fib(input1) == output1)
print(fib(input2))
print(fib(input2) == output2)

# --------------------------------------------------
dp = collections.defaultdict(int)
def fib2(N):
    global dp
    if N <= 1:
        return N

    if dp[N]:
        return dp[N]
    dp[N] = fib2(N-1) + fib2(N-2)
    return dp[N]

print(fib2(input1))
print(fib2(input1) == output1)
print(fib2(input2))
print(fib2(input2) == output2)

# --------------------------------------------------
class Solution:
    dp = collections.defaultdict(int)

    def fib2_1(self, N):
        if N <= 1:
            return N

        if self.dp[N]:
            return self.dp[N]

        self.dp[N] = self.fib2_1(N-1) + self.fib2_1(N-2)
        return self.dp[N]

sol = Solution()

print(sol.fib2_1(input1))
print(sol.fib2_1(input1) == output1)
print(sol.fib2_1(input2))
print(sol.fib2_1(input2) == output2)

# --------------------------------------------------
dp = collections.defaultdict(int)
def fib3(N):
    global dp
    dp[0] = 0
    dp[1] = 1

    for i in range(2, N + 1):
        dp[i] = dp[i-1] + dp[i-2]
    return dp[N]

print(fib3(input1))
print(fib3(input1) == output1)
print(fib3(input2))
print(fib3(input2) == output2)

# --------------------------------------------------
class Solution:
    dp = collections.defaultdict(int)

    def fib3_1(self, N):
        self.dp[0] = 0
        self.dp[1] = 1

        for i in range(2, N+1):
            self.dp[i] = self.dp[i-1] + self.dp[i-2]
        return self.dp[N]

sol = Solution()

print(sol.fib3_1(input1))
print(sol.fib3_1(input1) == output1)
print(sol.fib3_1(input2))
print(sol.fib3_1(input2) == output2)

# --------------------------------------------------
def fib4(N):
    x, y = 0, 1
    for i in range(2, N+1):
        x, y = y, x+y
    return y

print(fib4(input1))
print(fib4(input1) == output1)
print(fib4(input2))
print(fib4(input2) == output2)

# --------------------------------------------------
import numpy as np

def fib5(N):
    M = np.matrix([[0, 1], [1, 1]])
    print(M)
    vec = np.array([[0], [1]])

    return np.matmul(M ** N, vec)[0]

print(fib5(input1))
print(fib5(input1) == output1)
print(fib5(input2))
print(fib5(input2) == output2)


from loss.loss_class import Loss
import torch

class MSELoss(Loss):
    """MSELoss class. Inherits the Loss class.

    The MSELoss is used to calculate the MSE loss.
    """
    def __init__(self):
        """Inits MSELoss."""
        super().__init__('MSELoss')

    def compute_loss(self, y_true, output_model):
        """Calculates the MSE loss between the predictions and ground truths.

        Calculates the MSE on the passed predicted and true values.
        The loss is the mean of the squared differences between true
        and predicted values.

        Args:
            y_true: A pytorch tensor of ground truth values.
            output_model: A pytorch tensor of predicted values.

        Returns:
            Will return a float with the calculated MSE value.
            math::
            MSE = \\frac{\\sum_{i=1}^{n}(Y_i - \\hat{Y_i})^2}{n}

        Raises:
            TypeError: An error occured while accessing the arguments -
                one of the arguments is NoneType, or not a pytorch tensor.
            ValueError: An error occured when checking the dimensions of the
                y_true and output_model arguments if they are not equal.
                This is also raised if the output is not in [0,1].
        """
        if output_model is None:
            raise TypeError('Argument: output_model must be set.')
        if y_true is None:
            raise TypeError('Argument: y_true must be set.')
        if not isinstance(output_model, torch.Tensor):
            raise TypeError('Argument: output_model must be a pytorch tensor.')
        if not isinstance(y_true, torch.Tensor):
            raise TypeError('Argument: y_true must be a pytorch tensor.')
        # check if good dimensions between predictions and ground truth
        self.__check_dim_pred_gt__(output_model, y_true)

        result = ((output_model - y_true) ** 2).sum()\
            / output_model.data.nelement()
        if not (0 <= result <= 1):
            raise ValueError('The output of MSELoss.compute_loss \
                              must be in [0,1]')
        return result


from typing import Any
from amms.logger import l

DIVIDER = "-------------------------------------"

class Token:
    def __init__(self, x_i: float = 1, x_i_qty: float = 0):
        if x_i not in [1, 2]:
            return Exception("1 or 2 allowed")

        if x_i_qty <= 0:
            return Exception("must be positive")

        self.name = f"x_{x_i}"
        self.qty = x_i_qty
        self.complement = f"x_{2 if x_i == 1 else 1}"

    def __repr__(self):
        return f"Token(x_i={self.name}, x_i_qty={self.qty})"

class LogHelper:
    log = l

    @staticmethod
    def pool_created(o: Any):
        l.info(
            f"\nCREATED POOL.\n"
            f"x_1, x_2: {o.x_1:.4f}, {o.x_2:.4f}.\n"
            f"invariant {o.invariant}.\n"
            f"ex. rate x_1/x_2 = {o.x_2/o.x_1}.\n"
            f"{DIVIDER}\n"
        )

    @staticmethod
    def trade_executed(x_i: Token, x_j: float, o: Any):
        l.info(
            "\nEXECUTED TRADE.\n"
            f"swapped {x_i.qty} {x_i.name} for {x_j} {x_i.complement}\n"
            f"ex. rate x_1/x_2 = {o.x_2/o.x_1:.4f}.\n"
            f"x_1, x_2: {o.x_1:.4f}, {o.x_2:.4f}.\n"
            f"invariant {o.invariant}.\n"
            f"{DIVIDER}\n"
        )

    @staticmethod
    def liquidity_event(liquidity: float):
        l.info(
            f"\nLP TOKENS MINTED\n" f"minted {liquidity:.4f}\n" f"{DIVIDER}\n"
        )

    @staticmethod
    def added_liquidity(x_i: Token, x_j: float, o: Any):
        l.info(
            f"\nADDED LIQUIDITY.\n"
            f"added {x_i.qty} {x_i.name} and {x_j} {x_i.complement}\n"
            f"x_1, x_2: {o.x_1:.4f}, {o.x_2:.4f}.\n"
            f"{DIVIDER}\n"
        )

    @staticmethod
    def removed_liquidity(x_1: float, x_2: float, lp_tokens: float):
        l.info(
            f"\nREMOVED LIQUIDITY\n"
            f"removed {x_1} x_1 and {x_2} x_2 for {lp_tokens} lp tokens\n"
            f"{DIVIDER}\n"
        )

# x_i_qty is the user sent delta
def quote(x_i_qty: float, x_i_reserve: float, x_j_reserve: float):
    x_j_per_x_i = x_j_reserve / x_i_reserve  # this is your ex. rate: x_i / x_j
    x_j_qty = x_i_qty * x_j_per_x_i
    return x_j_qty

def get_amount_out(x_i: Token, x_i_reserve: float, x_j_reserve: float):
    x_i_with_fee = x_i.qty * 997
    x_j_out = (x_i_with_fee * x_j_reserve) / (
        x_i_with_fee + 1000 * x_i_reserve
    )
    return x_j_out


import pygame
import random
from os import path
from settings import IMG_DIR, SOUND_DIR, IMG_WIDTH, IMG_HEIGHT, BLACK, FPS, PLAY, CLOSE
from assets import load_assets

def intro_screen(window):

    clock = pygame.time.Clock()

    # Carrega a imagem de fundo
    background = pygame.image.load(path.join(IMG_DIR, 'introscreen-500x400.png')).convert()
    background = pygame.transform.scale(background, (IMG_WIDTH, IMG_HEIGHT))
    background_rect = background.get_rect()

    # Loop da tela
    run = True
    # Carrega a música da tela de início
    pygame.mixer.music.load(path.join(SOUND_DIR, 'init_screen.wav'))
    pygame.mixer.music.play(loops=-1)

    while run:

        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                gamestate = CLOSE
                run = False
            if event.type == pygame.KEYUP:
                gamestate = PLAY
                run = False

            window.fill(BLACK)
            window.blit(background, background_rect)
            pygame.display.flip()

    return gamestate