#!/usr/bin/python3
# ------------------------------------------------------------------------------
"""@package wss.py

WebSocket server for providing access and control over the Xiaomi temperature
and humidity sensors.
"""
# ------------------------------------------------------------------------------
#                  Kris Dunning ippie52@gmail.com 2020.
# ------------------------------------------------------------------------------
from threading import Lock
from bluetooth.ble import DiscoveryService, GATTRequester
from datetime import datetime, timedelta
from os import path
from json import loads, dumps
from bluepy.btle import BTLEDisconnectError
from time import sleep
from lywsd02 import Lywsd02Client
from get_sensor_data import ExitCodes
import asyncio
import websockets
import sys

class Constants:
    """
    Class to provide constant values
    """
    ADDR                    = ''
    PORT                    = 9042
    SETTINGS_FILENAME       = "wss_settings.json"
    TEMP_HUM_DEV_ADDR_START = "A4:C1:38"
    TEMP_HUM_DEV_NAME       = "LYWSD03MMC"

class SensorServer:
    """
    SensorServer class - Provides the server methods
    """

    def __init__(self, addr, port, settings_filename, loop):
        """
        Constructs the server
        """
        self._addr = addr
        self._port = port
        self._loop = loop
        self._queue = asyncio.Queue()
        self._settings_filename = settings_filename
        self._receiving = True
        self._gathering = True
        self._sensor_lock = Lock()
        self._client_lock = Lock()
        self._settings_lock = Lock()
        self._clients = {}
        self._client_count = 0

        # Load saved settings
        self._settings = SensorServer.load_settings(self._settings_filename)

        # Load saved device information
        self._devices = SensorServer.load_devices(self._settings['sensor_file'])

        self._loop.create_task(self.gather_readings())
        # self._loop.create_task(self.receive_messages())
        self._server = websockets.serve(self.new_client, self._addr, self._port)

    async def receive_messages(self):
        """
        Handles incoming messages
        """
        # sys.exit(1)
        count = 0
        print("Starting receive_messages()")
        while self._receiving:
            count += 1
            print("Heartbeat", count)
            await asyncio.sleep(1)
            # message = await self._queue.get()
            # print(f"Message received: {message}")

    async def gather_readings(self):
        """
        Runs forever gathering sensor data
        """
        # Prevent checking again too soon, but don't create a backlog
        print("Starting gather_readings()")
        self._settings_lock.acquire(True)
        next_scan = datetime.fromisoformat(self._settings['next_scan'])
        self._settings_lock.release()

        if datetime.now() > next_scan:
            next_scan = datetime.now()

        while self._gathering:
            if datetime.now() > next_scan:
                # Prepare the settings values to prevent
                # locking them up while scanning
                self._settings_lock.acquire(True)
                scan_seconds = self._settings['scan_seconds']
                sensor_file = self._settings['sensor_file']
                max_attempts = self._settings['max_attempts']
                interval = timedelta(
                    minutes=self._settings['interval']['mins'],
                    seconds=self._settings['interval']['secs']
                )
                self._settings_lock.release()
                print("Scanning for new devices...")

                # Load any newly discovered devices
                new_x_devices = await SensorServer.find_new_xiaomi_devices(
                    self._devices,
                    scan_seconds
                )
                if len(new_x_devices):
                    self._sensor_lock.acquire(True)
                    self._devices = { **self._devices, **new_x_devices }
                    print(f"Newly discovered devices: {new_x_devices}")
                    SensorServer.save_devices(
                        self._devices,
                        sensor_file
                    )
                    self._sensor_lock.release()

                print("Finished scanning, go get readings...")
                devices = await SensorServer.gather_sensor_readings(
                    self._devices,
                    max_attempts
                )
                self._sensor_lock.acquire(True)
                self._devices = devices
                # Save the updated readings
                SensorServer.save_devices(
                    self._devices,
                    sensor_file
                )
                self._sensor_lock.release()
                await self.broadcast_sensors()

                next_scan = next_scan + interval
                # Quickly check we're not running massively over with
                # the scanning time - start the next scan to try and
                # keep up with the interval, but don't make up for lost
                # scans
                if datetime.now() > next_scan:
                    next_scan = datetime.now()

                self._settings_lock.acquire(True)
                self._settings['next_scan'] = next_scan.isoformat()
                settings = self._settings
                self._settings_lock.release()

                SensorServer.save_settings(
                    settings,
                    self._settings_filename
                )
                print("Done for now.")
            else:
                await asyncio.sleep(1)

    async def broadcast_message(self, message_json, client_id=None):
        """
        Broadcasts a message to all or the selected client ID
        """
        # Assume we want to iterate through several messages
        if not isinstance(message_json, list):
            message_json = [message_json]

        keys = None
        if client_id is None:
            self._client_lock.acquire(True)
            keys = self._clients.keys()
            self._client_lock.release()
        else:
            if isinstance(client_id, list):
                keys = client_id
            else:
                keys = [client_id]

        for key in keys:
            try:
                self._client_lock.acquire(True)
                client = self._clients[key]
                self._client_lock.release()
                for msg in message_json:
                    # print(f"Sending {msg} to {key}")
                    await client.send(msg)
            except Exception as e:
                print("Failed to send message to client:", key)
                raise e

    async def broadcast_settings(self, client_id=None):
        """
        Broadcasts the current settings to the clients
        """
        self._settings_lock.acquire(True)
        message = {
            'cmd': 'settings',
            'data': self._settings
        }
        self._settings_lock.release()
        await self.broadcast_message(dumps(message), client_id)

    async def broadcast_sensors(self, client_id=None):
        """
        Broadcasts the latest sensor information to the clients
        """
        self._sensor_lock.acquire(True)
        message = {
            'cmd': 'sensors',
            'data': self._devices
        }
        self._sensor_lock.release()
        await self.broadcast_message(dumps(message), client_id)

    async def handle_message(self, message):
        """
        Handles incoming message and returns a response to be
        sent to the client
        """
        cmd = message['cmd']
        data = message['data']
        if cmd == 'settings':
            # The client wants to update the current settings
            print("Updating settings")
            self._settings_lock.acquire(True)
            self._settings = data
            self._settings_lock.release()
            print("Settings updated -> broadcasting")
            self.broadcast_settings()

        elif cmd  == 'sensors':
            # The client has made changes to all sensors
            print("Updating sensors")
            self._sensor_lock.acquire(True)
            self._devices = data
            self._sensor_lock.release()
            print("Sensors updated -> broadcasting")
            self.broadcast_sensors()

        elif cmd == 'single_sensor':
            # The client has made changes to one sensor
            addr = data['index']
            sensor_data = data['sensor']
            self._sensor_lock.acquire(True)
            if addr in self._sensors:
                self._sensors[addr]['sensor_name'] = sensor_data['sensor_name']
                self._sensors[addr]['active'] = sensor_data['active']
            self._sensor_lock.release()

        else:
            print("Unknown command:", cmd)

    async def new_client(self, client, path):
        """
        Handles incoming new client connections
        """
        self._client_lock.acquire(True)
        client_id = self._client_count
        self._client_count += 1
        self._clients[client_id] = client
        print(f"New client {client_id}: {client.remote_address}")
        self._client_lock.release()

        try:
            await self.broadcast_settings(client_id)
            await self.broadcast_sensors(client_id)
            # await self.broadcast_settings()
            # await self.broadcast_whatever()

            while True:
                json_str = ""
                try:
                    # pass
                    json_str = await client.recv()
                    json_data = None
                    try:
                        json_data = loads(json_str)
                    except:
                        print("Error parsing:", json_str)

                    if json_data is not None:
                        await self.handle_message(json_data)

                    # print(json_str)
                    # await client.send(json_str * 2)

                    # json_data = loads(json_str)
                    # Do something to obtain the message command and data

                    # if msg.cmd == whatever:
                    #     do the thing
                    # elif msg.cmd == some other:
                    #     do another thing
                    # else:
                    #     raise NotImplementedError(
                    #         f"Unknown command: {msg.cmd}")
                except Exception as e:
                    print("Failed to process incoming message:", json_str)
                    print(e)
                    raise e
        except websockets.exceptions.ConnectionClosedOK:
            print(f'Client {client_id} closed the connection')
            self.remove_client(client_id)
            print(f'Client {client_id} removed.')
        except websockets.exceptions.ConnectionClosedError:
            print(f'Client {client_id} closed the connection (error)')
            self.remove_client(client_id)
            print(f'Client {client_id} removed.')
        except KeyboardInterrupt:
            print('Keyboard interrupt hit')
            self.remove_client(client_id)
        except Exception as e:
            print('Unknown error encountered')
            print(e)
            self.remove_client(client_id)
            raise e

    def remove_client(self, client_id):
        """
        Removes a disconnected client
        """
        self._client_lock.acquire(True)
        del self._clients[client_id]
        self._client_lock.release()

    @staticmethod
    def save_settings(settings, filename):
        """
        Saves the current settings to file
        """
        if isinstance(settings, dict):
            settings['save_id'] += 1
            settings_json = dumps(settings, sort_keys=True, indent=4)
        else:
            raise Exception("Settings must be provided as a dictionary.")
        with open(filename, 'w') as f:
            f.write(settings_json)
            print(f"Settings saved with index {settings['save_id']}")

    @staticmethod
    async def find_new_xiaomi_devices(existing, duration):
        """
        Finds all new xiaomi devices by name and address.
        Assigns a new name to the device in the form:
        Sensor xx, where xx is the next available index

        Note: We use another script via subprocess, as it
        would otherwise block and prevent asyncio from running.
        Why this happens, I don't know!
        """
        x_devices = {}
        args = ['./find_new_xdevices.py', '-d', str(duration)]
        if len(existing):
            args += ['-e'] + list(existing.keys())
        proc = await asyncio.create_subprocess_exec(
            *args,
            stdout=asyncio.subprocess.PIPE
        )
        try:
            data = await asyncio.wait_for(
                proc.communicate(),
                timeout=180
            )
        except asyncio.TimeoutError:
            print("Finding devices timed out")

        if proc.returncode == 0:
            x_devices = loads(data[0])

        print('--------------------------')
        print(x_devices)
        print('--------------------------')
        return x_devices

    @staticmethod
    def save_devices(devices, filename):
        """
        Saves the device settings to the given file name
        """
        if isinstance(devices, dict):
            devices = dumps(devices, sort_keys=True, indent=4)
        else:
            print("We got dis: ", devices)
            raise Exception("Device information must be a dictionary")
        with open(filename, 'w') as f:
            f.write(devices)

    @staticmethod
    def load_settings(filename):
        """
        Loads the configuration settings for scanning and gathering
        """
        # Create some defaults
        settings = {
            'interval': {
                'mins': 2, 'secs': 30
            },
            'sensor_file': 'wss_sensors.json',
            'save_id': 0,
            'scan_seconds': 5,
            'max_attempts': 3,
            'next_scan': datetime.now().isoformat()
        }
        loaded = False
        if path.isfile(filename):
            try:
                with open(filename, 'r') as f:
                    settings_json = f.read()
                    settings = loads(settings_json)
                    print(f"Loaded settings: {settings_json}")
                    loaded = True
            except:
                pass
        if not loaded:
            print("Not loaded, using default")
            SensorServer.save_settings(settings, filename)
        return settings

    @staticmethod
    def load_devices(filename):
        """
        Loads existing device info from JSON file
        """
        devices ={}
        if path.isfile(filename):
            try:
                with open(filename, 'r') as f:
                    devices_json = f.read()
                    devices = loads(devices_json)
                    print(f"Found devices: {devices_json}")
            except Exception as e:
                print(f"Failed to open {filename}")
                raise e
                pass
        return devices

    @staticmethod
    async def gather_sensor_readings(devices, max_attempts):
        """
        Connects to each device and gathers the readings
        """
        keys = devices.keys()

        for addr in keys:
            device = devices[addr]
            attempts = 0
            readings_complete = False
            while attempts < max_attempts and not readings_complete:
                attempts += 1
                result = ExitCodes.OK
                try:
                    print(f"Attempting to read from sensor {device['sensor_name']}...")
                    proc = await asyncio.create_subprocess_exec(
                        './get_sensor_data.py',
                        device['addr'],
                        stdout=asyncio.subprocess.PIPE
                    )
                    reading = await asyncio.wait_for(
                        proc.communicate(),
                        timeout=180
                    )
                except concurrent.futures._base.TimeoutError:
                    result = ExitCodes.USER_CANCELLED
                except KeyboardInterrupt:
                    result = ExitCodes.USER_CANCELLED
                except TimeoutError:
                    result = ExitCodes.TIMED_OUT
                except Exception as e:
                    raise e

                if proc.returncode == ExitCodes.OK:
                    print('Data -> ', reading)
                    reading = loads(reading[0])
                    print('Return code -> ', proc.returncode)
                    devices[addr]['last_reading'] = reading
                    SensorServer.update_histories(devices[addr], reading)
                    print(f"Device {device['sensor_name']} ({device['addr']}) -> {dumps(reading, sort_keys=True, indent=4)}")
                    readings_complete = True
                elif proc.returncode == ExitCodes.INVALID_ARGS:
                    raise RuntimeError('The script requires an address!')
                elif proc.returncode == ExitCodes.USER_CANCELLED:
                    print("User cancelled scan.")
                    readings_complete = True
                elif proc.returncode == ExitCodes.TIMED_OUT:
                    print(f"Data wasn't sent ({attempts}/{max_attempts})")
                elif proc.returncode == ExitCodes.DISCONNECTED:
                    print(f"Failed to connect. Perhaps the device is busy elsewhere.")
                    attempts = max_attempts
                else:
                    print("Unknown exception.")
                    attempts = max_attempts

                # client = Lywsd02Client(device['addr'])
                # reading = {
                #     'timestamp': datetime.now().isoformat(),
                #     'temperature': client.temperature,
                #     'humidity': client.humidity,
                #     'battery': client.battery
                # }

                # except KeyboardInterrupt:

                # except TimeoutError:

                # except BTLEDisconnectError:

                #     # Force an early end to the loop - Waiting for a connection is time consuming

                # except Exception as e:
                #     # print(e)
                #     # print(f"Failed to gather readings for {device['addr']}. Trying again...")
                #     raise e
        return devices

    @staticmethod
    def update_histories(device, new_reading):
        """
        Updates the history file for the given device
        """
        history_json = ""
        history = {}
        if path.isfile(device['history_file']):
            try:
                with open(device['history_file'], 'r') as f:
                    history_json = f.read()
                    history = loads(history_json) if len(history_json) else {}
            except Exception as e:
                print("Failed to open history file.")
                raise e

        history[new_reading['timestamp']] = new_reading
        with open(device['history_file'], 'w') as f:
            f.write(dumps(history, sort_keys=True, indent=4))

if __name__ == '__main__':
    print(f"Starting server: {Constants.ADDR}:{Constants.PORT}")
    main_loop = asyncio.get_event_loop()
    server = SensorServer(
        Constants.ADDR,
        Constants.PORT,
        Constants.SETTINGS_FILENAME,
        main_loop
    )
    main_loop.run_until_complete(server._server)
    main_loop.run_forever()


from django.urls import path, include
from rest_framework.routers import SimpleRouter

from highm_server.custom_lookup_type_router import CustomLookupTypeRouter
from .views_for_student import (
    LessonViewSet,
)
from .views_for_teacher import (
    TeacherLessonViewSet,
    GroupStatementViewSet,
    LessonResultViewSet,
)

root_router = CustomLookupTypeRouter()
root_router.register('lesson', LessonViewSet, basename='lesson')

group_router = SimpleRouter()
group_router.register('lesson', TeacherLessonViewSet,
                      basename='teacher-lesson')
group_router.register('statement', GroupStatementViewSet,
                      basename='group-statement')

lesson_router = SimpleRouter()
lesson_router.register('result', LessonResultViewSet,
                       basename='lesson-result')

urlpatterns = [
    path('', include(root_router.urls)),
    path('teacher-panel/group/<str:group>/', include(group_router.urls)),
    path('teacher-panel/group/<str:group>/lesson/<int:lesson>/',
         include(lesson_router.urls)),
]


import os
import pandas as pd
import numpy as np
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
import time
import pickle

def unix_time(dt):
  timeArray = time.strptime(dt, "%Y-%m-%d %H:%M:%S")
  timestamp = int(time.mktime(timeArray))
  return timestamp

#extract data from database
def handle():
  target_db = "sqlite:///SideScan.db"
  print(target_db)
  engine = create_engine(target_db, echo=False)
  Session = sessionmaker(bind=engine)
  session = Session()
  ret = session.execute("select * from Side_Channel_Info;").fetchall()
  csv_header = session.execute("PRAGMA table_info(Side_Channel_Info);").fetchall()
  csv_header = [each[1] for each in csv_header]
  df = pd.DataFrame(ret,columns=csv_header)
  del(df['_id'])
  df.to_csv("raw_data.csv",index=0)

#Let the time interval be 50ms, and get the increments
def trans_mx(xv):
  r = xv[:1].values[:,1:]
  for i in range(1,61):
    start = xv.System_Time[:1].values[0]+i*50
    if len(xv[xv.System_Time==start])>0:
      r = np.concatenate([r,xv[xv.System_Time==start].values[:,1:]])
    else:
      point = xv[xv.System_Time<start].values[-1:,1:] if xv.System_Time[xv.System_Time<start].values[-1]>r[-1,0] else r[-1:,:] #the biggest one that less than
      r = np.concatenate([r,(xv[xv.System_Time>start][:1].values[:,1:]+point)/2])
  r = r[1:]-r[:-1]#increment
  return r[:,1:].reshape(1,60,17)

def get_mx():
  count = 0
  mx = []
  label = []
  all_event = pd.read_csv("all_event.csv")#get the ground truth
  time_col = np.array(list(all_event.timestamp))
  class_col = np.array(list(all_event["class"]))
  df = pd.read_csv("raw_data.csv")#get the raw data
  for each,each_class in zip(time_col,class_col):
    count = count+1
    print(count)
    xv = df[(df.System_Time>=each-1550)&(df.System_Time<each+1700)]#get the 3 seconds around the event
    #if there is no data or the duration is less than 3 seconds, skip the event
    if len(xv)==0 or xv.System_Time.iloc[-1]-xv.System_Time.iloc[0]<3050:
        print("Not Valid",each)
        continue
    xv = trans_mx(xv)
    if mx==[]:
      mx = xv
    else:
      mx = np.concatenate([mx,xv],axis=0)
    label.append(each_class)
  #get baseline
  pickle.dump([mx,label],open('multiple_mx.pkl','wb'),protocol=2)

#Get the increments of baseline
def trans_mx_baseline(xv):#
    r = xv.values[:,1:]
    r = r[1:]-r[:-1]
    return r[:,1:].reshape(1,60,17)

def get_baseline():
    x=pickle.load(open("multiple_mx.pkl",'rb'))
    label = np.array(x[1])
    df = pd.read_csv("raw_data.csv")
    x0 = df[df.System_Time>1591650000000]# make sure there is not event after the time point
    base = []
    for i in range(3000):#number of baseline
        xv=x0[i*61:(i+1)*61]
        if base==[]:
           base = trans_mx_baseline(xv)#get the increment
        else:
           base = np.concatenate([base,trans_mx_baseline(xv)],axis=0)
    x = np.concatenate([x[0],base],axis=0)#concatenate the data
    label = np.concatenate([label,np.zeros(3000)])#concatenate the label
    pickle.dump([x,label],open("multiple_mx.pkl","wb"),protocol=2)

if __name__ == "__main__":
  handle()#extract csv from database
  get_mx()#get the data with label
  get_baseline()#get the baseline(need all_event.csv file)


"""
Skylar Koroluk
Some pieces borrowed from Irene
Obstacle Avoidance Plan B for USST rover 2018

This process needs to be run on the Nvidia Jetson
(or other machine with the ZED sdk installed and working).
"""

import pyzed.camera as zcam
import pyzed.defines as sl
import pyzed.types as tp
import pyzed.core as core
import math
import time
import numpy as np
import sys
import cv2
from robocluster import Device
import config
log = config.getLogger()

AvoidanceDecision = Device('AvoidanceDecision', 'rover', network=config.network)

DISTANCE_THRESHOLD = 2 #meters
PIXEL_THRESHOLD = 30

AvoidanceDecision.storage.first = True
@AvoidanceDecision.every('0.0000001ms')
async def main():
    #init()
    start_time = time.time()
    """Takes in a sampled frame from the ZED camera and decides whether the rover needs to adjust its course
    returns either "right", "left" or None."""

    # Open the camera
    if AvoidanceDecision.storage.first==True:
        AvoidanceDecision.storage.zed = zcam.PyZEDCamera()
        # Create a PyInitParameters object and set configuration parameters
        AvoidanceDecision.storage.init_params = zcam.PyInitParameters()
        AvoidanceDecision.storage.init_params.depth_mode = sl.PyDEPTH_MODE.PyDEPTH_MODE_QUALITY  # Use PERFORMANCE depth mode
        AvoidanceDecision.storage.init_params.coordinate_units = sl.PyUNIT.PyUNIT_MILLIMETER  # Use milliliter units (for depth measurements)

        err = AvoidanceDecision.storage.zed.open(AvoidanceDecision.storage.init_params)

        log.debug("check time: ", time.time() - start_time)
        failed = 0
        while err != tp.PyERROR_CODE.PySUCCESS:
            failed += 1
            log.error("\rCould not open camera", failed, "times")
            err = AvoidanceDecision.storage.zed.open(AvoidanceDecision.storage.init_params)
        AvoidanceDecision.storage.first = False
        # Create and set PyRuntimeParameters after opening the camera
        AvoidanceDecision.storage.runtime_parameters = zcam.PyRuntimeParameters()
        AvoidanceDecision.storage.runtime_parameters.sensing_mode = sl.PySENSING_MODE.PySENSING_MODE_STANDARD  #use fill mode

    runtime_parameters = AvoidanceDecision.storage.runtime_parameters
    zed = AvoidanceDecision.storage.zed
    image = core.PyMat()
    depth = core.PyMat()
    point_cloud = core.PyMat()
    depth_display = core.PyMat()
    frame_capped = False
    while frame_capped==False:  # loops until it succesfully grabs a frame
        # A new image is available if grab() returns PySUCCES
        if zed.grab(runtime_parameters) == tp.PyERROR_CODE.PySUCCESS:
            start_logic_time = time.time()
            frame_capped = True
            # Retrieve left image
            zed.retrieve_image(image, sl.PyVIEW.PyVIEW_LEFT)
            #zed.retrieve_image(depth_display, sl.PyVIEW.PyVIEW_DEPTH)
            # Retrieve depth map. Depth is aligned on the left image
            #zed.retrieve_measure(depth, sl.PyMEASURE.PyMEASURE_DEPTH)
            # Retrieve colored point cloud. Point cloud is aligned on the left image.
            zed.retrieve_measure(point_cloud, sl.PyMEASURE.PyMEASURE_XYZRGBA)
            # We measure the distance camera - object using Euclidean distance
            # Break image into three zones vertically

            width = image.get_width()
            height = image.get_height()
            log.debug("pixels: {}".format(width*height))
            zone_end = [round(width/3), round(width/3)*2, width]
            zone_start = [0, round(width/3), round(width/3)*2]
            zone_count = [0, 0, 0]
            for index in range(3):
                for x in range(zone_start[index], zone_end[index], 10):
                    for y in range(0, round(height*2/3), 10):
                        err, point_cloud_value = point_cloud.get_value(x, y)
                        distance = math.sqrt(point_cloud_value[0] * point_cloud_value[0] +
                                 point_cloud_value[1] * point_cloud_value[1] +
                                 point_cloud_value[2] * point_cloud_value[2])
                        distance = distance/1000 #convert it to meters manually (for now at least)
                        if np.isnan(distance):
                            distance = 0
                        if np.isinf(distance):
                            distance = 4
                        #print("Distance to Camera at ({0}, {1}): {2} mm\n".format(x, y, distance))
                        if distance > DISTANCE_THRESHOLD:
                            zone_count[index] += 1
            turn = None
            if zone_count[0] > zone_count[2]:
                furthest = 0
                furthest_value = zone_count[0]
            else:
                furthest = 2
                furthest_value = zone_count[2]
            if zone_count[1] > (furthest_value - PIXEL_THRESHOLD):
                furthest = 1
            if furthest == 0:
                turn = "left"
            elif furthest == 2:
                turn = "right"

            sys.stdout.flush()

    # Close the camera
    #zed.close()
    total_time = time.time()-start_time
    logic_time = time.time()-start_logic_time
    startup_time = total_time-logic_time
    log.debug("Total elapsed time: ", total_time)
    log.debug("Startup time: ", startup_time)
    log.debug("Logic time: ", logic_time)
    log.debug("zone1: ", zone_count[0], "\nzone2: ", zone_count[1], "\nzone3: ", zone_count[2])
    log.debug("turn", turn)
    #cv2.imshow("Frame", depth_display.get_data())
    #key = cv2.waitKey(1)
    await AvoidanceDecision.publish('DirectionToTurn', turn)

AvoidanceDecision.start()
AvoidanceDecision.wait()


import torch
import utils

def torch_eval(val_fn):
    @torch.no_grad()
    def decorated(self, gen, *args, **kwargs):
        gen.eval()
        ret = val_fn(self, gen, *args, **kwargs)
        gen.train()

        return ret

    return decorated

class Evaluator:
    def __init__(self, writer):
        torch.backends.cudnn.benchmark = True
        self.writer = writer

    @torch_eval
    def comparable_val_saveimg(self, gen, loader, step, n_row, tag='val'):
        compare_batches = self.infer_fact_loader(gen, loader)
        comparable_grid = utils.make_comparable_grid(*compare_batches[::-1], nrow=n_row)
        saved_path = self.writer.add_image(tag, comparable_grid, global_step=step)

        return comparable_grid, saved_path

    @torch_eval
    def infer_fact_loader(self, gen, loader, save_dir=None):
        outs = []
        trgs = []

        for batch in loader:
            style_imgs = batch["style_imgs"].cuda()
            char_imgs = batch["source_imgs"].unsqueeze(1).cuda()

            out = gen.gen_from_style_char(style_imgs, char_imgs)
            outs.append(out.detach().cpu())
            if "trg_imgs" in batch:
                trgs.append(batch["trg_imgs"])

        outs = torch.cat(outs).float()
        ret = (outs,)
        if trgs:
            trgs = torch.cat(trgs)
            ret += (trgs,)

        return ret


import pandas as pd
import numpy as np
import math
from dataset import datasetSiap, n_fiturDataset, n_fiturKlasifikasi, dataklasifikasiSiap, data_klasifikasi

csv_bobotAwal = pd.read_csv("dataset/bobot_modal.csv", delimiter=',', header=0)
bobotAwal = np.array(csv_bobotAwal)                         #konversi bobot csv menjadi array
iBobot = bobotAwal.astype(float)                               #konversi bobot menjadi tipe float

n_kelas = len(iBobot[:,0])                                   #menghitung banyaknya bobot
n_epoch = 5                                                  #Jumlah Epoh
lr = 0.05                                                    #learning Rate
penurunan = 0.01                                            #penurunan learning rate

def euclidean(x,y):
    jarak = 0.0
    for i in range (0,n_fiturDataset):
        jarak += (x[i]-y[i])**2
    return float("{:.8f}".format(math.sqrt(jarak)))

def training(bobot,n_kelas,n_epoch,lr,penurunan):
    # Training
    itr = 0
    while(itr < n_epoch):
        alfa = lr
        reducingFactor = penurunan

        # Menghitunga Banyak Data
        n_dataTraining	= len(datasetSiap)
        print("Training data berdasarkan dataset dan bobot yang dimasukan")
        print("Epoch ke - " + str(itr+1))
        for i in range(0, int(n_dataTraining)):
            data  =[]
            kelas =[]

            print("Iterasi Data ke - " + str(i+1))
            #Menghitung Jarak Data
            for j in range(0, n_kelas ):

                x = datasetSiap[i]
                y = bobot[j]
                # print("J = "+str(datasetSiap[i]))

                data.append(euclidean(x,y))
                kelas.append(bobot[j][n_fiturDataset])
                #print("bobot akhir = "+ str(kelas))
                dataJarak = {'jarak' : pd.Series(data),
                            'kelas' : pd.Series(kelas)}
            # end for

            print("Cj = " +str(data))
            df = pd.DataFrame(dataJarak, columns = ['jarak', 'kelas'])
            df = df.sort_values(by=['jarak'])
            # print("df = "+str(df))

            kelasTemp = df.iloc[0,1]
            kelasTemp = int(kelasTemp)
            print("Cj minimum = W"+str(kelasTemp))

            if kelasTemp == datasetSiap[i][n_fiturDataset]:
                for x in range(0, n_fiturDataset):
                    bobot[kelasTemp-1][x] = bobot[kelasTemp-1][x] +  alfa * ( datasetSiap[i][x] - bobot[kelasTemp-1][x] )
                    # print("rumus (T=Cj): "+str(bobot[kelasTemp-1][x])+" + "+str(alfa)+" x ("+str(datasetSiap[i][x])+" - "+str(bobot[kelasTemp-1][x])+")")
            else:
                for x in range(0, n_fiturDataset):
                    bobot[kelasTemp-1][x] = bobot[kelasTemp-1][x] -  alfa * ( datasetSiap[i][x] - bobot[kelasTemp-1][x] )
                    # print("rumus (T><Cj): "+str(bobot[kelasTemp-1][x])+" - "+str(alfa)+" x ("+str(datasetSiap[i][x])+" - "+str(bobot[kelasTemp-1][x])+")")
            tampilBobotBaru = bobot[:,:4]
            print("W"+str(kelasTemp)+" baru = "+str(tampilBobotBaru[kelasTemp-1])+"\n")
        itr+=1
        rF = reducingFactor * alfa
        alfa = alfa - rF
    tBobotakhir = pd.DataFrame(tampilBobotBaru, index=pd.Index(['W1','W2','W3']),columns=['','','',''])
    print("Diperoleh bobot akhir: "+str(tBobotakhir)+"\n")

    return bobot

def klasifikasi(bobot,dataklasifikasiSiap,n_fiturKlasifikasi,data_klasifikasi):
    print("Klasifikasi Data Berdasarkan Bobot Hasil Training!")
    n_dataKlasifikasi = len(dataklasifikasiSiap)
    keputusan = []
    ketentuan_keputusan = ['TUNDA','TIDAK','YA']
    for i in range(0, int(n_dataKlasifikasi)):
            data =[]
            kelas=[]

            print("Klasifikasi Data Ke - " + str(i+1))
            #Menghitung Jarak Data
            for j in range(0, n_kelas ):

                x = dataklasifikasiSiap[i]
                y = bobot[j]
                # print("J = "+str(datasetSiap[i]))

                data.append(euclidean(x,y))
                kelas.append(bobot[j][n_fiturKlasifikasi])
                #print("bobot akhir = "+ str(kelas))
                dataJarak = {'jarak' : pd.Series(data),
                            'kelas' : pd.Series(kelas)}
            # end for

            print("Cj = " +str(data))
            df = pd.DataFrame(dataJarak, columns = ['jarak', 'kelas'])
            df = df.sort_values(by=['jarak'])
            # print("df = "+str(df))

            kelasTemp = df.iloc[0,1]
            kelasTemp = int(kelasTemp)
            keputusan += [ketentuan_keputusan[kelasTemp-1]]
            print("Jarak Terkecil adalah = W"+str(kelasTemp)+"\n")
    print("Berikut tabel klasifikasi hasil keputusan dengan ketentuan")
    print("W1 = Tunda")
    print("W2 = Tidak")
    print("W3 = Ya")
    keputusan = np.reshape(keputusan,(n_dataKlasifikasi,1))
    data_klasifikasi = np.append(data_klasifikasi,keputusan, axis=1)
    tData_klasifikasi = pd.DataFrame(data_klasifikasi, columns=['Nama UMKM','Lama Usaha','Jumlah Pekerjaan','Omzet','Jumlah Aset','Keputusan'])
    print(tData_klasifikasi)

#main program start here
bobotFinal = training(iBobot,n_kelas,n_epoch,lr,penurunan)
klasifikasi(bobotFinal,dataklasifikasiSiap,n_fiturKlasifikasi,data_klasifikasi)


# -*- coding: utf-8 -*-
"""
Created on Sun Feb  6 19:23:21 2022

@author: Abhi
"""

from constants import CONSTANTS
from file_utils import FileUtils
from text_utils import TextUtils, AdjectiveUtils
from citation_utils import CitationUtils

ALL_CITATIONS = []
ALL_ADJECTIVES = []

open(CONSTANTS.OUTPUTFILE, 'w').close()
open("default_sentiments.txt", 'w').close()

for currentText in CONSTANTS.PAPERS.values():
    print(f"Processing text pdf: {currentText}")
    # pages = readPageTextIncremented(currentText)
    pages = FileUtils.extract_paragraphs(currentText)
    restructuredText = TextUtils.restructure_text(pages)
    citations = CitationUtils.get_citations(restructuredText)
    # print(citations)
    #locations = CitationUtils.get_citations_locations(restructuredText)
    # print(locations)
    #paragraphs = TextUtils.get_paragraphs(restructuredText)
    #references = TextUtils.get_references(restructuredText)
    #references_list = TextUtils.get_references_list(references)
    #cited_text = CitationUtils.cite_text(restructuredText, citations)

    citations_dictionary = CitationUtils.get_citations_paragraph_location(
        restructuredText, citations)

    # ========== CITATIONS DATA ===========
    citations_data = CitationUtils.get_citations_data(citations_dictionary)
    ALL_CITATIONS.append(citations_data)

    # ========== ADJECTIVES DATA ===========
    ALL_ADJECTIVES.extend(
        AdjectiveUtils.get_adjectives_from_citations_data(citations_data))
    ALL_ADJECTIVES = sorted(list(set(ALL_ADJECTIVES)))

    AdjectiveUtils.write_adjectives_to_csv(ALL_ADJECTIVES)

    # print("============ SENTIMENTS (NLTK SIA) =============")
    sentiments = CitationUtils.get_sentiments(citations_dictionary)
    # printSentiments(sentiments)
    CitationUtils.write_sentiments_to_file(sentiments)

    # print("============ WRITE TO FILE =============")
    CitationUtils.write_citations_data(citations_data, currentText)

CitationUtils.write_citations_to_csv(ALL_CITATIONS)


from flask import Flask, request, g, url_for
import sqlite3
import json
import requests

app = Flask(__name__)

DATABASE = "report"

class Report:
    def __init__(self, type, date, time, description, latitude, longitude):
        self.type = type
        self.date = date
        self.time = time
        self.description = description
        self.latitude = latitude
        self.longitude = longitude

def get_db(param=0):
    db = getattr(g, '_database', None)
    if db is None or param == 1:
        db = g._database = sqlite3.connect(DATABASE)
        conn = sqlite3.connect(DATABASE)
        cur = conn.cursor()
        cur.execute("CREATE TABLE IF NOT EXISTS REPORT(Id INTEGER PRIMARY KEY AUTOINCREMENT, type VARCHAR, "
                    "date VARCHAR, time VARCHAR, description VARCHAR, latitude VARCHAR, longitude VARCHAR)")
        conn.commit()
        conn.close()
    return db

@app.route('/', methods=['GET', 'PUT', 'POST'])
def hello_world():
    return 'Hello World!'

@app.route('/report', methods=['PUT', 'POST', 'GET'])
def report():
    if request.method == 'POST' or request.method == 'PUT':
        print("putting/posting")
        print(request.json)
        type = request.json["type"]
        date = request.json["date"]
        time = request.json["time"]
        description = request.json["description"]
        latitude = request.json["latitude"]
        longitude = request.json["longitude"]
        report = Report(type, date, time, description, latitude, longitude)
        print(report)

        get_db(1).execute("INSERT INTO REPORT VALUES (NULL, ?, ?, ?, ?, ?, ?)",
                          (type, date, time, description, latitude, longitude))
        get_db().commit()
        get_db().close
        return("success")

    elif request.method == 'GET':
        print("here")
        reports = ""
        for col in get_db().execute("SELECT * FROM REPORT"):
            current_report = Report(col[1], col[2], col[3], col[4], col[5], col[6])
            json_obj = json.dumps(current_report.__dict__)
            reports += json_obj + '\n'
        print(reports)
        return reports
    else:
        return "nothing"

# host="localhost", port=5000

if __name__ == '__main__':
    app.run()


# -*- coding: utf-8 -*-

from skimage import io
import numpy as np

centers = np.load('codebook_tiger.npy')

c_image = io.imread('compressed_tiger.png')

image = np.zeros((c_image.shape[0],c_image.shape[1],3),dtype=np.uint8 )
for i in range(c_image.shape[0]):
    for j in range(c_image.shape[1]):
            image[i,j,:] = centers[c_image[i,j],:]

io.imsave('reconstructed_tiger.png',image);
io.imshow(image)
io.show()


import json
import logging
import math
import os
import re
import subprocess
from datetime import datetime
from random import randint
from typing import Dict, List, Optional, Union

from dateutil.parser import parse
from dateutil.tz import tzlocal

from toil.batchSystems.abstractBatchSystem import BatchJobExitReason
from toil.batchSystems.abstractGridEngineBatchSystem import \
    AbstractGridEngineBatchSystem
from toil.batchSystems.lsfHelper import (check_lsf_json_output_supported,
                                         parse_mem_and_cmd_from_output,
                                         parse_memory,
                                         per_core_reservation)
from toil.lib.misc import call_command

logger = logging.getLogger(__name__)

class LSFBatchSystem(AbstractGridEngineBatchSystem):

    class Worker(AbstractGridEngineBatchSystem.Worker):
        """LSF specific AbstractGridEngineWorker methods."""

        def getRunningJobIDs(self):
            times = {}
            with self.runningJobsLock:
                currentjobs = {str(self.batchJobIDs[x][0]): x for x in
                                   self.runningJobs}

            if check_lsf_json_output_supported:
                stdout = call_command(["bjobs","-json","-o", "jobid stat start_time"])

                bjobs_records = self.parseBjobs(stdout)
                if bjobs_records:
                    for single_item in bjobs_records:
                        if single_item['STAT'] == 'RUN' and single_item['JOBID'] in currentjobs:
                            jobstart = parse(single_item['START_TIME'], default=datetime.now(tzlocal()))
                            times[currentjobs[single_item['JOBID']]] = datetime.now(tzlocal()) \
                            - jobstart
            else:
                times = self.fallbackRunningJobIDs(currentjobs)
            return times

        def fallbackRunningJobIDs(self, currentjobs):
            times = {}
            stdout = call_command(["bjobs", "-o", "jobid stat start_time delimiter='|'"])
            for curline in stdout.split('\n'):
                items = curline.strip().split('|')
                if items[0] in currentjobs and items[1] == 'RUN':
                    jobstart = parse(items[2], default=datetime.now(tzlocal()))
                    times[currentjobs[items[0]]] = datetime.now(tzlocal()) \
                        - jobstart
            return times

        def killJob(self, jobID):
            call_command(['bkill', self.getBatchSystemID(jobID)])

        def prepareSubmission(self,
                              cpu: int,
                              memory: int,
                              jobID: int,
                              command: str,
                              jobName: str,
                              job_environment: Optional[Dict[str, str]] = None,
                              gpus: Optional[int] = None):
            return (self.prepareBsub(cpu, memory, jobID) + [command],
                    job_environment)  # pass job_environment to .submitJob()

        def submitJob(self, subLine):
            subLine, job_environment = subLine
            combinedEnv = self.boss.environment
            combinedEnv.update(os.environ)
            if job_environment:
                combinedEnv.update(job_environment)

            stdout = call_command(subLine, env=combinedEnv)
            # Example success: Job <39605914> is submitted to default queue <general>.
            # Example fail: Service class does not exist. Job not submitted.
            result_search = re.search('Job <(.*)> is submitted', stdout)

            if result_search:
                result = int(result_search.group(1))
                logger.debug(f"Got the job id: {result}")
            else:
                logger.error(f"Could not submit job\nReason: {stdout}")
                temp_id = randint(10000000, 99999999)
                # Flag this job to be handled by getJobExitCode
                result = f"NOT_SUBMITTED_{temp_id}"
            return result

        def coalesce_job_exit_codes(self, batch_job_id_list: list) -> list:
            status_dict = {}
            valid_batch_job_id_list = []
            status_resonse = []
            for single_lsf_id in batch_job_id_list:
                if "NOT_SUBMITTED" in single_lsf_id:
                    logger.error(
                        "bjobs detected job [%s] failed to submit", single_lsf_id
                    )
                    status_dict[single_lsf_id] = 1
                job = single_lsf_id
                if "." in single_lsf_id:
                    job = single_lsf_id.split(".", 1)[0]
                valid_batch_job_id_list.append(job)
            if valid_batch_job_id_list:
                args = [
                    "bjobs",
                    "-json",
                    "-o",
                    "jobid user exit_code stat exit_reason pend_reason",
                ] + valid_batch_job_id_list
                logger.debug("Getting coalesced job exit codes via bjobs")
                bjobs_records = self.parseBjobs(
                    subprocess.run(
                        args, check=False, stderr=subprocess.STDOUT, encoding="utf-8"
                    ).stdout
                )
                if bjobs_records:
                    for single_record in bjobs_records:
                        if "JOBID" in single_record:
                            single_job_id = single_record["JOBID"]
                            status_dict[single_job_id] = self.parse_bjobs_record(
                                single_record, single_job_id
                            )
            for single_lsf_id in batch_job_id_list:
                if "NOT_SUBMITTED" in single_lsf_id:
                    status_resonse.append(status_dict[single_lsf_id])
                else:
                    job = single_lsf_id
                    if "." in single_lsf_id:
                        job = single_lsf_id.split(".", 1)[0]
                    if job in status_dict:
                        status_resonse.append(status_dict[job])
                    else:
                        status_resonse.append(None)
            return status_resonse

        def getJobExitCode(self, lsfJobID):
            # the task is set as part of the job ID if using getBatchSystemID()
            if "NOT_SUBMITTED" in lsfJobID:
                logger.error("bjobs detected job failed to submit")
                return 1

            job, task = (lsfJobID, None)
            if '.' in lsfJobID:
                job, task = lsfJobID.split('.', 1)

            self.parseMaxMem(job)
            # first try bjobs to find out job state
            if check_lsf_json_output_supported:
                args = ["bjobs", "-json", "-o",
                        "user exit_code stat exit_reason pend_reason", str(job)]
                logger.debug("Checking job exit code for job via bjobs: "
                             "{}".format(job))
                stdout = call_command(args)
                bjobs_records = self.parseBjobs(stdout)
                if bjobs_records:
                    process_output = bjobs_records[0]
                    return self.parse_bjobs_record(process_output, job)

            return self.fallbackGetJobExitCode(job)

        def parse_bjobs_record(self, bjobs_record: dict, job: int) -> Union[int, None]:
            """
            Helper functions for getJobExitCode and  to parse the bjobs status record
            """
            if "STAT" in bjobs_record:
                process_status = bjobs_record["STAT"]
                if process_status == "DONE":
                    logger.debug("bjobs detected job completed for job: %s", job)
                    return 0
                if process_status == "PEND":
                    pending_info = ""
                    if "PEND_REASON" in bjobs_record:
                        if bjobs_record["PEND_REASON"]:
                            pending_info = "\n" + bjobs_record["PEND_REASON"]
                    logger.debug(
                        "bjobs detected job pending with: %s\nfor job: %s",
                        pending_info, job
                    )
                    return None
                if process_status == "EXIT":
                    exit_code = 1
                    exit_reason = ""
                    if "EXIT_CODE" in bjobs_record:
                        exit_code_str = bjobs_record["EXIT_CODE"]
                        if exit_code_str:
                            exit_code = int(exit_code_str)
                    if "EXIT_REASON" in bjobs_record:
                        exit_reason = bjobs_record["EXIT_REASON"]
                    exit_info = ""
                    if exit_code:
                        exit_info = f"\nexit code: {exit_code}"
                    if exit_reason:
                        exit_info += f"\nexit reason: {exit_reason}"
                    logger.error(
                        "bjobs detected job failed with: %s\nfor job: %s",
                        exit_info, job
                    )
                    if "TERM_MEMLIMIT" in exit_reason:
                        return BatchJobExitReason.MEMLIMIT
                    return exit_code
                if process_status == "RUN":
                    logger.debug(
                        "bjobs detected job started but not completed for job: %s", job
                    )
                    return None
                if process_status in {"PSUSP", "USUSP", "SSUSP"}:
                    logger.debug("bjobs detected job suspended for job: %s", job)
                    return None

                return self.getJobExitCodeBACCT(job)

        def getJobExitCodeBACCT(self,job):
            # if not found in bjobs, then try bacct (slower than bjobs)
            logger.debug("bjobs failed to detect job - trying bacct: "
                         "{}".format(job))

            args = ["bacct", "-l", str(job)]
            stdout = call_command(args)
            process_output = stdout.split('\n')
            for line in process_output:
                if line.find("Completed <done>") > -1 or line.find("<DONE>") > -1:
                    logger.debug("Detected job completed for job: "
                                 "{}".format(job))
                    return 0
                elif line.find("Completed <exit>") > -1 or line.find("<EXIT>") > -1:
                    logger.error("Detected job failed for job: "
                                 "{}".format(job))
                    return 1
            logger.debug("Can't determine exit code for job or job still "
                         "running: {}".format(job))
            return None

        def fallbackGetJobExitCode(self, job):
            args = ["bjobs", "-l", str(job)]
            logger.debug(f"Checking job exit code for job via bjobs (fallback): {job}")
            stdout = call_command(args)
            output = stdout.replace("\n                     ", "")
            process_output = output.split('\n')
            started = 0
            for line in process_output:
                if "Done successfully" in line or "Status <DONE>" in line:
                    logger.debug(f"bjobs detected job completed for job: {job}")
                    return 0
                elif "New job is waiting for scheduling" in line:
                    logger.debug(f"bjobs detected job pending scheduling for job: {job}")
                    return None
                elif "PENDING REASONS" in line or "Status <PEND>" in line:
                    logger.debug(f"bjobs detected job pending for job: {job}")
                    return None
                elif "Exited with exit code" in line:
                    exit = int(line[line.find("Exited with exit code ")+22:].split('.')[0])
                    logger.error(f"bjobs detected job exit code {exit} for job {job}")
                    return exit
                elif "Completed <exit>" in line:
                    logger.error(f"bjobs detected job failed for job: {job}")
                    return 1
                elif line.find("Started on ") > -1 or "Status <RUN>" in line:
                    started = 1
            if started == 1:
                logger.debug(f"bjobs detected job started but not completed: {job}")
                return None

            return self.getJobExitCodeBACCT(job)

        """
        Implementation-specific helper methods
        """
        def prepareBsub(self, cpu: int, mem: int, jobID: int) -> List[str]:
            """
            Make a bsub commandline to execute.

            params:
              cpu: number of cores needed
              mem: number of bytes of memory needed
              jobID: ID number of the job
            """
            bsubMem = []
            if mem:
                mem = float(mem)
                if per_core_reservation() and cpu:
                    mem = mem / math.ceil(cpu)
                mem = parse_memory(mem)
                bsubMem = ['-R',
                           f'select[mem>{mem}] '
                           f'rusage[mem={mem}]',
                           '-M', mem]
            bsubCpu = [] if cpu is None else ['-n', str(math.ceil(cpu))]
            bsubline = ["bsub", "-cwd", ".", "-J", f"toil_job_{jobID}"]
            bsubline.extend(bsubMem)
            bsubline.extend(bsubCpu)
            stdoutfile: str = self.boss.format_std_out_err_path(jobID, '%J', 'out')
            stderrfile: str = self.boss.format_std_out_err_path(jobID, '%J', 'err')
            bsubline.extend(['-o', stdoutfile, '-e', stderrfile])
            lsfArgs = os.getenv('TOIL_LSF_ARGS')
            if lsfArgs:
                bsubline.extend(lsfArgs.split())
            return bsubline

        def parseBjobs(self, bjobs_output_str):
            """
            Parse records from bjobs json type output

            :params bjobs_output_str: stdout of bjobs json type output
            """
            bjobs_dict = None
            bjobs_records = None
            # Handle Cannot connect to LSF. Please wait ... type messages
            dict_start = bjobs_output_str.find('{')
            dict_end = bjobs_output_str.rfind('}')
            if dict_start != -1 and dict_end != -1:
                bjobs_output = bjobs_output_str[dict_start:(dict_end+1)]
                try:
                    bjobs_dict = json.loads(bjobs_output)
                except json.decoder.JSONDecodeError:
                    logger.error(f"Could not parse bjobs output: {bjobs_output_str}")
                if 'RECORDS' in bjobs_dict:
                    bjobs_records = bjobs_dict['RECORDS']
            if bjobs_records is None:
                logger.error(f"Could not find bjobs output json in: {bjobs_output_str}")

            return bjobs_records

        def parseMaxMem(self, jobID):
            """
            Parse the maximum memory from job.

            :param jobID: ID number of the job
            """
            try:
                output = subprocess.check_output(["bjobs", "-l", str(jobID)], text=True)
                max_mem, command = parse_mem_and_cmd_from_output(output=output)
                if not max_mem:
                    logger.warning(f"[job ID {jobID}] Unable to Collect Maximum Memory Usage: {output}")
                    return

                if not command:
                    logger.warning(f"[job ID {jobID}] Cannot Parse Max Memory Due to Missing Command String: {output}")
                else:
                    logger.info(f"[job ID {jobID}, Command {command.group(1)}] Max Memory Used: {max_mem.group(1)}")
                return max_mem
            except subprocess.CalledProcessError as e:
                logger.warning(f"[job ID {jobID}] Unable to Collect Maximum Memory Usage: {e}")

    def getWaitDuration(self):
        """We give LSF a second to catch its breath (in seconds)"""
        return 60


#!/usr/bin/env python3

"""Test whether routing is (more or less) deterministic (https://github.com/r5py/r5py/issues/240)."""

import itertools

import pandas.testing
import pytest

import r5py

@pytest.fixture(scope="module")
def intermediate_results():
    # will be modified in each iteration,
    # used to compare results across computation runs
    yield {}

# Python 3.10+
def pairwise(iterable):
    """pairwise("ABCDEFG") --> AB BC CD DE EF FG ."""
    try:
        return itertools.pairwise(iterable)
    except AttributeError:  # Python<3.10
        a, b = itertools.tee(iterable)
        next(b, None)
        return zip(a, b)

class TestDeterministicBehaviour:
    @pytest.mark.parametrize(
        "transport_modes",
        [
            (r5py.TransportMode.WALK,),
            (r5py.TransportMode.BICYCLE,),
            (r5py.TransportMode.CAR,),
            (r5py.TransportMode.TRANSIT, r5py.TransportMode.WALK),
        ],
    )
    @pytest.mark.parametrize("iteration", range(5))
    def test_public_transport_travel_time_matrices(
        self,
        transport_network,
        population_grid_points,
        departure_datetime,
        transport_modes,
        intermediate_results,
        iteration,
    ):
        travel_time_matrix_computer = r5py.TravelTimeMatrixComputer(
            transport_network,
            population_grid_points,
            departure=departure_datetime,
            transport_modes=transport_modes,
            snap_to_network=True,
        )
        travel_time_matrix_computer.request.breakdown_stats = (
            r5py.r5.BreakdownStat.MINIMUM
        )
        travel_times = travel_time_matrix_computer.compute_travel_times()

        if transport_modes not in intermediate_results:
            intermediate_results[transport_modes] = []
        intermediate_results[transport_modes].append(travel_times)

        for matrix_a, matrix_b in pairwise(intermediate_results[transport_modes]):
            pandas.testing.assert_frame_equal(
                matrix_a,
                matrix_b,
                check_like=True,
            )


from io import BytesIO

import xlrd
from django.test import TestCase
from django.utils import translation
from model_bakery import baker

from evap.contributor.views import export_contributor_results
from evap.evaluation.models import (
    Contribution,
    Course,
    CourseType,
    Degree,
    Evaluation,
    Question,
    Questionnaire,
    QuestionType,
    Semester,
    TextAnswer,
    UserProfile,
)
from evap.evaluation.tests.tools import make_rating_answer_counters
from evap.results.exporters import ResultsExporter, TextAnswerExporter
from evap.results.tools import cache_results, get_results
from evap.results.views import filter_text_answers

class TestExporters(TestCase):
    def test_grade_color_calculation(self):
        exporter = ResultsExporter()
        self.assertEqual(exporter.STEP, 0.2)
        self.assertEqual(exporter.normalize_number(1.94999999999), 1.8)
        # self.assertEqual(exporter.normalize_number(1.95), 2.0)  # floats ftw
        self.assertEqual(exporter.normalize_number(1.95000000001), 2.0)
        self.assertEqual(exporter.normalize_number(1.99999999999), 2.0)
        self.assertEqual(exporter.normalize_number(2.0), 2.0)
        self.assertEqual(exporter.normalize_number(2.00000000001), 2.0)
        self.assertEqual(exporter.normalize_number(2.1), 2.0)
        self.assertEqual(exporter.normalize_number(2.149999999999), 2.0)
        # self.assertEqual(exporter.normalize_number(2.15), 2.2)  # floats again
        self.assertEqual(exporter.normalize_number(2.150000000001), 2.2)
        self.assertEqual(exporter.normalize_number(2.8), 2.8)

    def test_questionnaire_ordering(self):
        degree = baker.make(Degree)
        evaluation = baker.make(
            Evaluation,
            course__degrees=[degree],
            state=Evaluation.State.PUBLISHED,
            _participant_count=2,
            _voter_count=2,
        )

        questionnaire_1 = baker.make(Questionnaire, order=1, type=Questionnaire.Type.TOP)
        questionnaire_2 = baker.make(Questionnaire, order=4, type=Questionnaire.Type.TOP)
        questionnaire_3 = baker.make(Questionnaire, order=1, type=Questionnaire.Type.BOTTOM)
        questionnaire_4 = baker.make(Questionnaire, order=4, type=Questionnaire.Type.BOTTOM)

        question_1 = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=questionnaire_1)
        question_2 = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=questionnaire_2)
        question_3 = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=questionnaire_3)
        question_4 = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=questionnaire_4)

        evaluation.general_contribution.questionnaires.set(
            [questionnaire_1, questionnaire_2, questionnaire_3, questionnaire_4]
        )

        make_rating_answer_counters(question_1, evaluation.general_contribution)
        make_rating_answer_counters(question_2, evaluation.general_contribution)
        make_rating_answer_counters(question_3, evaluation.general_contribution)
        make_rating_answer_counters(question_4, evaluation.general_contribution)

        cache_results(evaluation)

        binary_content = BytesIO()
        ResultsExporter().export(
            binary_content,
            [evaluation.course.semester],
            [([course_degree.id for course_degree in evaluation.course.degrees.all()], [evaluation.course.type.id])],
            True,
            True,
        )
        binary_content.seek(0)
        workbook = xlrd.open_workbook(file_contents=binary_content.read())

        self.assertEqual(workbook.sheets()[0].row_values(4)[0], questionnaire_1.public_name)
        self.assertEqual(workbook.sheets()[0].row_values(5)[0], question_1.text)

        self.assertEqual(workbook.sheets()[0].row_values(7)[0], questionnaire_2.public_name)
        self.assertEqual(workbook.sheets()[0].row_values(8)[0], question_2.text)

        self.assertEqual(workbook.sheets()[0].row_values(10)[0], questionnaire_3.public_name)
        self.assertEqual(workbook.sheets()[0].row_values(11)[0], question_3.text)

        self.assertEqual(workbook.sheets()[0].row_values(13)[0], questionnaire_4.public_name)
        self.assertEqual(workbook.sheets()[0].row_values(14)[0], question_4.text)

    def test_heading_question_filtering(self):
        degree = baker.make(Degree)
        evaluation = baker.make(
            Evaluation,
            course__degrees=[degree],
            state=Evaluation.State.PUBLISHED,
            _participant_count=2,
            _voter_count=2,
        )
        contributor = baker.make(UserProfile)
        evaluation.general_contribution.questionnaires.set([baker.make(Questionnaire)])

        questionnaire = baker.make(Questionnaire)
        baker.make(Question, type=QuestionType.HEADING, questionnaire=questionnaire, order=0)
        heading_question = baker.make(Question, type=QuestionType.HEADING, questionnaire=questionnaire, order=1)
        likert_question = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=questionnaire, order=2)
        baker.make(Question, type=QuestionType.HEADING, questionnaire=questionnaire, order=3)

        contribution = baker.make(
            Contribution, evaluation=evaluation, questionnaires=[questionnaire], contributor=contributor
        )
        make_rating_answer_counters(likert_question, contribution)

        cache_results(evaluation)

        binary_content = BytesIO()
        ResultsExporter().export(
            binary_content,
            [evaluation.course.semester],
            [([course_degree.id for course_degree in evaluation.course.degrees.all()], [evaluation.course.type.id])],
            True,
            True,
        )
        binary_content.seek(0)
        workbook = xlrd.open_workbook(file_contents=binary_content.read())

        self.assertEqual(workbook.sheets()[0].row_values(4)[0], questionnaire.public_name)
        self.assertEqual(workbook.sheets()[0].row_values(5)[0], heading_question.text)
        self.assertEqual(workbook.sheets()[0].row_values(6)[0], likert_question.text)
        self.assertEqual(workbook.sheets()[0].row_values(7)[0], "")

    def test_view_excel_file_sorted(self):
        semester = baker.make(Semester)
        course_type = baker.make(CourseType)
        degree = baker.make(Degree)
        evaluation1 = baker.make(
            Evaluation,
            state=Evaluation.State.PUBLISHED,
            course=baker.make(Course, degrees=[degree], type=course_type, semester=semester, name_de="A", name_en="B"),
            name_de="Evaluation1",
            name_en="Evaluation1",
        )
        evaluation2 = baker.make(
            Evaluation,
            state=Evaluation.State.PUBLISHED,
            course=baker.make(Course, degrees=[degree], type=course_type, semester=semester, name_de="B", name_en="A"),
            name_de="Evaluation2",
            name_en="Evaluation2",
        )

        cache_results(evaluation1)
        cache_results(evaluation2)

        content_de = BytesIO()
        with translation.override("de"):
            ResultsExporter().export(content_de, [semester], [([degree.id], [course_type.id])], True, True)

        content_en = BytesIO()
        with translation.override("en"):
            ResultsExporter().export(content_en, [semester], [([degree.id], [course_type.id])], True, True)

        content_de.seek(0)
        content_en.seek(0)

        # Load responses as Excel files and check for correct sorting
        workbook = xlrd.open_workbook(file_contents=content_de.read())
        self.assertEqual(workbook.sheets()[0].row_values(0)[1], "A – Evaluation1\n")
        self.assertEqual(workbook.sheets()[0].row_values(0)[2], "B – Evaluation2\n")

        workbook = xlrd.open_workbook(file_contents=content_en.read())
        self.assertEqual(workbook.sheets()[0].row_values(0)[1], "A – Evaluation2\n")
        self.assertEqual(workbook.sheets()[0].row_values(0)[2], "B – Evaluation1\n")

    def test_course_type_ordering(self):
        degree = baker.make(Degree)
        course_type_1 = baker.make(CourseType, order=1)
        course_type_2 = baker.make(CourseType, order=2)
        semester = baker.make(Semester)
        evaluation_1 = baker.make(
            Evaluation,
            course=baker.make(Course, semester=semester, degrees=[degree], type=course_type_1),
            state=Evaluation.State.PUBLISHED,
            _participant_count=2,
            _voter_count=2,
        )
        evaluation_2 = baker.make(
            Evaluation,
            course=baker.make(Course, semester=semester, degrees=[degree], type=course_type_2),
            state=Evaluation.State.PUBLISHED,
            _participant_count=2,
            _voter_count=2,
        )

        cache_results(evaluation_1)
        cache_results(evaluation_2)

        questionnaire = baker.make(Questionnaire)
        question = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=questionnaire)

        evaluation_1.general_contribution.questionnaires.set([questionnaire])
        make_rating_answer_counters(question, evaluation_1.general_contribution)

        evaluation_2.general_contribution.questionnaires.set([questionnaire])
        make_rating_answer_counters(question, evaluation_2.general_contribution)

        binary_content = BytesIO()
        ResultsExporter().export(
            binary_content, [semester], [([degree.id], [course_type_1.id, course_type_2.id])], True, True
        )
        binary_content.seek(0)
        workbook = xlrd.open_workbook(file_contents=binary_content.read())

        self.assertEqual(workbook.sheets()[0].row_values(0)[1], evaluation_1.full_name + "\n")
        self.assertEqual(workbook.sheets()[0].row_values(0)[2], evaluation_2.full_name + "\n")

        course_type_2.order = 0
        course_type_2.save()

        binary_content = BytesIO()
        ResultsExporter().export(
            binary_content, [semester], [([degree.id], [course_type_1.id, course_type_2.id])], True, True
        )
        binary_content.seek(0)
        workbook = xlrd.open_workbook(file_contents=binary_content.read())

        self.assertEqual(workbook.sheets()[0].row_values(0)[1], evaluation_2.full_name + "\n")
        self.assertEqual(workbook.sheets()[0].row_values(0)[2], evaluation_1.full_name + "\n")

    def test_multiple_sheets(self):
        binary_content = BytesIO()
        semester = baker.make(Semester)
        ResultsExporter().export(binary_content, [semester], [([], []), ([], [])])

        binary_content.seek(0)
        workbook = xlrd.open_workbook(file_contents=binary_content.read())

        self.assertEqual(len(workbook.sheets()), 2)

    @staticmethod
    def get_export_sheet(semester, degree, course_types, include_unpublished=True, include_not_enough_voters=True):
        binary_content = BytesIO()
        ResultsExporter().export(
            binary_content,
            [semester],
            [([degree.id], course_types)],
            include_unpublished=include_unpublished,
            include_not_enough_voters=include_not_enough_voters,
        )
        binary_content.seek(0)
        workbook = xlrd.open_workbook(file_contents=binary_content.read())
        return workbook.sheets()[0]

    def test_include_unpublished(self):
        semester = baker.make(Semester)
        degree = baker.make(Degree)
        published_evaluation = baker.make(
            Evaluation,
            state=Evaluation.State.PUBLISHED,
            course__semester=semester,
            course__degrees=[degree],
            course__type__order=1,
        )
        unpublished_evaluation = baker.make(
            Evaluation,
            state=Evaluation.State.REVIEWED,
            course__semester=semester,
            course__degrees=[degree],
            course__type__order=2,
        )
        course_types = [published_evaluation.course.type.id, unpublished_evaluation.course.type.id]

        cache_results(published_evaluation)
        cache_results(unpublished_evaluation)

        # First, make sure that the unpublished does not appear
        sheet = self.get_export_sheet(
            include_unpublished=False, semester=semester, degree=degree, course_types=course_types
        )
        self.assertEqual(len(sheet.row_values(0)), 2)
        self.assertEqual(sheet.row_values(0)[1][:-1], published_evaluation.full_name)

        # Now, make sure that it appears when wanted
        sheet = self.get_export_sheet(
            include_unpublished=True, semester=semester, degree=degree, course_types=course_types
        )
        self.assertEqual(len(sheet.row_values(0)), 3)
        # These two should be ordered according to evaluation.course.type.order
        self.assertEqual(sheet.row_values(0)[1][:-1], published_evaluation.full_name)
        self.assertEqual(sheet.row_values(0)[2][:-1], unpublished_evaluation.full_name)

    def test_include_not_enough_voters(self):
        semester = baker.make(Semester)
        degree = baker.make(Degree)
        enough_voters_evaluation = baker.make(
            Evaluation,
            state=Evaluation.State.PUBLISHED,
            course__semester=semester,
            course__degrees=[degree],
            _voter_count=1000,
            _participant_count=1000,
        )
        not_enough_voters_evaluation = baker.make(
            Evaluation,
            state=Evaluation.State.PUBLISHED,
            course__semester=semester,
            course__degrees=[degree],
            _voter_count=1,
            _participant_count=1000,
        )

        cache_results(enough_voters_evaluation)
        cache_results(not_enough_voters_evaluation)

        course_types = [enough_voters_evaluation.course.type.id, not_enough_voters_evaluation.course.type.id]

        # First, make sure that the one with only a single voter does not appear
        sheet = self.get_export_sheet(semester, degree, course_types, include_not_enough_voters=False)
        self.assertEqual(len(sheet.row_values(0)), 2)
        self.assertEqual(sheet.row_values(0)[1][:-1], enough_voters_evaluation.full_name)

        # Now, check with the option enabled
        sheet = self.get_export_sheet(semester, degree, course_types, include_not_enough_voters=True)
        self.assertEqual(len(sheet.row_values(0)), 3)
        self.assertEqual(
            {enough_voters_evaluation.full_name, not_enough_voters_evaluation.full_name},
            {sheet.row_values(0)[1][:-1], sheet.row_values(0)[2][:-1]},
        )

    def test_no_degree_or_course_type(self):
        evaluation = baker.make(Evaluation)
        with self.assertRaises(AssertionError):
            ResultsExporter().export(BytesIO(), [evaluation.course.semester], [])

    def test_exclude_single_result(self):
        degree = baker.make(Degree)
        evaluation = baker.make(
            Evaluation, is_single_result=True, state=Evaluation.State.PUBLISHED, course__degrees=[degree]
        )
        cache_results(evaluation)
        sheet = self.get_export_sheet(evaluation.course.semester, degree, [evaluation.course.type.id])
        self.assertEqual(
            len(sheet.row_values(0)), 1, "There should be no column for the evaluation, only the row description"
        )

    def test_exclude_used_but_unanswered_questionnaires(self):
        degree = baker.make(Degree)
        evaluation = baker.make(
            Evaluation,
            _voter_count=10,
            _participant_count=10,
            state=Evaluation.State.PUBLISHED,
            course__degrees=[degree],
        )
        used_questionnaire = baker.make(Questionnaire)
        used_question = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=used_questionnaire)
        unused_questionnaire = baker.make(Questionnaire)
        unused_question = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=unused_questionnaire)

        evaluation.general_contribution.questionnaires.set([used_questionnaire, unused_questionnaire])
        make_rating_answer_counters(used_question, evaluation.general_contribution)
        cache_results(evaluation)

        sheet = self.get_export_sheet(evaluation.course.semester, degree, [evaluation.course.type.id])
        self.assertEqual(sheet.row_values(4)[0], used_questionnaire.public_name)
        self.assertEqual(sheet.row_values(5)[0], used_question.text)
        self.assertNotIn(unused_questionnaire.name, sheet.col_values(0))
        self.assertNotIn(unused_question.text, sheet.col_values(0))

    def test_degree_course_type_name(self):
        degree = baker.make(Degree, name_en="Celsius")
        course_type = baker.make(CourseType, name_en="LetsPlay")
        evaluation = baker.make(
            Evaluation, course__degrees=[degree], course__type=course_type, state=Evaluation.State.PUBLISHED
        )
        cache_results(evaluation)

        sheet = self.get_export_sheet(evaluation.course.semester, degree, [course_type.id])
        self.assertEqual(sheet.col_values(1)[1:3], [degree.name, course_type.name])

    def test_multiple_evaluations(self):
        semester = baker.make(Semester)
        degree = baker.make(Degree)
        evaluation1 = baker.make(
            Evaluation, course__semester=semester, course__degrees=[degree], state=Evaluation.State.PUBLISHED
        )
        evaluation2 = baker.make(
            Evaluation, course__semester=semester, course__degrees=[degree], state=Evaluation.State.PUBLISHED
        )
        cache_results(evaluation1)
        cache_results(evaluation2)

        sheet = self.get_export_sheet(semester, degree, [evaluation1.course.type.id, evaluation2.course.type.id])

        self.assertEqual(
            set(sheet.row_values(0)[1:]), set((evaluation1.full_name + "\n", evaluation2.full_name + "\n"))
        )

    def test_correct_grades_and_bottom_numbers(self):
        degree = baker.make(Degree)
        evaluation = baker.make(
            Evaluation,
            _voter_count=5,
            _participant_count=10,
            course__degrees=[degree],
            state=Evaluation.State.PUBLISHED,
        )
        questionnaire1 = baker.make(Questionnaire, order=1)
        questionnaire2 = baker.make(Questionnaire, order=2)
        question1 = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=questionnaire1)
        question2 = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=questionnaire2)

        make_rating_answer_counters(question1, evaluation.general_contribution, [1, 0, 1, 0, 0])
        make_rating_answer_counters(question2, evaluation.general_contribution, [0, 1, 0, 1, 0])

        evaluation.general_contribution.questionnaires.set([questionnaire1, questionnaire2])
        cache_results(evaluation)

        sheet = self.get_export_sheet(evaluation.course.semester, degree, [evaluation.course.type.id])

        self.assertEqual(sheet.row_values(5)[1], 2.0)  # question 1 average
        self.assertEqual(sheet.row_values(8)[1], 3.0)  # question 2 average
        self.assertEqual(sheet.row_values(10)[1], 2.5)  # Average grade
        self.assertEqual(sheet.row_values(11)[1], "5/10")  # Voters / Participants
        self.assertEqual(sheet.row_values(12)[1], "50%")  # Voter percentage

    def test_course_grade(self):
        degree = baker.make(Degree)
        course = baker.make(Course, degrees=[degree])
        evaluations = baker.make(
            Evaluation,
            course=course,
            name_en=iter(["eval0", "eval1", "eval2"]),
            name_de=iter(["eval0", "eval1", "eval2"]),
            state=Evaluation.State.PUBLISHED,
            _voter_count=5,
            _participant_count=10,
            _quantity=3,
        )

        grades_per_eval = [[1, 1, 0, 0, 0], [0, 1, 1, 0, 0], [1, 0, 1, 0, 0]]
        expected_average = 2.0

        questionnaire = baker.make(Questionnaire)
        question = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=questionnaire)
        for grades, e in zip(grades_per_eval, evaluations):
            make_rating_answer_counters(question, e.general_contribution, grades)
            e.general_contribution.questionnaires.set([questionnaire])
        for evaluation in evaluations:
            cache_results(evaluation)

        sheet = self.get_export_sheet(course.semester, degree, [course.type.id])
        self.assertEqual(sheet.row_values(12)[1], expected_average)
        self.assertEqual(sheet.row_values(12)[2], expected_average)
        self.assertEqual(sheet.row_values(12)[3], expected_average)

    def test_yes_no_question_result(self):
        degree = baker.make(Degree)
        evaluation = baker.make(
            Evaluation,
            _voter_count=6,
            _participant_count=10,
            course__degrees=[degree],
            state=Evaluation.State.PUBLISHED,
        )
        questionnaire = baker.make(Questionnaire)
        question = baker.make(Question, type=QuestionType.POSITIVE_YES_NO, questionnaire=questionnaire)

        make_rating_answer_counters(question, evaluation.general_contribution, [4, 2])

        evaluation.general_contribution.questionnaires.set([questionnaire])
        cache_results(evaluation)

        sheet = self.get_export_sheet(evaluation.course.semester, degree, [evaluation.course.type.id])
        self.assertEqual(sheet.row_values(5)[0], question.text)
        self.assertEqual(sheet.row_values(5)[1], "67%")

    def test_contributor_result_export(self):
        degree = baker.make(Degree)
        contributor = baker.make(UserProfile)
        other_contributor = baker.make(UserProfile)
        evaluation_1 = baker.make(
            Evaluation,
            course=baker.make(Course, degrees=[degree], responsibles=[contributor]),
            state=Evaluation.State.PUBLISHED,
            _participant_count=10,
            _voter_count=1,
        )
        evaluation_2 = baker.make(
            Evaluation,
            course=baker.make(Course, degrees=[degree], responsibles=[other_contributor]),
            state=Evaluation.State.PUBLISHED,
            _participant_count=2,
            _voter_count=2,
        )
        contribution = baker.make(Contribution, evaluation=evaluation_2, contributor=contributor)
        other_contribution = baker.make(Contribution, evaluation=evaluation_2, contributor=other_contributor)

        general_questionnaire = baker.make(Questionnaire, type=Questionnaire.Type.TOP)
        contributor_questionnaire = baker.make(Questionnaire, type=Questionnaire.Type.CONTRIBUTOR)
        general_question = baker.make(Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=general_questionnaire)
        contributor_question = baker.make(
            Question, type=QuestionType.POSITIVE_LIKERT, questionnaire=contributor_questionnaire
        )

        evaluation_1.general_contribution.questionnaires.set([general_questionnaire])
        make_rating_answer_counters(general_question, evaluation_1.general_contribution, [2, 0, 0, 0, 0])
        evaluation_2.general_contribution.questionnaires.set([general_questionnaire])
        make_rating_answer_counters(general_question, evaluation_2.general_contribution, [0, 0, 0, 2, 0])

        contribution.questionnaires.set([contributor_questionnaire])
        make_rating_answer_counters(contributor_question, contribution, [0, 0, 2, 0, 0])
        other_contribution.questionnaires.set([contributor_questionnaire])
        make_rating_answer_counters(contributor_question, other_contribution, [0, 2, 0, 0, 0])

        cache_results(evaluation_1)
        cache_results(evaluation_2)

        binary_content = export_contributor_results(contributor).content
        workbook = xlrd.open_workbook(file_contents=binary_content)

        self.assertEqual(
            workbook.sheets()[0].row_values(0)[1],
            f"{evaluation_1.full_name}\n{evaluation_1.course.semester.name}\n{contributor.full_name}",
        )
        self.assertEqual(
            workbook.sheets()[0].row_values(0)[2],
            f"{evaluation_2.full_name}\n{evaluation_2.course.semester.name}\n{other_contributor.full_name}",
        )
        self.assertEqual(workbook.sheets()[0].row_values(4)[0], general_questionnaire.public_name)
        self.assertEqual(workbook.sheets()[0].row_values(5)[0], general_question.text)
        self.assertEqual(workbook.sheets()[0].row_values(5)[2], 4.0)
        self.assertEqual(
            workbook.sheets()[0].row_values(7)[0],
            f"{contributor_questionnaire.public_name} ({contributor.full_name})",
        )
        self.assertEqual(workbook.sheets()[0].row_values(8)[0], contributor_question.text)
        self.assertEqual(workbook.sheets()[0].row_values(8)[2], 3.0)
        self.assertEqual(workbook.sheets()[0].row_values(10)[0], "Overall Average Grade")
        self.assertEqual(workbook.sheets()[0].row_values(10)[2], 3.25)

    def test_text_answer_export(self):
        evaluation = baker.make(Evaluation, state=Evaluation.State.PUBLISHED, can_publish_text_results=True)
        questions = baker.make(
            Question,
            questionnaire__type=iter(Questionnaire.Type.values),
            type=QuestionType.TEXT,
            _quantity=len(Questionnaire.Type.values),
            _bulk_create=True,
            allows_additional_textanswers=False,
        )

        baker.make(
            TextAnswer,
            question=iter(questions[idx] for idx in [0, 1, 2, 2, 0]),
            contribution__evaluation=evaluation,
            contribution__questionnaires=iter(questions[idx].questionnaire for idx in [0, 1, 2, 2, 0]),
            review_decision=TextAnswer.ReviewDecision.PUBLIC,
            _quantity=5,
        )

        cache_results(evaluation)
        evaluation_result = get_results(evaluation)
        filter_text_answers(evaluation_result)

        results = TextAnswerExporter.InputData(evaluation_result.contribution_results)

        binary_content = BytesIO()
        TextAnswerExporter(
            evaluation.name, evaluation.course.semester.name, evaluation.course.responsibles_names, results, None
        ).export(binary_content)
        binary_content.seek(0)
        workbook = xlrd.open_workbook(file_contents=binary_content.read())
        sheet = workbook.sheets()[0]

        # Sheet headline
        self.assertEqual(sheet.row_values(0)[0], evaluation.name)
        self.assertEqual(sheet.row_values(1)[0], evaluation.course.semester.name)
        self.assertEqual(sheet.row_values(2)[0], evaluation.course.responsibles_names)

        # Questions are ordered by questionnaire type, answers keep their order respectively
        self.assertEqual(sheet.row_values(3)[0], questions[0].text)
        self.assertEqual(sheet.row_values(5)[0], questions[1].text)
        self.assertEqual(sheet.row_values(6)[0], questions[2].text)


from odoo import api, fields, models
from odoo.osv import expression

class AccountMove(models.Model):

    _inherit = "account.move"

    date_range_fm_id = fields.Many2one(
        comodel_name="date.range",
        string="Fiscal month",
        compute="_compute_date_range_fm",
        search="_search_date_range_fm",
    )

    @api.depends("date", "company_id")
    def _compute_date_range_fm(self):
        for rec in self:
            date = rec.date
            company = rec.company_id
            rec.date_range_fm_id = company and company.find_daterange_fm(date) or False

    @api.model
    def _search_date_range_fm(self, operator, value):
        if operator in ("=", "!=", "in", "not in"):
            date_range_domain = [("id", operator, value)]
        else:
            date_range_domain = [("name", operator, value)]

        date_ranges = self.env["date.range"].search(date_range_domain)
        domain = []
        for date_range in date_ranges:
            domain = expression.OR(
                [
                    domain,
                    [
                        "&",
                        "&",
                        ("date", ">=", date_range.date_start),
                        ("date", "<=", date_range.date_end),
                        "|",
                        ("company_id", "=", False),
                        ("company_id", "=", date_range.company_id.id),
                    ],
                ]
            )
        return domain


from statistics import mode

response_key = {
    'A': ['Outside playing tag or trying a new move on the monkey bars', 'Red', 'Basketball', 'Hot fudge sundae', 'Doing any type of physical activity', 'You start experimenting with household items for fun', 'You bravely decide to climb the tree and free the cat yourself', 'You decide you are going to try something you have never eaten before', 'You choose the dog that is high energy and playful', 'Try rock climbing'],

    'B': ['Playing Chess or reading a good book', 'Blue', 'Swimming', 'Brownie', 'Meditating in a quiet place', 'You immediately start brainstorming and make a list of ideas to carefully plan', 'You are curious about how the cat got stuck there in the first place', 'You are excited by all the possibilities and ask the waiter lots of questions about the menu', 'You find the dog that seems to be sniffing and licking everything', 'To read as many books as possible from the library'],

    'C': ['Playing guitar or helping a friend with an art project', 'Yellow', 'Volleyball', ' Cake to share with friends', "As long as I'm sorrounded by friends I'm calm", 'You grab your friends together and decide to do a group project', 'You feel quite terrible for the poor cat', 'You decide to order an old favorite', 'You pick the dog that looks a bit sad and lonely in the corner', 'Volunteer your time '],

    'D': ['Working on perfecting your latest writing assignment or leading a game of soccer', 'Green', 'Fencing', 'Souffle', "Relax? Don't tell me to relax...I'm working on fine tuning my piano skills", 'You will be working solo on this one', 'You call for rescue and lead the rescue squad to the tree', 'You decide to try the dish which sounds the most flavorful', 'You choose the dog that listens to your commands', 'To work on mastering your tennis skills']}

class Question:

    def __init__(self, question, choices):

        self.question = question
        self.choices = choices

class Quiz:

    def __init__(self, title, questions):

        self.title = title
        self.questions = questions

sorting_hat_quiz = Quiz("Sorting Hat Quiz",
                        [Question("How do you like to spend your free time?", [response_key['A'][0], response_key['B'][0], response_key['C'][0], response_key['D'][0]],),

                         Question("What is your favorite color?", [
                                  response_key['A'][1], response_key['B'][1], response_key['C'][1], response_key['D'][1]]),

                         Question("Which sport do you most like to play?", [
                                  response_key['A'][2], response_key['B'][2], response_key['C'][2], response_key['D'][2]]),

                         Question("If you had to pick one treat off the table which would it be?", [
                                  response_key['A'][3], response_key['B'][3], response_key['C'][3], response_key['D'][3]]),

                         Question("How do you like to relax?", [
                                  response_key['A'][4], response_key['B'][4], response_key['C'][4], response_key['D'][4]]),

                         Question("You have a school science fair project coming up in a month...", [
                                  response_key['A'][5], response_key['B'][5], response_key['C'][5], response_key['D'][5]]),

                         Question("On your way to school you see a cat stuck in a tree", [
                                  response_key['A'][6], response_key['B'][6], response_key['C'][6], response_key['D'][6]]),

                         Question("Your parents take you out to dinner at a new restaurant", [
                                  response_key['A'][7], response_key['B'][7], response_key['C'][7], response_key['D'][7]]),

                         Question("Your parents allow you to adopt a dog from the shelter", [
                                  response_key['A'][8], response_key['B'][8], response_key['C'][8], response_key['D'][8]]),

                         Question("Your New Year's Resolution this year was...", [
                                  response_key['A'][9], response_key['B'][9], response_key['C'][9], response_key['D'][9]]),

                         ])

def determine_house(responses):
    results = []
    for k, v in response_key.items():
        for resp in responses:
            if resp in v:
                results.append(k)

    final_result = mode(results)
    if final_result == 'A':
        house = 'Gryffindor'
    if final_result == 'B':
        house = 'Ravenclaw'
    if final_result == 'C':
        house = 'Hufflepuff'
    if final_result == 'D':
        house = 'Slytherin'

    return house

def house_description(house):

    if house == "Gryffindor":
        desc = "
Gryffindor was one of the four Houses of Hogwarts School of Witchcraft and Wizardry and was founded by Godric Gryffindor. Gryffindor instructed the Sorting Hat to choose students possessing characteristics he most valued, such as courage, chivalry, nerve and determination,to be sorted into his house.

The emblematic animal was a lion, and its colours were scarlet and gold and its house point hourglass was filled with rubies. Sir Nicholas de Mimsy-Porpington, also known as 'Nearly Headless Nick', was the House ghost.

Gryffindor corresponded roughly to the element of fire, and it was for this reason that the colours scarlet and gold were chosen to represent the house. The colour of fire corresponded to that of a lion as well, with scarlet representing the mane and tail and gold representing the coat.

The Gryffindor motto was 'Forti Animo Estote', which was displayed on a stained glass window in the common room.
"

    elif house == "Ravenclaw":
        desc = ""

    elif house == "Hufflepuff":
        desc = ""

    else:
        desc = ""

    return desc

def find_wizard_name(inputs):
    username = inputs['name']
    username += " "
    color_name = get_color_name(inputs['color'])
    username += color_name
    month_name = get_month_name(inputs['month'])
    username += month_name

    return username

def get_month_name(month):
    if month == 'Jan':
        month_name = 'dore'
    if month == 'Feb':
        month_name = 'sley'
    if month == 'Mar':
        month_name = 'wald'
    if month == 'Apr':
        month_name = 'cour'
    if month == 'May':
        month_name = 'ry'
    if month == 'Jun':
        month_name = 'bottom'
    if month == 'Jul':
        month_name = 'diggle'
    if month == 'Aug':
        month_name = 'more'
    if month == 'Sep':
        month_name = 'back'
    if month == 'Oct':
        month_name = 'hart'
    if month == 'Nov':
        month_name = 'strange'
    if month == 'Dec':
        month_name = 'pin'

    return month_name

def get_color_name(color):
    if color == 'red':
        color_name = 'Love'
    if color == 'orange':
        color_name = 'Potter'
    if color == 'yellow':
        color_name = 'Lock'
    if color == 'green':
        color_name = 'Davi'
    if color == 'blue':
        color_name = 'Petti'
    if color == 'pink':
        color_name = 'Mim'
    if color == 'purple':
        color_name = 'Borgin'

    return color_name


# version Python 3.6.4
# 데이터 파씽 parsing 하는 test
# 명령행 인자를 받아서 실행되는 프로그램에서 인자를 파싱할때 사용.

# 옵션인자를 추가 옵션값 제한. [0, 1, 2]번의 선택 옵션.

import argparse

parser = argparse.ArgumentParser()                                                                          # 이용자에게 데이터 파싱을 받음.

parser.add_argument("square", type=int, help="This is display a square calculating of a number")            # 입력을 받으면 args.square에 파싱을 함.
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2], help="increase ouput verboiity")      # 옵션 인자 -v 또는 --verboisty 를 추가하면 옵션에 진입.
                                                                                # choice = [0, 1, 2]를 이용해서 옵션 3가지만 한정함. 그외는 오류.

args = parser.parse_args()
answer = args.square ** 2

if args.verbosity == 2:
    print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
    print("{}^2 == {}".format(args.square, answer))
else:
    print(answer)

'''
  >>> python 모듈이름.py
   -> 에러

  >>> python 모듈이름.py 4
   -> 16                            (기본 디폴트가 2의 제곱으로 되어있어 16만 출력) else조건.

  >>> python 모듈이름.py 4 -v 1     (옵션 0번)
   -> 4^2 == 16                     (elif 1번 모양으로 출력 )

  >>> python 모듈이름.py 4 -v 1     (옵션 1번)
   -> 4^2 == 16                     (elif 1번 모양으로 출력 )

  >>> python 모듈이름.py 4 -v 2     (옵션 2번)
  ->  the square of 4 equals 16     (elif 2번 모양으로 출력)

  >>> python 모듈이름.py 4 -v 4     (그외 -v 3,4,5... 등 을 파싱하면,)
  ->  에러

'''

4_옵션인자 제한.py


from pm4py.util.business_hours import BusinessHours
import datetime
from workalendar.europe import Italy

def execute_script():
    ts1 = 100000000
    ts2 = 110000000
    d1 = datetime.datetime.fromtimestamp(ts1)
    d2 = datetime.datetime.fromtimestamp(ts2)
    print(ts2-ts1)
    # default business hours: all the days of the week except Saturday and Sunday are working days.
    bh1 = BusinessHours(d1, d2, worktiming=[[7, 12.5], [13, 17]])
    print(bh1.getseconds())
    # let's calculate the business hours using a proper work calendar.
    bh2 = BusinessHours(d1, d2, worktiming=[[7, 12.25], [13.25, 17]], workcalendar=Italy())
    print(bh2.getseconds())

if __name__ == "__main__":
    execute_script()


from __future__ import division
__author__ = 'ShanthiS'

import numpy
import csv

from numpy import *
from scipy import stats

# Load data from csv
filename = "../digitsDataset/trainFeatures.csv"
training_features = numpy.loadtxt(filename, delimiter=',')

filename = "../digitsDataset/trainLabels.csv"
training_labels = numpy.loadtxt(filename, delimiter=',')

filename = "../digitsDataset/valFeatures.csv"
val_features = numpy.loadtxt(filename, delimiter=',')

filename = "../digitsDataset/valLabels.csv"
val_labels = numpy.loadtxt(filename, delimiter=',')

filename = "../digitsDataset/testFeatures.csv"
test_features = numpy.loadtxt(filename, delimiter=',')

#for each feature vector in val_features, compute distance between each feature vector in training_features
def kNearestNeighbors(k, num, to_classify):
	final_classifications = []
	for item1 in to_classify:
		row_num = 0
		distances = []
		sorted_indeces = []
		k_index = []
		if row_num < (num):
			for i in range(0, num):
				item2 = training_features[i]
				c = item2 - item1
				dist = numpy.sqrt(numpy.sum(c**2))
				#dist = numpy.linalg.norm(item2 - item1, axis=1)
				#dist = numpy.apply_along_axis(numpy.linalg.norm, 1, c)
				distances.append(dist)
			# find index of k smalled values -- index into training_labels
			sorted_indeces = numpy.argsort(distances)
			k_index = sorted_indeces[:k]

			# find corresponding training_labels
			candidates = []
			for index in k_index:
				candidates.append(training_labels[index])

			# choose mode (breaking ties)
			frequency = stats.itemfreq(candidates)
			# frequency takes form of [[val1, count], [val2, count]..]
			counts = []
			for item in frequency:
				counts.append(item[1])
			#use argmax to find value that has highest count, if multiple options with same highest count, return first occurence (arbitrary tie break)
			final_classifications.append(candidates[numpy.argmax(counts)])
		else:
			break
		row_num += 1
	#write final_classifications to a csv file == digitsOutputk.csv
	to_print = numpy.array(final_classifications).astype(int)
	filename = "digitsOutput" + str(k) + ".csv"
	numpy.savetxt(filename, to_print, fmt='%i', delimiter=',')
	return final_classifications

	#print("=== FINAL ====")
	#print(final_classifications)

def errorRate(k, num):
	# Calculate error rate
	# for each classifiation, set to 1 if right --- [actual (1000) - ours (sum of all 1)]/ actual (1000)
	final_classifications = kNearestNeighbors(k, num, val_features)
	num_correct = 0
	i = 0
	for item in final_classifications:
		if (item == val_labels[i]):
			num_correct += 1
		i += 1
	actual = len(final_classifications)
	error_rate = ((actual - num_correct)/actual)*100
	return error_rate

def experimentWithKVals(trial_k):
	for k in trial_k:
		error_rate = errorRate(k, 6000)
		print("error rate for k = " + str(k) + " is " + str(error_rate) + " %")

# === MAIN ===

experimentWithKVals([1, 2, 5, 10, 25])

#write test_classifications to a file
test_classifications = kNearestNeighbors(1, 6000, test_features)
to_print = numpy.array(test_classifications).astype(int)
numpy.savetxt('digitsOutput.csv', to_print, fmt='%i', delimiter=',') #prints as floats