import random
import time
import json
import cv2
import numpy as np

from glob import glob
try:
    from pynput import keyboard
    from pynput.keyboard import Key
except:
    pass
from matplotlib import pyplot as plt

class Graph():
    def __init__(self, config_path=None, config=None, img_paths=False):
        if config is None or type(config)!=dict:
            if config_path is None:
                print('Graph object needs either a config json/dict object or the path to one')
                breakpoint()
            else:
                with open(config_path) as f:
                    self.config = json.load(f)
        else:
            self.config = config

        self.img_paths = img_paths
        self.vertices = [None]*int(self.config['num_vertices'])
        self.populateVertices(self.config['num_vertices'], self.config['photoPath'])
        self.edges = np.zeros((self.config['num_vertices'],self.config['num_vertices']))
        self.makeEdgeTable(self.config['transitions'])
        # for [X,Y,theta,d], self.config['transitions'] has the vertices that are connected by edges [X,Y]
        # the directions (*approx* theta) you need to turn to get from X to Y assuming the wall opposite the door is
        # theta = 0/360. all angles are listed in degrees (not radians) and assuming the robot is facing theta = 0
        # and turning clockwise to get to Y
        # and the distance d (*approximate* but close enough) between the vertices in feet
        self.weights = [None]*int(self.config['num_vertices']) #TODO: is this just going to be the thing in the vertices list? or will this help with navigation/path planning?

        with open(self.config['envName']+'AngleKey.json') as f:
            self.angleKey = json.load(f)

    def populateVertices(self, num_vertices, assetPath):
        assets = glob(assetPath+'*')
        for i in range(num_vertices):
            if assetPath+'node'+str(i+1) in assets:
                self.vertices[i]=assetPath+'node'+str(i+1)+'/'

    def makeEdgeTable(self, transition_list):
        # TODO: should we put edges between the nodes and themselves? and choosing to stay means you're identifying you're in
        # the goal node?
        # assumes starts counting at Node 1 (no node 0)
        for transitions in transition_list:
            for transition in transitions:
                self.edges[transition[0]-1,transition[1]-1] = 1

    def getReachableVertices(self, current_node, current_direction, angle_threshold=15):
        return [x for x in self.config['transitions'][current_node-1] if abs(x[2]-current_direction)<angle_threshold]

    def getAllNeighbors(self, current_node):
        return self.config['transitions'][current_node - 1]

    def getVertexFeats(self, vertex_num, heading=None, heading_threshold=1):
        if heading is None:
            heading = random.choice(range(0,360,15)) #TODO: expand to all angles maybe?

        while str(heading) not in self.angleKey[str(vertex_num)].keys():
            heading = (heading + 1) % 360

        choice = random.choice(self.angleKey[str(vertex_num)][str(heading)])
        # breakpoint()
        path = self.vertices[vertex_num - 1] + 'node' + str(vertex_num) + '_' + choice + self.config['photoExtension']
        try:
            image = cv2.imread(path)
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        except:
            breakpoint()
        if self.img_paths:
            return path
        else:
            return image

    def addNode(self, nodeFeatures=None, nodeFeatPath=None):
        if nodeFeatures is None:
            if nodeFeatPath is None:
                print('addNode function in Graph object needs either nodeFeatures or nodeFeatPath defined')
                breakpoint()
            else:
                # TODO: load the node features? assuming it's a video? or sequence of frames?
                pass
        else:
            # TODO: add to vertices list (the end for simplicity)
            # TODO: add to edges table (the end for simplicity)
            edgesToAdd= None
            for edges in edgesToAdd:
                self.addEdge(edges[0], edges[1])

    def addEdge(self, node1Num, node2Num):
        # making the executive decision that this is going to be a two-way graph
        # assumes starts counting at Node 1 (no node 0)
        self.edges[node1Num-1, node2Num-1] = 1
        self.edges[node2Num-1, node1Num-1] = 1

    def removeEdge(self, node1Num, node2Num):
        # assumes starts counting at Node 1 (no node 0)
        self.edges[node1Num-1, node2Num-1] = 0
        self.edges[node2Num-1, node1Num-1] = 0

    def removeNode(self, node_num):
        print("remove node function not implemented!!!")
        pass

    def findPath(self, start_node, end_node,start_direction=None, end_direction=None, base_angle = 15):
        path = [start_node]
        actions = []

        next_nodes = self.getAllNeighbors(start_node)
        neighbors = [x[1] for x in next_nodes]

        if start_node == end_node:
            # breakpoint()
            if start_direction is not None and end_direction is not None:
                return [[end_node],self.computeRotationActions(start_direction, end_direction, base_angle)]
            return [[end_node],[]]
        elif end_node in neighbors:
            # breakpoint()
            node = [x for x in next_nodes if x[1]==end_node][0]
            next_steps = self.findPath(end_node, end_node, node[2], end_direction)
            path.append(node)
            path.extend(next_steps[0])
            # path.append(start_node)
            actions.extend(self.computeRotationActions(start_direction, node[2], base_angle))
            actions.append('move forward')
            actions.extend(next_steps[1])
            return [path, actions]
        else:
            # breakpoint()
            next_steps=[]
            for node in next_nodes:
                next_steps.append(self.findPath(node[1], end_node,start_direction,node[2]))
            step_lens = [[len(x[1]),i] for i,x in enumerate(next_steps) if len(x[1])>0]
            # breakpoint()
            smallest = min(step_lens)
            next_step = next_steps[smallest[-1]]
            # p = [n for n in next_step[0] if type(n)==int]
            path.append(next_nodes[smallest[-1]])
            path.extend(next_step[0])
            actions.append('move forward')
            actions.extend(next_step[1])

            return [path, actions]

        # if start_direction is not None:
        #     rotation_action_list = self.computeRotationActions(start_direction, )
        #     actions.extend(rotation_action_list)
        #
        #
        #
        # if end_direction is not None:
        #     rotation_action_list = self.computeRotationActions(end_direction, )
        #     actions.extend(rotation_action_list)

    def computeRotationActions(self, start_dir, end_dir, base_angle=15):
        ang_diff0 = (start_dir - end_dir) % 360
        ang_diff360 = (360 - (start_dir - end_dir)) % 360
        if abs(ang_diff360) >= base_angle and abs(ang_diff0) >= base_angle:
            if ang_diff0 < ang_diff360:
                times = ang_diff0 // base_angle
                return ['turn left'] * times
            else:
                times = ang_diff360 // base_angle
                return ['turn right'] * times
        return []

    def drawGraph(self):
        drawing = np.ones((81,81,3), np.uint8)*255

        ### draw outer walls ###
        #draw top wall
        cv2.line(drawing, (0,4), (16,4), (0,0,0))
        cv2.line(drawing, (16,4), (16,8), (0, 0, 0))
        cv2.line(drawing, (16,8), (72,8), (0, 0, 0))

        #draw left wall
        cv2.line(drawing, (0, 4), (0,80), (0, 0, 0))

        #draw bottom wall
        cv2.line(drawing, (0, 80), (72, 80), (0, 0, 0))

        #draw right wall
        cv2.line(drawing, (72, 80), (72, 8), (0, 0, 0))

        ### draw inner walls/obstacles ###
        # draw left bottom boxes/table
        cv2.line(drawing, (24, 80), (24, 60), (0, 0, 0))
        cv2.line(drawing, (24, 60), (20, 60), (0, 0, 0))
        cv2.line(drawing, (20, 60), (20, 36), (0, 0, 0))
        cv2.line(drawing, (20, 36), (12, 36), (0, 0, 0))
        cv2.line(drawing, (12, 36), (12, 44), (0, 0, 0))
        cv2.line(drawing, (12, 44), (0, 44), (0, 0, 0))

        #draw middle island
        cv2.line(drawing, (32, 80), (32, 20), (0, 0, 0))
        cv2.line(drawing, (32, 20), (60, 20), (0, 0, 0))
        cv2.line(drawing, (60, 20), (60, 56), (0, 0, 0))
        cv2.line(drawing, (60, 56), (56, 56), (0, 0, 0))
        cv2.line(drawing, (56, 56), (56, 80), (0, 0, 0))

        #draw bottom right corner
        cv2.line(drawing, (68, 76), (72, 76), (0, 0, 0))
        cv2.line(drawing, (68,76), (68, 80), (0, 0, 0))

        #draw red space on right wall
        cv2.line(drawing, (68, 60), (72, 60), (0, 0, 0))
        cv2.line(drawing, (68, 60), (68, 20), (0, 0, 0))
        cv2.line(drawing, (68, 20), (72, 20), (0, 0, 0))

        #draw red space on the top wall
        cv2.line(drawing, (72, 12), (60, 12), (0, 0, 0))
        cv2.line(drawing, (60, 12), (60, 8), (0, 0, 0))

        # draw dots for each node with labels
        for point in self.config['positions']:
            cv2.circle(drawing, (72-int(point[0]*4),80-int(point[1]*4)), 1, (204,0,204),-1)

        # label reference objects for easier localization

        return drawing

    def drawPathOnGraph(self, path):
        drawing = self.drawGraph()

        #draw path on the image
        for p in range(len(path)-1):
            point1 = self.config['positions'][str(path[p])]
            point2 = self.config['positions'][str(path[p+1])]
            cv2.line(drawing, (72-int(point1[0]*4),80-int(point1[1]*4)), (72-int(point2[0]*4),80-int(point2[1]*4)), (0,255,0))

        point = self.config['positions'][str(path[-1])]
        cv2.circle(drawing, (72-int(point[0]*4),80-int(point[1]*4)), 1, (0,0,255),-1)

        return drawing

class GraphTraverser():
    def __init__(self, graph: Graph, start_node=1, base_turn_angle=15, plotImgs=False, recordActions=False, distance_reward = True, human=False):
        self.graph = graph
        self.start_node = start_node
        self.base_turn_angle = base_turn_angle
        self.plotImgs = plotImgs
        self.recordActions = recordActions
        self.action_space = 3 #4
        self.distance_reward = distance_reward
        self.human = human #whether the traversal is by a human (true) or an RL agent (false)
        self.current_node = self.start_node
        self.current_direction = 180
        self.fig, self.ax = plt.subplots(1,1)
        self.setGoalNode()
        self.randomInit()
        if self.recordActions:
            self.path = [self.current_node]
            self.actions = []

    def reset(self):
        self.current_node = self.start_node
        self.current_direction = 180
        if self.recordActions:
            self.path = [self.current_node]
            self.actions = []
        # time.sleep(0.5)

    def randomInit(self):
        self.current_node = random.choice(range(1,self.graph.config['num_vertices']+1))
        self.current_direction = random.choice(range(0,360,15)) #TODO: expand to all angles maybe?
        # self.setGoalNode()
        if self.recordActions:
            self.path = [self.current_node]
            self.actions = []
        # time.sleep(0.5)

    def setGoalNode(self, goal=None, direction=None):
        if goal is None:
            # this is for going 2 nodes away
            # first_level_reachable = self.graph.getAllNeighbors(self.current_node)
            # second_level_reachable = []
            # for node_info in first_level_reachable:
            #     second_level_reachable.extend(self.graph.getAllNeighbors(node_info[1]))
            # choice = random.choice(second_level_reachable)
            # self.goalNode = choice[1]
            # self.goalDirection = choice[2]

            # first_level_reachable = self.graph.getAllNeighbors(self.current_node)
            # choice = random.choice(first_level_reachable)
            self.goalNode = random.choice(range(1,self.graph.config['num_vertices']+1))
            self.goalDirection = random.choice(range(0,360,15)) #TODO: expand to all angles maybe?
            # print(self.current_node, self.goalNode, first_level_reachable)
            # time.sleep(0.5)
            #TODO: change distance reward if you go back to the multi step goal
        else:
            self.goalNode = goal
            self.goalDirection=direction
            # time.sleep(0.5)

    def step(self, action):
        # returns reward and observation
        # action is [straight, backward, left, right]
        goal = self.goalNode
        goal_dir = self.goalDirection
        start = self.current_node
        start_dir = self.current_direction
        if action == 0:
            reward = self.moveForward()
            reward = self.checkGoal(reward)
        elif action == 1:
            reward = self.turnLeft()
            reward = self.checkGoal(reward)
        elif action == 2:
            reward = self.turnRight()
            reward = self.checkGoal(reward)
        # elif action == 3:
        #     reward = self.moveBack()
        #     reward = self.checkGoal(reward)
        else:
            print(action,"is not a valid action!")
            breakpoint()

        image = self.getImg()
        return image, reward, (start, start_dir, goal, goal_dir), reward>=1

    def checkGoal(self, reward):
        node1pos = self.graph.config['positions'][str(self.current_node)]
        node2pos = self.graph.config['positions'][str(self.goalNode)]
        landmark_pos = np.array(self.graph.config['landmarks'])
        if self.current_node == self.goalNode and abs(self.current_direction-self.goalDirection)<self.base_turn_angle:
            return 1
            # return abs(np.sum(np.dot(landmark_pos, node1pos)))/10
        elif self.distance_reward:
            node1pos=self.graph.config['positions'][str(self.current_node)]
            node2pos = self.graph.config['positions'][str(self.goalNode)]
            # goal_vec = self.getGoalVector()
            # return -((node2pos[0]-node1pos[0])**2+(node2pos[1]-node1pos[1])**2)**0.5
            # return - np.dot(node1pos, node2pos)/10 #???
            # if abs(np.sum(np.dot(landmark_pos, node2pos) - np.dot(landmark_pos, node1pos)))/10 >10:
            #     breakpoint()
            # return -abs(np.sum(np.dot(landmark_pos, node2pos) - np.dot(landmark_pos, node1pos)))/10#-abs(self.current_direction-self.goalDirection)/100
            return np.mean(-abs((landmark_pos - node2pos) - (landmark_pos - node1pos)))
        else:
            return reward/10

    def on_key_release(self, key):
        #https://pynput.readthedocs.io/en/latest/keyboard.html#monitoring-the-keyboard
        if key == Key.right:
            print('turning right')
            self.turnRight()
        elif key == Key.left:
            print('turning left')
            self.turnLeft()
        elif key == Key.up:
            print('moving forward')
            self.moveForward()
        elif key == Key.down:
            print('moving backward')
            self.moveBack()
        elif key.char == 'q':
            print("Exiting Graph Traversal")
            return False

    def turnRight(self, angle=None):
        if angle is None:
            turn_angle = self.base_turn_angle
        else:
            turn_angle = angle
        self.current_direction += turn_angle
        self.current_direction = self.current_direction % 360
        if self.recordActions:
            # update rule is start node, end node, amount moved forward, angle turned
            self.actions.append([self.current_node, self.current_node, 'r', 0, turn_angle])
        if self.plotImgs:
            self.render()
        return -1

    def turnLeft(self, angle=None):
        if angle is None:
            turn_angle = self.base_turn_angle
        else:
            turn_angle = angle
        self.current_direction -= turn_angle
        self.current_direction = (self.current_direction + 360) % 360
        if self.recordActions:
            # update rule is start node, end node, action, amount moved forward, angle turned
            self.actions.append([self.current_node, self.current_node, 'l', 0, -turn_angle])
        if self.plotImgs:
            self.render()
        return -1

    def moveForward(self):
        nodeOptions = self.graph.getReachableVertices(self.current_node, self.current_direction, angle_threshold=self.base_turn_angle)
        if len(nodeOptions)==0:
            if self.recordActions:
                # update rule is start node, end node, amount moved forward, angle turned
                self.actions.append([self.current_node, self.current_node, 'f', 0, 0])
            if self.human:
                print('Cant move forward!')
            # return -10
        else:
            if len(nodeOptions)==1:
                newNode = nodeOptions[0]
            else:
                breakpoint()
                angles = [abs(x[2]-self.current_direction) for x in nodeOptions]
                ang_ind = np.argmin(angles)
                newNode = nodeOptions[ang_ind]

            if self.recordActions:
                # update rule is start node, end node, amount moved forward, angle turned
                self.actions.append([self.current_node, newNode[1], 'f', newNode[3], 0])
                self.path.append(newNode[1])
            self.current_node = newNode[1]
        if self.plotImgs:
            self.render()
        return -1

    def moveBack(self):
        backwardsDirection = (self.current_direction+180) % 360
        nodeOptions = self.graph.getReachableVertices(self.current_node, backwardsDirection, angle_threshold=self.base_turn_angle)
        if len(nodeOptions) == 0:
            if self.recordActions:
                # update rule is start node, end node, amount moved forward, angle turned
                self.actions.append([self.current_node, self.current_node, 'b', 0, 0])
            if self.human:
                print('Cant move backward!')
            # return -10
        else:
            if len(nodeOptions) == 1:
                newNode = nodeOptions[0]
            else:
                angles = [abs(x[2] - backwardsDirection) for x in nodeOptions]
                ang_ind = np.argmin(angles)
                newNode = nodeOptions[ang_ind]

            if self.recordActions:
                # update rule is start node, end node, amount moved forward, angle turned
                self.actions.append([self.current_node, newNode[1], 'b', -newNode[3], 0])
                self.path.append(newNode[1])
            self.current_node = newNode[1]
        if self.plotImgs:
            self.render()
        return -1

    def getImg(self, node=None, direction=None):
        if node is None:
            node = self.current_node
        if direction is None:
            direction = self.current_direction

        image = self.graph.getVertexFeats(node, direction)
        while image is None:
            print("Warning! no image for node",node,"in direction",direction)
            direction = (direction + 1) % 360
            print("Using direction", direction, "instead!")
            image = self.graph.getVertexFeats(node, direction)
            # breakpoint()
        return image

    def getGoalImg(self):
        return self.getImg(self.goalNode,self.goalDirection)

    def getLandmarkVector(self, node=None):
        #TODO: normalize??? distances
        #TODO: get rid of sign(directionality) or implement properly?
        landmark_pos = np.array(self.graph.config['landmarks'])
        if node is None:
            #giving it distance to landmarks concat with [node num, current direction]
            # return landmark_pos - self.graph.config['positions'][str(self.current_node)]
            # return np.concatenate((landmark_pos-self.graph.config['positions'][str(self.current_node)],np.array([[self.current_node, self.current_direction]])),axis=0)
            return np.array([self.current_node/len(graph.vertices), self.current_direction/360])
        elif node=='goal':
            # giving it goal distance to landmarks concat with [goal node num, goal direction]
            # return landmark_pos - self.graph.config['positions'][str(self.goalNode)]
            # return np.concatenate((landmark_pos - self.graph.config['positions'][str(self.goalNode)],np.array([[self.goalNode, self.goalDirection]])), axis=0)
            return np.array([self.goalNode/len(graph.vertices), self.goalDirection/360])
        else:
            return landmark_pos - self.graph.config['positions'][str(node)]

    def render(self):
        image = self.getImg()
        # plt.imshow(image)
        # plt.title('Node: '+str(self.current_node)+', Heading: '+str(self.current_direction))
        self.ax.imshow(image)
        self.ax.set_title('Node: '+str(self.current_node)+', Heading: '+str(self.current_direction))
        plt.pause(0.01)

    def traverse(self):
        prompt_text = 'Use the arrow keys to look/turn left/right and move up/down\n' \
                      'Enter the "q" key to quit\n'
        print(prompt_text)
        if self.plotImgs:
            self.render()
        with keyboard.Listener(on_release=self.on_key_release) as listener:
            listener.join()
        if self.plotImgs:
            plt.close(1)

    def save(self):
        with open(self.graph.config['envName']+'Trials.json','a') as f:
            try:
                trials = json.load(f)
                keys = list(trials.keys())
                new_key = int(keys[-1]) + 1
            except:
                trials = {}
                new_key = 0

            trials[new_key] = {"actions":self.actions,
                               "path":self.path}
            json.dump(trials, f)

if __name__ == '__main__':
    graph = Graph(config_path='labGraphConfig.json')
    # breakpoint()
    traverser = GraphTraverser(graph, plotImgs=True, recordActions=True)
    traverser.setGoalNode()
    traverser.traverse()
    time.sleep(2)
    traverser.traverse()
    traverser.save()
    traverser.reset()


import time
from utils.selenium_utils import SeleniumUtils
from pages.search_common import SearchCommon
from utils.read_json_newhome import JsonReader
from pages.newHome.search_container import SearchContainer
from utils.test_utils import TestUtils
from pages.newHome.newhome_list_page import NewhomeListPage
import allure
import pytest
from workflow.newHome.search_check_results import CheckSearchResults

@pytest.mark.usefixtures("setup")
class TestSearchByPrice:
    testdata = JsonReader.get_filter_min_price_data()

    @allure.title("Newhome - search - filter - price range")
    @allure.description("verify: all returned house price must meet the filter 'price' ")
    @pytest.mark.parametrize("minPrice", testdata)
    def test_search_by_building_type(self, config, minPrice):
        if minPrice == "不限" or minPrice == "$100,000" or minPrice == "$200,000":
            return
        # 1 打开 新房首页
        search_container = SearchContainer(self.driver)
        search_container.open_home_page(config)
        search_common = SearchCommon(self.driver)
        search_common.close_modal()
        # 2 等待 mapbox fully loaded
        search_container.wait_mapbox_loaded()
        # 3 点击 价格 button， 显示所有价格后，把它们的页面元素放到 min_price_element_list 里面
        search_container.click_price_button()
        min_price_element_list = search_container.get_min_price_list()
        # 4 把所有的价格提取出来，放到 dropdown_list
        dropdown_list = TestUtils.get_text_list(min_price_element_list)
        # 5 如果 测试数据 minPrice 没有在 下拉框里，测试失败，打印错误价格，退出
        if minPrice not in dropdown_list:
            print("................test data Not in dropdown list: " + minPrice)
            assert False

        # 6 点击测试价格，作为起初价格 - min price
        TestUtils.click_filter(min_price_element_list, minPrice)

        # 7 最高价格显示后，把把它们的页面元素放到 max_price_element_list 里面
        max_price_element_list = search_container.get_max_price_list()
        # 8 提出最高价格 的第一个值 - integer 格式
        max_price_text = SeleniumUtils.get_text_by_element(max_price_element_list[1])
        max_price_int = TestUtils.get_price_int(max_price_text)
        # 9 把价格最低价转换成 Integer: i.e $1,000,000 to 1000000
        minPrice = TestUtils.get_price_int(minPrice)

        # 10 点击第二个值 （第一个为：不限）
        max_price_element_list[1].click()

        time.sleep(3)
        # 11 在搜索结果列表页，把所有房源的价格 的元素 放到 result_element_list
        list_page = NewhomeListPage(self.driver)
        result_element_list = list_page.get_price_list()
        # 12 验证 每个房源显示的价格范围 是否符合测试数据， 如果验证失败，跳出，并打印出房源地址
        check_result = CheckSearchResults(self.driver)
        error_counter = check_result.check_price(result_element_list, minPrice, max_price_int)

        assert error_counter == 0


import os
import pyrallis
import pytorch_lightning as pl

from pytorch_lightning.callbacks import LearningRateMonitor, ModelCheckpoint
from pytorch_lightning.loggers import WandbLogger

from definitions import CONFIGS_DIR
from src.training.dataset import Languages, MultiLanguageDataModule
from src.training.model import BertLangNER
from src.configs.config_classes import TrainConfig

@pyrallis.wrap(config_path=os.path.join(CONFIGS_DIR, "bert_config.yaml"))
def train(cfg: TrainConfig):
    pl.seed_everything(cfg.seed)

    n_classes = len(Languages)
    model = BertLangNER(cfg, n_classes)
    datamodule = MultiLanguageDataModule(cfg)

    logger = WandbLogger(project="text-lang-predictor", log_model=True)
    lr_logger = LearningRateMonitor()
    checkpoint_callback = ModelCheckpoint(
        filename="best",
        dirpath=logger.experiment.dir,
        save_top_k=1,
        save_last=False,
        verbose=True,
        monitor="val_f1",
        mode="max",
    )

    trainer = pl.Trainer(
        val_check_interval=cfg.training.val_interval,
        accumulate_grad_batches=cfg.training.accumulate_grad_batches,
        gpus=int(cfg.training.use_gpu),
        progress_bar_refresh_rate=1,
        log_every_n_steps=10,
        logger=logger,
        callbacks=[lr_logger, checkpoint_callback],
        deterministic=True,
        max_steps=cfg.training.n_steps,
    )

    trainer.fit(model, datamodule=datamodule)

if __name__ == "__main__":
    train()


import io
import logging
import os
import threading

import flask
import numpy as np
from flask import render_template
import tensorflow as tf
from celery import Celery
from consts import *
# import the necessary packages
from keras.applications import ResNet50, imagenet_utils
from keras.applications.resnet50 import ResNet50
from keras.models import load_model
from keras.preprocessing.image import img_to_array
from PIL import Image
from utils import prepare_image

# from google.appengine.ext import vendor
# vendor.add(LIB_DIR)

model = None
graph = None
# initialize our Flask application and the Keras model
app = flask.Flask(__name__, template_folder=TEMPLATE_DIR)
#get redis connection from env var , else use default
app.config['CELERY_BROKER_URL'] = os.environ.get('REDIS_URL', CELERY.BROKER_URL)
app.config['CELERY_RESULT_BACKEND'] = os.environ.get('REDIS_URL', CELERY.RESULT_BACKEND)

celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
print(os.environ.get("PORT", "Port does not exist"))

@app.before_first_request
def app_load_model():
    """load the model in a non blocking thread
    """
    def non_blocking_load():
        """Loads the model from keras
        """
        global model, graph
        print(LOADING_MESSAGE)
        # model = load_model(MODEL_LOCATION)
        model = ResNet50(weights="imagenet")
        print("Completed loading model... waiting for requests")
        graph = tf.get_default_graph()
    thread = threading.Thread(target=non_blocking_load)
    thread.start()

@app.errorhandler(500)
def server_error(e):
    logging.exception('An error occurred during a request.')
    return """
    An internal error occurred: <pre>{}</pre>
    See logs for full stacktrace.
    """.format(e), 500

@app.route("/")
def home():
    """index page for the predict api"""
    return render_template("index.html")

@app.route("/predict", methods=["POST"])
def predict():
    """initialize the data dictionary that will be returned from the
        view

    :rtype: `flask.json`
    """
    global model, graph
    data = {"success": False}

    # ensure an image was properly uploaded to our endpoint
    if flask.request.method == "POST":
        if flask.request.files.get("image"):
            # read the image in PIL format
            image = flask.request.files["image"].read()
            image = Image.open(io.BytesIO(image))

            # preprocess the image and prepare it for classification
            image = prepare_image(image=image, target_size=TARGET_SIZE)

            # classify the input image and then initialize the list
            # of predictions to return to the client
            #run predictions asynchronously and get output with collect()
            with graph.as_default():
                preds = model.predict(image)
            print(flask.request.args)
            if flask.request.args.get("top"):
                top = flask.request.args.get("top")
                results = imagenet_utils.decode_predictions(preds, top=top)
            else:
                results = imagenet_utils.decode_predictions(preds, top=DEFAULT_PREDICTION_NUMBER)
            data["predictions"] = []

            # loop over the results and add them to the list of
            # returned predictions
            for (imagenetID, label, prob) in results[0]:
                r = {"label": label, "probability": float(prob)}
                data["predictions"].append(r)

            # indicate that the request was a success
            data["success"] = True

    # return the data dictionary as a JSON response
    return flask.jsonify(data)

# if this is the main thread of execution first load the model and
# then start the server
if __name__ == "__main__":
    #locally run debug
    app.run(debug=True, port='8008')


from unittest import result
import cv2
import parametros as p
import numpy as np

def HSV_green_segmentation(img_og, img_hsv):
    mask = cv2.inRange(img_hsv, p.umbrales['HSVlow'], p.umbrales['HSVhigh'])
    result = cv2.bitwise_and(img_og, img_og, mask=mask)
    return result

def HSL_green_segmentation(img_og, img_hsl):
    A_low = (img_hsl >= p.umbrales['HLSlow']).all(axis = 2).astype(np.uint8)
    A_high = (img_hsl <= p.umbrales['HLShigh']).all(axis = 2).astype(np.uint8)
    mask = np.logical_and(A_low, A_high).astype(np.uint8)
    result = cv2.bitwise_and(img_og, img_og, mask=mask)
    return result

def RGB_green_segmentation(img_og, img_rgb):
    A_low = (img_rgb >= p.umbrales['RGBlow']).all(axis = 2).astype(np.uint8)
    A_high = (img_rgb <= p.umbrales['RGBhigh']).all(axis = 2).astype(np.uint8)
    mask = np.logical_and(A_low, A_high).astype(np.uint8)
    result = cv2.bitwise_and(img_og, img_og, mask=mask)
    return result

def HSV_with_opening(img, HSV):
    mask = cv2.inRange(HSV, p.umbrales['HSVlow'], p.umbrales['HSVhigh'])
    kernel1 = np.ones((3,3),np.uint8)
    kernel2 = np.ones((5,5),np.uint8)
    kernel3 = np.ones((7,7),np.uint8)
    opening1 = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel1)
    opening2 = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel2)
    opening3 = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel3)
    result1 = cv2.bitwise_and(img, img, mask=opening1)
    result2 = cv2.bitwise_and(img, img, mask=opening2)
    result3 = cv2.bitwise_and(img, img, mask=opening3)
    # cv2.imwrite('op1.png', result1)
    # cv2.imwrite('op2.png', result2)
    cv2.imwrite('op3.png', result3)

img = cv2.imread(p.imagenes['img3'])

# img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_HSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# img_HLS = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)
HSV_with_opening(img, img_HSV)
# RGB = cv2.cvtColor(RGB_green_segmentation(img, img_RGB), cv2.COLOR_RGB2BGR)
# HSV = cv2.cvtColor(HSV_green_segmentation(img, img_HSV), cv2.COLOR_HSV2BGR)
# HSL = cv2.cvtColor(HSL_green_segmentation(img, img_HLS), cv2.COLOR_HLS2BGR)


from datetime import datetime
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union

from bas_style_kit_jinja_templates import BskTemplates
from flask import session
from markdown import markdown
from werkzeug.datastructures import Headers
from werkzeug.wrappers import BaseResponse

"""
Python type for Flask responses
Source: https://stackoverflow.com/a/58866777
"""
_str_bytes = Union[str, bytes]
_data_type = Union[
    _str_bytes,
    BaseResponse,
    Dict[str, Any],
    Callable[
        [Dict[str, Any], Callable[[str, List[Tuple[str, str]]], None]],
        Iterable[bytes],
    ],
]
_status_type = Union[int, _str_bytes]
_headers_type = Union[
    Headers,
    Dict[_str_bytes, _str_bytes],
    Iterable[Tuple[_str_bytes, _str_bytes]],
]

FlaskResponseType = Union[
    _data_type,
    Tuple[_data_type],
    Tuple[_data_type, _status_type],
    Tuple[_data_type, _headers_type],
    Tuple[_data_type, _status_type, _headers_type],
]

def configure_bas_style_kit_templates() -> BskTemplates:
    """
    Configure BAS Style Kit Jinja Templates object for application.

    This application overrides (rather than appends to) the default navigation launcher entries, as part of prototyping
    a navbar for internal applications with a user login.

    :rtype: BskTemplates
    :return: BAS Style Kit templates configuration
    """
    bsk_templates = BskTemplates()
    bsk_templates.site_title = "MAGIC Projects Portfolio"
    bsk_templates.site_description = (
        "Portfolio of projects undertaken, or planned to be undertaken, by "
        "the BAS Mapping and Geographic Information Centre (MAGIC)"
    )
    bsk_templates.bsk_site_nav_brand_text = "MAGIC Projects Portfolio"
    bsk_templates.bsk_site_development_phase = "alpha"
    bsk_templates.bsk_site_feedback_href = (
        "https://gitlab.data.bas.ac.uk/MAGIC/magic-projects-portfolio/-/" "issues/new"
    )
    bsk_templates.bsk_site_footer_policies_cookies_href = "/legal/cookies"
    bsk_templates.bsk_site_footer_policies_copyright_href = "/legal/copyright"
    bsk_templates.bsk_site_footer_policies_privacy_href = "/legal/privacy"
    bsk_templates.site_styles.append({"href": "/static/css/app.css"})
    bsk_templates.site_styles.append(
        {
            "href": "https://cdn.web.bas.ac.uk/libs/font-awesome-pro/5.13.0/css/all.min.css",
            "integrity": "sha256-DjbUjEiuM4tczO997cVF1zbf91BC9OzycscGGk/ZKks=",
        }
    )

    bsk_templates.bsk_container_classes = ["bsk-container-fluid"]
    bsk_templates.bsk_site_nav_launcher = [
        {
            "value": "BAS Digital Workspace",
            "href": "https://nercacuk.sharepoint.com/sites/BASDigitalw",
        },
        {
            "value": "BAS Public Website",
            "href": "https://www.bas.ac.uk",
        },
        {
            "value": "BAS Data Catalogue",
            "href": "https://data.bas.ac.uk",
        },
        {
            "value": "MAGIC Team (BAS Digital Workspace)",
            "href": "https://nercacuk.sharepoint.com/sites/BASDigitalw/people-teams/magic/Pages/default.aspx",
        },
        {
            "value": "MAGIC Team (BAS Public Website)",
            "href": "https://www.bas.ac.uk/team/magic",
        },
    ]

    return bsk_templates

def group_projects(projects: list, group_property: str) -> dict:  # noqa: C901
    """
    Group a set of projects by a common property.

    Note: This function is (rightly) flagged as too complex due to the number of branches etc.
    It needs to be refactored once we know what it needs to do.

    :type projects: list
    :param projects: collection of projects to group
    :type group_property: str
    :param group_property: property to group projects by
    :rtype: dict
    :return: projects grouped by common property
    """
    group_properties = {
        "strategic-objectives": "Strategic Objectives",
        "activity-areas": "Activity Areas",
        "status": "_status",
    }
    grouped_projects = {"none": []}

    for project in projects:
        try:
            if group_property == "status":
                project["fields"]["_status"] = None
                if project["fields"]["Duration"] == "Fixed Term":
                    project["fields"]["_status"] = (
                        str(project["fields"]["Status"]).lower().replace(" ", "_")
                    )
                elif project["fields"]["Duration"] == "Open Ended":
                    project["fields"]["_status"] = "open_ended"

            if isinstance(project["fields"][group_properties[group_property]], str):
                if (
                    project["fields"][group_properties[group_property]]
                    not in grouped_projects.keys()
                ):
                    grouped_projects[
                        project["fields"][group_properties[group_property]]
                    ] = []
                grouped_projects[
                    project["fields"][group_properties[group_property]]
                ].append(project)
                continue

            for group_property_value in project["fields"][
                group_properties[group_property]
            ]:
                if group_property_value not in grouped_projects.keys():
                    grouped_projects[group_property_value] = []
                grouped_projects[group_property_value].append(project)
        except KeyError:
            grouped_projects["none"].append(project)

    return grouped_projects

def index_people(people: list) -> dict:
    """
    Index a list of people by ID property.

    :type: list
    :param people: collection of people to index
    :rtype: dict
    :return: people indexed by ID property
    """
    _people = {}

    for person in people:
        _people[person["id"]] = person

    return _people

def index_projects(projects: list) -> dict:
    """
    Index a list of projects by ID property.

    :type: list
    :param projects: collection of projects to index
    :rtype: dict
    :return: projects indexed by ID property
    """
    _projects = {}

    for project in projects:
        _projects[project["id"]] = project

    return _projects

def index_project_roles(roles: list) -> dict:
    """
    Index a list of project roles by ID property.

    :type: list
    :param roles: collection of project roles to index
    :rtype: dict
    :return: project roles indexed by ID property
    """
    _roles = {}

    for role in roles:
        _roles[role["id"]] = role

    return _roles

def format_project_temporal_extent(project: Dict[str, Any]) -> Dict[str, Any]:
    """
    Format a project temporal extent as a text string.

    Only applies to projects with a fixed term duration. Projects must have a start date but may have an unbound end
    date, represented by '..'. The temporal extent is assumed to an ISO 8601 date interval, which will currently be
    reduced to a set of dates.

    As Airtable encodes the timezone in datetime's using 'Z' instead of an offset ('+00:00') the Python `fromisoformat`
    method cannot parse it - a workaround is used to replace the the shorthand offset with a full version.

    The formatted date will be added as a new '_formatted_temporal_extent' field.

    E.g.
    - '2009-04-26/2020-05-06' will become '2009 - 2020'
    - '2009-04-26/..' will become '2009 - ..'

    :type project: dict
    :param project: a project
    :rtype: dict
    :return: project with a '_formatted_temporal_extent' field
    """
    if project["fields"]["Duration"] == "Open Ended":
        project["fields"]["_formatted_temporal_extent"] = ".. - .."
        return project

    _temporal_extent: Dict[str, str] = {"start": "", "end": ".."}
    if project["fields"]["Duration"] == "Fixed Term":
        temporal_extent: List[str] = str(project["fields"]["Temporal Extent"]).split(
            "/"
        )
        temporal_extent[0] = temporal_extent[0].replace("Z", "+00:00")
        _temporal_extent["start"] = str(datetime.fromisoformat(temporal_extent[0]).year)
        if temporal_extent[1] != "..":
            temporal_extent[1] = temporal_extent[1].replace("Z", "+00:00")
            _temporal_extent["end"] = str(
                datetime.fromisoformat(temporal_extent[1]).year
            )

        project["fields"][
            "_formatted_temporal_extent"
        ] = f"{_temporal_extent['start']} - {_temporal_extent['end']}"

    return project

def format_project_markdown(project: Dict[str, Any]) -> Dict[str, Any]:
    """
    Format project properties that support Markdown as HTML.

    Properties which support rich text formatting are encoded using Markdown within Airtable. When disabled in this
    application, this formatting needs to be converted to HTML for display.

    :type project: dict
    :param project: a project
    :rtype: dict
    :return: project with markdown encoded properties replaced by HTML encoded equivalents
    """
    if "Description" in project["fields"]:
        project["fields"]["Description"] = markdown(
            project["fields"]["Description"], output_format="html"
        )
    if "Progress" in project["fields"]:
        project["fields"]["Progress"] = markdown(
            project["fields"]["Progress"], output_format="html"
        )

    return project

def group_people_by_project_roles(roles: list, people: dict) -> dict:
    """
    Group a set of people by their role in a project.

    Use the `index_people` method to create a dict of people indexed by their ID.

    :type roles: list
    :param roles: list of roles
    :type people: dict
    :param people: dict of people indexed by their ID
    :rtype: dict
    :return: people grouped by their role type
    """
    people_grouped_by_roles = {"principal_investigator": [], "co_investigator": []}

    for role in roles:
        if role["fields"]["Type"] == "Principal Investigator":
            role["person"] = people[role["fields"]["Person"][0]]
            people_grouped_by_roles["principal_investigator"].append(role)
        elif role["fields"]["Type"] == "Co-Investigator":
            role["person"] = people[role["fields"]["Person"][0]]
            people_grouped_by_roles["co_investigator"].append(role)

    return people_grouped_by_roles

def grid_people(people: list, roles: list, projects: list) -> dict:
    """
    Structure projects into a 2-dimensional dict (people, role type) containing projects.

    E.g.

    {
        'person_1_id': {
            'person': 'person_1_details',
            'roles': {
                'principal_investigator': [
                    'project_1_details',
                    'project_2_details
                ],
                'co_investigator': [
                    'project_3_details'
                ]
            }
        }
    }

    :type people: list
    :param people: collection of people to group projects by
    :type roles: list
    :param roles: collection of roles linking people to projects with a role type
    :type projects: list
    :param projects: collection of projects to group
    :rtype: dict
    :return: projects grouped by person and role type
    """
    gridded_people = {}

    _roles = index_project_roles(roles=roles)
    _projects = index_projects(projects=projects)

    # manually sort people (manager first then first name alphabetically)
    _people = index_people(people=people)
    people = [
        _people["recwIW809DXQYiyHV"],
        _people["recfnitYBKy20Ebgo"],
        _people["reciJAhHgJ0KnlMR6"],
        _people["rec7K5gHzpEHuJHWL"],
        _people["rec6uoMg3PnU2u5a8"],
        _people["rechLCe2RLWdoqK6f"],
        _people["recauYRd4gPkydVg7"],
        _people["recimanpWZuTAwOpb"],
        _people["rec6IStBcaX9hCg4h"],
    ]

    for person in people:
        gridded_people[person["id"]] = {
            "person": person,
            "roles": {"principal_investigator": [], "co_investigator": []},
        }
        if "Project Roles (V2)" in person["fields"]:
            for role_id in person["fields"]["Project Roles (V2)"]:
                if _roles[role_id]["fields"]["Type"] == "Principal Investigator":
                    gridded_people[person["id"]]["roles"][
                        "principal_investigator"
                    ].append(_projects[_roles[role_id]["fields"]["Project"][0]])
                elif _roles[role_id]["fields"]["Type"] == "Co-Investigator":
                    gridded_people[person["id"]]["roles"]["co_investigator"].append(
                        _projects[_roles[role_id]["fields"]["Project"][0]]
                    )

    return gridded_people

def grid_projects(projects: list, group_property: str) -> dict:
    """
    Structure projects into a 2-dimensional dict (grouped property, status) containing projects.

    E.g.

    {
        'category_1_name': {
            'status_1_slug': [
                'project_1_details',
                'project_2_details',
            ]
        }
    }

    :type projects: list
    :param projects: collection of projects to group
    :type group_property: str
    :param group_property: property to group projects by
    :rtype: dict
    :return: projects grouped by status and common property
    """
    projects_grouped_by_status = group_projects(
        projects=projects, group_property="status"
    )
    projects = []
    for project_status in projects_grouped_by_status.values():
        for project in project_status:
            projects.append(project)

    grouped_projects = group_projects(projects=projects, group_property=group_property)

    gridded_projects = {}
    for group, projects_in_group in grouped_projects.items():
        gridded_projects[group] = {}
        for project in projects_in_group:
            if project["fields"]["_status"] not in gridded_projects[group].keys():
                gridded_projects[group][project["fields"]["_status"]] = []
            gridded_projects[group][project["fields"]["_status"]].append(project)

    return gridded_projects

def check_permissions(required_roles: List[str]) -> bool:
    """
    Check whether the current application user has a set of required roles.

    The roles granted to a user are recorded as part of their OAuth ID token, which is stored in the session under the
    'user' key.

    To return true, this list of roles must contain at least all the roles in `required_roles`, otherwise this method
    returns false.

    :type required_roles: List[str]
    :param required_roles: List of roles required to view information or complete an action
    :return: Boolean decision, True if allowed, False if not allowed
    """
    try:
        user: Optional[Dict[str, Union[str, List[str]]]] = session.get("user")
        if set(required_roles).issubset(user["roles"]):
            return True
        return False
    except KeyError:
        return False


import serial  # pyserial is required
from micropyGPS import MicropyGPS #https://github.com/inmcm/micropyGPS

gps = MicropyGPS()
uart = serial.Serial("/dev/ttyS0", baudrate=9600, timeout=30)

x1 = gps.longitude[1]
y1 = gps.latitude[1]

x2 = float(input("Enter Latitude"))
y2 = float(input("Enter Longitude"))

while 1:
    sent = uart.readline().decode('utf-8')
    print(sent)
    for x in sent:
        gps.update(x)
    if gps.latitude[1] <= 35:
    	print("test")

    #print("fix_type 1 means no fix: ",gps.fix_type)
    print("lat " ,gps.latitude)
    print("long " ,gps.longitude)
    print("speed ", gps.speed)
    #print("satellites_visible " ,gps.satellites_visible())
    #print("direction ", gps.compass_direction())


from handDetectorModule import *
import cv2

cap = cv2.VideoCapture(0)
hands=HandDetector(conf=0.6,tracking_conf=0.6,mode=True)

while True:
    ret,frame=cap.read()
    frame=cv2.flip(frame,1)
    if frame is None:
        continue
    frame,mask=hands.DetectHand(frame)
    index=hands.handPostion(frame,0)
    if len(index)>0:

        r=hands.isup(frame,[0])
        c=sum(r.values())-1
        cv2.putText(mask,str(c),org=(50,50),fontFace=cv2.FONT_HERSHEY_SIMPLEX,thickness=3,fontScale=2,color=(32,255,2))

    cv2.imshow('mask',mask)

    if cv2.waitKey(1) & 0xFF == ord('q'):
                break
cap.release()
cv2.destroyAllWindows()


import gym
from gym_vision import envs
import matplotlib.pyplot as plt

env_names = [k for k in envs.custom_envs.keys() if 'NV' not in k]

for env_name in env_names:
    env = gym.make(env_name)
    env.reset()
    for _ in range(20):
        obs,_,_,_ = env.step(env.action_space.sample())

    plt.figure()
    plt.imshow(obs)
    plt.savefig('docs/%s.png'%env_name)

for env_name in env_names:
    print("""
**%s**
![Alt text](docs/%s.png?raw=true "%s")"""%(env_name,env_name,env_name))


import os
import shutil
import tempfile
import map_files

from collections import defaultdict
from threading import Lock

class HeaderRepository:
    """
    Abstract base class for implementing header file repository.

    The purpose of the repository is to store headers, so that each session
    does not have to send its entire world. This class will eventually,
    given enough tasks, create a mirror of the Clients include paths.
    """
    def __init__(self, scratch_dir):
        self.scratch_dir = scratch_dir
        self.checksums = defaultdict(dict)
        self.locks = defaultdict(Lock)
        self.dir = os.path.join(scratch_dir, 'Headers')
        os.makedirs(self.dir, exist_ok=True)
        self.session_lock = Lock()
        self.session_data = {}
        self.tempdirs = {}

        self.global_map = defaultdict(map_files.FileMap)
        self.temp_map = defaultdict(map_files.FileMap)

    def create_temp_file(self, session_id, remote_dir, name, content):
        """
        Create a temporary header, which will be needed for one session only.
        """
        tmpdir = self.tempdirs[session_id]
        self._create_virtual_file(tmpdir, self.temp_map[session_id],
            os.path.join(remote_dir, name), content)

    def create_shared_file(self, machine_id, remote_dir, name, content):
        """
        Create a shared header file, i.e. one which is saved and used by
        multiple sessions.
        """
        self._create_virtual_file(self.dir, self.global_map[machine_id],
            os.path.join(remote_dir, name), content)

    def missing_files(self, machine_id, session_id, in_list):
        """
        Given a machine identification and a list of header files,
        return a 2-tuple, list of files which are missing, and a
        session unique identifier which will be passed to
        prepare_dir() together with the missing files.
        """
        needed_files = {}
        out_list = set()
        for remote_dir, data in in_list:
            for name, checksum in data:
                key = (remote_dir, name)
                if self.checksums[machine_id].get(key) != checksum:
                    needed_files[key] = checksum
                    out_list.add(key)
        with self.session_lock:
            self.session_data[session_id] = needed_files
        return out_list

    def prepare_dir(self, machine_id, session_id, new_files, include_dirs):
        """
        We received files which we reported missing.
        """
        with self.session_lock:
            needed_files = self.session_data.pop(session_id)

        checksums = self.checksums[machine_id]
        sandbox_dir = self.tempdir(session_id)

        temp_files = []
        # Update headers.
        for (remote_dir, name), content in new_files.items():
            shared = (remote_dir, name) in needed_files
            if shared:
                checksum = needed_files[(remote_dir, name)]
                self.create_shared(machine_id, session_id, remote_dir, name, checksum, content)
            else:
                temp_files.append((remote_dir, name, content))
                # If not a part of needed_files, extract it directly to
                # sandbox_dir and do not store it.
        src_file = self._process_temp_files(session_id, temp_files)
        return include_dirs, src_file

    def create_shared(self, machine_id, session_id, remote_dir, name, checksum, content):
        create_shared = False
        create_local = False
        key = (remote_dir, name)
        lock = self.locks[machine_id]
        checksums = self.checksums[machine_id]
        with lock:
            old_checksum = checksums.get(key)
            if old_checksum is None:
                checksums[key] = 'IN_PROGRESS'
                create_shared = True
            else:
                create_local = old_checksum != checksum
        if create_local:
            self.create_temp_file(session_id, remote_dir, name, content)
        if create_shared:
            self.create_shared_file(machine_id, remote_dir, name, content)
            with lock:
                checksums[key] = checksum

    def session_complete(self, session_id):
        self.temp_map.pop(session_id, None)
        if session_id in self.tempdirs:
            try:
                shutil.rmtree(self.tempdirs[session_id])
            except Exception:
                pass
            finally:
                del self.tempdirs[session_id]

    def get_mappings(self, machine_id, session_id):
        return [self.global_map[machine_id], self.temp_map[session_id]]

    def tempdir(self, session_id):
        return self.tempdirs.get(session_id) or self.tempdirs.setdefault(
            session_id, tempfile.mkdtemp(dir=self.scratch_dir))

    def _create_virtual_file(self, dir, file_map, virtual_file, content):
        handle, real_file = tempfile.mkstemp(dir=dir)
        with os.fdopen(handle, 'wb') as file:
            file.write(content)
        file_map.map_file(virtual_file, real_file)

    def _process_temp_files(self, session_id, temp_files):
        src_file = None
        for remote_dir, name, content in temp_files:
            if not remote_dir:
                tmpdir = self.tempdirs[session_id]
                self._create_virtual_file(tmpdir, self.temp_map[session_id],
                    name, content)
                src_file = name
            else:
                self.create_temp_file(session_id, remote_dir, name, content)
        return src_file


import numpy as np
import pandas as pd
import math
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

def distance(x,y):
    d = math.sqrt((x[0]-y[0])**2+(x[1]-y[1])**2) #distance between testing point and train point
    return d

def KNN(X,x,y,K):
    s = x.shape[0]
    S = X.shape[0]
    new_Y = np.zeros((S,1))
    for i in range(S):
        d = np.zeros((s,1))
        for j in range(s):
            d[j] = distance(X[i,:],x[j,:]) #distance for all the points respect with one point i in testing file
        index = np.argsort(d,axis = 0)     #sort according to the distance and index include index of distance
        count = 0                          #from smallest to longest
        for k in range(K):    #find k nearest points in training respect to testing i
            y_1 = y[index[k]] #find the class of testing point i with its index
            if y_1 == 0:      #if the nearest test points are in class 0, and more than half of these points are in
                count += 1    # class 0, the i training point is ->predict into class 0
        if count > K*0.5:     # else ->predict into class 1
            new_y = 0
        else:
            new_y = 1
        new_Y[i] = new_y      #build the new class of traiing points after classfied

    return new_Y

def misclassified_point(X,Y,new_Y):
    mis_0 = []
    mis_1 = []
    right_0 = []
    right_1 = []
    s = Y.shape[0]
    for i in range(s):
        if (Y[i] != new_Y[i] and new_Y[i] == 0):   #find original class 1 training point -> predict class 0
            mis_0.append(X[i,:])
        elif (Y[i] != new_Y[i] and new_Y[i] == 1): #find original class 0 training point -> predict class 1
            mis_1.append(X[i,:])
        elif (Y[i] == new_Y[i] and new_Y[i] == 0): #find original class 0 training point -> predict class 0
            right_0.append(X[i,:])                 #find original class 1 training point -> predict class 1
        else:
            right_1.append(X[i,:])
    mis_0 = np.matrix(mis_0)
    right_0 = np.matrix(right_0)
    mis_1 = np.matrix(mis_1)
    right_1 = np.matrix(right_1)
    return mis_0,mis_1,right_0,right_1

def plot(X,Y,x,y,K):
    new_Y = KNN(X,x,y,K)

    #mis points
    mis_0,mis_1,right_0,right_1 = misclassified_point(X,Y,new_Y)

    plt.scatter(right_0[:,0],right_0[:,1],30,
            color='black', marker='o', label='healthy controls')
    plt.scatter(right_1[:,0],right_1[:,1],30,
            color='black', marker='x', label='disease cases')
    plt.scatter(mis_0[:,0],mis_0[:,1],30,
            color='red', marker='o', label='healthy controls,mis')
    plt.scatter(mis_1[:,0],mis_1[:,1],30,
            color='red', marker='x', label='disease cases,mis')

    plt.xlabel('x1')
    plt.ylabel('x2')
    plt.legend(scatterpoints=1,
           loc='lower right',
           ncol=1,
           fontsize=7)
    plt.savefig('k={:d}.eps'.format(K),format='eps')
    plt.show()

    return 0

def parameters(X,Y,x,y,K):
    new_Y = KNN(X,x,y,K)
    right0 = [] #True Negtive, TN
    right1 = [] #True Positive, TP
    mis0 = [] #class 0 -> predict class 1 #False Negtive, FN
    mis1 = [] #class 1 -> predict class 0 #False Positive, FP

    for i in range(Y.shape[0]):
        if np.allclose(new_Y[i],0):
            # Class 0
            if np.allclose(new_Y[i],Y[i]): #Correct class0
                right0.append(new_Y[i])
            else:
                mis0.append(new_Y[i])
        else:
            if np.allclose(new_Y[i],Y[i]):
                right1.append(new_Y[i])
            else:
                mis1.append(new_Y[i])
    TN = len(right0)
    TP = len(right1)
    FN = len(mis0)
    FP = len(mis1)
    sensitivity = TP / float((TP + FN))
    specificity = TN / float((TN + FP))
    false_discovery_rate = FP / float((FP + TP))

    print('When k={:d}, \nsensitivity = {:f},\nspecificity={:f}, \nfalse discovery rate={:f} \n'.format(K,sensitivity,specificity, false_discovery_rate))

#read data
f1 = pd.read_csv('/Users/ccai28/Desktop/HW_1_training.txt',sep='\t', header = None, skiprows = 1)
f2 = pd.read_csv('/Users/ccai28/Desktop/HW_1_testing.txt',sep='\t', header = None, skiprows = 1)

#read testing data
X = f2.loc[:,[0,1]].values
Y = f2.loc[:,[2]].values

#read traing data
x = f1.loc[:,[0,1]].values
y = f1.loc[:,[2]].values

#parameters,k=1
parameters(X,Y,x,y,1)
#plot,k=1
plot(X,Y,x,y,1)

#k=5
parameters(X,Y,x,y,5)
plot(X,Y,x,y,5)

#k=10
parameters(X,Y,x,y,10)
plot(X,Y,x,y,10)


import os
import shutil

import numpy as np
import pandas as pd

from tqdm import tqdm

from src.utils import load_json

def add_negative_samples(df, item_set, neg_to_pos_ratio):
    user_positive_examples = df[["uid", "item"]].groupby("uid").agg(list).reset_index().values.tolist()
    user_positive_examples = {k[0]: k[1] for k in user_positive_examples}
    neg_examples = []
    for i, user_id in tqdm(enumerate(user_positive_examples.keys())):
        num_neg_samples = int(np.ceil(neg_to_pos_ratio * len(user_positive_examples[user_id])))
        neg_candidates = list(item_set - set(user_positive_examples[user_id]))
        if num_neg_samples > len(neg_candidates):
            num_neg_samples = len(neg_candidates)
        neg_items = np.random.choice(neg_candidates, num_neg_samples, replace=False)
        neg_examples.extend([{"uid": user_id, "item": j, "label": 0} for j in neg_items])
        if (i + 1) % 10000 == 0:
            df = df.append(neg_examples, ignore_index=True)
            neg_examples = []
    if len(neg_examples):
        df = df.append(neg_examples, ignore_index=True)
    return df

def normalize(type, *args, **kwargs):
    def binary(v):
        if v > 0: return 1
        return 0

    def linear(v):
        return kwargs["a"] * v + kwargs["b"]

    def log_linear(v):
        return kwargs["a"] * np.log10(v) + kwargs["b"]

    d = {
        "binary": binary,
        "linear": linear,
        "log_linear": log_linear
    }
    return d[type]

def main(config_path, force):
    config = load_json(config_path)
    raw_data_dir = config["path"]["raw"]
    processed_data_dir = config["path"]["processed"]
    if os.path.exists(processed_data_dir):
        if force:
            shutil.rmtree(processed_data_dir)
        else:
            raise ValueError(f"{processed_data_dir} already exists!")
    os.makedirs(processed_data_dir, exist_ok=True)
    shutil.copyfile(config_path, os.path.join(processed_data_dir, "config.json"))

    resources = ["metadata.csv", "user_map.csv", "item_map.csv", "kw_map.json"]
    for f in resources:
        shutil.copy(
            os.path.join(raw_data_dir, f),
            os.path.join(processed_data_dir, f))

    train_file = os.path.join(raw_data_dir, "train.csv")
    train_df = pd.read_csv(train_file)

    val_file = os.path.join(raw_data_dir, "val.csv")
    val_df = pd.read_csv(val_file)

    item_map_file = os.path.join(raw_data_dir, "item_map.csv")
    item_map_df = pd.read_csv(item_map_file)
    item_set = set(item_map_df["index"].tolist())

    if "normalize" in config:
        norm_func = normalize(**config["normalize"])
        train_df = train_df["label"].map(lambda v: norm_func(v))
        val_df = train_df["label"].map(lambda v: norm_func(v))
    train_df = add_negative_samples(train_df, item_set, config["negative_sampling"]["neg_to_pos_ratio"])
    train_df = train_df.sample(frac=1, random_state=442).reset_index(drop=True)
    train_df.to_csv(os.path.join(processed_data_dir, "train.csv"), index=False)

    val_df = add_negative_samples(val_df, item_set, 1)
    val_df.to_csv(os.path.join(processed_data_dir, "val.csv"), index=False)


from flask_restful import Resource, reqparse, fields, marshal_with, abort
from flask_jwt_extended import (create_access_token, create_refresh_token, jwt_required, jwt_refresh_token_required, get_jwt_identity, get_raw_jwt)
from app import db
from app.models import User, RevokedToken, Client
from datetime import datetime,timedelta
import time

post_parser = reqparse.RequestParser()
post_parser.add_argument('username', location='json', required=True)
post_parser.add_argument('password', location='json',  required=True)
class Auth(Resource):
	def post(self):
		args = post_parser.parse_args()
		user = User.query.filter(User.username == args.username, User.activo == 1).first()
		if (user):
			if(user.check_password(args.password)):
				last = user.ultimo_acceso
				user.ultimo_acceso = datetime.utcnow()
				db.session.commit()
				expires = timedelta(days=1)
				access_token = create_access_token(identity = args.username, expires_delta=expires)
				refresh_token = create_refresh_token(identity = args.username)
				expiredToken = datetime.now() + expires
				return {
					'user':{
						'id': user.id,
						'nombre': user.nombre,
						'apellido': user.apellido,
						'email': user.email,
						'ultimo_acceso': last.strftime("%d/%m/%Y %H:%M:%S"),
						'puntaje': user.puntaje,
						'role':user.rols.descripcion
					},
					'access_token':access_token,
					'refresh_token':refresh_token,
					'expired_token': time.mktime(expiredToken.timetuple())
				}, 200
		abort(404, message="You username {} and/or password {} is incorrect, please try again".format(args.username, args.password))

class TokenRefresh(Resource):
		@jwt_refresh_token_required
		def post(self):
				current_user = get_jwt_identity()
				expires = timedelta(days=1)
				access_token = create_access_token(identity = current_user, expires_delta=expires)
				expiredToken = datetime.now() + expires
				return {
					'access_token': access_token,
					'expired_token': time.mktime(expiredToken.timetuple())
				},200

class LogoutAccess(Resource):
		@jwt_required
		def post(self):
				jti = get_raw_jwt()['jti']
				try:
						revoked_token = RevokedToken(jti = jti)
						revoked_token.add()
						return {'message': 'Access token has been revoked'},200
				except:
					return {'message': 'Something went wrong'}, 500

class LogoutRefresh(Resource):
		@jwt_refresh_token_required
		def post(self):
				jti = get_raw_jwt()['jti']
				try:
						revoked_token = RevokedToken(jti = jti)
						revoked_token.add()
						return {'message': 'Refresh token has been revoked'}
				except:
						return {'message': 'Something went wrong'}, 500


from collections import defaultdict

t = int(input())

for i in range(t):
  c_count = defaultdict(list)
  w = str(input())
  k = int(input())

  for i in range(len(w)):
    if w.count(w[i]) >= k:
        c_count[w[i]].append(i)

  if not c_count:
    print(-1)
    continue

  a = len(w)
  b = 0

  for idx_list in c_count.values():
    for j in range(len(idx_list)-k+1):
        temp = idx_list[j+k-1] - idx_list[j] + 1

        if temp < a:
            a = temp

        if temp > b:
            b  = temp

  print(a, b)


import os
import json
from data_utils.data_reader import Dataset
import string
import re

class SnipsDataset(Dataset):
    def __init__(self, data_path, split='train'):
        self.idx = 0
        self.examples = []
        self.intent_classes = set()
        self.slot_classes = set()

        # Could not find official splits
        if split != 'train':
            return

        with open(os.path.join(data_path, 'benchmark_data.json')) as f:
            data = json.load(f)

        domains = data["domains"]

        for domain_dict in domains:
            intents = domain_dict["intents"]
            for intent_dict in intents:
                intent = intent_dict["benchmark"]["Snips"]["original_intent_name"]
                self.intent_classes.add(intent)
                queries = intent_dict["queries"]
                for query_dict in queries:
                    query_text = query_dict["text"]
                    slots_data = query_dict["results_per_service"]["Snips"]["slots"]

                    query_slots = query_text.translate(str.maketrans('', '', string.punctuation))

                    for slot in slots_data:
                        if len(slot['value']) > 0:
                            query_slots = query_slots.replace(slot['value'],
                                                              'SLOT_{}_{}'.format(len(slot['value'].split()),
                                                                                slot['name']))

                    slots = []
                    for w in query_slots.split():
                        if w.startswith('SLOT_'):
                            m = re.match(r'SLOT_([0-9]+)_(.*)', w)
                            n, field = m.group(1), m.group(2)
                            slots.append('B-{}'.format(field))
                            for i in range(int(n) - 1):
                                slots.append('I-{}'.format(field))
                                self.slot_classes.add('I-{}'.format(field))
                        else:
                            slots.append('O')

                    slots = ' '.join(slots)

                    self.examples.append({
                        'response': query_text,
                        'slots': slots,
                        'intent_label': intent,
                        'ind': len(self.examples)
                    })

        self.slot_classes = list(self.slot_classes)
        self.intent_classes = list(self.intent_classes)


from typing import List, Union

def first_recurring_char(chars: List[Union[str, int]]) -> Union[str, int, None]:
    """
    Returns the first recurring character from the list of characters, returns None if there is no such character

    Args:
        chars (List[Union[str, int]]): List of characters represented as strings or integers

    Returns:
        Union[str, int, None]: First recurring character or None if there is no such character
    """
    if not chars:
        raise ValueError("No characters")

    if not all(isinstance(char, (str, int)) for char in chars):
        raise TypeError("All characters must be either string or int")

    if len(chars) < 1:
        return None

    char_set = set()

    for char in chars:
        if char in char_set:
            return char
        char_set.add(char)

    return None

print(first_recurring_char([""]))
print(first_recurring_char(['a', 'b', 'a', 'c']))
print(first_recurring_char(['a', 'b', 'b', 'c']))


import logging
import os
import datetime

from webcore.wdcore_driver import WdCoreDriver
from spotify.config import TestSuiteConfig

class StaticLogger:
    started = False
    logger = None

    __console_handler = None

    def __init__(self):
        if not StaticLogger.started:
            StaticLogger.start()

    @staticmethod
    def start():

        config = TestSuiteConfig()
        desired_level = logging.getLevelName(config.log_level)   # Put config level here

        log_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'logs')
        dt_now = datetime.datetime.now().strftime('%Y%m%d%H%M%S')

        StaticLogger.started = True
        root_logger = logging.getLogger()
        root_logger.setLevel(desired_level)
        log_format = '%(asctime)s [%(levelname)s] %(name)s %(message)s'
        log_handler = logging.FileHandler('{0}/{1}.log'.format(log_dir, dt_now))
        log_formatter = logging.Formatter(log_format)
        log_handler.setFormatter(log_formatter)
        root_logger.addHandler(log_handler)

        StaticLogger.logger = logging.getLogger('hempbombs_tests')

        if config.console:  # Put console flag here
            console_format = '[%(levelname)s] %(name)s %(message)s'
            console_handler = logging.StreamHandler()
            console_formatter = logging.Formatter(console_format)
            console_handler.setFormatter(console_formatter)
            root_logger.addHandler(console_handler)

    @staticmethod
    def take_screenshot(filename=None):
        current_test_info = os.environ.get('PYTEST_CURRENT_TEST').split('::')
        # Getting this info in case we need to add it in the future
        test_class_name = current_test_info[1]
        test_name = current_test_info[2].split(' ')[0]

        if filename is None:
            filename = '{0}-{1}'.format(test_class_name, test_name)

        screen_shots_folder = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'screenshots')

        # Create the filename using the filename and the current timestamp
        current_time = datetime.datetime.now()
        filename = '{filename}_{timestamp}.png'.format(
            filename=filename, timestamp=current_time.strftime('%m-%d-%H%M%S')
        )
        full_file_path = os.path.join(screen_shots_folder, filename)
        WdCoreDriver.get_active_driver().save_screenshot(full_file_path)


import cv2

path = "img/lenna.png"

image = cv2.imread(path)

print(image[230,80]) # BGR olarak piksel değerini döndürür. [85 70 175]
print("image size : ", image.size)
print("image shape : ", image.shape)
print("image data type : ", image.dtype)

h, w, _ = image.shape

for i in range(w):
    image[50,i] = [255,255,255] # piksellere bgr değeri atayarak pikselin değerlerini değiştirebiliyoruz.

cv2.imshow("lenna",image)

cv2.waitKey(0)
cv2.destroyAllWindows()


#!/usr/bin/python3

"""
This test checks that we can assemble and disassemble all the instructions
found in `arm_test.dis` and similar dumps obtained via `objdump`
"""

import re
import sys
from typing import List

from CpuA32 import symbolic
from CpuA32 import opcode_tab as a32

def FixupAliases(opcode: a32.Opcode, name: str, ops: List[str]) -> str:
    if name[:3] in ["lsl", "lsr", "asr", "ror"]:
        # lsrs	r3, r3, #7  -> movs  r3, r3, lsr #7
        ops.insert(-1, name[:3])
        return "mov" + name[3:]

    if a32.OPC_FLAG.MULTIPLE in opcode.classes:
        # unsplit '{r0', 'r1}'
        while not ops[-1].startswith("{"):
            end = ops.pop(-1)
            ops[-1] = ops[-1] + "," + end

        if "push" in name:
            ops.insert(0, "sp")
            return name.replace("push", "stmdb")
        elif "pop" in name:
            ops.append("sp")
            return name.replace("pop", "ldmia")

        # ia suffix if no suffix is specified
        start = 1 if name[0] == "v" else 0
        if name[start + 3: start + 5] not in {"ia", "ib", "da", "db"}:
            name = name[:start + 3] + "ia" + name[start + 3:]
        if name.startswith("ldm") or name.startswith("vldm"):
            # move loadee to end of ops
            loadee = ops.pop(0)
            ops.append(loadee)
        return name

    if name.startswith("strex"):
        storee = ops.pop(1)
        ops.append(storee)
    elif name.startswith("str") or name.startswith("vstr"):
        storee = ops.pop(0)
        ops.append(storee)
    return name

# range looks like: {d8-d10} or  {d1}
def _GetFirstRegFromRange(range):
    token = range[1:-1].split("-")
    assert len(token) <= 2, f"{token}"
    return token[0]

def _GetWidthFromRange(range) -> int:
    token = range[1:-1].split("-")
    if len(token) == 1:
        return 1
    assert len(token) == 2
    return 1 + int(token[1][1:]) - int(token[0][1:])

def _GetRegListFromMask(reg_mask_str: str) -> str:
    if reg_mask_str.startswith("reglist:"):
        reg_mask_str = reg_mask_str[8:]
    reg_mask = int(reg_mask_str, 0)
    regs = [a32.REG(x).name for x in range(16) if reg_mask & (1 << x)]
    expr = "{%s}" % (",".join(regs))
    return expr

def OperandsMatch(opcode: a32.Opcode, objdump_name: str,
                  objdump_ops: List[str], std_ops: List[str]) -> bool:
    j = 0
    for i, (ok, op) in enumerate(zip(opcode.fields, std_ops)):
        objdump_op = objdump_ops[j] if j < len(objdump_ops) else ""
        if objdump_op.startswith("#"):
            objdump_op = objdump_op[1:]

        # This is a bit hackish and incomplete
        if objdump_op.startswith("-") and a32.OPC_FLAG.ADDR_DEC in opcode.classes:
            # that is a bit weak
            assert ok in {a32.OK.REG_0_3, a32.OK.IMM_0_7_TIMES_4, a32.OK.IMM_0_11, a32.OK.IMM_0_3_8_11}
            objdump_op = objdump_op[1:]

        if op == objdump_op:
            j += 1
        elif (ok is a32.OK.PRED_28_31 and
              op == "al" or objdump_name.endswith(op)):
            continue
        elif ok in {a32.OK.IMM_0_7_TIMES_4, a32.OK.IMM_7_11,
                    a32.OK.IMM_0_11, a32.OK.IMM_0_3_8_11,
                    a32.OK.IMM_10_11_TIMES_8} and op == "0":
            continue
        elif ok is a32.OK.SHIFT_MODE_5_6 and op == "lsl":
            continue
        elif a32.OPC_FLAG.MULTIPLE in opcode.classes:
            if a32.OPC_FLAG.VFP in opcode.classes:
                if ok in {a32.OK.DREG_12_15_22, a32.OK.SREG_12_15_22}:
                    if _GetFirstRegFromRange(objdump_op) == op:
                        continue
                elif ok in {a32.OK.REG_RANGE_0_7, a32.OK.REG_RANGE_1_7}:
                    if f"regrange:{_GetWidthFromRange(objdump_op)}" == op:
                        j += 1
                        continue
            else:
                if _GetRegListFromMask(op) == objdump_op:
                    j += 1
                    continue

            return False

        else:
            return False
    return True

def HandleOneInstruction(count: int, line: str,
                         data: int,
                         actual_name: str, actual_ops: List):
    ins = a32.Disassemble(data)
    assert ins is not None, f"cannot disassemble [{count}]: {line}"
    assert ins.opcode is not None and ins.operands is not None, f"unknown opcode {line}"
    data2 = a32.Assemble(ins)
    assert data == data2, f"disass mismatch [{ins.opcode.name}] {data:x} vs {data2:x}"
    actual_name = FixupAliases(ins.opcode, actual_name, actual_ops)
    if not actual_name.startswith(ins.opcode.official_name):
        print("BAD NAME", ins.opcode.name, actual_name, line, end="")

    name, operands_str = symbolic.InsSymbolize(ins)
    if not OperandsMatch(ins.opcode, actual_name, actual_ops, operands_str):
        print(f"OPERANDS differ {operands_str} {actual_ops} in line  {line}", end="")

    ins2 = symbolic.InsFromSymbolized(name, operands_str)
    assert tuple(ins.operands) == tuple(ins2.operands), f"{ins.operands} vs {ins2.operands}"

def main(argv):
    for fn in argv:
        with open(fn) as fp:
            # actual_XXX: derived from the text assembler listing
            # expected_XXX: derived from decoding the `data`
            count = 0
            for line in fp:
                count += 1
                token = line.split(None, 2)
                data = int(token[0], 16)
                actual_name = token[1]
                actual_ops = []
                if len(token) == 3:
                    # Note this removes all the address mode syntax
                    # so we lose a bit of fidelity
                    actual_ops = [x for x in re.split("[, \t\n\[\]!]+", token[2]) if x]
                HandleOneInstruction(
                    count, line, data, actual_name, actual_ops)

if __name__ == "__main__":
    # import cProfile
    # cProfile.run("main(sys.argv[1:])")
    main(sys.argv[1:])


import pandas as pd
import numpy as np
import requests
import psycopg2
import psycopg2.extras as extras
from datetime import date
import os

def add_date_inserted_column(dfs):
    for i in dfs:
        i['date_inserted'] = date.today()

def api_call():
    url = 'https://fantasy.premierleague.com/api/bootstrap-static/'
    r = requests.get(url)
    json = r.json()

    elements_df = pd.DataFrame(json['elements'])
    elements_types_df = pd.DataFrame(json['element_types'])
    teams_df = pd.DataFrame(json['teams'])
    events_df = pd.DataFrame(json['events'])

    events_df.drop("chip_plays", axis=1, inplace=True) #### REMOVE COLUMN WITH NESTED JSON, INFORMATION NOT REQUIRED FOR NOW
    events_df.drop('top_element_info', axis=1, inplace = True) #### REMOVE COLUMN WITH NESTED JSON, INFORMATION NOT REQUIRED FOR NOW

    teams_df = teams_df.rename(columns = {'name':'name_'}) ### CHANGED COLUMN NAME BECAUSE COMMAND IN SQL
    events_df = events_df.rename(columns = {'name':'name_'}) ### CHANGED COLUMN NAME BECAUSE COMMAND IN SQL

    elements_df = elements_df.rename(columns = {'id':'id_'}) ### CHANGED COLUMN NAME BECAUSE COMMAND IN SQL
    elements_types_df = elements_types_df.rename(columns = {'id':'id_'}) ### CHANGED COLUMN NAME BECAUSE COMMAND IN SQL

    dfs = [elements_df, elements_types_df, teams_df, events_df]
    add_date_inserted_column(dfs)
    return  dfs


import cv2 as cv
import numpy as np
import pytest
import torch
import torchvision.transforms.functional as F
from PIL import Image

from src.scaling import *
from src.scaling.cv import ScalingBackendCV
from src.scaling.pil import ScalingBackendPIL

lib_list = list(ScalingLib)
alg_list = list(ScalingAlg)
src_list = [(666, 555), (333, 444)]
tgt_list = [(555, 444), (333, 222), (111, 66)]

def _setup(src_shape: Shape, tgt_shape: Shape, lib: ScalingLib, alg: ScalingAlg):
    # Generate random image
    shape = src_shape + (3,)
    img = np.random.randint(256, size=shape, dtype=np.uint8)

    # API result
    api = ScalingAPI(src_shape, tgt_shape, lib, alg)
    api_img = F.to_tensor(img).numpy()
    api_img = api(api_img)
    return img, api_img, api

class TestScalingAPI(object):

    @pytest.mark.parametrize('src_shape', src_list)
    @pytest.mark.parametrize('tgt_shape', tgt_list)
    @pytest.mark.parametrize('alg', alg_list)
    def test_pil_api(self, src_shape: Shape, tgt_shape: Shape, alg: ScalingAlg):
        # API
        img, api_img, _ = _setup(src_shape, tgt_shape, ScalingLib.PIL, alg)

        # PIL
        pil_alg = ScalingBackendPIL.algorithms[alg]
        pil_img = Image.fromarray(img).resize(tgt_shape[::-1], resample=pil_alg)
        pil_img = F.to_tensor(pil_img).numpy()

        assert pil_img.shape == api_img.shape
        assert pil_img.shape[1:] == tgt_shape
        assert np.allclose(pil_img, api_img)

    @pytest.mark.parametrize('src_shape', src_list)
    @pytest.mark.parametrize('tgt_shape', tgt_list)
    @pytest.mark.parametrize('alg', alg_list)
    def test_cv_api(self, src_shape: Shape, tgt_shape: Shape, alg: ScalingAlg):
        # API
        img, api_img, api = _setup(src_shape, tgt_shape, ScalingLib.CV, alg)
        api_img = np.clip(api_img, 0, 1)

        # CV
        cv_alg = ScalingBackendCV.algorithms[alg]
        cv_img = cv.resize(img.astype(np.float32), tgt_shape[::-1], interpolation=cv_alg)
        cv_img = np.clip(cv_img, 0, 255)
        cv_img = F.to_tensor(cv_img.astype(np.uint8)).numpy()

        assert cv_img.shape == api_img.shape
        assert cv_img.shape[1:] == tgt_shape
        assert np.allclose(cv_img, api_img, atol=1.e-2)

    @pytest.mark.parametrize('src_shape', src_list)
    @pytest.mark.parametrize('tgt_shape', tgt_list)
    @pytest.mark.parametrize('lib', lib_list)
    @pytest.mark.parametrize('alg', alg_list)
    def test_matrix(self, src_shape: Shape, tgt_shape: Shape, lib: ScalingLib, alg: ScalingAlg):
        # By API
        img, api_img, api = _setup(src_shape, tgt_shape, lib, alg)

        # By matrix
        mat_img = F.to_tensor(img).numpy()
        mat_img = api.cl @ mat_img @ api.cr
        mat_img = np.clip(mat_img, 0, 1)

        diff = np.percentile(np.abs(mat_img - api_img), 98)
        assert np.isclose(diff, 0, atol=0.1)

    @pytest.mark.parametrize('src_shape', src_list)
    @pytest.mark.parametrize('tgt_shape', tgt_list)
    @pytest.mark.parametrize('lib', lib_list)
    @pytest.mark.parametrize('alg', alg_list)
    def test_mask_shape(self, src_shape: Shape, tgt_shape: Shape, lib: ScalingLib, alg: ScalingAlg):
        api = ScalingAPI(src_shape, tgt_shape, lib, alg)
        assert api.mask.shape == src_shape

class TestScalingLayer(object):

    @pytest.mark.parametrize('src_shape', src_list)
    @pytest.mark.parametrize('tgt_shape', tgt_list)
    @pytest.mark.parametrize('lib', lib_list)
    @pytest.mark.parametrize('alg', alg_list)
    def test_scaling_layer(self, src_shape: Shape, tgt_shape: Shape, lib: ScalingLib, alg: ScalingAlg):
        # Generate random image
        img = np.random.rand(3, *src_shape).astype(np.float32)

        # Normal scale
        api = ScalingAPI(src_shape, tgt_shape, lib, alg)
        api_img = api(img)

        # Scaling layer
        from src.models import ScalingLayer
        scaling = ScalingLayer.from_api(api).cuda()
        img = torch.tensor(img)[None, ...].cuda()
        scaling_img = scaling(img)[0].cpu().numpy()

        diff = np.percentile(np.abs(api_img - scaling_img), 98)
        assert np.allclose(diff, 0, atol=0.1)