import numpy as np
import cv2
from collections import deque

# Detects red color
# See below for creating modifiable values
#lower_red = np.array([161, 155, 84])
#upper_red = np.array([179, 255, 255])

# Empty function to make createTrackbar valid
def empty(x):
    pass

# Create a window to set the values
cv2.namedWindow("Tracker Color")
cv2.createTrackbar("Lower Hue", "Tracker Color", 0, 255, empty)
cv2.createTrackbar("Lower Sat.", "Tracker Color", 0, 255, empty)
cv2.createTrackbar("Lower Value", "Tracker Color", 0, 255, empty)
cv2.createTrackbar("Upper Hue", "Tracker Color", 255, 255, empty)
cv2.createTrackbar("Upper Sat.", "Tracker Color", 255, 255, empty)
cv2.createTrackbar("Upper Value", "Tracker Color", 255, 255, empty)

bpoints = [deque(maxlen=1024)]
gpoints = [deque(maxlen=1024)]
rpoints = [deque(maxlen=1024)]
ypoints = [deque(maxlen=1024)]
bindex, gindex, rindex, yindex = 0, 0, 0, 0

colors = [(255,0,0), (0,255,0), (0,0,255), (0,255,255)]

color_index = 0

# Create the window to paint on
paintWindow = np.zeros((471, 636, 3)) + 255
paintWindow = cv2.rectangle(paintWindow, (40,1), (140,65), (0,0,0), 2)
paintWindow = cv2.rectangle(paintWindow, (160,1), (255,65), colors[0], -1)
paintWindow = cv2.rectangle(paintWindow, (275,1), (370,65), colors[1], -1)
paintWindow = cv2.rectangle(paintWindow, (390,1), (485,65), colors[2], -1)
paintWindow = cv2.rectangle(paintWindow, (505,1), (600,65), colors[3], -1)

# These have set values on the paintWindow screen
# This is so it's easy to determine whether the tracker is in that certain area
cv2.putText(paintWindow, "CLEAR ALL", (49, 33),
    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,0), 2,
    cv2.LINE_AA)
cv2.putText(paintWindow, "BLUE", (185, 33),
    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 2,
    cv2.LINE_AA)
cv2.putText(paintWindow, "GREEN", (298, 33),
    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 2,
    cv2.LINE_AA)
cv2.putText(paintWindow, "RED", (420, 33),
    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 2,
    cv2.LINE_AA)
cv2.putText(paintWindow, "YELLOW", (520, 33),
    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (150,150,150), 2,
    cv2.LINE_AA)

cv2.namedWindow('Paint', cv2.WINDOW_AUTOSIZE)

# Capturing video
camera = cv2.VideoCapture(0)

while True:
    (grabbed, frame) = camera.read()
    frame = cv2.flip(frame, 1)
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    if not grabbed:
        break

    l_h = cv2.getTrackbarPos("Lower Hue", "Tracker Color")
    l_s = cv2.getTrackbarPos("Lower Sat.", "Tracker Color")
    l_v = cv2.getTrackbarPos("Lower Value", "Tracker Color")
    u_h = cv2.getTrackbarPos("Upper Hue", "Tracker Color")
    u_s = cv2.getTrackbarPos("Upper Sat.", "Tracker Color")
    u_v = cv2.getTrackbarPos("Upper Value", "Tracker Color")

    lower_colors = np.array([l_h,l_s,l_v])
    upper_colors = np.array([u_h,u_s,u_v])

    frame = cv2.rectangle(frame, (40,1), (140,65), (122,122,122), -1)
    frame = cv2.rectangle(frame, (160,1), (255,65), colors[0], -1)
    frame = cv2.rectangle(frame, (275,1), (370,65), colors[1], -1)
    frame = cv2.rectangle(frame, (390,1), (485,65), colors[2], -1)
    frame = cv2.rectangle(frame, (505,1), (600,65), colors[3], -1)
    cv2.putText(frame, "CLEAR ALL", (49, 33), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)
    cv2.putText(frame, "BLUE", (185, 33), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)
    cv2.putText(frame, "GREEN", (298, 33), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)
    cv2.putText(frame, "RED", (420, 33), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)
    cv2.putText(frame, "YELLOW", (520, 33), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (150,150,150), 2, cv2.LINE_AA)

    # Preprocess input
    kernel = np.ones((5,5), np.uint8)
    Mask = cv2.inRange(hsv, lower_colors, upper_colors)
    Mask = cv2.erode(Mask, kernel, iterations=2)
    Mask = cv2.morphologyEx(Mask, cv2.MORPH_OPEN, kernel)
    Mask = cv2.dilate(Mask, kernel, iterations=1)

    (cnts, _) = cv2.findContours(Mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    center = None

    if len(cnts) > 0:
        cnt = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
        ((x,y), radius) = cv2.minEnclosingCircle(cnt)
        cv2.circle(frame, (int(x), int(y)), int(radius), (0,255,255), 2)
        M = cv2.moments(cnt)
        center = (int(M['m10']/M['m00']), int(M['m01'] / M['m00']))

        if center[1] <= 65:
            if 40 <= center[0] <= 140:
                bpoints = [deque(maxlen=512)]
                gpoints = [deque(maxlen=512)]
                rpoints = [deque(maxlen=512)]
                ypoints = [deque(maxlen=512)]
                bindex, gindex, rindex, yindex = 0, 0, 0, 0
                paintWindow[67:,:,:] = 255
            elif 160 <= center[0] <= 255:
                color_index = 0
            elif 275 <= center[0] <= 370:
                color_index = 1
            elif 390 <= center[0] <= 485:
                color_index = 2
            elif 505 <= center[0] <= 600:
                color_index = 3
        else:
            if color_index == 0:
                bpoints[bindex].appendleft(center)
            elif color_index == 1:
                gpoints[gindex].appendleft(center)
            elif color_index == 2:
                rpoints[rindex].appendleft(center)
            elif color_index == 3:
                ypoints[yindex].appendleft(center)
    else:
        bpoints.append(deque(maxlen=512))
        bindex += 1
        gpoints.append(deque(maxlen=512))
        gindex += 1
        rpoints.append(deque(maxlen=512))
        rindex += 1
        ypoints.append(deque(maxlen=512))
        yindex += 1

    points = [bpoints, gpoints, rpoints, ypoints]
    for i in range(len(points)):
        for j in range(len(points[i])):
            for k in range(1, len(points[i][j])):
                if points[i][j][k-1] is None or points[i][j][k] is None:
                    continue
                cv2.line(frame, points[i][j][k-1], points[i][j][k], colors[i], 2)
                cv2.line(paintWindow, points[i][j][k-1], points[i][j][k], colors[i], 2)

    # Display output
    cv2.imshow("Web_cam_video", frame)
    cv2.imshow("Paint", paintWindow)
    cv2.imshow("Mask", Mask)

    if cv2.waitKey(1) & 0xFF ==ord("q"):
        break

camera.release()
cv2.destroyAllWindows()


#!/usr/bin/env python
# -*- coding: utf8 -*-

import sys
import time
from collections import namedtuple
import argparse

"""
load file
for loop
read line
next line
"""

data = None

def load_data(category='git'):
    """
    :return: A tuple of (index, data)
    """
    trainingfiles = {
        "git": "training_git.txt",
        "python": "py_training.txt",
    }
    filename = trainingfiles[category]

    retval = namedtuple('TrainingData', 'index data')
    retval.index = {}
    retval.data = []

    ii = 0
    with open(filename) as f:
        for line in f:
            line = line.rstrip()

            if line.startswith("#"):
                title = line.lstrip('#')
                # retval.index[title] = ii
                retval.index[ii] = title.strip()

            else:
                retval.data.append(line)
                ii += 1

    return retval

if __name__ == "__main__":

    # get argument
    arg_category = None
    if len(sys.argv) > 1:
        arg_category = sys.argv[1]

    arg_index = None
    if len(sys.argv) > 2:
        arg_index = sys.argv[2]

    training_data = load_data()

    startat = 0
    if arg_index:
        if arg_index.isdigit():
            startat = int(arg_index)
        else:
            for key, value in list(training_data.index.items()):
                if value == arg_index:
                    startat = key
            if not startat:
                print("no key found '%s'" % arg_index)

    else:
        # show index

        print('indexes')
        print('-' * 20)
        for k in sorted(training_data.index.keys()):
            print(k, training_data.index[k])

        # input index or go
        print('-' * 20)
        print('input index number or enter to go> ')
        inputstr = sys.stdin.readline().strip()

        try:
            startat = int(inputstr)
        except:
            startat = 0

    # go to index
    for ii in range(0, startat):
        training_data.data.pop(0)

    # start timer
    start = time.time()

    for line in training_data.data:
        inputstr = None

        passed = False
        while not passed:
            print('-' * 20)
            print(line.strip())
            substart = time.time()
            inputstr = sys.stdin.readline().strip()
            subend = time.time()
            print("ellapsed : %s" % str(subend - substart))

            if inputstr == line:
                passed = True

    end = time.time()
    print("time ellapsed %s" % str(end - start))


#!/home/msj/miniconda3/bin/python3

import matplotlib as mpl
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.font_manager import FontProperties

from jinja2 import Environment, FileSystemLoader
import time
import datetime
import mobitools.weather.vanweather as vw
from mobitools.core.helpers import *
import matplotlib.lines as mlines
import os

from pandas.plotting import register_matplotlib_converters
register_matplotlib_converters()

#print('Setting MPL defaults')
#https://matplotlib.org/users/customizing.html
colors = ['#3778bf','#7bb274','#825f87',
          '#feb308','#59656d','#984447',
          '#e17701','#add9f4','#1b264f',
          '#4b296b','#380835','#607c8e',
          '#ca6641']
# colors = ['#3778bf','#7bb274',,'#feb308',
#               '#59656d']

mpl.rcParams['axes.prop_cycle'] = mpl.cycler('color',colors)
bg_color = '#ffffff'
fg_color = colors[1]
fg_color2 = colors[0]
fg_color3 = colors[3]
fg_color4 = colors[2]
ax_color = colors[4]

mpl.rcParams['font.family'] = 'sans-serif'
#mpl.rcParams['font.sans-serif'] = ['Helvetica']
plt.rc('text', color=ax_color)
#plt.rc('axes',titleweight='bold')

mpl.rcParams['axes.spines.right'] = False
mpl.rcParams['axes.spines.top'] = False
mpl.rcParams['axes.edgecolor'] = ax_color
mpl.rcParams['ytick.color'] = ax_color
mpl.rcParams['xtick.color'] = ax_color
mpl.rcParams['axes.labelcolor'] = ax_color
mpl.rcParams['axes.labelsize'] = 13
mpl.rcParams["figure.figsize"] = (7,5)

def yoy_plot(df,fname):

    yday_day = (datetime.datetime.now() - datetime.timedelta(1)).day
    yday_month = (datetime.datetime.now() - datetime.timedelta(1)).month

    todaydf = df[(df.index.month==yday_month) & (df.index.day==yday_day)]
    f,ax = plt.subplots()
    ax.plot(df,color=ax_color,alpha=0.5)
    ax.plot(todaydf.index,todaydf.values,color=fg_color)
    s = ax.scatter(todaydf.index,todaydf.values,color=fg_color)

    ax.tick_params(axis='x',labelrotation=45)

    labelfont = FontProperties(weight='roman',size=12)

    ax.set_ylabel('Trips')

    def get_text(date):
        wdf = vw.get_monthly_weather(date.strftime('%Y'),date.strftime('%m'))
        text = f"{date.strftime('%A, %b %d %Y')}\nDaily High: {wdf.loc[date.strftime('%Y-%m-%d'),'Max Temp']}°C\nPrecipitation: {wdf.loc[date.strftime('%Y-%m-%d'),'Total Precipmm']}mm"
        return text

    try:
        texts = [get_text(x) for x in todaydf.index]
    except:
        texts = [x for x in todaydf.index]

    for c,text in zip(s.get_offsets().data,texts):
        ax.annotate(text,c,color=fg_color,
                textcoords='offset points',xytext=(-60,-50),
                bbox=dict(boxstyle="round",fc='w',ec='w',alpha=0.9),
                fontproperties=labelfont
                #arrowprops=dict(arrowstyle = '-|>',color='k',alpha=0.5)
               )

    f.tight_layout()
    f.savefig(fname)

def simple_plot(df,fname,kind='line',highlight=False):
    f,ax = plt.subplots()

    if kind == 'line':

        if highlight:
            line = ax.plot(df[:-24].index,df.values[:-24],color=fg_color,alpha=0.3)
            ax.fill_between(df.index[:-24],0,df.values[:-24],color=fg_color,alpha=0.2)
            line = ax.plot(df.index[-25:],df.values[-25:],color=fg_color)
            ax.fill_between(df.index[-25:],0,df.values[-25:],color=fg_color,alpha=0.8)
        else:
            line = ax.plot(df.index,df.values,color=fg_color)
            ax.fill_between(df.index,0,df.values,color=fg_color,alpha=0.8)

        ax.xaxis.set_major_formatter(mdates.DateFormatter("%A"))
        ax.xaxis.get_ticklabels()[-1].set_visible(False) # removes the rightmost tick label

    if kind=='bar':
        bars = ax.bar(df.index,df.values,color=fg_color)
        # set ticks every week
        ax.xaxis.set_major_locator(mdates.WeekdayLocator())
        #set major ticks format
        ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))
        if highlight:
            [x.set_alpha(0.6) for x in bars[:-1]]
    ax.tick_params(axis='x',labelrotation=45)
    ax.set_ylabel('Trips')
    f.tight_layout()
    f.savefig(fname)
    return f,ax

def weather_plot(df,fname,kind='line',highlight=False):
    f,(ax,wax) = plt.subplots(2,sharex=True,gridspec_kw={'height_ratios':[4.5,1]})

    if kind == 'line':
        line = ax.plot(df.index,df.values,color=fg_color)
        ax.fill_between(df.index,0,df.values,color=fg_color,alpha=0.8)

    if kind=='bar':
        bars = ax.bar(df.index,df.values,color=fg_color)
        # set ticks every week
        ax.xaxis.set_major_locator(mdates.WeekdayLocator())
        #set major ticks format
        ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))
        if highlight:
            [x.set_alpha(0.6) for x in bars[:-1]]
    ax.set_ylabel('Trips')

    wdf = vw.get_weather_range(df.index[0].strftime('%Y-%m'),
                                    (df.index[-1]-datetime.timedelta(1)).strftime('%Y-%m'))
    wdf = wdf.loc[[x for x in wdf.index if x in df.index],:]

    wax.bar(wdf.index,wdf['Total Precipmm'],color=fg_color2)
    wax2 = wax.twinx()
    #wax2 = set_ax_props(wax2)
    wax2.plot(wdf.index,wdf['Max Temp'],color=fg_color3)
    wax2.set_ylabel('High ($^\circ$C)')
    wax2.yaxis.label.set_color(fg_color3)
    wax.set_ylabel('Rain (mm)')
    wax.yaxis.label.set_color(fg_color2)
    wax.spines['right'].set_visible(True)
    wax2.spines['right'].set_visible(True)
    wax.tick_params(axis='x',labelrotation=45)
    wax2.tick_params(axis='x',labelrotation=45)

    f.tight_layout()
    f.savefig(fname)

    return f,ax

def cumsum_plot(df,fname):
    today = datetime.datetime.now().strftime('%Y-%m-%d')
    thisyear = datetime.datetime.now().strftime('%Y')
    f,ax = plt.subplots()
    def cumsum(df):

        df = df.sum(1).cumsum()
        df.index = df.index.map(lambda x: x.strftime('%H:%M'))
        df = df.shift()
        df.iloc[0] = 0
        return df

    for i,df in df.groupby(pd.Grouper(freq='d')):
        try:
            df = cumsum(df)
            ax.plot(df.index,df,color='gray',alpha=0.1)
        except IndexError:
            pass
    #print(df.head())
    ax.plot(df.index,df,alpha=1,color=fg_color)
    ax.scatter(df.index,df,alpha=1,color=fg_color)
    ax.set_ylabel("Hourly Cumulative Trips",color=ax_color)
    ax.set_xlabel("Time",color=ax_color)

    #year = df.index[-1].year
    #date = df.index[-1].date().strftime('%Y-%m-%d')
    gray_line = mlines.Line2D([], [], color=ax_color,  label="{} so far".format(thisyear))
    green_line= mlines.Line2D([], [], color=fg_color, marker='.',label="{}".format(today))
    ax.legend(handles=[green_line,gray_line])
    ax.tick_params(axis='x',labelrotation=45)
    f.tight_layout()
    f.savefig(fname)
    return f,ax

def make_station_map(date,fname,workingdir):

    #Load mobi daily data
    thdf = load_csv(workingdir+'/taken_hourly_df.csv')
    rhdf = load_csv(workingdir+'/returned_hourly_df.csv')
    tddf = thdf.groupby(pd.Grouper(freq='d')).sum()
    rddf = rhdf.groupby(pd.Grouper(freq='d')).sum()

    # Load stations geoDF
    sdf = get_stationsdf(workingdir)

    # Get day's trip counts
    addf = tddf + rddf
    addf = addf[sdf.loc[sdf['active'],'name']]
    trips = addf.loc[date].reset_index()
    trips.columns = ['name','trips']

    # make sure geoDF is on the left so it stays a geoDF
    sdf = pd.merge(sdf, trips, how='inner',on='name')

    bikeways_df = geopandas.read_file(f'{workingdir}/shapes/bikeways.shp')
    shoreline_df = geopandas.read_file(f'{workingdir}/shapes/shoreline2002.shp')

    f,ax = plt.subplots()
    bikeways_df.plot(ax=ax,color=fg_color)
    shoreline_df.plot(ax=ax,color='k')
    left = 485844
    right = 495513
    bottom = 5453579
    top = 5462500
    ax.axis('off')
    ax.set_xlim(left,right)
    ax.set_ylim(bottom,top)

    # Dummy scatters for the legend
    l1 = ax.scatter([0],[0], s=10, edgecolors='none',color=fg_color2,alpha=0.7)
    l2 = ax.scatter([0],[0], s=100, edgecolors='none',color=fg_color2,alpha=0.7)
    l3 = ax.scatter([0],[0], s=10, marker='x',edgecolors='none',color=fg_color4,alpha=0.7)

    labels=['0','10','100']
    ax.legend([l3,l1,l2],labels,title=f'Station Activity\n{date}')

    sdf.plot(ax=ax,markersize=list(sdf['trips']), alpha=0.7, zorder=100, color=fg_color2)

    sdf[sdf.trips==0].plot(ax=ax,color=fg_color4,alpha=0.7,markersize=10,marker='x',zorder=100)

    f.savefig(fname,bbox_inches='tight',pad_inches=0.0,transparent=False)

if __name__ == '__main__':
    weather_plot(tddf.sum(1).iloc[-365:],'./images/lastyear_daily.png',kind='line')
    weather_plot(tddf.sum(1)['2018-01':],'./images/2018-current_daily.png',kind='line')
    weather_plot(tddf.sum(1).iloc[-30:],'./images/lastmonth_daily.png',kind='bar',highlight=True)
    weather_plot(tddf.sum(1).loc[yday_min31:yday],'./images/lastmonth_daily_yesterday.png',kind='bar',highlight=True)

    simple_plot(thdf.sum(1).iloc[-7*24:-1],'./images/lastweek_hourly.png',kind='line')
    simple_plot(thdf.sum(1).loc[yday_min7:yday],'./images/lastweek_hourly_yesterday.png',kind='line')

    cumsum_plot(thdf[thisyear:],'./images/today_cumsum.png')
    cumsum_plot(thdf[thisyear:yday],imdir+'yesterday_cumsum.png')

    make_station_map(yday,imdir+'station_map_yesterday.png',workingdir)
    make_station_ani(yday,imdir+'station_ani_yesterday.gif',workingdir)

       # p = Plot().draw(thdf.sum(1).iloc[-7*24:-1],imdir+'lastweek_hourly.png',kind='line')
    #print("Drawing plots")
    #Plot().draw(tddf.sum(1),imdir+'alltime_rolling.png',weather=True,rolling=7)
    #p = Plot().draw(thdf.sum(1).loc[yday_min7:yday],imdir+'lastweek_hourly_yesterday.png')
    #p = Plot().draw(thdf[thisyear:],imdir+'today_cumsum.png',kind='cumsum')
    #p = Plot().draw(thdf[thisyear:yday],imdir+'yesterday_cumsum.png',kind='cumsum')
    #p = Plot().draw(tddf.sum(1).loc[yday_min31:yday],
    #            imdir+'lastmonth_daily_yesterday.png',
    #            kind='bar',weather=True,highlight=True)


# ###
# Mathematical expression recognition tool.
# Written as a part of masters thesis at VUT FIT Brno, 2022

# Author: Vladislav Halva
# Login: xhalva04
# ###

from torch import nn
from torch_geometric.nn import Linear

from src.model.AttentionBlock import AttentionBlock
from src.model.GCN import GCN

class DecoderBlock(nn.Module):
    """
    Decoder layer containing modified GCN and attention to source graph.
    """
    def __init__(self, device, f_size, in_size, out_size, att_size, att_dropout_p, init_size, is_first=False):
        """
        :param device: device
        :param f_size: source graph node features size
        :param in_size: output graph in node features size
        :param out_size: output graph out node features size
        :param att_size: attention vector size
        :param att_dropout_p: dropout probability
        :param init_size: output graph initial node embedding size
        :param is_first: True if layer is first in decoder
        """
        super(DecoderBlock, self).__init__()
        self.device = device
        self.f_size = f_size
        self.in_size = in_size
        self.out_size = out_size
        self.init_size = init_size
        self.is_first = is_first

        self.gcn = GCN(device, in_size, out_size, is_first)
        self.attBlock = AttentionBlock(device, f_size, out_size, init_size, att_size, att_dropout_p, is_first=is_first)
        self.lin = Linear(out_size, out_size, bias=True, weight_initializer='glorot')

    def reset_parameters(self):
        self.lin.reset_parameters()

    def forward(self, f, x, edge_index, edge_type, f_batch, x_batch, x_init):
        """
        :param f: source graph node features
        :param x: output graph node features
        :param edge_index: output graph edge index
        :param edge_type: output graph edge types
        :param f_batch: source graph nodes batch indices
        :param x_batch: output graph nodes batch indices
        :param x_init: output graph initial node embeddings
        :return: output graph new node features, attention alpha coefficients
        """
        # process output graph with GCN
        h = self.gcn(x, edge_index, edge_type)
        # get context vector from source graph
        c, alpha = self.attBlock(f, h, edge_index, edge_type, f_batch, x_batch, x_init)
        # combine
        z = h + c
        z = self.lin(z)
        return z, alpha


from django.conf.urls import url, include
from jerarquia import views

urlpatterns = [
    url(r'^jerarquia/$', views.jerarquia, name='jerarquia'),
    url(r'^editar/(?P<codigo_unico>\w+)/$', views.consulta_codigo, name='consulta'),
    url(r'^aprobar/', views.aprobar, name='aprobar'),
    url(r'^aprobar_cambio/(?P<codigo_unico>\w+)/$', views.consulta_cambio, name='aprobar_cambio'),
    url(r'^cerrar/$', views.cerrar_jerarquia, name='cerrar'),
    url(r'^descarga_especial/$', views.descarga_especial, name='descarga_especial'),
    url(r'^descarga_jerarquia/$', views.descarga_jerarquia, name='descarga_jerarquia'),
    url(r'^formulario_aprobador/$', views.formularios_vista_aprobador, name='vista_aprobador'),
    url(r'^nuevo_agente/$', views.nuevo_agente, name='agregar_agente'),]


import logging

def init_stream_logger(name, level=logging.INFO):
    log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    logger = logging.getLogger(name)
    logger.setLevel(level)

    # Use StreamHandler() to log to the console
    handler = logging.StreamHandler()
    formatter = logging.Formatter(log_format)
    handler.setFormatter(formatter)

    # Don't forget to add the stream handler
    logger.addHandler(handler)
    return logger


import json
import uuid

from cloudify import ctx
from cloudify.utils import convert2bool
from tencentcloud.cbs.v20170312.models import (
    Placement
)

from abstract_plugin.platforms.common.utils import validate_parameter, clear_runtime_properties
from abstract_plugin.platforms.common.volume import CommonVolume
from abstract_plugin.platforms.tencentcloud.restclient import VolumeHelper
from abstract_plugin.platforms.tencentcloud.utils import Base
from abstract_plugin.platforms.common.constants import EXTERNAL_ID

class Volume(CommonVolume, Base):

    def __init__(self):
        super(Volume, self).__init__()
        self.action_mapper = {
            "attach_volume": self.custom_attach,
            "detach_volume": self.custom_detach,
            "resize_volume": self.custom_resize
        }

    def _prepare_placement(self):
        placement = Placement()
        placement.Zone = self.resource_config.get('available_zone_id')
        return placement

    def _is_create_system_disk(self):
        '''
        System_disk has been created where instance was created
        We Just need to update runtime_properties.
        '''
        if not convert2bool(ctx.node.properties['resource_config'].get('is_system_disk')):
            return False
        instance_id = ctx.instance.relationships[0].target.instance.runtime_properties.get('external_id')
        volume_info = json.loads(VolumeHelper().describe_system_disk(instance_id).to_json_string())
        volume_id = volume_info['DiskId']
        ctx.instance.runtime_properties['disk_info'] = volume_info
        ctx.instance.runtime_properties['volume'] = {'size': volume_info['DiskSize']}
        ctx.instance.runtime_properties['volume_type'] = volume_info['DiskType']
        ctx.instance.runtime_properties['host_id'] = instance_id
        self.set_base_runtime_props(resource_id=volume_id, name=volume_info['DiskName'])
        return True

    def create(self, **kwargs):
        if self._is_create_system_disk():
            return
        volume_id = ctx.instance.runtime_properties.get(EXTERNAL_ID)
        if not volume_id:
            client_token = ctx.instance.runtime_properties.get('ClientToken', uuid.uuid4().hex)
            request_body = {
                "DiskType": self.resource_config.get('volume_type'),
                'DiskChargeType': self.resource_config.get('DiskChargeType', 'POSTPAID_BY_HOUR'),
                'Placement': self._prepare_placement(),
                'DiskName': self.resource_config.get('name') or ctx.instance.id,
                'DiskSize': self.resource_config.get('size'),
                'ClientToken': client_token
            }
            ctx.logger.info('Creating tencentcloud volume with parameters: {}.'.format(request_body))
            if not ctx.instance.runtime_properties.get('ClientToken'):
                ctx.instance.runtime_properties['ClientToken'] = client_token
            volume = VolumeHelper().create_disk(request_body)
            volume_id = volume['DiskId']
            ctx.instance.runtime_properties['disk_info'] = volume
            ctx.instance.runtime_properties['volume'] = {'size': volume['DiskSize']}
            ctx.instance.runtime_properties['volume_type'] = volume['DiskType']
            self.set_base_runtime_props(resource_id=volume_id, name=volume['DiskName'])

        self.attach_volume(volume_id)
        ctx.logger.info('Volume {} created.'.format(volume_id))

    def delete(self, **kwargs):
        if ctx.node.properties['resource_config'].get('is_system_disk'):
            return
        volume_id = ctx.instance.runtime_properties.get(EXTERNAL_ID)
        if not volume_id:
            return
        ctx.logger.info('Deleting tencentcloud volume {}.'.format(volume_id))
        self.detach_volume(volume_id)
        VolumeHelper().delete_disks([volume_id])
        clear_runtime_properties()
        ctx.logger.info('Volume {} deleted.'.format(volume_id))

    def attach_volume(self, volume_id, instance_id=None):
        vm_instance = self.get_related_vm()
        if not instance_id:
            instance_id = vm_instance.runtime_properties[EXTERNAL_ID]

        ctx.logger.info('Attaching volume {} to instance {}.'.format(volume_id, instance_id))
        VolumeHelper().attach_disks(instance_id, [volume_id])
        ctx.instance.runtime_properties['host_id'] = instance_id
        ctx.logger.info('Volume {} attached to instance {}.'.format(volume_id, instance_id))

    def detach_volume(self, volume_id, instance_id=None):
        if not instance_id:
            instance_id = self.get_related_vm().runtime_properties[EXTERNAL_ID]
        ctx.logger.info('Detaching volume {} from vm {}.'.format(volume_id, instance_id))
        VolumeHelper().detach_disks(instance_id, [volume_id])
        ctx.instance.runtime_properties['host_id'] = ''
        ctx.logger.info('Volume {} detached from instance {}.'.format(volume_id, instance_id))

    def custom_attach(self, **kwargs):
        volume_id = ctx.instance.runtime_properties.get(EXTERNAL_ID)
        instance_id = validate_parameter('server_id', kwargs)
        self.attach_volume(volume_id, instance_id)
        ctx.instance.runtime_properties['host_id'] = instance_id

    def custom_detach(self, **kwargs):
        volume_id = ctx.instance.runtime_properties.get(EXTERNAL_ID)
        instance_id = validate_parameter('server_id', kwargs)
        self.detach_volume(volume_id, instance_id)
        ctx.instance.runtime_properties['host_id'] = ''

    def custom_resize(self, **kwargs):
        volume_id = ctx.instance.runtime_properties.get(EXTERNAL_ID)
        ctx.logger.info('Resizing volume {}.'.format(volume_id))

        size = validate_parameter('size', kwargs)
        VolumeHelper().resize_disk(volume_id, size)
        ctx.logger.info('Volume {} resized to {} GB.'.format(volume_id, size))
        ctx.instance.runtime_properties['volume'] = {'size': size}
        ctx.instance.update()


import cv2
import numpy as np
from random import expovariate

refPT = list()

class ModelTransformer:
    def __init__(self, model, first_frame, hsv_values, with_points=False):
        self.H = np.identity(3, np.float32)
        self.hsv_low, self.hsv_high = hsv_values

        self.last_frame = first_frame
        self.rows, self.cols, channels = first_frame.shape
        self.model = cv2.resize(model.image, (self.cols, self.rows))

        frame_pts = list()
        model_pts = list()
        if with_points:
            self.refPT = list()
            cv2.imshow('frame', first_frame)
            cv2.setMouseCallback("frame", self.click_gather_point)
            cv2.waitKey()
            cv2.destroyAllWindows()
            frame_pts = self.refPT
            self.refPT = list()

            cv2.imshow('model', self.model)
            cv2.setMouseCallback("model", self.click_gather_point)
            cv2.waitKey()
            cv2.destroyAllWindows()
            model_pts = self.refPT

        else:
            frame_pts.append((85, 125))
            frame_pts.append((353, 107))
            frame_pts.append((167, 258))
            frame_pts.append((681, 197))

            model_pts.append((503, 49))
            model_pts.append((605, 49))
            model_pts.append((505, 289))
            model_pts.append((606, 289))

        self.H = cv2.getPerspectiveTransform(np.float32(frame_pts), np.float32(model_pts))
        self.last_H = self.H
        self.last_good_H = self.H
        self.idx = 0

    def click_gather_point(self, event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDOWN:
            print(len(self.refPT), (x, y))
            point = (x, y)
            self.refPT.append(point)

    def new_frame(self, frame):
        self.idx += 1
        #warped_frame = cv2.warpPerspective(frame, self.H, (self.cols, self.rows))
        #warped_mask = cv2.warpPerspective(np.ones(frame.shape, np.uint8)*255, self.H, (self.cols, self.rows))

        #lines = self.find_global_lines(frame)
        counter = 0
        max_percent_good_lines = 200000
        best_transform = [max_percent_good_lines, self.last_good_H]
        while 1:#best_transform[0] >= max_percent_good_lines:
            #M = self.get_homography_between_frames(frame, self.last_frame)
            M = self.get_homography_between_lines(frame, self.last_frame)
            if M is None or M.shape != (3, 3):
                break
            #M = np.linalg.inv(M)
            tmp_H = np.dot(self.last_H, M)
            #tmp_H = np.dot(self.H, M)
            percent_good_lines = np.power(tmp_H - self.H, 2).sum() / 9
            #percent_good_lines = self.are_lines_vertical_or_horizontal_in_model(lines, tmp_H)

            # if percent_good_lines > best_transform[0]:
            if percent_good_lines < best_transform[0]:
                best_transform[0] = percent_good_lines
                best_transform[1] = tmp_H

            if counter >= 0:
                break
            counter += 1

        print(best_transform[0])
        if best_transform[0] < max_percent_good_lines:
            self.last_good_H = best_transform[1]

        self.H = best_transform[1]
        #self.last_frame = frame

    def get_homography_between_frames(self, img1, img2, mask=None):
        cv2.ocl.setUseOpenCL(False)

        # Initiate SIFT detector
        kernel_size = 9
        orb = cv2.ORB_create(1000, 1.2, 8, kernel_size, 0, 4, cv2.ORB_HARRIS_SCORE, kernel_size, 20)

        mask1 = mask
        mask2 = mask

        if mask is None:
            mask1 = 255 - self.get_terrain_mask(img1)
            mask2 = 255 - self.get_terrain_mask(img2)

            #line_mask1, _ = self.line_mask(img1)
            #line_mask2, _ = self.line_mask(img2)

            #mask1 = cv2.bitwise_or(mask1, line_mask1[:, :, 0])
            #mask2 = cv2.bitwise_or(mask2, line_mask2[:, :, 0])

        # find the keypoints and descriptors with SIFT
        kp1, des1 = orb.detectAndCompute(img1, None)#mask1)
        kp2, des2 = orb.detectAndCompute(img2, None)#mask2)

        # create BFMatcher object
        bf = cv2.BFMatcher(cv2.NORM_HAMMING2, crossCheck=True)

        # Match descriptors.
        matches = bf.match(des1, des2)

        # Sort them in the order of their distance.
        matches = sorted(matches, key=lambda x: x.distance)
        matches_int = np.asarray([(m.queryIdx, m.trainIdx) for m in matches], np.int)

        random_draw = list()
        for i in range(8):
            value = int(expovariate(1/4))
            while value in random_draw:
                value = int(expovariate(1/4))
            random_draw.append(value)
        random_draw = np.asarray(random_draw, np.int)

        src_pts = np.float32([kp1[m[0]].pt for m in matches_int[random_draw]]).reshape(-1, 1, 2)
        dst_pts = np.float32([kp2[m[1]].pt for m in matches_int[random_draw]]).reshape(-1, 1, 2)

        img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:4], None,  matchColor=(0, 255, 0), flags=0)
        cv2.imshow('matches', img3)
        #cv2.waitKey()

        M, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 40)
        #M = cv2.getPerspectiveTransform(src_pts, dst_pts)
        return M

    def get_terrain_mask(self, frame):
        hsv_image = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        mask = cv2.inRange(hsv_image, self.hsv_low, self.hsv_high)

        mask = cv2.erode(mask, np.ones((3, 3)))
        mask = cv2.dilate(mask, np.ones((31, 31)))
        ret, thresh = cv2.threshold(mask, 127, 255, 0)
        im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        cv2.drawContours(mask, contours, -1, 255, cv2.FILLED)

        return mask

    def find_lines(self, frame):
        list_of_line_points = list()

        mask = self.get_terrain_mask(frame)

        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        blured_frame = cv2.blur(gray, (7, 7))

        #cv2.imshow('blured_frame', blured_frame)

        edges = cv2.Canny(blured_frame, 20, 50, apertureSize=3)
        edges = cv2.blur(edges, (3, 3))

        #cv2.imshow('Canny_blurred', edges)

        edges = cv2.bitwise_and(mask, edges)

        #cv2.imshow('Canny_cut', edges)

        lines = cv2.HoughLinesP(edges, 1, np.pi/180, 400, 50, 125)[:, 0, :]
        for x1, y1, x2, y2 in lines:
            list_of_line_points.append(((x1, y1, 1), (x2, y2, 1)))

        list_of_line_points = np.float32(list_of_line_points)

        #cv2.imshow('all_lines', self.line_mask(np.zeros(frame.shape, np.uint8), list_of_line_points[:, :, :2]))
        #cv2.waitKey()

        return list_of_line_points

    def are_lines_vertical_or_horizontal_in_model(self, list_of_line_points, H):
        count_good_lines = 0

        for p1, p2 in list_of_line_points:
            p1_prime = np.dot(H, (p1[0], p1[1], 1))
            p2_prime = np.dot(H, (p2[0], p2[1], 1))

            p1_prime /= p1_prime[-1]
            p2_prime /= p2_prime[-1]

            x_pos = [69, 171, 197, 223, 248, 274, 298, 325, 349, 375, 401, 426, 478, 504, 555, 579,
                     606, 630, 656, 683, 707, 733, 834]
            y_pos = [49, 432]

            if p2_prime[0] - 10 <= p1_prime[0] <= p2_prime[0] + 10:
                mid_x = p1_prime[0] + p2_prime[0]
                mid_x /= 2
                for x in x_pos:
                    if x - 10 <= mid_x <= x + 10:
                        count_good_lines += 1
                        break

            if p2_prime[1] - 10 <= p1_prime[1] <= p2_prime[1] + 10:
                mid_y = p1_prime[1] + p2_prime[1]
                mid_y /= 2
                for y in y_pos:
                    if y - 10 <= mid_y <= y + 10:
                        count_good_lines += 1
                        break

        return count_good_lines / len(list_of_line_points)

    def find_global_lines(self, frame):
        pts = self.find_lines(frame)
        clusters = []
        cluster_values = []

        new_lines = []

        for i, (pt1, pt2) in enumerate(pts):

            if pt1[0] > pt2[0]:
                pt_tmp = pt1
                pt2 = pt1
                pt1 = pt2

                pts[i] = (pt1, pt2)

            pente = (pt2[1] - pt1[1]) / (pt2[0] - pt1[0])
            if np.isnan(pente):
                pente = frame.shape[0]
            elif np.isinf(pente):
                pente = 0

            y_intercept = pt1[1] - pente * pt1[0]

            x_intercept = (frame.shape[0] / 2 - y_intercept) / pente
            if np.isnan(x_intercept) or np.isinf(x_intercept):
                x_intercept = y_intercept

            x1 = 0
            y1 = pente * x1 + y_intercept
            x2 = frame.shape[1]
            y2 = pente * x2 + y_intercept

            pt1 = np.asarray([x1, y1], np.int)
            pt2 = np.asarray([x2, y2], np.int)
            rectangle = (0, 0, frame.shape[1], frame.shape[0])
            _, pt1, pt2 = cv2.clipLine(rectangle, (pt1[0], pt1[1]), (pt2[0], pt2[1]))

            new_lines.append([pt1, pt2])

            found = False
            for index, values in enumerate(cluster_values):
                if np.abs(pente) <= 0.1:
                    if y_intercept - 10 <= values['y_intercept'] <= y_intercept + 10:
                        clusters[index].append(i)
                        found = True
                        break
                else:
                    if x_intercept - 20 <= values['x_intercept'] <= x_intercept + 20:
                        clusters[index].append(i)
                        found = True
                        break

            if not found:
                cluster_values.append({'pente': pente, 'x_intercept': x_intercept, 'y_intercept': y_intercept})
                clusters.append([i])

        new_lines = np.asarray(new_lines, np.int)

        global_lines = []
        for index, cluster in enumerate(clusters):
            if len(cluster) < 2:
                continue

            pt1, pt2 = (new_lines[cluster].sum(axis=0) / len(cluster)).astype(int)
            rectangle = (0, 0, frame.shape[1], frame.shape[0])
            clipped = cv2.clipLine(rectangle, (pt1[0], pt1[1]), (pt2[0], pt2[1]))
            _, pt1, pt2 = clipped
            global_lines.append([np.asarray(pt1), np.asarray(pt2)])

        for i in range(len(global_lines)):
            for j in range(len(global_lines)):
                if i == j:
                    continue

                o1, p1 = global_lines[i]
                o2, p2 = global_lines[j]

                x = o2 - o1
                d1 = p1 - o1
                d2 = p2 - o2

                cross = (d1[0] * d2[1]) - (d1[1] * d2[0])

                if np.abs(cross) > 1e-8:
                    t1 = ((x[0] * d2[1]) - (x[1] * d2[0])) / cross
                    r = (o1 + d1 * t1).astype(int)

                    max_1 = np.max([o1, o2], axis=0)
                    min_1 = np.min([o1, o2], axis=0)
                    max_2 = np.max([p1, p2], axis=0)
                    min_2 = np.min([p1, p2], axis=0)

                    max = np.max([max_1, min_1, max_2, min_2], axis=0)
                    min = np.min([max_1, min_1, max_2, min_2], axis=0)

                    if min[0] <= r[0] <= max[0] and min[1] <= r[1] <= max[1]:
                        pente_1 = (p1[1] - o1[1]) / (p1[0] - o1[1])
                        pente_2 = (p2[1] - o2[1]) / (p2[0] - o2[1])
                        if np.abs(pente_1) >= 0.1:
                            if pente_1 >= 0:
                                global_lines[i][0] = r
                            else:
                                global_lines[i][1] = r
                        if np.abs(pente_2) >= 0.1:
                            if pente_2 >=0:
                                global_lines[j][0] = r
                            else:
                                global_lines[j][1] = r

        return np.asarray(global_lines)

    def line_mask(self, mask, global_lines):
        #print(global_lines)

        for line in global_lines:
            (o1, p1), (o2, p2) = line
            cv2.line(mask, (o1, p1), (o2, p2), (255, 255, 255), 3)

        return mask

    def match_lines(self, lines_1, lines_2):
        matches_dst = np.zeros((len(lines_1), len(lines_2)), np.float32)

        for i, line_i in enumerate(lines_1):
            for j, line_j in enumerate(lines_2):
                matches_dst[i, j] = np.power(line_i - line_j, 2).sum() / 4

        matches = list()
        for i in range(len(lines_1)):
            best_match = None
            for j in range(len(lines_2)):
                if best_match is None:
                    best_match = [j, matches_dst[i, j]]
                elif best_match[1] > matches_dst[i, j]:
                    best_match[0] = j
                    best_match[1] = matches_dst[i, j]

            matches.append([[i, best_match[0]], best_match[1]])

        return matches

    def get_homography_between_lines(self, img1, img2):
        mask = np.zeros(img1.shape, np.uint8)
        global_lines_1 = self.find_global_lines(img1)
        global_lines_2 = self.find_global_lines(img2)

        matches = self.match_lines(global_lines_1, global_lines_2)

        matches = sorted(matches, key=lambda x: x[1])

        print(matches)

        src_pts = list()
        dst_pts = list()

        colors1 = ((255, 0, 0), (0, 255, 0), (0, 0, 255))
        colors2 = ((128, 0, 0), (0, 128, 0), (0, 0, 128))
        for i in range(min(3, len(matches) - 1)):
            index_1, index_2 = matches[i][0]

            pt11, pt12 = global_lines_1[index_1]
            pt21, pt22 = global_lines_2[index_2]

            src_pts.append(pt11)
            src_pts.append(pt12)
            dst_pts.append(pt21)
            dst_pts.append(pt22)

            cv2.line(mask, (pt11[0], pt11[1]), (pt12[0], pt12[1]), colors1[i], 2, cv2.LINE_AA)
            cv2.line(mask, (pt21[0], pt21[1]), (pt22[0], pt22[1]), colors2[i], 2, cv2.LINE_AA)

        cv2.imshow('matching lines', mask)
        cv2.waitKey(1)

        src_pts = np.float32(src_pts)
        dst_pts = np.float32(dst_pts)

        try:
            M, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 20)
        except Exception as e:
            print(e)
            M = None

        return M


import torch
from .metric import Metric

class ClipMeanPerclassAccuracyMetric(Metric):
    """Don't need activation softmax for clip accuracy calculation.
    """
    def __init__(self, *args, exclude_classes_less_sample_than = 1, **kwargs):
        super().__init__(*args, **kwargs)
        assert exclude_classes_less_sample_than >= 1
        self.exclude_classes_less_sample_than = exclude_classes_less_sample_than

    def clean_data(self):
        super().clean_data()
        self.num_seen_samples = None
        self.num_true_positives = None

    def add_clip_predictions(self, video_ids, clip_predictions, labels):
        video_ids, clip_predictions, labels = super().add_clip_predictions(video_ids, clip_predictions, labels)
        if video_ids is None:
            return      # sometimes, after filtering the samples, there can be no samples to do anything.

        assert labels.dim() in [1,2], f'labels has to be 1D or 2D tensor but got {labels.dim()}-D.'
        if labels.dim() == 2:
            # Convert 2D one-hot label to 1D label
            labels = labels.argmax(dim=1)

        if self.num_seen_samples is None:
            self.num_seen_samples = [0] * self.num_classes
            self.num_true_positives = [0] * self.num_classes

        pred_labels = torch.argmax(clip_predictions, dim=1)
        for pred, label in zip(pred_labels, labels):
            self.num_seen_samples[label] += 1
            if pred == label:
                self.num_true_positives[label] += 1

    def calculate_metrics(self):
        if self.num_seen_samples is None:
            self.last_calculated_metrics = 0.
        else:
            filtered_perclass_accuracies = [tp / sample_count for tp, sample_count in zip(self.num_true_positives, self.num_seen_samples) if sample_count >= self.exclude_classes_less_sample_than]
            if len(filtered_perclass_accuracies) > 0:
                self.last_calculated_metrics = sum(filtered_perclass_accuracies) / len(filtered_perclass_accuracies)
            else:
                self.last_calculated_metrics = 0.

    def types_of_metrics(self):
        return float

    def tensorboard_tags(self):
        if self.exclude_classes_less_sample_than == 1:
            return 'Clip mean per-class accuracy'
        else:
            return f'Clip mean per-class accuracy {self.exclude_classes_less_sample_than}+'

    def get_csv_fieldnames(self):
        if self.exclude_classes_less_sample_than == 1:
            return f'{self.split}_meanperclassacc'
        else:
            return f'{self.split}_meanperclassacc{self.exclude_classes_less_sample_than}+'

    def logging_msg_iter(self):
        """
        Return:
            None to skip logging this metric
            or a single str that combines all self.last_calculated_metrics
        """
        if self.split == 'train':
            prefix = ''
        else:
            prefix = f'{self.split}_'

        if self.exclude_classes_less_sample_than == 1:
            suffix = ''
        else:
            suffix = f'{self.exclude_classes_less_sample_than}+'

        message = f'{prefix}meanperclassacc{suffix}: {self.last_calculated_metrics:.4f}'
        return message

    def logging_msg_epoch(self):
        """
        Return:
            None to skip logging this metric
            or a single str that combines all self.last_calculated_metrics
        """
        return self.logging_msg_iter()

    def plot_legend_labels(self):
        """
        Return:
            either tuple or a single str
        """
        if self.exclude_classes_less_sample_than == 1:
            suffix = ''
        else:
            suffix = f' ({self.exclude_classes_less_sample_than}+ samples)'

        if self.split == 'train':
            return f'Training average per-class accuracy{suffix}'
        elif self.split == 'val':
            return f'Validation average per-class accuracy{suffix}'
        elif self.split == 'multicropval':
            return f'Multicrop validation average per-class accuracy{suffix}'
        else:
            return f'{self.split} average per-class accuracy{suffix}'

    def plot_file_basenames(self):
        """
        Return:
            either tuple or a single str
        """
        # output plot file names will be e.g.) accuracy.png/pdf, accuracy_top5.png/pdf, ...
        if self.exclude_classes_less_sample_than == 1:
            suffix = ''
        else:
            suffix = f'_{self.exclude_classes_less_sample_than}+'
        return f'mean_perclass_accuracy{suffix}'

    @staticmethod
    def is_better(value_1, value_2):
        """Metric comparison function

        params:
            two values of the metric

        return:
            True if value_1 is better. False if value_2 is better or they're equal.
        """
        return value_1 > value_2

    def __len__(self):
        return sum(self.num_seen_samples)


from uscope.planner.planner_util import get_planner
from uscope.planner.planner import PlannerStop
from uscope.benchmark import Benchmark
from uscope.motion.hal import AxisExceeded, MotionHAL, MotionCritical
from uscope.motion.plugins import get_motion_hal
from uscope.joystick import Joystick, JoystickNotFound
from uscope import cloud_stitch
from uscope import config
from uscope.imager.autofocus import choose_best_image
from PyQt5.QtCore import QThread, pyqtSignal
import traceback
import datetime
import queue
import threading
import time
from queue import Queue, Empty
import subprocess
from uscope.planner.planner_util import microscope_to_planner_config
from uscope.kinematics import Kinematics
from collections import OrderedDict

def dbg(*args):
    if len(args) == 0:
        print()
    elif len(args) == 1:
        print('threading: %s' % (args[0], ))
    else:
        print('threading: ' + (args[0] % args[1:]))

'''
Offloads controller processing to another thread (or potentially even process)
Makes it easier to keep RT deadlines and such
However, it doesn't provide feedback completion so use with care
(other blocks until done)
TODO: should block?
'''

class MotionThreadMotion(MotionHAL):
    def __init__(self, mt):
        self.mt = mt
        MotionHAL.__init__(self, log=mt.motion.log, verbose=mt.motion.verbose)

        # Don't re-apply pipeline (scaling, etc)
        self.configure({})

    def axes(self):
        return self.mt.motion.axes()

    def home(self, axes):
        self.mt.home(block=True)

    def _move_absolute(self, pos):
        self.mt.move_absolute(pos, block=True)

    def _move_relative(self, pos):
        self.mt.move_relative(pos, block=True)

    def _pos(self):
        # return self.mt.pos_cache
        return self.mt.pos()

    def settle(self):
        # No hardware to let settle
        pass

    def ar_stop(self):
        pass

class MotionThread(QThread):
    log_msg = pyqtSignal(str)

    def __init__(self, usc):
        QThread.__init__(self)
        self.usc = usc
        self.verbose = False
        self.queue = queue.Queue()
        self.motion = None
        self.running = threading.Event()
        self.idle = threading.Event()
        self.idle.set()
        self.normal_running = threading.Event()
        self.normal_running.set()
        self.lock = threading.Event()
        # Let main gui get the last position from a different thread
        # It can request updates
        self.pos_cache = None
        self._stop = False
        self._estop = False
        # XXX: add config directive
        self.allow_motion_reboot = False

        # Seed state / refuse to start without motion
        self.init_motion()

    def init_motion(self):
        self.motion = get_motion_hal(usc=self.usc, log=self.log)

    def log(self, msg):
        self.log_msg.emit(msg)

    def setRunning(self, running):
        if running:
            self.normal_running.set()
        else:
            self.normal_running.clear()

    def wait_idle(self):
        while True:
            time.sleep(0.15)
            if self.idle.is_set():
                break

    def command(self, command, *args, block=False, callback=None):
        command_done = None
        if block or callback:
            ready = threading.Event()
            ret = []

            def command_done(command, args, ret_e):
                ret.append(ret_e)
                ready.set()
                if callback:
                    callback()

        self.queue.put((command, args, command_done))
        if block:
            ready.wait()
            ret = ret[0]
            if type(ret) is Exception:
                raise Exception("oopsie: %s" % (ret, ))
            return ret

    def pos(self):
        # XXX: this caused crashes but I'm not sure why
        # Just offload to the thread to avoid this special case
        if 0:
            self.lock.set()
            ret = self.motion.pos()
            self.lock.clear()
            return ret
        else:
            return self.command("pos", block=True)

    def mdi(self, cmd):
        self.command("mdi", cmd)

    def jog(self, pos):
        self.command("jog", pos)

    def jog_lazy(self, pos):
        """
        Only jog if events haven't already stacked up high
        """
        if self.qsize() < 1:
            self.command("jog", pos)

    def stop(self):
        # self.command("stop")
        self._stop = True

    def estop(self):
        # self.command("estop")
        self._estop = True

    def home(self, block=False):
        self.command("home", block=block)

    def backlash_disable(self, block=False):
        self.command("backlash_disable", block=block)

    def backlash_enable(self, block=False):
        self.command("backlash_enable", block=block)

    def move_absolute(self, pos, block=False, callback=None):
        self.command("move_absolute", pos, block=block, callback=callback)

    def move_relative(self, pos, block=False, callback=None):
        self.command("move_relative", pos, block=block, callback=callback)

    def set_jog_rate(self, rate):
        self.command("set_jog_rate", rate)

    def update_pos_cache(self):
        self.command("update_pos_cache")

    def qsize(self):
        return self.queue.qsize()

    def queue_clear(self):
        while True:
            try:
                self.queue.get(block=False)
            except queue.Empty:
                break

    def get_planner_motion(self):
        return MotionThreadMotion(self)

    def shutdown(self):
        self.running.clear()

    def run(self):
        self.verbose and print("Motion thread started: %s" %
                               (threading.get_ident(), ))
        self.running.set()
        self.idle.clear()
        self.motion.on()

        def motion_status(status):
            # print("register_status_cb: via motion-status: %s" % (status,))
            self.pos_cache = status["pos"]

        self.motion.register_status_cb(motion_status)

        try:
            while self.running.is_set():
                self.lock.set()

                if not self.motion:
                    if not self.allow_motion_reboot:
                        self.log("Fatal error: motion controller is dead")
                        break
                    else:
                        # See if its back...
                        try:
                            self.init_motion()
                            self.motion.on()
                        except Exception as e:
                            self.log(
                                "Failed to reboot motion controller :( %s" %
                                (str(e), ))
                            time.sleep(3)
                        continue

                if self._estop:
                    self.motion.estop()
                    self.queue_clear()
                    self._estop = False
                    continue

                if self._stop:
                    self.motion.stop()
                    self.queue_clear()
                    self._stop = False
                    continue

                if not self.normal_running.isSet():
                    self.normal_running.wait(0.1)
                    continue
                try:
                    self.lock.clear()
                    (command, args, command_done) = self.queue.get(True, 0.1)
                except queue.Empty:
                    self.idle.set()
                    continue
                finally:
                    self.lock.set()

                self.idle.clear()

                def default(*args):
                    raise Exception("Bad command %s" % (command, ))

                def move_absolute(pos):
                    try:
                        self.motion.move_absolute(pos)
                    except AxisExceeded as e:
                        self.log(str(e))
                    return self.motion.pos()

                def move_relative(pos):
                    try:
                        self.motion.move_relative(pos)
                    except AxisExceeded as e:
                        self.log(str(e))
                    return self.motion.pos()

                def update_pos_cache():
                    pos = self.motion.pos()
                    self.pos_cache = pos
                    # print("register_status_cb: via update_pos_cache: %s" % (pos,))

                self.verbose and print("")
                self.verbose and print(
                    "process @ %s" % datetime.datetime.utcnow().isoformat())
                #print 'cnc thread: dispatch %s' % command
                # Maybe I should just always emit the pos
                f = {
                    'update_pos_cache': update_pos_cache,
                    'move_absolute': move_absolute,
                    'move_relative': move_relative,
                    'jog': self.motion.jog,
                    'pos': self.motion.pos,
                    'set_jog_rate': self.motion.set_jog_rate,
                    'home': self.motion.home,
                    'backlash_disable': self.motion.backlash_disable,
                    'backlash_enable': self.motion.backlash_enable,
                    # 'stop': self.motion.stop,
                    # 'estop': self.motion.estop,
                    'unestop': self.motion.unestop,
                    'mdi': self.motion.command,
                }.get(command, default)
                try:
                    ret = f(*args)
                # Depending on the motion controller this may be a bad idea
                # Only some of them retain the old coordinate system / may need re-home
                except MotionCritical as e:
                    print("")
                    print("ERROR: motion controller crashed w/ critical error")
                    print(traceback.format_exc())
                    self.motion.close()
                    self.motion = None
                    if command_done:
                        command_done(command, args, e)
                    continue
                except Exception as e:
                    print("")
                    print("WARNING: motion thread crashed")
                    print(traceback.format_exc())
                    if command_done:
                        command_done(command, args, e)
                    continue

                if command_done:
                    command_done(command, args, ret)

        finally:
            if self.motion:
                self.motion.stop()
                # self.motion.ar_stop()

"""
Sends events to the imaging and movement threads

rconfig: misc parmeters including complex objects
plannerj: planner configuration JSON. Written to disk
"""

class PlannerThread(QThread):
    plannerDone = pyqtSignal(dict)
    log_msg = pyqtSignal(str)

    def __init__(self, parent, planner_args, progress_cb):
        QThread.__init__(self, parent)
        self.planner_args = planner_args
        self.planner = None
        self.progress_cb = progress_cb

    def log(self, msg=""):
        #print 'emitting log %s' % msg
        #self.log_buff += str(msg) + '\n'
        self.log_msg.emit(str(msg))

    def setRunning(self, running):
        planner = self.planner
        if planner:
            planner.setRunning(running)

    def is_paused(self):
        if self.planner:
            return self.planner.is_paused()
        return False

    def pause(self):
        if self.planner:
            self.planner.pause()

    def unpause(self):
        if self.planner:
            self.planner.unpause()

    def shutdown(self):
        if self.planner:
            self.planner.stop()

    def run(self):
        ret = {
            "result": None,
        }
        try:
            self.log('Initializing planner!')
            # print("Planner thread started: %s" % (threading.get_ident(), ))

            self.planner = get_planner(log=self.log, **self.planner_args)
            self.planner.register_progress_callback(self.progress_cb)
            self.log('Running planner')
            b = Benchmark()
            self.log()
            self.log()
            self.log()
            self.log()
            ret["meta"] = self.planner.run()
            ret["result"] = "ok"
            b.stop()
            self.log('Planner done!  Took : %s' % str(b))
        except PlannerStop as e:
            ret["result"] = "stopped"
        except Exception as e:
            self.log('WARNING: planner thread crashed: %s' % str(e))
            traceback.print_exc()
            ret["result"] = "exception"
            ret["exception"] = e
            #raise
        finally:
            self.plannerDone.emit(ret)

class StitcherThread(QThread):
    # stitcherDone = pyqtSignal()
    log_msg = pyqtSignal(str)

    def __init__(self, parent=None):
        QThread.__init__(self, parent)
        self.queue = Queue()
        self.running = threading.Event()
        self.running.set()

    def log(self, msg):
        self.log_msg.emit(msg)

    def shutdown(self):
        self.running.clear()

    def cli_stitch_add(self, directory, command):
        j = {
            "type": "cli",
            "directory": directory,
            "command": command,
        }
        self.queue.put(j)

    # Offload uploads etc to thread since they might take a while
    def cloud_stitch_add(
        self,
        directory,
        access_key,
        secret_key,
        id_key,
        notification_email,
    ):

        j = {
            "type": "CloudStitch",
            "directory": directory,
            "access_key": access_key,
            "secret_key": secret_key,
            "id_key": id_key,
            "notification_email": notification_email,
        }
        self.queue.put(j)

    def run(self):
        while self.running:
            try:
                j = self.queue.get(block=True, timeout=0.1)
            except Empty:
                continue
            try:
                if j["type"] == "CloudStitch":
                    cloud_stitch.upload_dir(
                        directory=j["directory"],
                        id_key=j["id_key"],
                        access_key=j["access_key"],
                        secret_key=j["secret_key"],
                        notification_email=j["notification_email"],
                        log=self.log,
                        running=self.running)
                elif j["type"] == "cli":
                    self.log(
                        f"Stitch CLI: kicking off {j['command']} {j['directory']}"
                    )
                    # Hacky but good enough for now
                    # Check terminal for process output
                    print("")
                    print("")
                    print("")
                    print(
                        f"Stitch CLI: kicking off {j['command']} {j['directory']}"
                    )
                    subprocess.check_call([j['command'], j['directory']])
                    self.log(f"Stitch CLI: finished job")
                    print(f"Stitch CLI: finished job")
                else:
                    assert 0, j

            except Exception as e:
                self.log('WARNING: stitcher thread crashed: %s' % str(e))
                traceback.print_exc()
            finally:
                # self.stitcherDone.emit()
                pass

class ImageProcessingThread(QThread):
    log_msg = pyqtSignal(str)

    def __init__(self, motion_thread, ac, parent=None):
        QThread.__init__(self, parent)
        self.queue = Queue()
        self.running = threading.Event()
        self.running.set()
        self.motion_thread = motion_thread
        self.ac = ac
        self.imager = self.ac.imager
        self.kinematics = None

        self.kinematics = Kinematics(
            microscope=self.ac.microscope,
            log=self.log,
        )
        self.kinematics.configure()

    def log(self, msg):
        self.log_msg.emit(msg)

    def shutdown(self):
        self.running.clear()

    def command(self, command, block=False, callback=None):
        command_done = None
        if block or callback:
            ready = threading.Event()
            ret = []

            def command_done(command, ret_e):
                ret.append(ret_e)
                ready.set()
                if callback:
                    callback()

        self.queue.put((command, command_done))
        if block:
            ready.wait()
            ret = ret[0]
            if type(ret) is Exception:
                raise Exception("oopsie: %s" % (ret, ))
            return ret

    def auto_focus_coarse(self, callback=None):
        j = {
            "type": "auto_focus_coarse",
        }
        self.command(j, callback=callback)

    def auto_focus_fine(self, callback=None):
        j = {
            "type": "auto_focus_fine",
        }
        self.command(j, callback=callback)

    def move_absolute(self, pos):
        self.motion_thread.move_absolute(pos, block=True)
        self.kinematics.wait_imaging_ok()

    def pos(self):
        return self.motion_thread.pos()

    def auto_focus_pass(self, step_size, step_pm):
        """
        for outer_i in range(3):
            self.log("autofocus: try %u / 3" % (outer_i + 1,))
            # If we are reasonably confident we found the local minima stop
            # TODO: if repeats should bias further since otherwise we are repeating steps
            if abs(step_pm - fni) <= 2:
                self.log("autofocus: converged")
                return
        self.log("autofocus: timed out")
        """

        # Very basic short range
        start_pos = self.pos()["z"]
        steps = step_pm * 2 + 1

        # Doing generator allows easier to process images as movement is done / settling
        def gen_images():
            for focusi in range(steps):
                # FIXME: use backlash compensation direction here
                target_pos = start_pos + -(focusi - step_pm) * step_size
                self.log("autofocus round %u / %u: try %0.6f" %
                         (focusi + 1, steps, target_pos))
                self.move_absolute({"z": target_pos})
                im_pil = self.imager.get()["0"]
                yield target_pos, im_pil

        target_pos, fni = choose_best_image(gen_images())
        self.log("autofocus: set %0.6f at %u / %u" %
                 (target_pos, fni + 1, steps))
        self.move_absolute({"z": target_pos})

    def do_auto_focus_coarse(self):
        # MVP intended for 20x
        # 2 um is standard focus step size
        self.log("autofocus: coarse")
        self.auto_focus_pass(step_size=0.006, step_pm=3)
        self.log("autofocus: medium")
        self.auto_focus_pass(step_size=0.002, step_pm=3)
        self.log("autofocus: done")

    def do_auto_focus_fine(self):
        # MVP intended for 60x
        # 500 nm is standard focus step size
        self.log("autofocus: fine")
        self.auto_focus_pass(step_size=0.0005, step_pm=3)
        self.log("autofocus: done")

    def run(self):
        while self.running:
            try:
                j, command_done = self.queue.get(block=True, timeout=0.1)
            except Empty:
                continue
            try:
                if j["type"] == "auto_focus_coarse":
                    self.do_auto_focus_coarse()
                elif j["type"] == "auto_focus_fine":
                    self.do_auto_focus_fine()
                else:
                    assert 0, j

                if command_done:
                    command_done(j, None)

            except Exception as e:
                self.log('WARNING: image processing thread crashed: %s' %
                         str(e))
                traceback.print_exc()
                if command_done:
                    command_done(j, e)
            finally:
                # self.stitcherDone.emit()
                pass

class JoystickThread(QThread):
    joystickDone = pyqtSignal()
    log_msg = pyqtSignal(str)

    def __init__(self, ac):
        QThread.__init__(self)
        self.joystick = None
        self.ac = ac
        self.queue = Queue()
        self.running = threading.Event()
        self.running.set()
        try:
            self.joystick = Joystick(microscope=self.ac.microscope)
        except JoystickNotFound:
            raise JoystickNotFound()
        self.ac.microscope.joystick = self.joystick

    def log_info(self):
        self.log("Joystick")
        self.log(f"  Name: {self.joystick.joystick.name}")
        self.log(f"  Axes: {self.joystick.joystick.numaxes}")
        self.log(f"  Trackballs: {self.joystick.joystick.numballs}")
        self.log(f"  Hats: {self.joystick.joystick.numhats}")
        self.log(f"  Buttons: {self.joystick.joystick.numbuttons}")

    def log(self, msg):
        self.log_msg.emit(msg)

    def shutdown(self):
        self.running.clear()

    def run(self):
        while self.running:
            try:
                time.sleep(self.ac.bc.joystick.scan_secs())
                # It is important to check that the button is both enabled and
                # active before performing actions. This allows us to preserve
                # state by disabling and enabling the button only during scans.
                if self.ac.mw.mainTab.motion_widget.joystick_listener.joystick_executing:
                    #self.joystick.debug_dump()
                    self.joystick.execute()
            except Exception as e:
                self.log('WARNING: joystick thread crashed: %s' % str(e))
                traceback.print_exc()
            finally:
                self.joystickDone.emit()


from datetime import date

from odoo.tests.common import TransactionCase

class TestStockPickingPurchaseOrderLink(TransactionCase):
    def setUp(self):
        super(TestStockPickingPurchaseOrderLink, self).setUp()
        self.stock_location_obj = self.env["stock.location"]
        self.stock_picking_type_obj = self.env["stock.picking.type"]
        self.stock_picking_obj = self.env["stock.picking"]
        self.product_product_obj = self.env["product.product"]
        self.warehouse = self.env["stock.warehouse"].create(
            {"name": "warehouse - test", "code": "WH-TEST"}
        )

        self.product = self.product_product_obj.create(
            {
                "name": "product_template_obj - Test",
                "type": "product",
                "standard_price": 100.00,
            }
        )
        self.partner = self.env["res.partner"].create({"name": "Vendor - test"})
        self.picking_type = self.stock_picking_type_obj.search(
            [("warehouse_id", "=", self.warehouse.id), ("code", "=", "incoming")]
        )
        purchase_order = self.env["purchase.order"].create(
            {
                "partner_id": self.partner.id,
                "order_line": [
                    (
                        0,
                        0,
                        {
                            "name": self.product.name,
                            "product_id": self.product.id,
                            "product_qty": 1.0,
                            "product_uom": self.product.uom_po_id.id,
                            "price_unit": 10.0,
                            "date_planned": date.today(),
                        },
                    )
                ],
            }
        )
        purchase_order.button_confirm()
        self.picking = self.stock_picking_obj.search(
            [("purchase_id", "=", purchase_order.id)]
        )

    def test_picking_to_purchase_order(self):
        result = self.picking.action_view_purchase_order()
        self.assertEqual(result["res_id"], self.picking.purchase_id.id)


from enum import Enum
from typing import Optional, Tuple

import torch
import torch.nn.functional as F
from torch import Tensor

class LossType(Enum):
    BCE = "bce"
    FOCAL = "focal"
    SOFTMAX = "softmax"

def compute_spot_loss_class(
    class_preds: Tensor,
    class_target: Tensor,
    has_label: Tensor,
    ignore_class: Tensor,
    class_weights: Tensor,
    mixup_weights: Tensor | None,
    loss_type: LossType = LossType.BCE,
    alpha: float = 0.25,
    gamma: float = 2,
):
    if LossType(loss_type) in [LossType.BCE, LossType.FOCAL]:
        bce_weights, num_bce = get_spot_bce_weights(
            class_target=class_target,
            has_class_target=has_label,
            class_weights=class_weights,
            ignore_class=ignore_class,
        )

        bce_loss = F.binary_cross_entropy_with_logits(
            class_preds,
            class_target,
            reduction="none",
        )

        if LossType(loss_type) == LossType.FOCAL:
            p = torch.sigmoid(class_preds)
            p_t = p * class_target + (1 - p) * (1 - class_target)
            bce_loss = bce_loss * ((1 - p_t) ** gamma)

            if alpha >= 0:
                alpha_t = alpha * class_target + (1 - alpha) * (1 - class_target)
                bce_loss = alpha_t * bce_loss

        if mixup_weights is not None:
            bce_loss *= bce_weights.type_as(bce_loss)
            bce_loss = bce_loss.sum((-1, -2))
            bce_loss *= mixup_weights.squeeze()
            bce_loss = bce_loss.sum() / torch.maximum(
                num_bce, torch.tensor(1, device=bce_loss.device)
            )

        else:
            bce_loss *= bce_weights.type_as(bce_loss)
            bce_loss = bce_loss.sum()
            bce_loss /= torch.maximum(num_bce, torch.tensor(1, device=bce_loss.device))

        return bce_loss

    elif LossType(loss_type) == LossType.SOFTMAX:
        b, t, c = class_preds.shape

        class_weights, num_softmax = get_spot_softmax_weights(
            has_class=has_label,
            ignore_class=ignore_class,
        )

        class_preds = class_preds.reshape(b * t, c)
        class_target = class_target.reshape(b * t, c)

        softmax_loss: Tensor = F.cross_entropy(
            class_preds, class_target, reduction="none", weight=class_weights
        )

        softmax_loss = softmax_loss.reshape(b, t)

        if mixup_weights is not None:
            softmax_loss *= class_weights.type_as(softmax_loss)
            softmax_loss = softmax_loss.sum(-1)
            softmax_loss *= mixup_weights.squeeze()
            softmax_loss /= torch.maximum(
                num_softmax, torch.tensor(1, device=softmax_loss.device)
            )

        else:
            softmax_loss *= class_weights.type_as(softmax_loss)
            softmax_loss = softmax_loss.sum()
            softmax_loss /= torch.maximum(
                num_softmax, torch.tensor(1, device=softmax_loss.device)
            )

        return softmax_loss
    else:
        raise NotImplementedError(f"{type} does not exist.")

def get_spot_bce_weights(
    class_target: Tensor,
    has_class_target: Tensor,
    class_weights: Optional[Tensor],
    ignore_class: Tensor,
) -> Tensor:
    """Get the soccernet weights for binary cross entropy loss.

    Args:
        class_target: The one-hot encoder of target classes.
        has_class_target: Whether there is a label.
        class_weights: Vector of weights for each class.
        ignore_class: Whether to ignore class.

    Returns:
        The weights.
    """

    has_class_target = has_class_target.bool()
    ignore_class = ignore_class.bool()

    if class_weights is not None:
        not_has_class_target = torch.logical_not(has_class_target)
        bce_weights = (
            has_class_target.type_as(class_weights) * class_weights[1:2, :]
            + not_has_class_target.type_as(class_weights) * class_weights[0:1, :]
        )
    else:
        bce_weights = torch.ones(class_target.shape, device=class_target.device)

    bce_weights *= 1 - ignore_class.type_as(bce_weights)

    return bce_weights, torch.logical_not(ignore_class).sum()

def get_spot_softmax_weights(
    has_class: Tensor,
    ignore_class: Tensor,
) -> Tuple[Tensor, Tensor]:
    """Get the soccernet weights for card loss.

    Args:
        has_class: Whether each element in each sample in the batch has the class.
        ignore_class: Whether to ignore for each element in each sample in the batch the class

    Returns:
        The softmax weights.
    """

    has_class = has_class.bool()
    has_class = has_class.any(dim=-1)

    ignore_class = ignore_class.bool()
    ignore_class = ignore_class.any(dim=-1)

    use_softmax_criterion = torch.logical_and(
        has_class,
        torch.logical_not(ignore_class),
    )

    softmax_weights = torch.ones(has_class.shape, device=has_class.device)
    softmax_weights *= use_softmax_criterion.type_as(softmax_weights)

    return softmax_weights, use_softmax_criterion.sum()

def compute_spot_loss_fn(
    class_preds: Tensor,
    class_target: Tensor,
    has_label: Tensor,
    ignore_class: Tensor,
    class_weights: Optional[Tensor] = None,
    mixup_weights: Optional[Tensor] = None,
    class_loss_type: LossType = LossType.BCE,
    alpha: float = 0.25,
    gamma: float = 2,
) -> Tensor:
    """Compute the soccernet loss function which is a classification loss.

    Args:
        class_preds: Predictions for the classes. Expected shape: (B, T, C).
        class_target: Multi-label encoded class. Can be continuous label in case of mixup. Expected shape: (B, T, C).
        has_label: Whether there is a label. Expected shape: (B', T, C).
        ignore_class: Whether to ignore class. Expected shape: (B', T, C).
        class_weights: Weights of negatives and positives for BCE loss. Expected shape: (2, C).
        mixup_weights: Weights for mixup for loss. Expected shape: (B', T, C).
        class_loss_type: Type of loss to use. Can be BCE, softmax or Focal.
        alpha: For focal loss.
        gamma: For focal loss.

    Returns:
        The reduced sum of the classification losses.
    """
    if class_weights is not None:
        class_weights = class_weights.to(device=class_target.device)

    if mixup_weights is not None:
        mixup_weights_1, mixup_weights_2 = mixup_weights.chunk(2)

        class_target_1, class_target_2 = class_target.chunk(2)
        ignore_class_1, ignore_class_2 = ignore_class.chunk(2)
        has_label_1, has_label_2 = has_label.chunk(2)

        bce_loss_1 = compute_spot_loss_class(
            class_preds=class_preds,
            class_target=class_target_1,
            has_label=has_label_1,
            ignore_class=ignore_class_1,
            class_weights=class_weights,
            mixup_weights=mixup_weights_1,
            loss_type=class_loss_type,
            alpha=alpha,
            gamma=gamma,
        )

        bce_loss_2 = compute_spot_loss_class(
            class_preds=class_preds,
            class_target=class_target_2,
            has_label=has_label_2,
            ignore_class=ignore_class_2,
            class_weights=class_weights,
            mixup_weights=mixup_weights_2,
            loss_type=class_loss_type,
            alpha=alpha,
            gamma=gamma,
        )

        bce_loss = bce_loss_1 + bce_loss_2

    else:
        bce_loss = compute_spot_loss_class(
            class_preds=class_preds,
            class_target=class_target,
            has_label=has_label,
            ignore_class=ignore_class,
            class_weights=class_weights,
            mixup_weights=mixup_weights,
            loss_type=class_loss_type,
            alpha=alpha,
            gamma=gamma,
        )

    return bce_loss

def compute_soccernet_softmax_loss_fn(
    class_preds: Tensor,
    class_target: Tensor,
    has_target: Tensor,
    ignore_class: Tensor,
    class_weights: Tensor | None = None,
    mixup_weights: Optional[Tensor] = None,
) -> Tensor:
    b, t, c = class_preds.shape

    if mixup_weights is None:

        softmax_weights, num_softmax = get_spot_softmax_weights(
            has_class=has_target,
            ignore_class=ignore_class,
        )

        class_preds = class_preds.reshape(b * t, c)
        class_target = class_target.reshape(b * t, c)

        class_loss: Tensor = F.cross_entropy(
            class_preds, class_target, reduction="none", weight=class_weights
        )

        class_loss = class_loss.reshape(b, t)

        class_loss *= softmax_weights.type_as(class_loss)
        class_loss = class_loss.sum()
        class_loss /= torch.maximum(
            num_softmax, torch.tensor(1, device=class_loss.device)
        )
    else:
        mixup_weights_1, mixup_weights_2 = mixup_weights.chunk(2)
        class_target_1, class_target_2 = class_target.chunk(2)
        has_target_1, has_target_2 = has_target.chunk(2)
        ignore_class_1, ignore_class_2 = ignore_class.chunk(2)

        class_preds = class_preds.reshape(b * t, c)

        softmax_weights_1, num_softmax_1 = get_spot_softmax_weights(
            has_class=has_target_1,
            ignore_class=ignore_class_1,
        )

        softmax_weights_2, num_softmax_2 = get_spot_softmax_weights(
            has_class=has_target_2,
            ignore_class=ignore_class_2,
        )

        class_target_1 = class_target_1.reshape(b * t, c)
        class_target_2 = class_target_2.reshape(b * t, c)

        class_loss_1: Tensor = F.cross_entropy(
            class_preds, class_target_1, reduction="none", weight=class_weights
        )
        class_loss_2: Tensor = F.cross_entropy(
            class_preds, class_target_2, reduction="none", weight=class_weights
        )

        class_loss_1 = class_loss_1.reshape(b, t)
        class_loss_2 = class_loss_2.reshape(b, t)

        class_loss_1 *= softmax_weights_1.type_as(class_loss_1)
        class_loss_1 = class_loss_1.sum(-1)
        class_loss_1 *= mixup_weights_1.squeeze()
        class_loss_1 = class_loss_1.sum() / torch.maximum(
            num_softmax_1, torch.tensor(1, device=class_loss_1.device)
        )

        class_loss_2 *= softmax_weights_2.type_as(class_loss_2)
        class_loss_2 = class_loss_2.sum(-1)
        class_loss_2 *= mixup_weights_2.squeeze()
        class_loss_2 = class_loss_2.sum() / torch.maximum(
            num_softmax_2, torch.tensor(1, device=class_loss_2.device)
        )

        class_loss = class_loss_1 + class_loss_2

    return class_loss


#===============================================================================
# acdumo.py
#===============================================================================

# Imports ======================================================================

import matplotlib; matplotlib.use('agg')
import misaka as m
import os
import pandas as pd
import statistics
import seaborn as sns

from argparse import ArgumentParser
from datetime import datetime, timedelta
from operator import itemgetter
from yahoofinancials import YahooFinancials

# Constants ====================================================================

TICKERS = ['SPY', 'TLT', 'VSS', 'SCZ']
BONDS = 'TLT'
REPORT = """Date
====
{date}

Prices
======
![prices plot](prices.svg)

One Month Returns
=================
| Ticker | Return |
| ------ | ------ |
{returns}

Signals
=======
| Ticker | Signal |
| ------ | ------ |
{signals}

Strategy
========
{strategy}
"""

# Functions ====================================================================

def download_historical_price_data(date, *tickers, freq: str = 'monthly'):
    yahoo_financials = YahooFinancials(tickers)
    if freq == 'monthly':
        days = 217
    elif freq == 'weekly':
        days = 186

    def download_into_dfs(buffer=0):
        historical_price_data = yahoo_financials.get_historical_price_data(
            (date - timedelta(days=days)).strftime('%Y-%m-%d'),
            (date + timedelta(days=buffer)).strftime('%Y-%m-%d'),
            freq
        )
        return [
            pd.DataFrame(historical_price_data[ticker]['prices'][::-1])
            for ticker in tickers
        ]

    dfs = download_into_dfs()
    if freq == 'monthly':
        buffer = 0
        while date.strftime('%Y-%m-01') not in set(dfs[0].formatted_date):
            buffer += 1
            dfs = download_into_dfs(buffer=buffer)
    elif freq == 'weekly':
        dfs = [df.drop(0) for df in dfs]
    return {
        ticker: (
            df[
                [
                    d.endswith('-01') if freq == 'monthly' else True
                    for d in df.formatted_date
                ]
            ]
            .reset_index().dropna()
        )
        for ticker, df in zip(tickers, dfs)
    }

def plot_prices(historical_price_data: dict, file_name: str):
    hpd = pd.concat(
        df.assign(
            normalized_adjclose=df.adjclose / df.adjclose.iloc[-1],
            ticker=[ticker] * len(df.index)
        )[['formatted_date', 'normalized_adjclose', 'ticker']]
        for ticker, df in reversed(tuple(historical_price_data.items()))
    )
    ax = sns.lineplot(
        x='formatted_date',
        y='normalized_adjclose',
        hue='ticker',
        data=hpd[::-1]
    )
    ax.set_xlabel('')
    ax.set_ylabel(f'adjclose relative to {hpd.formatted_date.iloc[-1]}')
    ax.set_xticklabels(labels=hpd.formatted_date[::-1], rotation=30, ha='right')
    if len(ax.get_xticklabels()) > 8:
        for ind, label in enumerate(ax.get_xticklabels()):
            if ind % 4 == 0:
                label.set_visible(True)
            else:
                label.set_visible(False)
    fig = ax.get_figure()
    fig.tight_layout()
    fig.savefig(file_name, format='svg')
    fig.clf()

def compute_one_month_return(df, freq: str = 'monthly'):
    if freq == 'monthly':
        return df.adjclose[0] / df.adjclose[1] - 1
    elif freq == 'weekly':
        return (
            df.adjclose[0] / df.adjclose[4] - 1
        )

def compute_one_month_returns(
    historical_price_data: dict,
    freq: str = 'monthly'
):
    return {
        ticker: compute_one_month_return(df, freq=freq)
        for ticker, df in historical_price_data.items()
    }

def compute_signal(df, freq: str = 'monthly'):
    if freq == 'monthly':
        return sum(df.adjclose[0] / df.adjclose[x] - 1 for x in (1, 3, 6))
    elif freq == 'weekly':
        current_month_price = df.adjclose[0]
        return sum(
            current_month_price / df.adjclose[x] - 1
            for x in (4, 12, 24)
        )

def compute_signals(historical_price_data: dict, freq: str = 'monthly'):
    return {
        ticker: compute_signal(df, freq=freq)
        for ticker, df in historical_price_data.items()
    }

def decide_strategy(signals: dict, bonds: str = BONDS):
    signals_sans_bonds = dict((t, s) for t, s in signals.items() if t != bonds)
    if any(s > 0 for s in signals_sans_bonds.values()):
        choice = max(signals_sans_bonds.items(), key=itemgetter(1))[0]
    else:
        choice = bonds
    return f'Buy/Hold {choice}'

def generate_report(date, returns: dict, signals: dict, strategy: str):
    return REPORT.format(
        date=date.strftime('%Y-%m-%d'),
        returns='\n'.join(
            f'|    {t} | {s*100:.3}% |' for t, s in returns.items()
        ),
        signals='\n'.join(
            f'|    {t} | {s*100:.4}% |' for t, s in signals.items()
        ),
        strategy=strategy
    )

def parse_arguments():
    parser = ArgumentParser(description='Accelerated Dual Momentum')
    parser.add_argument(
        'report_dir',
        metavar='<path/to/report/dir/>',
        nargs='?',
        help='write a HTML report'
    )
    parser.add_argument(
        '--date',
        metavar='<yyyy-mm-dd>',
        default=datetime.today().strftime('%Y-%m-%d'),
        help=f'date of interest (default: today)'
    )
    parser.add_argument(
        '--tickers',
        metavar='<TIC>',
        nargs='+',
        default=TICKERS,
        help=f"tickers to use (default: {' '.join(TICKERS)})"
    )
    parser.add_argument(
        '--bonds',
        metavar='<TIC>',
        default=BONDS,
        help=f"ticker representing bonds (default: {BONDS})"
    )
    parser.add_argument(
        '--frequency',
        choices=('monthly', 'weekly'),
        default='monthly',
        help='frequency of data to fetch (default: monthly)'
    )
    return parser.parse_args()

def main():
    args = parse_arguments()
    date = datetime.strptime(args.date, '%Y-%m-%d')
    if date > datetime.today():
        raise RuntimeError(
            "I can't predict the future! Choose an earlier date."
        )
    historical_price_data = download_historical_price_data(
        date, *args.tickers, freq=args.frequency
    )
    returns = compute_one_month_returns(
        historical_price_data, freq=args.frequency
    )
    signals = compute_signals(historical_price_data, freq=args.frequency)
    strategy = decide_strategy(signals, bonds=args.bonds)
    report_text = generate_report(date, returns, signals, strategy)
    print(report_text, end='')
    if args.report_dir:
        if not os.path.isdir(args.report_dir):
            os.mkdir(args.report_dir)
        for ticker, df in historical_price_data.items():
            df.to_csv(
                os.path.join(args.report_dir, f'{ticker}.csv'),
                index=False
            )
        plot_prices(
            historical_price_data,
            os.path.join(args.report_dir, 'prices.svg')
        )
        with open(os.path.join(args.report_dir, 'acdumo.html'), 'w') as f:
            f.write(m.html(report_text, extensions=['tables']))

# Execute ======================================================================

if __name__ == '__main__':
    main()


import pytest

from vexl import parse
from vexl.visitors import EvaluationVisitor

cases = [
    # Null
    ["null", None],
    # Booleans
    ["false", False],
    ["true", True],
    # Numbers
    ["0", 0],
    ["1", 1],
    ["3.14", 3.14],
    ["0.6", 0.6],
    ["42.0", 42],
    ["999999999", 999999999],
    # Identifiers
    ["a", None],
    ["word", None],
    ["b6_a4_c3", None],
    # Unary operators
    ["not null", None],
    ["not false", True],
    ["not true", False],
    ["not not null", None],
    ["not not false", False],
    ["not not true", True],
    # Boolean operators
    ["1 and 2", 2],
    ["1 and 2 and 3", 3],
    ["1 or 2", 1],
    ["1 or 2 or 3", 1],
    ["not 1 and 2", False],
    ["not 1 or 2", 2],
    ["1 and not 2", False],
    ["1 or not 2", 1],
    ["1 and 2 or 3", 2],
    ["1 or 2 and 3", 1],
    ["(1 or 2) and 3", 3],
    # Binary operators
]

@pytest.mark.parametrize("expr,value", cases)
def test_evaluate(expr, value):
    assert EvaluationVisitor({}).visit(parse(expr)) == value


# from concurrent.futures import thread

import os

try:
   from time import sleep
   import discord
   from discord.ext import commands
   import requests
   from datetime import datetime

   intents = discord.Intents.default() #Create an instance of a Client. This client is our connection to Discord.
   intents.message_content = True
   bot = commands.Bot(command_prefix="#",intents=intents)

   def giveMeTime(letter):
      req = requests.get("https://timeapi.io/api/Time/current/zone?timeZone=Asia/Riyadh").json()
      return req[letter]

   @bot.event
   async def on_ready():
      await times()

   def athkar(letter):
      thkr=requests.get(f"https://azkar-api.nawafhq.repl.co/zekr?{letter}&json").json()
      x=str(thkr["content"])
      return x

   async def sleepp(): # اذكار قبل النوم
      channel = bot.get_channel(channelId)
      thkr=athkar("bs")
      await channel.send(thkr)
      channel = bot.get_channel(channelId)
      await channel.send(thkr)

   async def Massa():
      channel = bot.get_channel(channelId)
      thkr=athkar("e")
      await channel.send(thkr)
      channel = bot.get_channel(channelId)
      await channel.send(thkr)

   async def Morning():
      print("in morning")
      channel = bot.get_channel(channelId)
      thkr=athkar("m")
      await channel.send(thkr)
      channel = bot.get_channel(channelId)
      await channel.send(thkr)

   async def Pray(h,m,p):

      channel = bot.get_channel(channelId)
      if(h==4 and m==00 and p=="AM"):
         await channel.send("حان وقت صلاة الفجر")
         sleep(900)
         await channel.send(athkar("as"))

      elif(h==11 and m==45 and p=="AM"):
         await channel.send("حان وقت صلاة الظهر")
         sleep(900)
         await channel.send(athkar("as"))

      elif(h==15 and m==15 and  p=="PM"):
         await channel.send("حان وقت صلاة العصر")
         sleep(900)
         await channel.send(athkar("as"))

      elif(h==18 and m==10 and p=="PM"):
         await channel.send("حان وقت صلاة المغرب")
         sleep(900)
         await channel.send(athkar("as"))

      elif(h==19 and m==40 and p=="PM"):
         await channel.send("حان وقت صلاة العشاء")
         sleep(900)
         await channel.send(athkar("as"))

   async def times():
      s=00
      print("I'M in times")
      try:
         while True:

          h=giveMeTime("hour")
          m=giveMeTime("minute")

          if(h>=0 and h<=11):

           p="AM"
          else:
            p="PM"

          print(h,m,s,p)
          if (h == 6 and m == 14 and p=="AM"):
            await Morning()
            sleep(60)

          elif h == 23 and m ==35 and p == 'PM':
            await sleepp()
            sleep(60)

          elif((h==4 and m==00 and p=="AM") or(h==11 and m==45 and p=="AM")or (h==15 and m==15 and p=="PM") or (h==18 and m==10 and p=="PM") or (h==19 and m==40 and p=="PM")):
            await Pray(h,m,p)

            sleep(60)
          elif h == 21 and m == 35 and p == 'PM':
            await sleepp()
            sleep(60)
          elif h == 16 and m == 30 and p == 'PM':
            await Massa()
            sleep(60)
          sleep(20)

      except Exception as e:
       async def alert():
         channel = bot.get_channel(channelId)
         await channel.send(str(e))
       await alert()

except:
   os.system("pip install discord")
   os.system("pip install requests")
   os.system("pip install time")

bot.run("Token")


import random
import typing
from collections import Counter
from pathlib import Path
import numpy as np
import pandas as pd
import torch
import pickle
from tqdm import tqdm
from torch.utils.data import Dataset
from torchtext.vocab import vocab
from torchtext.data.utils import get_tokenizer
from gensim.models import Word2Vec
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
class TULDataset(Dataset):
    TOKENIZED_SEQ = 'Tokenized_seq'
    ORIGINAL_SEQ = 'Original_seq'
    USER_LABEL = 'User'
    ORIGINAL_LENGTH = 'Original_seq_length'

    def __init__(self, path, word_dim=256):

        self.ds: pd.Series = pd.read_csv(path)
        self.tokenizer = get_tokenizer(None)
        self.counter = Counter()
        self.vocab = None
        self.max_length = 0
        self.num_users = 0
        self.columns = [self.ORIGINAL_SEQ, self.TOKENIZED_SEQ ,self.USER_LABEL , self.ORIGINAL_LENGTH]
        self.pad_value = 0
        self.word_dim = word_dim
        self.df = self.prepare_dataset()

    def __len__(self):
        return len(self.df)

    def __getitem__(self, idx):
        item = self.df.iloc[idx]
        inp = torch.Tensor(item[self.TOKENIZED_SEQ]).long()
        user_label = torch.Tensor([item[self.USER_LABEL]]).long()
        un_padded_length = torch.Tensor([item[self.ORIGINAL_LENGTH]]).long()
        return (
            inp.to(device),
            user_label.to(device),
            un_padded_length.to(device)
            )

    def prepare_dataset(self) -> pd.DataFrame:
        output=[]
        sentences=[]
        length=[]
        users=set()
        for i in self.ds['poi']:
          sentences.append(i.split())
          length.append(len(i.split()))
        for i in self.ds['user']:
          users.add(i)
        self.num_users = len(users)
        self.max_length = max(length)
        print("Create vocabulary From word2vec model")
        model = Word2Vec(sentences=sentences, window=10, min_count=1, workers=4, size=self.word_dim)
        self._fill_vocab_pre_trained(model)
        print("Done !!")
        print("Preprocessing dataset")
        for index, row in tqdm(self.ds.iterrows(), total=self.ds.shape[0]):
            seq = self.tokenizer(row['poi'])
            output.append(self._create_item(seq,row["user"]))
        #print(output)
        df = pd.DataFrame(output, columns=self.columns)
        return df

    def _fill_vocab_pre_trained(self , model):
        self.vocab = vocab({}, min_freq=1)
        for i in model.wv.index2entity:
          self.vocab.insert_token(i, len(self.vocab))
        self.pad_value = len(self.vocab)

    def _create_item(self, seq: typing.List[str], user_label:int):
        # We tokenize the input, converting seq into an array of Integers
        original_seq = seq
        original_len = len(seq)
        tokenized_seq = self.vocab.lookup_indices(seq.copy())
        s = tokenized_seq + [self.pad_value] * (self.max_length - original_len)
        return (original_seq,s,user_label,original_len)


from pathlib import Path
import pickle
import re

from matplotlib import cm, lines, pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from utils import sort_file_with_tSiz

window=3

def get_paths():
    paths = list(Path("results/travel_dist/pickle").glob(f"BM{bm_size}_*{agent}*a0.1*g0.9*greedy0.1*.pickle"))
    paths = sort_file_with_tSiz(paths)
    pathr = list(Path("results/travel_dist/pickle").glob(f"BM{bm_size}_*RandomAgent*a0.1*g0.9*greedy0.1*.pickle"))
    return paths, pathr

def out_dist(pash):
    f_distr, r_distr = distribution(pash)
    d = pd.DataFrame({"first":f_distr, "last":r_distr})
    d.to_csv(f"{agent}_BM{bm_size}_first_last.csv")

def distribution(path):
    with open(path, "rb") as f:
        travel_dists = pickle.load(f)
    first_distr = []
    for travel_dist in travel_dists:
        first_distr += travel_dist[:window]
    last_distr = []
    for travel_dist in travel_dists:
        last_distr += travel_dist[-window:]
    return first_distr, last_distr

# plot histograms of first and last 5 trials, x is lo
def plot():
    paths, pathr = get_paths()
    fig, ax = plt.subplots(1, 1, figsize=(10, 5))
    # カラーマップから色を取得
    n_colors = len(paths)
    colors = cm.jet(np.linspace(0, 1, n_colors))
    for i, path in enumerate(paths):
        first_distr, last_distr = distribution(path)
        table_width = int(re.search(r'table_width_(\d*)\D', path.name).group(1))
        sns.kdeplot(first_distr, color=colors[i], linestyle="--", bw_adjust=0.5, log_scale=True)
        sns.kdeplot(last_distr, label=f'tSiz={table_width}', color=colors[i], bw_adjust=0.5, log_scale=True)
        print("plotting", path.name)
    first_distr_r, last_distr_r = distribution(pathr[0])
    sns.kdeplot(first_distr_r, color="gray",label='Random', bw_adjust=0.5, log_scale=True)
    print("plotting", pathr[0].name)

    # カスタム凡例エントリの作成
    legend_line_a = lines.Line2D([], [], color='black', marker='', linestyle='--', label=f'First {window} trials')
    legend_line_b = lines.Line2D([], [], color='black', marker='', linestyle='-', label=f'Last {window} trials')

    # 既存のプロットから凡例エントリを取得
    existing_legend = plt.gca().get_legend_handles_labels()

    # 既存の凡例エントリにカスタムエントリを追加
    handles, labels = existing_legend
    handles.extend([legend_line_a, legend_line_b])
    labels.extend([f'First {window} trials', f'Last {window} trials'])

    # 凡例をプロットに追加
    plt.legend(handles=handles, labels=labels)
    ax.set_xlabel("travel distance")
    ax.set_ylabel("frequency")
    ax.set_title(f"BM{bm_size} {agent}")
    plt.savefig(f"{save_path}")
    print(f"{save_path} saved")

for agent in ["Q", "SR"]:
    for bm_size in [1,3,5,10,20]:
        save_path = Path(f"results2/kde/{agent}_BM{bm_size}.png")
        save_path.parent.mkdir(parents=True, exist_ok=True)
        plot()