# author : Arief Anbiya (anbarief@live.com)
# Algorithm for the visualization

import numpy
sqrt = numpy.sqrt
cos = numpy.cos
sin = numpy.sin
pi =  numpy.pi
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
from matplotlib import colors as mplcolors
from statistweepy import utils
from statistweepy import models
import operator
import itertools

def rbar(ax, radii, radius, col, label, bar_width = 5, text_size = 7):

   n = len(radii)
   indexs = range(n)

   x = [-radius+i*0.01 for i in range(2*100*radius + 1)]
   y_upper = [sqrt(radius**2 - i**2) for i in x]
   y_lower = [-y for y in y_upper]

   ax.plot(x, y_upper, color = 'k')
   ax.plot(x, y_lower, color = 'k')

   dr = float(1)/float(n)

   indicator = 1

   for index in indexs:

      rx = (radius+((1/50)*radius)*radii[index])*cos(dr*index*2*pi)
      ry = (radius+((1/50)*radius)*radii[index])*sin(dr*index*2*pi)

      cx = radius*cos(dr*index*2*pi)
      cy = radius*sin(dr*index*2*pi)

      ax.plot([cx, rx], \
              [cy, ry], \
              lw = bar_width, color = col[index])

      degree = (dr*index*2*pi)*360/(2*pi)

      ha_split = 'right'
      ha_text = 'left'
      degree_split = degree
      degree_freq = degree

      if 90 < degree < 270 :

         degree_split = -(180-degree)
         ha_split = 'left'

      split = label[index]

      Rx = (radius+((1/50)*radius)*max(radii))*cos(dr*index*2*pi)
      Ry = (radius+((1/50)*radius)*max(radii))*sin(dr*index*2*pi)

      text = ax.text(1.5*Rx, 1.5*Ry, split+' ('+str(radii[index])+')', ha = ha_split, va = 'center', rotation = degree_split, rotation_mode = 'anchor', size = text_size)

      ax.plot([rx, 1.5*Rx], \
              [ry, 1.5*Ry], \
              lw = 0.5, color = 'gray')

   ax.set_aspect('equal')

def hbar_plot_Authors(model, ax, meas = 'followers_count', color = (0,0,1,1), incolor_meas = None, space = True, text_sizes = [7, 12], freq_lim = None):

    if not isinstance(model, models.Authors):
       raise TypeError("model must be a statistweepy.models.Authors object.")

    width = 5

    if freq_lim != None:
       dic = getattr(model, meas)
       meas_dic = {key: dic[key] for key in dic.keys() if freq_lim[0] <= dic[key] <= freq_lim[1]}
    else:
       meas_dic = getattr(model, meas)

    sorted_authors = sorted(meas_dic, key = lambda x : meas_dic[x])

    if isinstance(color, str):

        color = mplcolors.hex2color(mplcolors.cnames[color])

    colors = len(meas_dic)*[color]
    if incolor_meas != None:

        icdic = getattr(model, incolor_meas)
        meas_icdic = {key: icdic[key] for key in meas_dic.keys()}
        minor_max = max(meas_icdic.values())
        transparency = [meas_icdic[author]/minor_max for author in sorted_authors]
        colors = [(color[0], color[1], color[2], trans) for trans in transparency]

    if space:
        space = width
    else:
        space = 0

    var = ( 1 +  i*(space + width)  for i in range(len(meas_dic)) )

    bar_pos = []; ytick_pos = []

    for i in var:

        bar_pos.append(i - width/2)
        ytick_pos.append(i)

    ax.barh(bar_pos, [meas_dic[author] for author in sorted_authors], height = width, color = colors)
    ax.set_yticks(ytick_pos)
    helvetica = fm.FontProperties(fname = '/fonts/HelveticaNeueBold.ttf', size = text_sizes[0])
    ax.set_yticklabels(sorted_authors, rotation = 'horizontal', fontproperties = helvetica)

    ax.set_ylim([1 - width/2 - space, max(ytick_pos) + width/2 + space])

    if incolor_meas != None:

        ax.set_xlabel(meas + ' (color : '+incolor_meas+')', size = text_sizes[1])

    else :

        ax.set_xlabel(meas, size = text_sizes[1])

    plt.tight_layout()

def hbar2sided_plot_Authors(model, ax, meas_left = 'followers_count', meas_right = 'following_count', colors = ['red', 'blue'], incolor_meas = None, space = True, text_sizes = [7, 10], aux_size = 5, freq_lim = (None, None)):

    if not isinstance(model, models.Authors):
       raise TypeError("model must be a statistweepy.models.Authors object.")

    width = 5

    if freq_lim[0] != None:
       leftdic = getattr(model, meas_left)
       rightdic = getattr(model, meas_right)
       if freq_lim[0] == 'left':
          meas_leftdic = {key: leftdic[key] for key in leftdic.keys() if freq_lim[1][0] <= leftdic[key] <= freq_lim[1][1]}
          meas_rightdic = {key: rightdic[key] for key in meas_leftdic.keys()}
       elif freq_lim[0] == 'right':
          meas_rightdic = {key: rightdic[key] for key in rightdic.keys() if freq_lim[1][0] <= rightdic[key] <= freq_lim[1][1]}
          meas_leftdic = {key: leftdic[key] for key in meas_rightdic.keys()}
    else:
       meas_leftdic = getattr(model, meas_left)
       meas_rightdic = getattr(model, meas_right)

    if isinstance(colors[0], str):
        color_left = mplcolors.hex2color(mplcolors.cnames[colors[0]])

    if isinstance(colors[1], str):
        color_right = mplcolors.hex2color(mplcolors.cnames[colors[1]])

    colors_left = len(meas_leftdic)*[color_left]
    colors_right = len(colors_left)*[color_right]

    if incolor_meas != None:
        dic = getattr(model, incolor_meas)
        if freq_lim[0] != None:
           incolor = {key : dic[key] for key in meas_leftdic}
        else:
           incolor = dic
        minor_max = max(incolor.values())
        trans_both = [incolor[author]/minor_max for author in meas_leftdic.keys()]
        colors_left = [(color_left[0], color_left[1], color_left[2], trans) for trans in trans_both]
        colors_right = [(color_right[0], color_right[1], color_right[2], trans) for trans in trans_both]

    if space:
        space = width
    else:
        space = 0

    var = ( 1 +  i*(space + width)  for i in range(len(colors_left)) )

    bar_pos = []; ytick_pos = []

    for i in var:

        bar_pos.append(i - width/2)
        ytick_pos.append(i)

    left_values = [-meas_leftdic[author] for author in meas_leftdic.keys()]
    right_values = [meas_rightdic[author] for author in meas_leftdic.keys()]
    ax.barh(bar_pos, left_values, left = -1, height = width, color = colors_left)
    ax.barh(bar_pos, right_values, left = 1, height = width, color = colors_right)

    xlim = ax.get_xlim()
    ax.set_yticks(ytick_pos)
    for i in ytick_pos:
       ax.plot([xlim[0], 0], [i, i], '--', color = 'gray', linewidth = aux_size)

    helvetica = fm.FontProperties(fname = '/fonts/HelveticaNeueBold.ttf', size = text_sizes[0])
    ax.set_yticklabels(meas_leftdic.keys(), rotation = 'horizontal', fontproperties = helvetica)

    ax.set_xticks([min(left_values), max(right_values)])
    ax.set_xticklabels([str(-min(left_values)), str(max(right_values))])

    ax.set_ylim([1 - width/2 - space, max(ytick_pos) + width/2 + space])

    if incolor_meas != None:

        ax.set_xlabel('left side : {}, right side : {}, (color : {})'.format(meas_left, meas_right, incolor_meas), size = text_sizes[1])

    else :

        ax.set_xlabel('left side : {}, right side : {}'.format(meas_left, meas_right), size = text_sizes[1])

    plt.tight_layout()

def scatter_plot_Authors(model, ax, meas_x='followers_count', meas_y='following_count', incolor_meas = None, marker_size = 10, color = 'blue', text_sizes = [20, 10]):

   if not isinstance(model, models.Authors):
      raise TypeError("model must be a statistweepy.models.Authors")

   if isinstance(color, str):
        color = mplcolors.hex2color(mplcolors.cnames[color])

   meas_xdic = getattr(model, meas_x)
   meas_ydic = getattr(model, meas_y)

   authors = model.authors_tweets.keys()
   helvetica = fm.FontProperties(fname = '/fonts/HelveticaNeueBold.ttf', size = text_sizes[0])

   if incolor_meas != None:
      meas_icdic = getattr(model, incolor_meas)
      icmax = max(meas_icdic.values())

   for author in authors:
      x=meas_xdic[author]
      y=meas_ydic[author]
      if incolor_meas != None:
         trans = meas_icdic[author]/icmax
      else:
         trans = 0.6
      ax.plot(x, y, 'o', markersize = marker_size, color = [color[0], color[1], color[2], trans], markeredgecolor = 'black', markeredgewidth = 1)
      ax.text(x, y, author, fontproperties = helvetica)
   ax.margins(0.2)

   ax.set_xlabel(meas_x, size = text_sizes[1])
   ax.set_ylabel(meas_y, size = text_sizes[1])

   plt.tight_layout()

def hbar_plot_Splits(model, ax, adjustment = None, color = (0, 0.6, 1, 1), incolor = None, space = True, text_sizes = [7, 12]):

    if not isinstance(model, models.Splits):
       raise TypeError("model must be a statistweepy.models.Splits object.")

    width = 5

    if adjustment != None:

        model.apply_adjustment(adjustment)
        splits_freq = model.adjusted['splits_freq']
        sorted_splits_by_freq = model.adjusted['sorted_splits_by_freq']

    else:

        splits_freq = model.splits_freq
        sorted_splits_by_freq = model.sorted_splits_by_freq

    if isinstance(color, str):

        color = mplcolors.hex2color(mplcolors.cnames[color])

    if incolor == 'retweets':

        sorted_total_rts = [utils.total_rts(model.tweets, string_inclusion = split, naive = model.naive) for split in sorted_splits_by_freq]
        max_total_rts = max(sorted_total_rts)
        col = [(color[0], color[1], color[2], total_rts/max_total_rts) for total_rts in sorted_total_rts]

    elif incolor == 'likes':

        sorted_total_likes = [utils.total_likes(model.tweets, string_inclusion = split, naive = model.naive) for split in sorted_splits_by_freq]
        max_total_likes = max(sorted_total_likes)
        col = [(color[0], color[1], color[2], total_likes/max_total_likes) for total_likes in sorted_total_likes]

    else:

        col = color

    if space:

        space = width

    else:

        space = 0

    var = ( 1 +  i*(space + width)  for i in range(len(splits_freq)) )

    bar_pos = []; ytick_pos = []

    for i in var:

        bar_pos.append(i - width/2)
        ytick_pos.append(i)

    ax.barh(bar_pos, [splits_freq[split] for split in sorted_splits_by_freq], height = width, color = col)

    ax.set_yticks(ytick_pos)
    helvetica = fm.FontProperties(fname = '/fonts/HelveticaNeueBold.ttf', size = text_sizes[0])
    ax.set_yticklabels(sorted_splits_by_freq, rotation = 'horizontal', fontproperties = helvetica)
    ax.set_ylim([1 - width/2 - space, max(ytick_pos) + width/2 + space])

    if incolor == 'retweets':

        ax.set_xlabel('Splits frequency count. (color : total retweets)', size = text_sizes[1])

    elif incolor == 'likes':

        ax.set_xlabel('Splits frequency count. (color : total likes)', size = text_sizes[1])

    else :

        ax.set_xlabel('Splits frequency count.')

    plt.tight_layout()

def rbar_plot_Splits(model, ax, adjustment = None, base_radius = 50, bar_width = 6, color = (0, 0, 1, 1), text_sizes = [7, 12]):

    if not isinstance(model, models.Splits):
       raise TypeError("model must be a statistweepy.models.Splits")

    if adjustment != None:

        model.apply_adjustment(adjustment)
        splits_freq = model.adjusted['splits_freq']
        sorted_splits_by_freq = model.adjusted['sorted_splits_by_freq']

    else:

        splits_freq = model.splits_freq
        sorted_splits_by_freq = model.sorted_splits_by_freq

    if isinstance(color, str):

        color = mplcolors.hex2color(mplcolors.cnames[color])

    colors = [color]*len(splits_freq)

    rbar(ax, [splits_freq[sorted_splits_by_freq[index]] for index in range(len(splits_freq))], \
                  base_radius, col = colors, \
                  label = sorted_splits_by_freq, bar_width = bar_width, text_size = text_sizes[0])

    ax.tick_params(colors=(0,0,0,0))

    ax.set_xlabel('Splits frequency count.', size = text_sizes[1])

    plt.tight_layout()

def time_distribution(model, ax, unit = 'hour', **kwargs):

   if not isinstance(model, models.Tweets):
      raise TypeError(' model must be of statistweepy.models.Tweets ')

   if not any([unit == 'year', unit == 'month', unit == 'day', unit == 'hour']):
      raise AssertionError(' The argument unit must be one of \'year\',  \'month\',  \'day\', or \'hour\'. ')

   unicity_key = operator.attrgetter('created_at.'+unit)
   tweets = sorted(model, key=unicity_key)
   distribution = {}

   for time, time_tweets in itertools.groupby(tweets, key=unicity_key):

      time_tweets = list(time_tweets)
      distribution[time] = len(time_tweets)
      fy = lambda x: x

   if unit == 'year':

      xt = [tweet.created_at.year + (tweet.created_at.month + (tweet.created_at.day + tweet.created_at.hour/24)/31)/12 \
            for tweet in model]

      a = int(min(xt))
      b = int(max(xt))
      ticks = range(a-1, b+2)

   elif unit == 'month':

      xt = [(tweet.created_at.month + (tweet.created_at.day + tweet.created_at.hour/24)/31) \
            for tweet in model]

      ticks = range(1, 13)

   elif unit == 'day':

      xt = [(tweet.created_at.day + tweet.created_at.hour/24) \
            for tweet in model]

      ticks = range(1, 32)

   elif unit == 'hour':

      xt = [tweet.created_at.hour + (tweet.created_at.minute + tweet.created_at.second/60)/60 \
            for tweet in model]

      ticks = range(0, 25)

   histogram = ax.hist(xt, **kwargs)
   ax.set_xticks(ticks)
   ax.set_xticklabels([str(i) for i in ticks])
   ax.set_xlabel(unit)
   ax.set_ylabel('frequency')

   return distribution, histogram


from sklearn.metrics import precision_recall_fscore_support
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
import numpy as np
import itertools

#file path predicted labels
file_path_pred = '/home/kenelly/workspaces/emotionnewsheadlines/emotiondetection/resources/model_unsupervised_ml_output.txt'

#file path true labels
file_path_true = '/home/kenelly/workspaces/emotionnewsheadlines/emotiondetection/resources/semeval2007testunsupervisedml.txt'

#function to collect the predicted and true labels from the files
def emotion_pred(file_path_pred):
    with open(file_path_pred) as f:
        emotion_list_pred = list()
        for line in f:
            line_vec = line.split(" ")
            emotion = line_vec[0].replace("__label__", "")
            emotion_list_pred.append(emotion)
        return emotion_list_pred

#function to collect the predicted and true labels from the files
def emotions(file_path_pred, file_path_true):
    pred = emotion_pred(file_path_pred)#src/python/evaluationml.py:24
    pred_final = []
    true_final = []
    i=0
    with open(file_path_true) as p:
        for line in p:
            line_vec = line.split(" ")
            for label in line_vec:
                if label.startswith('__label__'):
                    emotion_true = label.replace("__label__", "")
                    true_final.append(emotion_true)
                    pred_final.append(pred[i])
            i+=1

    return pred_final, true_final

y_pred, y_true = emotions(file_path_pred, file_path_true)

#function to calculate multilabel metrics
def metrics(y_true, y_pred):

    precision, recall, f_score, _ = \
        precision_recall_fscore_support(y_true, y_pred, average='macro')

    accuracy = accuracy_score(y_true, y_pred)

    print('Precision:' + str(precision) + '\nRecall:' + str(recall) +
          '\nF1 score:'+ str(f_score) + '\nAccuracy:' + str(accuracy))

#function to calculate the metrics by label
def metrics_labels(y_true, y_pred):

    target_names = ['anger','disgust','fear','joy','sadness','surprise']
    print(classification_report(y_true, y_pred, target_names=target_names))

def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    Normalization can be applied by setting `normalize=True`.
    """
    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
        print("Normalized confusion matrix")
    else:
        print('Confusion matrix')

    print(cm)

    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    fmt = '17f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.ylabel('True label')
    plt.xlabel('Predicted label')
    plt.tight_layout()

target_names = ['anger','disgust','fear','joy','sadness','surprise']
metrics(y_true, y_pred)
metrics_labels(y_true, y_pred)
confusionmatrix = confusion_matrix(y_true, y_pred, labels=['anger','disgust','fear','joy','sadness','surprise'])
np.set_printoptions(precision=2)
plt.figure()
plot_confusion_matrix(confusionmatrix, classes=target_names,
                      title='Confusion matrix multi-label')
plt.show()
print(confusionmatrix)


from scapy.all import *
from scapy.contrib.pnio_rpc import *
from scapy.contrib.dce_rpc import *
from scapy.contrib.pnio import *

import crcmod

from helper.gsdml_parser import XMLDevice

load_contrib("pnio")
load_contrib("pnio_rpc")
load_contrib("dce_rpc")

class PNIOPSMessage:
    def __init__(self) -> None:
        self.cycle_counter = 0
        self.data_status = {
            "ignore": False,  # 1: Ignore 0: Evaluate
            "reserved_2": False,  # should be zero
            "station_problem_indicator": False,  # 1: Ok, 0: Problem
            "provider_state": False,  # 1: Run 0: Stop
            "reserved_1": False,  # should be zero
            "data_valid": False,  # 1: Valid, 0: Invalid
            "redundancy": False,  # has no meaning for outputCRs
            "state": False,  # 1: primary, 0. backup
        }
        self.input_data = {"iops": [], "iocs": [], "data": []}

    def convert_number_to_state_array(self, flags):
        self.data_status = {
            "ignore": flags.ignore,  # 1: Ignore 0: Evaluate
            "reserved_2": flags.reserved_2,  # should be zero
            "station_problem_indicator": flags.no_problem,  # 1: Ok, 0: Problem
            "provider_state": flags.run,  # 1: Run 0: Stop
            "reserved_1": flags.reserved_1,  # should be zero
            "data_valid": flags.validData,  # 1: Valid, 0: Invalid
            "redundancy": flags.redundancy,  # has no meaning for outputCRs
            "state": flags.primary,  # 1: primary, 0. backup
        }

    def bitarray_to_number(self, array):
        i = 0
        for bit in array:
            i = (i << 1) | bit
        return i

    def parse_io_state(self, state, slot, subslot):
        status_array = [int(digit) for digit in bin(state + 0x100)[2:]][1:]
        return {
            "module": str(slot),
            "submodule": str(subslot),
            "data_state": bool(status_array[0]),  # 1: Good 0: Bad
            "instance": self.bitarray_to_number(
                status_array[1:3]
            ),  # should be zero 0: Detected by subslot
            "reserved": self.bitarray_to_number(status_array[3:7]),  # should be zero
            "extension": bool(
                status_array[7]
            ),  # 0: No IOxS octet follows 1: IOxS octet follows
        }

    def parse_input_data(self, data, device):
        usable_modules = device.body.dap_list[0].usable_modules

        payload_bytes = list(data)

        first_iops = self.parse_io_state(payload_bytes[0], 0x1, 0x1)

        sec_iops = self.parse_io_state(payload_bytes[1], 0x1, 0x8000)

        thir_iops = self.parse_io_state(payload_bytes[2], 0x1, 0x8001)

        iops = [first_iops, sec_iops, thir_iops]
        iocs = []
        data = []

        output_frame_offset = 3

        for module in usable_modules:
            if module.used_in_slots != "" and module.output_length != 0:
                data.append(
                    payload_bytes[
                        output_frame_offset : (
                            output_frame_offset + module.output_length
                        )
                    ]
                )
                iops.append(
                    self.parse_io_state(
                        payload_bytes[output_frame_offset + module.output_length],
                        module.module_ident_number,
                        module.submodule_ident_number,
                    )
                )
                output_frame_offset += module.output_length + 1
        for module in usable_modules:
            if module.used_in_slots != "" and module.input_length != 0:
                iocs.append(
                    self.parse_io_state(
                        payload_bytes[output_frame_offset],
                        module.module_ident_number,
                        module.submodule_ident_number,
                    )
                )
                output_frame_offset += 1

        self.input_data = {"iops": iops, "iocs": iocs, "data": data}

def parse_data_message(packet, device):
    message = PNIOPSMessage()

    if packet.haslayer("PROFINET IO Real Time Cyclic Default Raw Data"):
        pkt_rt = packet.getlayer("PROFINET Real-Time")
        pkt_raw_layer = packet.getlayer("PROFINET IO Real Time Cyclic Default Raw Data")
        message.convert_number_to_state_array(pkt_rt.dataStatus)
        message.cycle_counter = pkt_rt.cycleCounter
        message.parse_input_data(pkt_raw_layer.data, device)

        return message

    else:
        return

    # CRC24bit

crc24_func = crcmod.mkCrcFun(0x15D6DCB, initCrc=0, xorOut=0x0, rev=False)
scapy_cap = rdpcap("../sniff/only_status_msgs.pcap")
device = XMLDevice("../gsdml/test_project.xml")

for idx, packet in enumerate(scapy_cap[4:]):
    pdu = bytearray(parse_data_message(packet, device).input_data["data"][0])
    print(pdu[0:9])
    print(hex(int.from_bytes(bytes(pdu[9:]), "little")))
    # checksum should be #3345b6
    def someWork():
        for i1 in range(0, 256):
            for i2 in range(0, 256):
                for i3 in range(0, 256):
                    # print(bytearray([0x0, i1, i2, i3] + list(pdu[0:9])))
                    if (
                        hex(
                            crc24_func(
                                bytearray([0x0, i1, i2, i3] + list(pdu[0:9]))[::-1],
                                crc=0x22FF,
                            )
                        )
                        == hex(int.from_bytes(bytes(pdu[9:]), "big"))
                    ):
                        print(idx)
                        print("vcn", hex(i1), hex(i2), hex(i3))
                        print("_---------------------")
                        return
    someWork()

    # packet.getlayer("PROFINET Real-Time").data.getLayer("PROFINET IO Real Time Cyclic Default Raw Data").show()
    # PROFIsafe(packet.getlayer("PROFINET Real-Time").getLayer("PROFINET IO Real Time Cyclic Default Raw Data")).show()


import pymongo
from app import app
from flask import request, jsonify, render_template, redirect, url_for, flash
import sys
from app.database import tasks_collection, redis_conn
from rq import Queue
from bson import ObjectId
from datetime import datetime
from app.worker import clean_data, transform_data, analyze_data
from app.task import Task

# Priority mapping for filtering tasks by priority
priority_mapping = {
    "low": 0,
    "medium": 1,
    "high": 2
}

# RQ queues
low_priority_queue = Queue('low', connection=redis_conn)
medium_priority_queue = Queue('medium', connection=redis_conn)
high_priority_queue = Queue('high', connection=redis_conn)

# Check RQ queue connection
try:
    queue_length = low_priority_queue.count  # Updated to use low_priority_queue instead of queue
    print(f"Successfully connected to RQ queue. Current queue length: {queue_length}")
except Exception as e:
    print("Error connecting to RQ queue: ", e, file=sys.stderr)
    sys.exit(1)

@app.route('/', methods=['GET'])
def index():
    return render_template('index.html')

@app.route('/tasks/create', methods=['GET', 'POST'])
def create_task():
    if request.method == 'POST':
        task_data = {
            'description': request.form['description'],
            'priority': request.form['priority'],
            'task_type': request.form['task_type'],
        }

        response, status = create_task_with_data(task_data)

        if status == 201:
            task_id = response['task_id']
            flash(f'Task created successfully with ID: {task_id}')
            return redirect(url_for('get_task', task_id=task_id))
        else:
            flash(f'Error creating task: {response["error"]}')
            return render_template('create_task.html'), status
    else:
        return render_template('create_task.html')

# New function to create a task with the given data
def create_task_with_data(data):
    # Validate required fields
    if 'description' not in data or 'priority' not in data or 'task_type' not in data:
        return {'error': 'Missing required fields'}, 400

    # Create a Task object
    new_task = Task(data)
    # Save the task to the database
    new_task.save()

    # Add 'created_at' field with the current timestamp as an ISO-formatted string
    data['created_at'] = datetime.utcnow()
    # Add 'status' field with the default value 'pending'
    data['status'] = 'pending'

    allowed_task_types = ['data_cleaning', 'data_transformation', 'data_analysis']

    if data['task_type'] not in allowed_task_types:
        return {'error': 'Invalid task type'}, 400

    task_id = tasks_collection.insert_one(data).inserted_id

    # Determine target queue based on priority
    priority = priority_mapping.get(data['priority'], 1)  # Default to low priority if not provided

    if priority == 'high':
        target_queue = high_priority_queue
    elif priority == 'medium':
        target_queue = medium_priority_queue
    else:
        target_queue = low_priority_queue

    task_type_to_function = {
        'data_cleaning': clean_data,
        'data_transformation': transform_data,
        'data_analysis': analyze_data,
    }

    worker_function = task_type_to_function[data['task_type']]

    job = target_queue.enqueue_call(
    func=task_type_to_function[data['task_type']],
    args=(str(task_id), data),
    result_ttl=86400
)

    return {'task_id': str(task_id)}, 201

# Function to get the status and result of a task
@app.route('/tasks/<task_id>', methods=['GET'])
def get_task(task_id):
    task = tasks_collection.find_one({'_id': ObjectId(task_id)}, sort=[('priority', -1), ('created_at', 1)])
    if not task:
        flash('Task not found')
        return redirect(url_for('index'))

    return render_template('view_task.html', task=task)

# Route for monitoring task status
@app.route('/tasks/<job_id>/status', methods=['GET'])
def get_task_status(job_id):
    job = None
    for queue in [high_priority_queue, medium_priority_queue, low_priority_queue]:
        job = queue.fetch_job(job_id)
        if job:
            break

    if not job:
        return jsonify({'error': 'Job not found'}), 404

    task_status = job.get_status()
    return render_template('task_status.html', task_id=job_id, status=task_status, result=job.result if task_status == 'finished' else None)

# Function to update the status of a task
@app.route('/tasks/<task_id>/status', methods=['PUT'])
def update_task_status(task_id):
    if request.content_type != 'application/json':
        return 'request Content-Type was not "application/json".', 415

    data = request.get_json()
    new_status = data.get('status', None)
    new_result = data.get('result', None)  # Get the result from the request data
    if new_result:
        update_data['result'] = new_result
        print("New result:", new_result)
    if new_status:
        update_data = {'status': new_status}
        if new_result:
            update_data['result'] = new_result  # Add the result to the update data if it's not None

        tasks_collection.update_one(
            {'_id': ObjectId(task_id)},
            {'$set': update_data}
        )
        return jsonify({'message': 'Task status updated'}), 200
    else:
        return jsonify({'error': 'Invalid status'}), 400

    if new_result:
        update_data['result'] = new_result
        print("New result:", new_result)

@app.route('/tasks/<task_id>/details', methods=['GET'])
def get_task_details(task_id):
    task = tasks_collection.find_one({'_id': ObjectId(task_id)})
    if task:
        response = {
            'task_id': str(task['_id']),
            'description': task['description'],
            'priority': task['priority'],
            'created_at': task.get('created_at', None),  # Use get() to avoid KeyError
            'status': task.get('status', None)  # Use get() to avoid KeyError
        }
        return jsonify(response), 200
    else:
        return jsonify({'error': 'Task not found'}), 404

# Function to retrieve all tasks
@app.route('/tasks/view', methods=['GET'])
def get_all_tasks():
    # Get query parameters for pagination, sorting, and filtering
    page = int(request.args.get('page', 1))
    per_page = int(request.args.get('per_page', 10))
    sort_by = request.args.get('sort_by', 'created_at')
    sort_order = request.args.get('sort_order', 'asc')
    priority = request.args.get('priority', None)
    status = request.args.get('status', None)
    search = request.args.get('search', None)

    tasks_data = get_all_tasks_data(page, per_page, sort_by, sort_order, priority, status, search)

    # Render the tasks_data and pagination data in the template (adapt this to your needs)
    return render_template('tasks_view.html', tasks=tasks_data['tasks'], pagination=tasks_data['pagination'])

def get_all_tasks_data(page, per_page, sort_by, sort_order, priority, status, search):
    # Build filter query based on priority and status
    filter_query = {}
    if priority:
        filter_query['priority'] = priority_mapping.get(priority)
    if status:
        filter_query['status'] = status
    if search:
        filter_query['description'] = {'$regex': search, '$options': 'i'}

    # Build sort query based on sort_by and sort_order
    sort_query = [(sort_by, pymongo.ASCENDING if sort_order == 'asc' else pymongo.DESCENDING)]

    # Retrieve tasks from collection using find(), skip(), limit(), and sort()
    tasks = tasks_collection.find(filter_query).skip((page - 1) * per_page).limit(per_page).sort(sort_query)

    # Get the total number of tasks
    total_tasks = tasks_collection.count_documents(filter_query)

    # Calculate the total number of pages
    total_pages = total_tasks // per_page + (1 if total_tasks % per_page > 0 else 0)

    # Convert tasks to JSON format
    response = {
        'tasks': [],
        'pagination': {
            'current_page': page,
            'total_pages': total_pages,
            'per_page': per_page
        }
    }
    for task in tasks:
        task_obj = Task(task)
        task_obj.id = str(task['_id'])
        response['tasks'].append({
            'task_id': task_obj.id,
            'description': task_obj.input_data.get('description'),
            'priority': task_obj.input_data.get('priority'),
            'created_at': task_obj.created_at,
            'status': task_obj.status,
            'result': task_obj.result
        })

    return response

# Function to delete a task by ID
@app.route('/tasks/<task_id>', methods=['DELETE'])
def delete_task(task_id):
    task = tasks_collection.find_one({'_id': ObjectId(task_id)})
    if task:
        tasks_collection.delete_one({'_id': ObjectId(task_id)})

        # Get the job_id from the task document
        job_id = task.get('job_id', None)  # Use get() to avoid KeyError

        # Cancel the corresponding job in the task queue
        if job_id:
            for queue in [low_priority_queue, medium_priority_queue, high_priority_queue]:
                job = queue.fetch_job(job_id)
                if job:
                    job.cancel()
                    break

        return jsonify({'message': 'Task deleted'}), 200
    else:
        return jsonify({'error': 'Task not found'}), 404

@app.route('/tasks/delete_all', methods=['DELETE'])
def delete_all_tasks():
    # Delete all tasks in the database
    result = tasks_collection.delete_many({})

    # Cancel all jobs in the task queues
    for queue in [low_priority_queue, medium_priority_queue, high_priority_queue]:
        job_ids = [job.id for job in queue.get_jobs()]
        for job_id in job_ids:
            job = queue.fetch_job(job_id)
            if job:
                job.cancel()

    return jsonify({'message': f'{result.deleted_count} tasks deleted'}), 200


import asyncio

from services import file_manager
from services import rcon_manager
from services import permission_manager
from services import db_access
from datetime import datetime, timedelta

config = file_manager.load_config()

def main_check():
    for x in config["servers"]:

        tribe_list = rcon_manager.player_tribe_check(x["address"], x["password"], x["rcon_port"])
        print(tribe_list)
        if tribe_list is None:
            continue

        map_name = rcon_manager.get_server_map(x["address"], x["query_port"])
        if map_name is None:
            continue

        print("checking " + map_name + " on server: " + x["address"], x["query_port"])
        update_users(tribe_list, map_name)

    check_tribe_leave()

def update_users(tribe_list: list, map_name: str):
    timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
    new_members = []

    # update or set database record for user
    connection = db_access.get_connection()
    context = connection.cursor(buffered=True)
    for x in tribe_list:
        # try to find user
        context.execute(f"SELECT * FROM tribes WHERE steam_id = {x[0]} AND tribe_name = '{x[1]}' AND map_name = '{map_name}'")
        record = context.fetchone()

        if record is None:
            # set user record
            context.execute(f"INSERT INTO `tribes`(steam_id,tribe_name,tribe_size,map_name,last_seen)"
                            f" VALUES ({x[0]}, '{x[1]}', {0}, '{map_name}', '{timestamp}')")

            # update tribe size
            new_members.append(x[0])
        else:
            # update last seen
            context.execute(f"UPDATE tribes SET last_seen = '{timestamp}' WHERE steam_id = {x[0]} AND map_name = '{map_name}'")

    context.close()
    connection.commit()
    connection.close()

    members_to_update = []
    # update added members
    for y in new_members:
        tribe_name = get_current_tribe(y, map_name)
        members_to_update.extend(get_tribe_members(tribe_name, map_name))

    for z in members_to_update:

        tribe_name = get_current_tribe(z, map_name)
        calculate_tribe_size(z, tribe_name, map_name)
        max_size = get_max_tribe_size(z)
        print("updating member: " + str(z))
        permission_manager.set_tribe_rank(z, max_size)

# calculate all ranks for tribe based on one new member who joined
def calculate_tribe_size(steam_id: int, tribe_name: str, map_name: str):
    connection = db_access.get_connection()
    context = connection.cursor(buffered=True)

    tribe_size = len(get_tribe_members(tribe_name, map_name))
    tribe_name = get_current_tribe(steam_id, map_name)

    # solo no tribe Check
    # if tribe_name == "":
    #     tribe_size = 1

    context.execute(f"UPDATE tribes SET tribe_size = {tribe_size} WHERE steam_id = {steam_id} AND tribe_name = '{tribe_name}' AND map_name = '{map_name}' AND tribe_name != '[]' ")

    context.close()
    connection.commit()
    connection.close()

def get_tribe_members(tribe_name: str, map_name: str):
    connection = db_access.get_connection()
    context = connection.cursor(buffered=True)

    context.execute(f"SELECT steam_id FROM tribes WHERE tribe_name = '{tribe_name}' AND map_name = '{map_name}'")
    tribe_members = list(sum(context.fetchall(), ()))

    context.close()
    connection.close()
    return tribe_members

def get_current_tribe(steam_id: int, map_name: str):
    connection = db_access.get_connection()
    context = connection.cursor(buffered=True)

    # review
    context.execute(f"SELECT tribe_name FROM tribes WHERE steam_id = {steam_id} AND map_name = '{map_name}' AND last_seen = (SELECT MAX(last_seen) FROM tribes WHERE steam_id = {steam_id} AND map_name = '{map_name}')")
    record = context.fetchone()

    context.close()
    connection.close()
    return record[0]

def check_tribe_leave():
    connection = db_access.get_connection()
    context = connection.cursor(buffered=True)
    time = datetime.utcnow() - timedelta(hours=22)

    context.execute(f"SELECT * FROM tribes WHERE last_seen < '{time}'")
    expired_members = context.fetchall()
    context.execute(f"DELETE FROM tribes WHERE last_seen < '{time}'")

    context.close()
    connection.commit()
    connection.close()

    # recalculate tribe size for all tribes who had members that expired
    for x in expired_members:
        print(x)
        tribe_members = get_tribe_members(x[2], x[4])
        print("found members")
        print(tribe_members)

        for y in tribe_members:
            calculate_tribe_size(y, x[2], x[4])

def get_max_tribe_size(steam_id: str):
    connection = db_access.get_connection()
    context = connection.cursor(buffered=True)
    time = datetime.utcnow() - timedelta(hours=22)

    # exclude solo tribes here
    context.execute(f"SELECT tribe_size FROM tribes WHERE steam_id = '{steam_id}' AND tribe_name != '[]' ")
    record = context.fetchall()

    max_size = 0
    for x in record:
        if x[0] > max_size:
            max_size = x[0]

    context.close()
    connection.commit()
    connection.close()

    return max_size

def get_steam_id(discord_id: str):
    connection = db_access.get_connection()
    context = connection.cursor(buffered=True)

    context.execute(f"SELECT SteamId FROM discordaddonplayers WHERE discid = '{discord_id}'")
    record = context.fetchone()

    if not record:
        context.close()
        connection.commit()
        connection.close()
        return None

    else:
        context.close()
        connection.commit()
        connection.close()
        return record[0]

async def loop_task():
    while True:
        main_check()
        await asyncio.sleep(config["general"]["tribe_check_interval"])

task = asyncio.ensure_future(loop_task())


def findancestors(thing):
	''' specify and object (thing) to traceback,
	returns G wich is an AGraph object
	to plot:
	G.draw('<filename>.svg/jpg/tiff/pdf', prog = 'dot')

	'''
	import pygraphviz as pgv

	mro = thing.__mro__
	base_relations = {}
	base_relations_literal = {}
	for i in mro:
		base_relations[i] = i.__bases__
		base_relations_literal[i.__name__] = [r.__name__ for r in base_relations[i]]
	# draw out main graph

	G = pgv.AGraph()
	G.node_attr['shape']='box'
	G.edge_attr['dir']='backward'
	G.edge_attr['arrowhead'] = 'normal'
	G.graph_attr['rankdir']='BT'

	edges = []

	for i in base_relations:
		for k in base_relations[i]:
			edges.append((i.__name__,k.__name__))

	G.add_edges_from(edges)

	return G


import time
import random
import pygame
import sys
import os
import json
import gym
import numpy as np

from stable_baselines3 import A2C, PPO, DQN
from pygame_ped_env.entities.maskedDQN import MaskableDQN
from sb3_contrib.ppo_mask import MaskablePPO

from pygame.sprite import Sprite, Group, GroupSingle
from typing import Union, Sequence

import pygame_ped_env
from pygame_ped_env.entities.agents import (
    RLVehicle,
    KeyboardVehicle,
    KeyboardPedestrian,
    RandomPedestrian,
)

class RLCrossingSim(gym.Env):
    def __init__(
        self,
        sim_area,
        scenarioList: Union[None, Sequence[int]] = None,
        human_controlled_ped=False,
        human_controlled_car=False,
        headless=False,
        seed=1234,
        basic_model: any = None,
        attr_model: any = None,
        log_path=None,
        speed_coefficient=1.0,
        position_coefficient=1.0,
        steering_coefficient=1.0,
        device="cpu",
    ) -> None:
        super().__init__()
        # np.random.seed(seed)

        self.log_path = log_path
        self.parent_log_path = os.path.dirname(os.path.dirname(self.log_path))

        # whether to render or not
        self.headless = headless

        # whether to have shaped reward or not
        # true by default, scenario will change it in reset()
        self.simple_reward = True

        # reward weightings, used to adjust for repsonse data
        self.speed_coeff = speed_coefficient
        self.pos_coeff = position_coefficient
        self.steer_coeff = steering_coefficient

        # this varible structure allows for sim_area =/= screen_rect so events on edges
        # and spawn are not displayed as all display updates use screen_rect
        self.screen_rect = pygame.Rect((0, 0), sim_area)
        assert (
            self.screen_rect.w <= 1280
        ), "simulation must be less than 1280 pixels wide"
        assert self.screen_rect.h <= 720, "simulation must be less than 720 pixels high"
        assert self.screen_rect.w > self.screen_rect.h, "simulation must be portrait"

        # 150 is offscreen pixel buffer,
        # TODO: should be associated to agent sprite rect programatically
        # self.observation_space = gym.spaces.Box(
        #     low=-150, high=self.screen_rect.w + 150, shape=(14,)
        # )
        self.observation_space = gym.spaces.Box(low=-150, high=1280 + 150, shape=(14,))

        # (8 directions * 3 speeds) + 1 no_op action
        self.action_space = gym.spaces.Discrete(25)
        self.action_space.seed(seed)

        init_sprite = Sprite()
        init_sprite.model = None
        init_sprite.rect = pygame.Rect(0, 0, 0, 0)

        self.vehicle = GroupSingle(init_sprite)
        self.traffic = GroupSingle(init_sprite)
        self.pedestrian = GroupSingle(init_sprite)

        # hook to hold ped for saving if removed from env
        self.done_ped = None

        if scenarioList is not None:
            self.scenarioList = scenarioList
        else:
            self.scenarioList = [0]

        self.scenario = lambda: np.random.choice(self.scenarioList)
        self.info = {"num_steps": 0, "done_cause": ""}

        self.roads = Group()
        self.generateRoads()

        # if not self.headless:
        #     pygame.init()
        #     self.screen = pygame.display.set_mode(self.screen_rect.size)
        #     pygame.display.set_caption("PaAVI - Pedestrian Crossing Simulation")
        #     self.background = self.generateBackground()

        self.device = device

        self.modelA = self._load_model_from_string(attr_model)
        self.modelB = self._load_model_from_string(basic_model)

        self.training = not human_controlled_ped

        self.H_traj_path = os.path.join(self.parent_log_path, "human_car_trajectories")
        self.H_collect = human_controlled_car
        if self.H_collect:
            os.makedirs(self.H_traj_path, exist_ok=True)

        self.num_steps = 0

    def action_masks(self):
        mask = np.ones(self.action_space.n).astype(bool)
        for action in range(self.action_space.n):
            mapped_action = (
                self.vehicle.sprite.action_map[action % 8]
                * self.vehicle.sprite.speed_map[np.floor(action / 8)]
            )
            next_pos = self.vehicle.sprite.rect.center + mapped_action
            if not self.screen_rect.collidepoint(next_pos):
                mask[action] = False
            else:
                mask[action] = True

        return mask

    def get_max_reward(self, shaped_reward=True):
        # highest possible reward for both cases
        # returned as +ve value to make
        # reward for perfect performance = 0
        if not shaped_reward:
            return 3 * (self.screen_rect.w / 3)
        else:
            return np.sum(
                np.array(range(0, self.screen_rect.w, 3)) / self.screen_rect.w
            )

    def generateBackground(self):
        # Setting background image and scaling to window size
        bkgrd = pygame.Surface(self.screen_rect.size)
        width_divisor, height_divisor = 2, 2
        tile_width, tile_height = (
            self.screen_rect.w / width_divisor,
            self.screen_rect.h / height_divisor,
        )
        bkgrd_img = pygame.image.load(
            os.path.join(
                os.path.dirname(pygame_ped_env.__file__),
                "images/grass/ground_grass_gen_02.png",
            )
        ).convert()
        # bkgrd = pygame.transform.scale(bkgrd_img, (self.screen_rect.size))
        bkgrd_tile = pygame.transform.scale(bkgrd_img, (tile_width, tile_height))
        for i in range(width_divisor):
            for j in range(height_divisor):
                bkgrd.blit(bkgrd_tile, (tile_width * i, tile_height * j))

        # bkgrd = pygame.transform.scale(
        #     pygame.image.load("../images/intersection.png"), screen_rect.size
        # )

        return bkgrd

    def generateRoads(self):
        scale = (self.screen_rect.w / 5, self.screen_rect.h / 5)
        # fairly ineffecient TODO: refactor to road class for generality

        vertical_path = Sprite(self.roads)
        image = pygame.transform.scale(
            pygame.image.load(
                os.path.join(
                    os.path.dirname(pygame_ped_env.__file__),
                    "images/roads/roadPLAZA.tga",
                )
            ),
            scale,
        )
        image_tile = pygame.Surface((scale[0] / 2, scale[1] / 2))
        image_tile.blit(image, (0, 0), image_tile.get_rect())
        image_rect = image_tile.get_rect()
        surf = pygame.Surface((image_rect.w, self.screen_rect.h))
        for t in range(np.floor(self.screen_rect.h / image_rect.h).astype(int) + 1):
            surf.blit(image_tile, (0, t * image_rect.h))

        vertical_path.image = surf
        vertical_path.rect = vertical_path.image.get_rect()
        vertical_path.rect.midtop = (self.screen_rect.w / 2, 0)

        horizontal_road = Sprite(self.roads)
        image_tile = pygame.transform.scale(
            pygame.image.load(
                os.path.join(
                    os.path.dirname(pygame_ped_env.__file__),
                    "images/roads/roadEW.tga",
                )
            ),
            scale,
        )
        image_rect = image_tile.get_rect()
        surf = pygame.Surface((self.screen_rect.w, image_rect.h))
        for t in range(np.floor(self.screen_rect.w / image_rect.h).astype(int) + 1):
            surf.blit(image_tile, (t * image_rect.w, 0))

        horizontal_road.image = surf
        horizontal_road.rect = horizontal_road.image.get_rect()
        horizontal_road.rect.midleft = (0, self.screen_rect.h / 2)

    def get_reward(self, agent: RLVehicle):
        if isinstance(agent.sprite, KeyboardVehicle) or isinstance(
            self.pedestrian, KeyboardPedestrian
        ):
            done = False
            if pygame.sprite.spritecollide(agent.sprite, self.pedestrian, False):
                self.done_ped = self.pedestrian.sprite
                self.pedestrian.remove(self.pedestrian.sprite)
                self.info["done_cause"] = "hit_pedestrian"
                done = True
            if pygame.sprite.spritecollide(agent.sprite, self.traffic, True):
                self.info["done_cause"] = "hit_traffic"
                done = True
            rwd = 0.0
        elif self.simple_reward:
            rwd, done = self.get_simple_reward(agent)
            # rwd += 4
        else:
            # function for social rewards
            # speed_rwd, position_rwd, heading_rwd, collide_rwd, done = self.get_primitive_reward(agent)
            p_rwd, s_rwd, h_rwd, c_rwd, done = self.get_primitive_reward(agent)
            # linearly combine rewards
            # apply convex or concave adjustment, and then shift to [-1,0)
            rwd = (
                (np.power(p_rwd, self.pos_coeff) - 1)
                + (np.power(s_rwd, self.speed_coeff) - 1)
                + (np.power(h_rwd, self.steer_coeff) - 1)
                + c_rwd
            )
            if np.isnan(rwd):
                rwd = (p_rwd - 1) + (s_rwd - 1) + (h_rwd - 1) + c_rwd

        return rwd, done

    def get_simple_reward(self, agent: RLVehicle):
        done = False
        collide_rwd = 0  # 0 no collision, -1 offroad, -infty hit pedestrian

        # if run over pedestrian, give reward reduction and end episode
        if pygame.sprite.spritecollide(agent.sprite, self.pedestrian, False):
            self.done_ped = self.pedestrian.sprite
            self.pedestrian.remove(self.pedestrian.sprite)
            self.info["done_cause"] = "hit_pedestrian"
            return -np.infty, True

        on_road = 1
        if not pygame.sprite.spritecollide(agent.sprite, self.roads, False):
            on_road = -1

        # if we have reached the destination, give max rwd
        if agent.sprite.rect.collidepoint(
            pygame.Rect([agent.sprite.objective, (0, 0)]).center
        ):
            # rwd for hitting goal
            rwd = self.get_max_reward(self.simple_reward)
            self.info["done_cause"] = "objective_reached"
            done = True
        else:
            rwd = -4 + on_road

        return rwd, done

    def get_primitive_reward(self, agent: RLVehicle):
        done = False
        collide_rwd = 0  # 0 no collision, -1 offroad, -infty hit pedestrian

        # if run over pedestrian, give reward reduction and end episode
        if pygame.sprite.spritecollide(agent.sprite, self.pedestrian, False):
            self.done_ped = self.pedestrian.sprite
            self.pedestrian.remove(self.pedestrian.sprite)
            collide_rwd = -np.infty
            self.info["done_cause"] = "hit_pedestrian"
            done = True

        if not pygame.sprite.spritecollide(agent.sprite, self.roads, False):
            collide_rwd = -1
            # elif not pygame.sprite.spritecollide(agent.sprite, self.roads.lane, False):
            # collide_rwd = -0.5
            # self.info["done_cause"] = "off_road"

        # if we have reached the destination, give max rwd
        if agent.sprite.rect.collidepoint(
            pygame.Rect([agent.sprite.objective, (0, 0)]).center
        ):
            # rwd for hitting goal
            position_rwd = self.get_max_reward(self.simple_reward)
            self.info["done_cause"] = "objective_reached"
            done = True
        else:
            # rwd for distance to goal
            # max dist same for left and right by symmetry, so just use left
            # dist from midright to topleft same as from either right corner to midleft
            # +20 to allow for vehicle size/lane offset
            max_dist = (
                np.linalg.norm(self.screen_rect.midright, ord=1) + 20
            )  # ord=1 is manhattan distance
            position_rwd = (
                (max_dist - np.linalg.norm(agent.sprite.dist_to_objective(), ord=1))
                # +1e-15 to avoid 0 issues
                + 1e-15
            ) / max_dist

        # rewarded for going as fast as possible, speed as fraction of top speed
        speed_rwd = (
            np.maximum(
                np.abs(agent.sprite.moved[-1][0]), np.abs(agent.sprite.moved[-1][1])
            )
            / agent.sprite.speed
        )

        # movement vector magnitude for this action
        delta_rho_1 = np.hypot(agent.sprite.moved[-1][0], agent.sprite.moved[-1][1])

        # movement vector magnitude for previous action
        delta_rho_2 = np.hypot(agent.sprite.moved[-2][0], agent.sprite.moved[-2][1])

        # from angle between 2 vectors definition
        theta_s = np.arccos(
            np.clip(
                np.dot(agent.sprite.moved[-1], agent.sprite.moved[-2])
                / (
                    (delta_rho_1 * delta_rho_2) + 1e-15
                ),  # 1e-15 to avoid divide by zero
                -1.0,
                1.0,
            )  # clip to avoid round off error or +1e-15 taking arg out of arccos domain [-1,1]
        )
        # punishes proportionally for turning in this action
        heading_rwd = (np.pi - theta_s) / np.pi

        # catch any bad edge cases from calculations
        if any(np.isnan([position_rwd, speed_rwd, heading_rwd, collide_rwd])):
            print("primitive reward returned NaN")
            import pdb

            pdb.set_trace()

        return position_rwd, speed_rwd, heading_rwd, collide_rwd, done

    def step(self, action):
        if isinstance(action, dict):
            if isinstance(self.vehicle.sprite, KeyboardVehicle):
                self.vehicle.sprite.update()
            else:
                self.vehicle.sprite.act(self.modelL.predict(action["obs"]))
        else:
            self.vehicle.sprite.act(action)
            # self.vehicle.sprite.act([[6], None]) # just move right for testing without trained model
        # print(self.vehicle.sprite.rect)
        self.traffic.sprite.update(self.traffic_action)
        if pygame.sprite.spritecollide(self.traffic.sprite, self.vehicle, False):
            self.traffic.empty()
            self.traffic.add(Sprite())
            self.traffic.sprite.rect = pygame.Rect(0, 0, 0, 0)
        if pygame.sprite.spritecollide(self.traffic.sprite, self.pedestrian, False):
            self.done_ped = self.pedestrian.sprite
            self.pedestrian.remove(self.pedestrian.sprite)
            self.traffic.empty()
            self.traffic.add(Sprite())
            self.traffic.sprite.rect = pygame.Rect(0, 0, 0, 0)

        # add placeholder ped if its gone offscreen and been removed
        try:
            self.pedestrian.sprite.update()
        except:
            self.pedestrian.empty()
            self.pedestrian.add(Sprite())
            self.pedestrian.sprite.rect = pygame.Rect(0, 0, 0, 0)

        if not self.headless:
            self.render()

        obs = np.hstack(
            [
                [self.vehicle.sprite.rect],
                [self.traffic.sprite.rect],  # padding for single vehicle scenario
                [self.pedestrian.sprite.rect],
                [self.vehicle.sprite.objective],
            ],
        )
        try:
            self.traffic_action = self.traffic.sprite.model.predict(obs)
        except:
            self.traffic_action = None

        rwd, self.done = self.get_reward(self.vehicle)

        self.num_steps += 1
        self.info["num_steps"] = self.num_steps

        if self.num_steps > self.screen_rect.w:
            self.info["done_cause"] = "timed_out"
            self.done = True

        # remove vehicles once they drive offscreen and finish episode
        if not self.done:
            ### should be handled by valid_action_mask now ###

            veh_pos = self.vehicle.sprite.rect.center
            if not self.screen_rect.collidepoint(veh_pos):
                print("OFFSCREEN!!!")
                self.done = True
                # if isinstance(self.vehicle.sprite, KeyboardVehicle):
                #     rwd = 0.0
                # # if not roughly at destination then punish
                # elif not (
                #     self.vehicle.sprite.dist_to_objective() <= np.ones(2) * 3
                # ).all():
                #     rwd -= 3000
                # self.info["done_cause"] = "vehicle_offscreen"
                # self.done = True

            # if vehicle not done, check for ped offscreen
            if not self.screen_rect.contains(self.pedestrian.sprite):
                self.done_ped = self.pedestrian.sprite
                self.pedestrian.remove(self.pedestrian.sprite)

        if self.H_collect and self.done and self.scenarioName != "H2":
            # save trajectory
            os.makedirs(
                os.path.join(self.H_traj_path, self.scenarioName), exist_ok=True
            )
            save_path = os.path.join(
                self.H_traj_path,
                f"{self.scenarioName}",
                f"traj_{np.random.randint(5)}",
            )

            if isinstance(self.vehicle.sprite, KeyboardVehicle):
                self.vehicle.sprite.save(save_path)
            if isinstance(self.traffic.sprite, KeyboardVehicle):
                self.traffic.sprite.save(save_path)

            print(save_path)

        return obs, rwd, self.done, self.info

    def render(self):
        self.screen.blit(self.background, (0, 0))  # display background in simulation
        self.roads.draw(self.screen)
        self.vehicle.draw(self.screen)

        try:
            self.pedestrian.draw(self.screen)
        except:
            pass

        try:
            self.traffic.draw(self.screen)
        except:
            pass

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        pygame.display.update()

        # hacky way to have ~60 FPS TODO: implentment properly
        time.sleep(0.016)

    def reset(self, scenario: int = None):
        self.num_steps = 0

        if scenario is not None:
            scenario = self.scenario_map()[scenario]
        else:
            scenario = self.scenario_map()[self.scenario()]
        self.scenarioName = scenario["name"]
        self.info["scenario"] = self.scenarioName

        self.vehicle.empty()
        agent = scenario["agentSprites"][0]

        colours = [
            "blue",
            "silver",
            "white",
            "red",
            "yellow",
            "green",
            "brown",
            "orange",
        ]
        print(colours)
        random.seed(time.process_time())
        c = random.randint(0, 7)
        print(c)
        colour = colours[c]
        print(colour)

        if agent[0] == "R":
            agentL = RLVehicle.init_from_scenario(
                agent, self.screen_rect.size, headless=self.headless, colour=colour
            )
            agentL.model = self.modelA
            self.simple_reward = False
        elif agent[0] == "S":
            agentL = RLVehicle.init_from_scenario(
                agent[1:], self.screen_rect.size, headless=self.headless, colour=colour
            )
            agentL.model = self.modelB
            self.simple_reward = True
        else:
            if self.scenarioName == "H2":
                agentL = KeyboardVehicle.init_from_scenario(
                    agent,
                    self.screen_rect.size,
                    load_path=os.path.join(
                        self.H_traj_path,
                        "H_r",
                        f"traj_{np.random.randint(5)}",
                    ),
                    colour=colour,
                )
            elif self.H_collect:
                agentL = KeyboardVehicle.init_from_scenario(
                    agent,
                    self.screen_rect.size,
                    load_path=None,
                    colour=colour,
                )
            else:
                agentL = KeyboardVehicle.init_from_scenario(
                    agent,
                    self.screen_rect.size,
                    load_path=os.path.join(
                        self.H_traj_path,
                        self.scenarioName,
                        f"traj_{np.random.randint(5)}",
                    ),
                    colour=colour,
                )

            agentL.model = None

        self.modelL = agentL.model
        self.vehicle.add(agentL)

        self.traffic.empty()
        try:
            traffic_agent = scenario["agentSprites"][1]

            if traffic_agent[0] == "R":
                agentT = RLVehicle.init_from_scenario(
                    traffic_agent, self.screen_rect.size, headless=self.headless
                )
                agentT.model = self.modelA
            elif traffic_agent[0] == "S":
                agentT = RLVehicle.init_from_scenario(
                    traffic_agent[1:], self.screen_rect.size, headless=self.headless
                )
                agentT.model = self.modelB
            else:
                if self.scenarioName == "H2":
                    agentT = KeyboardVehicle.init_from_scenario(
                        traffic_agent,
                        self.screen_rect.size,
                        load_path=os.path.join(
                            self.H_traj_path,
                            "H_l",
                            f"traj_{np.random.randint(5)}",
                        ),
                    )
                elif self.H_collect:
                    agentT = KeyboardVehicle.init_from_scenario(
                        traffic_agent,
                        self.screen_rect.size,
                        load_path=None,
                    )
                    # if isinstance(agentL, KeyboardVehicle):
                    #     agentT.load(
                    #         os.path.join(
                    #             self.H_traj_path,
                    #             f"{self.scenarioName}",
                    #             f"traj_{np.random.randint(5)}",
                    #         )
                    #     ),

                else:
                    agentT = KeyboardVehicle.init_from_scenario(
                        traffic_agent,
                        self.screen_rect.size,
                        load_path=os.path.join(
                            self.H_traj_path,
                            f"{self.scenarioName}",
                            f"traj_{np.random.randint(5)}",
                        ),
                    )
                    # agentT.replay = True

                agentT.model = None
        except:
            agentT = Sprite()
            agentT.rect = pygame.Rect(0, 0, 0, 0)
            agentT.model = None

        self.modelT = agentT.model
        self.traffic.add(agentT)

        # add pedestrian, keyboard for experiment or random if just training or code evalling
        self.pedestrian.empty()
        if self.training or self.H_collect:
            self.pedestrian.add(
                RandomPedestrian(
                    # add slight noise in pedestrian start position
                    (self.screen_rect.w / 2) + (np.random.rand() * 4) - 2,
                    (self.screen_rect.h * (7 / 8)) + (np.random.rand() * 4) - 2,
                    "up",
                    headless=self.headless,
                )
            )
        else:
            self.pedestrian.add(
                KeyboardPedestrian(
                    self.screen_rect.w / 2, self.screen_rect.h * (7 / 8), "up"
                )
            )

        if not self.headless:
            pygame.init()
            self.screen = pygame.display.set_mode(self.screen_rect.size)
            pygame.display.set_caption("PaAVI - Pedestrian Crossing Simulation")
            self.background = self.generateBackground()
            # grey out screen and wait 1 second
            # on starting next scenario when rendering
            self.screen.fill([127, 127, 127])
            pygame.display.flip()
            time.sleep(1)

        try:
            self.traffic_action = self.traffic.sprite.model.predict(obs)
        except:
            self.traffic_action = None

        self.done = False
        obs = np.hstack(
            [
                [self.vehicle.sprite.rect],
                [self.traffic.sprite.rect],  # padding for single vehicle scenario
                [self.pedestrian.sprite.rect],
                [self.vehicle.sprite.objective],
            ],
        )

        return obs

    def run(self):
        # count = 0
        obs = self.reset()
        done = self.done
        while not done:
            # advance sim by one timestep
            obs, _, done, _ = self.step(self.vehicle.sprite.model.predict(obs)[0])

            # display the simulation
            if not self.headless:
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        sys.exit()

                self.render()
        pygame.quit()

    def load_model(self, path):
        return self._load_model_from_string(path)

    def close(self, saveDir=None, info=None):
        if not self.headless:
            pygame.display.quit()
            pygame.quit()
            # sys.exit()

        if saveDir is None:
            saveDir = self.log_path

        try:
            self.traffic.sprite.save(os.path.join(saveDir, "second_vehicle_agent.pkl"))
            self.vehicle.sprite.save(os.path.join(saveDir, "first_vehicle_agent.pkl"))
        except:
            self.vehicle.sprite.save(os.path.join(saveDir, "vehicle_agent.pkl"))

        try:
            self.pedestrian.sprite.save(os.path.join(saveDir, "pedestrian.pkl"))
        except:
            self.done_ped.save(os.path.join(saveDir, "pedestrian.pkl"))

        if info is not None:
            with open(os.path.join(saveDir, "env_info.json"), "w+") as filePath:
                json.dump(info, filePath, ensure_ascii=False, indent=4)
                # json.dumps() # returns string obj and doesnt save

    @classmethod
    def init_scenario(cls, scenario, sim_area, **kwargs):
        # entities = []
        # scen = cls.scenario_map()[scenario]
        # for agent in scen.agentSprites:
        #     if agent[0] == "H":
        #         entities.append(RLVehicle.init_from_scenario(agent))
        #         kwargs["simple_reward"] = False
        #     elif agent[0] == "S":
        #         entities.append(RLVehicle.init_from_scenario(agent[1:]))
        #         kwargs["simple_reward"] = True
        #     else:
        #         entities.append(KeyboardVehicle.init_from_scenario(agent))

        # # add pedestrian
        # if training:
        #     entities.append(
        #         RandomPedestrian(sim_area[0] / 2, sim_area[1] * (7 / 8), "up")
        #     )
        # else:
        #     entities.append(
        #         KeyboardPedestrian(sim_area[0] / 2, sim_area[1] * (7 / 8), "up")
        #     )

        return (cls(sim_area, cls.scenario_map()[scenario].agentList, **kwargs),)

    def _load_model_from_string(self, path):
        name = path.split(os.sep)[-1].split("_")[0]
        if name == "dqn":
            alg = DQN
        elif name == "ppo":
            alg = PPO
        elif name == "a2c":
            alg = A2C
        elif name == "maskedDQN":
            alg = MaskableDQN
        elif name == "train":
            alg = MaskableDQN
        elif name == "maskedPPO":
            alg = MaskablePPO
        else:
            raise ValueError("Unknown model type: ", name)

        try:
            return alg.load(path, env=self, device=self.device)
        # if no or wrong model given
        except FileNotFoundError:
            # load default
            init_path = os.path.join(
                self.parent_log_path, "simple_reward_agent", f"{name}_init_model"
            )
            try:
                model = alg.load(init_path, env=self, device=self.device)
                print("No model found, loading default model")
                print("Loading default model from: ", init_path)
                return model
            # if no default exists make it
            except FileNotFoundError:
                model = alg(
                    "MlpPolicy",
                    self,
                    verbose=0,
                    tensorboard_log=os.path.join(
                        self.parent_log_path, "simple_reward_agent"
                    ),
                )
                model._setup_model()
                model.save(init_path, include="env")
                print("No model found, nor a default model of this algorithm.")
                print("Created default and saved to: ", init_path)

                return alg.load(init_path, env=self, device=self.device)

    @staticmethod
    def scenario_map():
        return {
            0: {
                "name": "RL_r",
                "agentSprites": ["RL_right"],
            },
            1: {
                "name": "RL_l",
                "agentSprites": ["RL_left"],
            },
            2: {
                "name": "RL2_r",
                "agentSprites": ["RL_right", "RL_left"],
            },
            3: {
                "name": "RL2_l",
                "agentSprites": ["RL_left", "RL_right"],
            },
            4: {
                "name": "RL_SRL_r",
                "agentSprites": ["RL_right", "SRL_left"],
            },
            5: {
                "name": "RL_SRL_l",
                "agentSprites": ["RL_left", "SRL_right"],
            },
            6: {
                "name": "RL_H_r",
                "agentSprites": ["RL_right", "H_left"],
            },
            7: {
                "name": "RL_H_l",
                "agentSprites": ["RL_left", "H_right"],
            },
            8: {
                "name": "SRL_r",
                "agentSprites": ["SRL_right"],
            },
            9: {
                "name": "SRL_l",
                "agentSprites": ["SRL_left"],
            },
            10: {
                "name": "SRL2_r",
                "agentSprites": ["SRL_right", "SRL_left"],
            },
            11: {
                "name": "SRL2_l",
                "agentSprites": ["SRL_left", "SRL_right"],
            },
            12: {
                "name": "SRL_RL_r",
                "agentSprites": ["SRL_right", "RL_left"],
            },
            13: {
                "name": "SRL_RL_l",
                "agentSprites": ["SRL_left", "RL_right"],
            },
            14: {
                "name": "SRL_H_r",
                "agentSprites": ["SRL_right", "H_left"],
            },
            15: {
                "name": "SRL_H_l",
                "agentSprites": ["SRL_left", "H_right"],
            },
            16: {
                "name": "H_r",
                "agentSprites": ["H_right"],
            },
            17: {
                "name": "H_l",
                "agentSprites": ["H_left"],
            },
            18: {
                "name": "H2",
                "agentSprites": ["H_right", "H_left"],
            },
        }


import cv2
import numpy as np
import pandas as pd
from tensorflow import keras
model = keras.models.load_model('MNIST_AmericanSignLanguage.h5')

cap = cv2.VideoCapture(0)
while True:
    ret, photo = cap.read()
    photo = cv2.cvtColor(photo, cv2.COLOR_BGR2GRAY)

    # photo = cv2.flip(photo,1)
    photo = cv2.rectangle(photo,(0,0),(200,200),(0,0,255),thickness=3,)
    hand = photo[0:200,0:200]
    hand = cv2.resize(hand,(28,28))

    # r = model.predict(hand)
    # print(np.argmax(r))
    # cphoto = photo[100:400, 50:350]
    cv2.imshow('hi', photo)
    cv2.imshow('hand', hand)
    # hand = cv2.resize(hand,(28,28))
    hand = hand.reshape(1,28,28,1)
    # print(hand.shape)
    r = model.predict(hand)
    print(np.argmax(r))
    if cv2.waitKey(1) == 13:
        break

cv2.destroyAllWindows()
cap.release()


from django.shortcuts import render, redirect
from django.contrib import messages
from django.http import HttpResponse
from subtitlingAI.forms import VideoFileForm
from subtitlingAI.models import VideoFile
from subtitlingAI.data_process.transcription import main
import shutil, os

def clean_database():
    n = VideoFile.objects.count()
    if n > 0:
        db = VideoFile.objects.all()
        for i in range(n):
            db[0].delete()

def index(request):
    clean_database()
    if request.method == "POST":
        form = VideoFileForm(request.POST, request.FILES)
        if form.is_valid():
            video = form.save()
            video_name = os.path.splitext(os.path.basename(video.video_file.path))[0]
            srt_content = main(video.google_project_id, video.video_file.path, video.transcription_language)
            video.delete()
            shutil.rmtree(f'uploaded/{video.google_project_id}')
            response = HttpResponse(srt_content, content_type='application/octet-stream')
            response['Content-Disposition'] = f'attachment; filename="{video_name}.srt"'
            return response
        else:
            messages.warning(request, "Form not valid. Are the Google Project ID and the extension format of the video correct?")
            return redirect('index')
    form = VideoFileForm()
    return render(request,
                  'subtitlingAI/index.html',
                  {'form': form})

def about(request):
    return render(request,
                  'subtitlingAI/about.html')


import json
import pytest

from anno.crud import CRUD
from anno.errors import InvalidInputWebAnnotationError
from anno.json_models import AnnoJS
from anno.json_models import Catcha

from .conftest import make_wa_object

@pytest.mark.skip('debugging fixture')
@pytest.mark.usefixtures('wa_list')
def test_fixture_wa_list(wa_list):
    print(json.dumps(wa_list, sort_keys=True, indent=2))
    assert wa_list == 'blah'

@pytest.mark.skip('debugging fixture')
@pytest.mark.usefixtures('js_list')
def test_fixture_js_list(js_list):
    print(json.dumps(js_list, sort_keys=True, indent=2))
    assert js_list == 'blah'

@pytest.mark.usefixtures('js_list')
@pytest.mark.django_db
def test_to_annotatorjs(js_list):
    for js in js_list:
        catcha = AnnoJS.convert_to_catcha(js)
        anno = CRUD.create_anno(catcha, catcha['creator']['name'])
        js_back = AnnoJS.convert_from_anno(anno)
        assert AnnoJS.are_similar(js, js_back)

def test_body_sanitize():
    body_unsafe_text = [
        '  <   script same_attr=blah other_attr="pooh"></scritp>',
        '<script>',
        'something <\tscript\t  somethingelse="{}">'.format('blah'),
    ]
    catcha = make_wa_object()

    for b_text in body_unsafe_text:
        catcha['body']['items'][0]['value'] = b_text
        with pytest.raises(InvalidInputWebAnnotationError) as e:
            safe = Catcha.safe_body_text_value(catcha)

    catcha['body']['items'][0]['value'] = \
        'body of annotation that is safe and has no script tags.'
    safe = Catcha.safe_body_text_value(catcha)
    assert safe

@pytest.mark.usefixtures('wa_image')
@pytest.mark.django_db
def test_catcha_normalize_ok(wa_image):
    catch = wa_image
    catcha = Catcha.normalize(catch)

    assert json.dumps(catch, sort_keys=True, indent=4) == json.dumps(
        catcha, sort_keys=True, indent=4)


from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Session
from sqlalchemy.sql import text
from sqlalchemy.orm import Session
from models import games as games_model
from models import users as users_model
from schemas import games as games_schema

def read_boxes(db: Session):
    BoxList = db.query(games_model.boxes).all()
    return BoxList

# お題一覧取得
def read_boxes(db: Session, skip: int = 0):
    BoxList = db.query(games_model.boxes).offset(skip).all()
    return BoxList

# 札セット取得
def read_playing_cards(db: Session, box_id: int) -> games_model.boxes_cards :
    play_cards_select = f"SELECT CAST(qac.card_id AS CHAR) AS card_id, CAST(qt.question_id AS CHAR) AS question_id, \
            CAST(at.answer_id AS CHAR) AS answer_id, qt.question_text AS question_text, \
            CONCAT(qt.question_text, '  ', at.answer_text) AS card_text,\
            at.answer_text AS answer_text, aimg.answer_file_pass AS answer_file_pass \
            FROM (box_card AS bc \
            INNER JOIN question_answer_cards AS qac \
            ON bc.card_id = qac.card_id AND bc.box_id = {box_id} \
            INNER JOIN question_texts AS qt \
            ON qac.question_id = qt.question_id) \
            INNER JOIN answer_texts AS at \
            ON qac.answer_id = at.answer_id \
            LEFT JOIN answer_images AS aimg \
            ON at.answer_id = aimg.answer_id \
            ORDER BY RAND() LIMIT 10 \
            " #結合抽出処理
    t = text(play_cards_select)
    PlayCardsList = db.execute(t).fetchall()

    return PlayCardsList

# 苦手札セット取得
def weak_point_cards(db: Session, user_id: str) -> users_model.Users :

    t = f"SELECT card_id FROM answer_rate_view WHERE answer_rate <= 0.8 AND user_id = :user_id"
    select_text = text(t)
    cid = db.execute(select_text,{"user_id": user_id}).fetchall()
    card_id_list = [item[0] for item in cid]
    weak_card_id = ', '.join(map(str, card_id_list))
    print(weak_card_id)
    weak_cards_select = f"SELECT CAST(qac.card_id AS CHAR) AS card_id, CAST(qt.question_id AS CHAR) AS question_id, \
            CAST(at.answer_id AS CHAR) AS answer_id, qt.question_text AS question_text, \
            CONCAT(qt.question_text, '  ', at.answer_text) AS card_text,\
            at.answer_text AS answer_text, aimg.answer_file_pass AS answer_file_pass \
            FROM (box_card AS bc \
            INNER JOIN question_answer_cards AS qac \
            ON bc.card_id = qac.card_id AND qac.card_id IN({weak_card_id}) \
            INNER JOIN question_texts AS qt \
            ON qac.question_id = qt.question_id) \
            INNER JOIN answer_texts AS at \
            ON qac.answer_id = at.answer_id \
            LEFT JOIN answer_images AS aimg \
            ON at.answer_id = aimg.answer_id \
            ORDER BY RAND() LIMIT 10 \
            " #結合抽出処理
    cards_set = text(weak_cards_select)
    WeakCardsList = db.execute(cards_set).fetchall()

    return WeakCardsList

#　プレイ結果記録 非同期にする！！
async def play_records(db: AsyncSession, result: games_schema.Results):
    #プレイ記録親テーブルに記録
    db_result = games_model.play_records(
        user_id = result.user_id,
        number_of_question = result.number_of_question,
        number_of_corrected = result.number_of_corrected,
        played_at = result.played_at,
        play_type = result.play_type
    )
    db.add(db_result)
    db.commit()
    db.refresh(db_result)

    #↑で発行されたplayed_idを取得
    played_db = db.query(games_model.play_records).\
        filter(games_model.play_records.played_at == db_result.played_at).\
            filter(games_model.play_records.user_id == db_result.user_id).first()
    print(played_db)
    played_id = played_db.played_id

    # プレイしたお題の記録
    db_box = games_model.play_type_boxes(
        played_id = played_id,
        box_id = result.box_id
    )
    db.add(db_box)
    db.commit()
    db.refresh(db_box)

    record_result = result.record_result

    for card_id, judgement in record_result.items():

        db_detail = games_model.record_details(
            played_id = played_id,
            card_id = card_id,
            judgement = judgement,
            user_id = result.user_id
        )

        db.add(db_detail)
        db.commit()
        db.refresh(db_detail)

    return {"message": "Good!"}


import argparse
import os
import sys
import time
import mmap
import signal
from os import fork

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-f', '--file',
                        help='Path del archivo')
    args = parser.parse_args()

    mapp(args)

def store_up(segment, cpid, args):
    for line in sys.stdin:
        if os.getpid() == cpid[0]:
            print(line)
            if os.getpid() == cpid[0]:
                print('Hijo_1')
                segment.write(bytes(line))
                signal.raise_signal(signal.SIGUSR1)

            elif os.getpid() == cpid[1]:
                print('Hijo_2')
            for h in range(2):
                signal.sigwait([signal.SIGUSR1])

            readLine = segment.readline()

            with open(args.file, 'w') as file:
                file.write(readLine.upper())

def mapp(args):

    with open('file.txt', 'w+') as file:
        segment = mmap.mmap(-1, 1024)
        signal.signal(signal.SIGUSR1, signal.SIG_DFL)
        ppid = os.getpid()
        cpid = []

        for i in range(2):
            if os.getpid() == ppid:
                cpid.append(os.fork())
            else:
                os.wait()
        if os.fork():
            w = os.fdopen(w, 'w')
            print(cpid)
            w.write(f'{cpid}')
            w.close()
        else:
            os.close(w)
            r = os.fdopen(r, 'r')
            cpid = r.read()
            print(cpid)
            r.close()

        store_up(segment, cpid, args)

if __name__ == '__main__':
    main()


from turtle import Turtle, Screen
import random
import colorgram
import re
turtle = Turtle()

turtle.shape("turtle")
turtle.color("DarkOrchid1")

def draw_square():
    """draw a square"""
    turtle.forward(100)
    turtle.right(90)
    turtle.forward(100)
    turtle.right(90)
    turtle.forward(100)
    turtle.right(90)
    turtle.forward(100)
    turtle.right(90)

# draw_square()

def draw_dash():
    """draw a dash line"""
    i = 0
    while i < 25:
        for c in ('black', 'white'):
            turtle.color(c)
            turtle.forward(10)
            i += 1

# draw_dash()

def draw_different_shapes():
    """draw different type of shapes, and each shape is outside the last shape"""
    for angle in range(3,10):
        inside_angle = (180 * (angle - 2)) / angle
        turn_angle = 180 - inside_angle

        c1,c2,c3 = random.uniform(0,1), random.uniform(0,1), random.uniform(0,1)
        tup = (c1, c2, c3)
        turtle.pencolor(tup)

        i = 1
        while i <= angle:
            turtle.right(turn_angle)
            turtle.forward(100)
            i += 1

# draw_different_shapes()

def random_walk():
    """walk random direction for same steps but change color each steps"""
    direction = [0, 90, 180, 270]

    for i in range(100):
        turtle.pensize(15)
        dir_choose = int(random.choice(direction))
        turtle.right(dir_choose)
        turtle.forward(20)
        c1, c2, c3 = random.uniform(0, 1), random.uniform(0, 1), random.uniform(0, 1)
        tup = (c1, c2, c3)
        turtle.pencolor(tup)

# random_walk()

def draw_circle():
    """draw loop circles that spin 360 degree"""
    for time in range(36):
        c1, c2, c3 = random.uniform(0, 1), random.uniform(0, 1), random.uniform(0, 1)
        tup = (c1, c2, c3)
        turtle.pencolor(tup)
        turtle.circle(50)
        turtle.right(10)

# draw_circle()

def hirs_spot():
    """draw the dot in random color, which extract from hirs spot painting"""
    colors = colorgram.extract('image.jpg', 35)

    rgb_colors = []
    for color in range(len(colors)):
        rgb_colors.append(colors[color].rgb)

    formatted_rgb = []
    for rgb in rgb_colors:
        rgb_value = f"({rgb.r},{rgb.g},{rgb.b})"
        # 將字串中的數字切割並轉換成整數，然後用 tuple 函數來創建元組
        rgb_color_tuple = tuple(map(int, rgb_value[1:-1].split(',')))
        formatted_rgb.append(rgb_color_tuple)

    turtle.penup()
    turtle.speed("fastest")
    turtle.setheading(225)
    turtle.forward(350)
    turtle.setheading(0)
    number_of_dots = 100

    for dot_count in range(1, number_of_dots + 1):
        # 將 numeric color tuple 轉換成 Turtle 模組接受的顏色字串格式
        turtle.dot(20, "#%02x%02x%02x" % random.choice(formatted_rgb))
        turtle.fd(50)

        if dot_count % 10 == 0:
            turtle.setheading(90)
            turtle.fd(50)
            turtle.setheading(180)
            turtle.fd(500)
            turtle.setheading(0)

# hirs_spot()

screen = Screen()
screen.exitonclick()


import json
from datetime import datetime, date

from flask import Blueprint, request
from flask import current_app as app

from db.wikishield_connection import DBConnection as WS
from db.wikishield_db import WikishieldDB
from lang.langs import Lang, LangsManager
from db.connection_info import read_sql_user_name
from clf.classifier_manager import reload_classifier
from sklearn.exceptions import NotFittedError

api = Blueprint('api', __name__)

# default number of revisions to fetch
_DEF_NUM_OF_REVS = 50
# maximum number of revisions to fetch
_MAX_NUM_OF_REVS = 500

# TODO: close wikishield_db at all routes

# TODO: define HTTP status codes here

# ----------------------------------------------------------------------------------------------------
class WikishieldApiResult:
    """
    this class generate JSON results for Wikishield API
    """

    @staticmethod
    def build_good_res(data=None):
        """
        build good result JSON of API

        param data: data to send at response. data must be serializable value (i.e. can converted to dictionary).
                    if your data it numpy value. use `data = my_numpy_obj.item()` function for converting to pure Python object

        return JSON result
        """
        return json.dumps(obj={'status': 'ok', 'data': data}, default=WikishieldApiResult._encode_object)

    @staticmethod
    def build_err_res(err_msg: str = None):
        """
        build error result JSON of API

        param err_msg: error message

        return JSON result
        """

        return json.dumps(obj={'status': 'error', 'errInfo': {'errMsg': err_msg}},
                          default=WikishieldApiResult._encode_object)

    @staticmethod
    def _encode_object(obj):
        if isinstance(obj, (datetime, date)):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(obj, ValueError):
            return obj.__str__()  # return error message
        else:
            return obj.__dict__()

# ----------------------------------------------------------------------------------------------------
def _get_lang(lang_name: str):
    """
    get language from query parameters

    param lang_name: name of the language

    return language
    """

    lang_manager = LangsManager()
    if not lang_manager.is_support_lang(lang_name):
        raise ValueError("API not support `lang`={}".format(lang_name))
    return lang_manager.get_lang(lang_name)

# ----------------------------------------------------------------------------------------------------
def _try_parse_int(value: str):
    """
    try parse integer from string

    param value: string to parse

    return int
    """

    try:
        return int(value)
    except ValueError:
        raise ValueError("Cannot parse int from string")

# ----------------------------------------------------------------------------------------------------
def _try_parse_bool(value: str):
    """
    try parse boolean from string

    param value: string to parse

    return boolean
    """

    try:
        return bool(value)
    except ValueError:
        raise ValueError("Cannot parse boolean from string")

# ----------------------------------------------------------------------------------------------------
def _get_wikishield_db(lang: Lang):
    """
    get Wikishield database

    param lang: language

    return Wikishield database
    """

    sql_user_name = read_sql_user_name()
    wikishield_conn = WS(sql_user_name)
    wikishield_conn.start()
    return WikishieldDB(wikishield_conn.ctx, lang, sql_user_name)

# ----------------------------------------------------------------------------------------------------
@api.route('/score_rev', methods=['GET'])  # TODO: change to POST
def score_rev():
    """
    score revision route

    request parameters:
                `rev_text` - revision text
                `lang` = language
    response:
                JSON with `scoreResult`
    """

    try:
        rev_text = request.args.get("rev_text")
        rev_text = rev_text.strip() if rev_text else False
        if not rev_text:
            return WikishieldApiResult.build_err_res("Missing `rev_text` parameter"), 400
        lang_name = request.args.get("lang")
        _get_lang(lang_name)  # check language
        wiki_classifier = reload_classifier(lang_name)
        score_result = wiki_classifier.score_rev(rev_text)
        return WikishieldApiResult.build_good_res({'scoreResult': score_result})
    except Exception as ex:
        err_msg = str(ex)
        return WikishieldApiResult.build_err_res(err_msg), 400

# ----------------------------------------------------------------------------------------------------
@api.route('/manage_rev', methods=['POST'])
def manage_rev():
    """
    manage a revision at Wikipedia and update revision good editing in Wikishield table

    request parameters:
                `rev_id` - revision id to verify or restore
                `lang` = language
    response: /
    """

    try:
        req_data = request.get_json()
        rev_id = _try_parse_int(req_data['rev_id'])
        if rev_id < 1:
            raise ValueError("Parameter out of range `rev_id`={}".format(rev_id))
        lang_name = req_data['lang']
        lang = _get_lang(lang_name)
        good_editing = _try_parse_bool(req_data['good_editing'])
        wikishield_db = _get_wikishield_db(lang)
        wikishield_db.update_rev_good_editing(rev_id, good_editing)
        wikishield_db.commit()
        return WikishieldApiResult.build_good_res({})
    except Exception as ex:
        err_msg = str(ex)
        return WikishieldApiResult.build_err_res(err_msg), 400

# ----------------------------------------------------------------------------------------------------
@api.route('/get_revs', methods=['GET'])
def get_revs():
    """
    get last unverified revisions

    request parameters:
                `num_revs` - number of revisions to fetch
                 `lang` = language
    response:
                JSON with list of revisions
    """
    try:
        num_revs = _try_parse_int(request.args.get("num_revs", default=_DEF_NUM_OF_REVS))
        if num_revs < 0 or num_revs > _MAX_NUM_OF_REVS:
            raise ValueError("Illegal parameter range `num_revs`")
        lang_name = request.args.get("lang")
        lang = _get_lang(lang_name)
        wikishield_db = _get_wikishield_db(lang)
        revs = wikishield_db.fetch_recent_unverified_revs(num_revs)
        return WikishieldApiResult.build_good_res({'revs': revs})
    except Exception as ex:
        err_msg = str(ex)
        return WikishieldApiResult.build_err_res(err_msg), 400

# ----------------------------------------------------------------------------------------------------
@api.route('/<path:path>')
def unknown_request(path: str):
    """
    route for all unknown requests in API

    this return an error JSON response
    """

    err_msg = "Unknown request at path '{}'".format(path)
    return WikishieldApiResult.build_err_res(err_msg), 404


from typing import Any, Dict, List, Tuple
from itertools import product

import numpy as np

DataSpec = Tuple[str, Dict[str, Any]]

def hmm_dataset(non_null_mean_min: int,
                non_null_mean_max: int,
                trans_min: float = 0.1,
                trans_max: float = 1,
                trans_step: float = 0.1,
                init_p: float = 0.5) -> List[DataSpec]:
    """Produces spec associated with HMM datasets.

    Range of signals in datasets span integer values [non_null_mean_min,
    non_null_mean_max] and transition probabilities at trans_step
    intervals from trans_min to trans_max (exclusive). init_p specifies
    the parameter for the Bernoulli RV determining whether the initial
    hypothesis is non-null.
    """
    param_list = list(
        product(np.arange(trans_min, trans_max, trans_step),
                range(non_null_mean_min, non_null_mean_max + 1)))
    seeds = range(322, 322 + len(param_list))
    return [('hmm', {
        'trials': 200,
        'hypotheses': 1000,
        'transition_prob': transition_prob,
        'non_null_mean': non_null_mean,
        'init_p': init_p,
        'seed': seed
    }) for seed, (transition_prob, non_null_mean) in zip(seeds, param_list)]

def signal_comp_dataset(signal_min: int,
                        signal_max: int,
                        prob_min: float = 0.1,
                        prob_max: float = 1,
                        prob_step: float = 0.1) -> List[DataSpec]:
    """Produces spec associated with constant datasets.

    Range of signals in datasets span integer values in [signal_min,
    signal_max] with probabilities of non-null ranging from prob_min to
    prob_max (exclusive) in steps of prob_step.
    """
    gaussian_params = list(
        product(np.arange(prob_min, prob_max, prob_step),
                range(signal_min, signal_max + 1)))
    gaussian_seeds = range(322, 322 + len(gaussian_params))
    return list(
        product(['gaussian'], [{
            'trials': 200,
            'hypotheses': 1000,
            'non_null_mean': mean,
            'non_null_sd': 1,
            'null_mean': 0,
            'null_sd': 1,
            'non_null_p': p,
            'seed': seed
        } for seed, (p, mean) in zip(gaussian_seeds, gaussian_params)]))


import json
import random

import requests
from discord.ext.commands import Bot, BucketType, Cog, Context, command, cooldown

from .utils.logs import *

class RandomAPI:
    def __init__(self, api_key: str) -> None:
        self.api_key = api_key
        self.cache = []
        self.d1k = []
        self.cache_size = 20

    def _api_request(self, i: int, j: int, n: int) -> list:
        """ Get a number from random.org """

        # JSON request to random.org API
        url = "https://api.random.org/json-rpc/2/invoke"
        headers = {'content-type': 'application/json'}
        payload = {
            "jsonrpc": "2.0",
            "method": "generateIntegers",
            "params": {
                "apiKey": self.api_key,
                "n": n,
                "min": min(i, j),
                "max": max(i, j),
            },
            "id": 0
        }
        response = requests.post(url, data=json.dumps(payload), headers=headers).json()
        assert response["id"] == 0

        # If something went wrong, generate pseudorandom number
        if "result" not in response:
            result = [random.randint(min(i, j), max(i, j)) for x in range(n)]

        # Grab the result
        else:
            result = response["result"]["random"]["data"]

        return result

    def roll(self, i: int, j: int, n: int=1) -> list:
        """ Return cached result if any, generate otherwise."""

        # No roll needed if same
        if i == j:
            result = [i for x in range(n)]

        # Generate standard roll, from cache if available
        elif (i == 1 and j == 6):
            if len(self.cache) == 0:
                self.cache += self._api_request(1, 6, self.cache_size)
            result = self.cache[0:n]
            del self.cache[0]

        # Generate d1k roll, from cache if available
        elif (i == 1 and j == 1000):
            if len(self.d1k) == 0:
                self.d1k += self._api_request(1, 1000, self.cache_size)
            result = self.d1k[0:n]
            del self.d1k[0]

        # Generate non-standard dice roll
        else:
            result = self._api_request(i, j, n)

        return result

class Dice(Cog):
    """
    Dice roll command using random.org
    Given no arguments, generates number between 1 and 6 inclusive.
    Given one integer j, generates number between 1 and j inclusive.
    Given two integers i and j, generates number between i and j inclusive.
    Given three integers i, j, and n, generates n numbers between i and j inclusive.
    """

    def __init__(self, bot: Bot) -> None:
        self.bot = bot
        self.dice = RandomAPI(bot.keys['random'])

    # Initialise dice cache on start up
    @Cog.listener()
    async def on_ready(self) -> None:
        self.dice.roll(1, 6)     # initialise dice cache
        self.dice.roll(1, 1000)  # initialise d1k cache

    @command()
    @cooldown(5, 60, BucketType.user)
    async def roll(self, ctx: Context, j: str='6', i: str='1', n: str='1') -> None:
        if ctx.guild and ctx.guild.id in self.bot.quiet['guilds']:
            return
        if ctx.channel.id in self.bot.quiet['channels']:
            return

        log_command(ctx)
        try:
            i = int(i)
            j = int(j)
            n = int(n)
        except ValueError:
            return
        async with ctx.typing():
            result = self.dice.roll(i, j, n)
        await self._send(ctx, result)

    async def _send(self, ctx: Context, result: list) -> None:
        """Send formatted output to Discord."""
        message = ', '.join(str(x) for x in result)
        if len(message) > 2000:
            await ctx.send("Your roll is too big for Discord.")
        elif len(message) > 250:
            await ctx.send("Your roll has been sent as a DM.")
            await ctx.author.send(message)
        else:
            await ctx.send(message)

def setup(bot: Bot) -> None:
    """Load cog into bot."""
    bot.add_cog(Dice(bot))