from PIL import Image
from selenium import webdriver

class ScreenShot:
    def __init__(self, file_name_: str = "screenshot.png"):
        """
        :type file_name_: str
        """
        self._filename = file_name_
        self._driver = webdriver.PhantomJS()
        self._driver.set_window_size(1024, 768)
        self._crop_margin = 0

    def screen_shot(self, url_: str) -> bool:
        """
        Take a screenshot of the specified url.
        :return: Success is True, Fail is False
        :param url_: the webpage to save screenshot
        """
        try:
            self._driver.get(url_)
            self._driver.save_screenshot(self._filename)
        except Exception as e:
            print(e)
            return False
        return True

    def screen_shot_crop(self, url_: str, search_element_name: str, search_element_type: str = "Id") -> bool:
        """
        Take a screenshot of the specified class of the specified url destination.
        :return: Success is True, Fail is False
        :param url_: the webpage to save screenshot
        :param search_element_name: search to element name
        :param search_element_type: search to element type
        """
        self.screen_shot(url_)
        before_script = """
                        var element = document.getElementBy""" + search_element_type + "('" + search_element_name + """');
                        var rect = element.getBoundingClientRect();
                        """
        try:
            left = self._driver.execute_script(before_script + "return rect.left;") - self._crop_margin
            top = self._driver.execute_script(before_script + "return rect.top;")
            right = self._driver.execute_script(before_script + "return rect.width;") + left + self._crop_margin
            bottom = self._driver.execute_script(before_script + "return rect.height;") + top + self._crop_margin
        except Exception as e:
            print(e)
            return False
        im = Image.open(self._filename)
        im = im.crop((left, top, right, bottom))
        im.save(self._filename)
        im.close()
        return True

    def set_file_name(self, filename_: str):
        self._filename = filename_

    def set_window_size(self, width_: int, height_: int):
        self._driver.set_window_size(width=width_, height=height_)

    def get_window_size(self) -> object:
        return self._driver.get_window_size()

    def set_crop_margin(self, crop_margin_: int):
        self._crop_margin = crop_margin_

    def ger_crop_margin(self) -> object:
        return self._crop_margin

    def __del__(self):
        self._driver.close()

if __name__ == "__main__":
    # スクリーンショットを撮るURLを指定
    screen_url = "http://gakumu.of.miyazaki-u.ac.jp/gakumu/campuslifeinfo/campuslifeinfo/3470-2017-07-06-07-36-07.html"
    # クロップする要素の属性を指定
    element_type = "Id"
    # クロップする要素名を指定
    element_name = "wrapper2"
    # インスタンスを生成するときに保存先ファイル名を指定
    ss = ScreenShot("screenshot.png")
    # screen_urlのスクリーンショットを保存
    ss.screen_shot(screen_url)
    # 保存先ファイル名を変更
    ss.set_file_name("screenshot_crop.png")
    # screen_urlのelement_type属性のelement_nameという要素のスクリーンショットを保存
    ss.screen_shot_crop(screen_url, element_name, element_type)
    # インスタンスの削除
    del ss


from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import time

class AmazonHomePage:

    def __init__(self, driver):
        self.driver = driver

    def search_item(self, item="Wrist Watches"):
        try:
            self.driver.find_element_by_id("twotabsearchtextbox").send_keys(item)
            self.driver.find_element_by_id("twotabsearchtextbox").send_keys(Keys.ENTER)
            return self.driver
        except:
            return None

    def click_analog_type_watch(self):
        try:
            time.sleep(10)
            element = "//span[contains(text(),'Watch Display Type')]/../../..//span[text()='Analogue']/..//input[@type='checkbox']"
            action_chains = ActionChains(self.driver)
            action_chains.move_to_element(self.driver.find_element_by_xpath(element))
            action_chains.perform()
            element = "//span[contains(text(),'Watch Display Type')]/../../..//span[text()='Analogue']/..//input[@type='checkbox']/following-sibling::i"
            self.driver.find_element_by_xpath(element).click()
            print("Selected Watch Type: Analog ")
        except Exception as e:
            print(e)

    def click_leather_type_material(self):
        try:
            time.sleep(10)
            element = "//span[contains(text(),'Band Material')]/../..//span[contains(text(), 'Leather')]/..//input[@type='checkbox']"
            action_chains = ActionChains(self.driver)
            action_chains.move_to_element(self.driver.find_element_by_xpath(element))
            action_chains.perform()
            element = "//span[contains(text(),'Band Material')]/../..//span[contains(text(), 'Leather')]/..//input[@type='checkbox']/following-sibling::i"
            self.driver.find_element_by_xpath(element).click()
            print("Selected Material Type: Leather ")
        except Exception as e:
            print(e)

    def expand_brands(self):
        try:
            time.sleep(10)
            element = "//span[contains(text(), 'Brands')]/../..//span[contains(text(), 'See more')]"
            action_chains = ActionChains(self.driver)
            action_chains.move_to_element(self.driver.find_element_by_xpath(element))
            action_chains.perform()
            element = "//span[contains(text(), 'Brands')]/../..//span[contains(text(), 'See more')]/preceding-sibling::i"
            self.driver.find_element_by_xpath(element).click()
            print("Expanded all available Brands")
        except Exception as e:
            print(e)

    def select_brand_titan(self):
        try:
            time.sleep(10)
            element = "//div[@id='brandsRefinements']//span[contains(text(), 'Titan')]/..//i"
            action_chains = ActionChains(self.driver)
            action_chains.move_to_element(self.driver.find_element_by_xpath(element))
            action_chains.perform()
            self.driver.find_element_by_xpath(element).click()
            print("Selected Brand: Titan")
        except Exception as e:
            print(e)

    def select_discount_25(self):
        try:
            time.sleep(10)
            self.driver.find_element_by_xpath('//span[contains(text(), \'25% Off or more\')]').click()
            print("Selected Discount: '25% Off or more'")
        except Exception as e:
            print(e)

    def get_all_items(self):
        try:
            time.sleep(10)
            description=self.driver.find_elements_by_xpath('//div[@class=\'s-result-list s-search-results sg-row\']//span[@class=\'a-size-base-plus a-color-base a-text-normal\']')
            print("Found {} items".format(len(description)))
            # for item in description:
            #     print(item.text)
        except Exception as e:
            print(e)


import os
import re
import sys
from policy.models import Policy, Application, API
from utils import utils
from threading import Lock

class PolicyEngine:

  POLICY_STORE_DIR = 'policystore'

  def __init__(self):
    parent_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
    self.policy_store_dir = os.path.join(parent_dir, self.POLICY_STORE_DIR)
    self.active_policies = []
    self.inactive_policies = []
    self.lock = Lock()

    if os.path.exists(self.policy_store_dir):
      for policy_file in os.listdir(self.policy_store_dir):
        if policy_file.endswith('.a.py'):
          full_path = os.path.join(self.policy_store_dir, policy_file)
          try:
            policy = Policy(full_path)
            self.active_policies.append(policy)
          except Exception as ex:
            utils.log("Error while loading policy '{0}': {1}".format(policy_file, str(ex)))
        elif policy_file.endswith('.i.py'):
          full_path = os.path.join(self.policy_store_dir, policy_file)
          try:
            policy = Policy(full_path)
            self.inactive_policies.append(policy)
          except Exception as ex:
            utils.log("Error while loading policy '{0}': {1}".format(policy_file, str(ex)))

    if self.active_policies:
      msg_suffix = '1 policy'
      if len(self.active_policies) > 1:
        msg_suffix = '{0} policies'.format(len(self.active_policies))
      utils.log("Initialized policy engine with {0}.".format(msg_suffix))
    else:
      utils.log("No active policies found.")

  def __run_policy_enforcement(self, name, version, dependencies, api_list, owner):
    if self.active_policies:
      immutable_api_list = []
      for api in api_list:
        immutable_api_list.append(API(api['name'], api['version']))
      app = Application(name, version, dependencies, immutable_api_list, owner)
      errors = []
      for policy in self.active_policies:
        policy.evaluate(app, errors)
      if errors:
        return False, '|'.join(errors)
    return True, None

  def __add_policy(self, name, content, active):
    regex = re.compile("^[a-zA-Z0-9_]+$")
    if not regex.match(name):
      return False, 'Invalid policy name: Only letters, digits and underscores are allowed'

    reg_name = re.compile(name + '\\.([ai])\\.py')
    for item in os.listdir(self.policy_store_dir):
      if reg_name.match(item):
       return False, 'Policy {0} already exists'.format(name)

    if active:
      file_path = os.path.join(self.policy_store_dir, name + '.a.py')
    else:
      file_path = os.path.join(self.policy_store_dir, name + '.i.py')

    file_handle = open(file_path, 'w')
    file_handle.write(content)
    file_handle.flush()
    file_handle.close()
    try:
      new_policy = Policy(file_path)
      if active:
        self.active_policies.append(new_policy)
      else:
        self.inactive_policies.append(new_policy)
      return True, None
    except Exception as ex:
      os.remove(file_path)
      return False, 'Error while parsing policy: {0}'.format(ex.message)

  def __remove_policy(self, name):
    reg_name = re.compile(name + '\\.([ai])\\.py')
    for item in os.listdir(self.policy_store_dir):
      match = reg_name.match(item)
      if match:
        break;

    if not match:
      return False, "Policy {0} is not found!".format(name)

    path = os.path.join(self.policy_store_dir, name + '.' + match.groups()[0] + '.py')
    os.remove(path)
    if match.groups()[0] == 'a':
      for p in self.active_policies:
        if p.name == name:
          self.active_policies.remove(p)
    else:
      for p in self.inactive_policies:
        if p.name == name:
          self.inactive_policies.remove(p)
    return True, "Policy removed successfully"

  # Enable an inactive policy, this policy must exist in the policy store
  def __enable_policy(self, name):
    path = os.path.join(self.policy_store_dir, name + '.i.py')
    if not os.path.exists(path):
      return False, "Error while enabling policy: {0}, no such inactive policy!".format(name)

    for p in self.inactive_policies:
      if p.name == name:
        try:
          os.rename(path, os.path.join(self.policy_store_dir, name + '.a.py'))
        except Exception as ex:
          return False, "Erro while enabling policy: {0}, {1}".format(name, ex.message)

        self.active_policies.append(p)
        self.inactive_policies.remove(p)
        return True, None
    # This should never happen:
    return False, "Error while enabling policy: {0}, policy engine is not consistent!".format(name)

  # Disable an active policy, this policy must exist in the policy store
  def __disable_policy(self, name):
    path = os.path.join(self.policy_store_dir, name + '.a.py')
    if not os.path.exists(path):
      return False, "Error while disabling policy: {0}, no such active policy!".format(name)

    for p in self.active_policies:
      if p.name == name:
        try:
          os.rename(path, os.path.join(self.policy_store_dir, name + '.i.py'))
        except Exception as ex:
          return False, "Error while disabling policy: {0}, {1}".format(name, ex.message)
        self.inactive_policies.append(p)
        self.active_policies.remove(p)
        return True, None

    #This should never happen:
    return False, "Error while disabling policy: {0}, policy engine is not consistent!".format(name)

  def __list_policy(self, status):
    if status == 'active':
      return [policy.name for policy in self.active_policies]

    if status == "inactive":
      return [policy.name  for policy in self.inactive_policies]

    if status == "all":
      return [policy.name for policy in self.active_policies + self.inactive_policies]

  def __info_policy(self, name):
    for p in self.active_policies + self.inactive_policies:
      if p.name == name:
        return (p.name, p.source_code)
    return None

  # Wrapper methods, added mutex
  def run_policy_enforcement(self, name, version, dependencies, api_list, owner):
    self.lock.acquire()
    res = self.__run_policy_enforcement(name, version, dependencies, api_list, owner)
    self.lock.release()
    return res

  def add_policy(self, name, content, active):
    self.lock.acquire()
    res = self.__add_policy(name, content, active)
    self.lock.release()
    return res

  def remove_policy(self, name):
    self.lock.acquire()
    res = self.__remove_policy(name)
    self.lock.release()
    return res

  def enable_policy(self, name):
    self.lock.acquire()
    res = self.__enable_policy(name)
    self.lock.release()
    return res

  def disable_policy(self, name):
    self.lock.acquire()
    res = self.__disable_policy(name)
    self.lock.release()
    return res

  def list_policy(self, status):
    self.lock.acquire()
    res = self.__list_policy(status)
    self.lock.release()
    utils.log(res[0])
    return res

  def info_policy(self, name):
    self.lock.acquire()
    res = self.__info_policy(name)
    self.lock.release()
    return res


import json
from datetime import datetime
from typing import Tuple, List, Optional, Any
from ..entity import ticket_db
from ..entity.ticket import Ticket
from ..public import RECOMMEND_PRICE_COEF, RECOMMEND_REMAIN_COEF, RECOMMEND_TIME_COEF
from ..util import PriorityQueue

# 将一个航班插入数据库
def created_ticket(airline: str,
                   flight_number: str,
                   begin_place: str,
                   end_place: str,
                   begin_time: float,
                   end_time: float,
                   fly_by: str,
                   tickets: int,
                   price: float) -> Tuple[bool, str, Any]:

    # 构建航班节点
    ticket = Ticket(airline=airline, flight_number=flight_number,
                    begin_place=begin_place, end_place=end_place,
                    begin_time=begin_time, end_time=end_time,
                    fly_by=fly_by,
                    tickets=tickets, remain=tickets,
                    created_time=datetime.now().timestamp(), user_list='[]',
                    valid=1, appointment='[]', price=price)
    try:
        # 插入数据库
        res = ticket_db.insert(ticket)
    except ValueError as e:
        # 构建参数异常
        return False, "ValueError:" + str(e), None
    if res:
        return True, "航班添加成功", None
    else:
        return False, "航班添加失败", None

# 将一个航班从数据库中删除
def delete_ticket(ticket_id: int) -> Tuple[bool, str, Any]:

    # 从节点中删除航班
    res = ticket_db.delete('id', ticket_id)
    if res is False:
        return False, "航班删除失败", None
    else:
        return True, "航班删除成功", None

# 更新航班
def update(ticket: Ticket) -> Tuple[bool, str, Any]:
    if ticket.id is None:
        return False, "不知道要更新哪个节点", None
    else:
        if ticket_db.update_entirely('id', ticket.id, ticket):
            return True, "航班修改成功", None
        else:
            return False, "航班修改失败", None

# 增加改航班的预定用户
def append_appointment(ticket_id: int, username: str) -> Tuple[bool, str, Any]:
    node = ticket_db.search_one('id', ticket_id)
    if node is not None:

        # 解析json
        appointment = json.loads(node.appointment)
        if isinstance(appointment, List):
            appointment.append(username)

            # 更新json
            node.appointment = json.dumps(appointment)
            return True, "添加预订成功", None
        else:
            return False, "预订字段格式异常", None
    else:
        return False, "ticket_id不存在", None

# 从航班弹出一个预定用户
def pop_appointment(ticket_id: int) -> Tuple[bool, str, Any]:
    node = ticket_db.search_one('id', ticket_id)
    if node is not None:

        # 解析json
        appointment = json.loads(node.appointment)
        if isinstance(appointment, List):
            if len(appointment) == 0:
                return False, "the list is empty", None

            # 弹出节点
            username = appointment.pop(0)

            # 更新节点
            node.appointment = json.dumps(appointment)
            return True, '', username
        else:
            return False, "预订字段格式异常", None
    else:
        return False, "ticket_id不存在", None

# 将某用户取消该航班的预定
def cancel_appointment(ticket_id: int, username: str) -> Tuple[bool, str, Any]:
    node = ticket_db.search_one('id', ticket_id)
    if node is not None:

        # 解析json
        appointment = json.loads(node.appointment)
        if isinstance(appointment, List):
            if len(appointment) == 0:
                return False, "该用户未预定此机票", None
            try:

                # 删除用户
                appointment.remove(username)

                # 更新json
                node.appointment = json.dumps(appointment)
            except ValueError as e:
                return False, "Value Error: " + str(e), None
            return True, "取消预定成功", None
        else:
            return False, "appointment字段格式异常", None
    else:
        return False, "ticket_id不存在", None

# 获取所有航班信息
def list_ticket() -> Tuple[bool, str, Any]:
    return True, '获取列表成功', ticket_db.list_all_json()

# 查询航班
def search_one(ticket_id: int) -> Tuple[bool, str, Optional[Ticket]]:
    return True, '', ticket_db.search_one('id', ticket_id)

# 推荐传入航班的相似航班
def recommend_ticket(ticket_id: int) -> Tuple[bool, str, List[Ticket]]:
    node = ticket_db.search_one('id', ticket_id)
    if node is None:
        return True, '', []
    begin_place_list = ticket_db.search('begin_place', node.begin_place)
    end_place_list = ticket_db.search('end_place', node.end_place)

    # 查询两个集合的交，并过滤余票和有效性
    return True, '', [val for val in begin_place_list if val in end_place_list and val.remain > 0 and val.valid == 1]

# 航班增加一个用户
def ticket_add_user(ticket_id: int, username: str) -> Tuple[bool, str, Any]:
    node = ticket_db.search_one('id', ticket_id)
    if node is None:
        return False, "ticket_id不存在", None

    # 解析json
    user_list = json.loads(node.user_list)
    if isinstance(user_list, List):

        # 增加用户
        user_list.append(username)

        # 更新json
        node.user_list = json.dumps(user_list)
        return True, "机票增加客户成功", None
    else:
        return False, 'user_list字段格式异常', None

# 航班减少一张余票
def buy_one_ticket(ticket_id: int) -> Tuple[bool, str, Any]:
    node = ticket_db.search_one('id', ticket_id)
    if node is None:
        return False, "the ticket id is not exist", None
    elif node.remain >= 1:
        node.remain = node.remain - 1
        return True, "购买机票成功", None
    else:
        return False, "机票为空，购买失败", None

# 航班删除一个用户，并增加余票
def ticket_remove_user(ticket_id: int, username: str) -> Tuple[bool, str, Any]:
    node = ticket_db.search_one('id', ticket_id)

    # 解析json
    user_list = json.loads(node.user_list)
    if node is None:
        return False, "ticket_id不存在", None
    elif node.remain >= node.tickets:
        return False, "余票不能超过总票数", None
    elif user_list is None \
            or (not isinstance(user_list, List)):
        return False, "user_list字段格式异常", None
    else:
        try:

            # 删除用户
            user_list.remove(username)

            # 更新json
            node.user_list = json.dumps(user_list)
        except ValueError:
            return False, "该用户尚未购买此机票", None
        node.remain = node.remain + 1
        return True, '机票删除用户成功', None

# 检查航班的预定情况，并弹出一个预定用户
def check_appointment(ticket_id: int) -> Tuple[bool, str, Any]:
    node = ticket_db.search_one('id', ticket_id)
    if node is None:
        return False, "ticket_id不存在", None
    elif node.appointment is None:
        return True, "无剩余预定", None

    # 解析json
    appointment_list = json.loads(node.appointment)
    if appointment_list is None or len(appointment_list) == 0:
        return True, "无剩余预定", None
    elif not isinstance(appointment_list, List):
        return False, "appointment字段格式异常", None
    elif len(appointment_list):
        return True, "无剩余预定", None
    else:
        # 返回预定用户
        return pop_appointment(ticket_id)

# 查询
def search_by_column(column_name: str, column_value: Any) -> Tuple[bool, str, List[Ticket]]:
    tickets = ticket_db.search(column_name, column_value)
    res = []
    for ticket in tickets:
        res.append(ticket)
    return True, '', res

# 查询低于某价格的航班
def search_under_price(price: float) -> Tuple[bool, str, List[Ticket]]:
    tickets = ticket_db.list_all()
    res = []
    for ticket in tickets:
        if ticket.price <= price:
            res.append(ticket)
    return True, '', res

# 查询有余票的航班
def search_has_remain(has_remain: bool) -> Tuple[bool, str, List[Ticket]]:
    tickets = ticket_db.list_all()
    res = []
    if has_remain:
        for ticket in tickets:
            if ticket.remain > 0:
                res.append(ticket)
        return True, '', res
    else:
        return True, '', tickets

# 给定出发地和目的地，推荐航班
def ticket_recommend_begin_end(begin_place: str, end_place: str) -> Tuple[bool, str, Any]:

    # 获取所有航班
    ticket_list = ticket_db.list_all()

    # 初始化变量
    index = 0
    place_dict = {}
    place_list = []

    # 将城市转为数字编码
    for ticket in ticket_list:
        if ticket.begin_place not in place_dict:
            place_dict[ticket.begin_place] = index
            place_list.append(ticket.begin_place)
            index = index + 1
        if ticket.end_place not in place_dict:
            place_dict[ticket.end_place] = index
            place_list.append(ticket.end_place)
            index = index + 1

    # 验证推荐路径的起点终点是否在图中
    if begin_place not in place_dict or end_place not in place_dict:
        return True, '', '[]'

    # 将起点终点转为数字编码
    begin_place = place_dict[begin_place]
    end_place = place_dict[end_place]

    # 初始化图
    graph: List[List[Edge]] = []
    dist = []
    vis = []
    path = []
    for i in range(index):
        dist.append(float('inf'))
        graph.append([])
        vis.append(False)
        path.append({'from': -1, 'ticket_id': None})

    # 计算边权并建图
    for ticket in ticket_list:
        cost = \
            RECOMMEND_PRICE_COEF * ticket.price + \
            RECOMMEND_TIME_COEF * (ticket.end_time - ticket.begin_time) + \
            RECOMMEND_REMAIN_COEF * ticket.remain
        edge = Edge(place_dict[ticket.end_place], cost, ticket.id, ticket.begin_time, ticket.end_time)
        graph[place_dict[ticket.begin_place]].append(edge)

    dist[begin_place] = 0
    pq = PriorityQueue()
    pq.push(Edge(begin_place, 0, None, 0, 0))
    while not pq.is_empty():

        # 获取边权最小的边
        u = pq.pop()
        v = u.to
        if dist[v] < u.cost or vis[v]:
            continue
        vis[v] = True
        for edge in graph[v]:
            if not vis[edge.to] and dist[edge.to] > dist[v] + edge.cost and edge.begin_time > u.end_time:
                # 如果松弛更新最短边
                dist[edge.to] = dist[v] + edge.cost
                pq.push(Edge(edge.to, dist[edge.to], edge.ticket_id, begin_time=edge.begin_time, end_time=edge.end_time))
                # 存储路径
                path[edge.to] = {'from': v, 'ticket_id': edge.ticket_id}

    # 获取路径
    res = []
    cur = path[end_place]
    while cur['from'] != -1:
        res.append(cur['ticket_id'])
        cur = path[cur['from']]
    res_ticket = []
    for ticket_id in res:
        _, _, ticket = search_one(ticket_id)
        res_ticket.append(ticket.to_dict)
    res_ticket.reverse()
    return True, '', json.dumps(res_ticket)

# 图的一条有向边
class Edge:
    def __init__(self, to, cost, ticket_id, begin_time, end_time):
        self.to = to
        self.cost = cost
        self.ticket_id = ticket_id
        self.begin_time = begin_time
        self.end_time = end_time

    def __lt__(self, other):
        return self.cost < other.cost


import functools
import logging
import uuid

from deap import tools
import numpy as np

from ..utilities.logging_utilities import TOKENS, log_parseable_event
from .mutation import random_valid_mutation
from .modified_deap import cxOnePoint

log = logging.getLogger(__name__)
created_individuals = {}

def is_new(item):
    """ Check whether this individual (genotype) has been seen before. If not, store it as seen. """
    _is_new = str(item) not in created_individuals
    if _is_new:
        created_individuals[str(item)] = item
    return _is_new

def try_until_new(func):
    def fn_new(*args, **kwargs):
        max_tries = 50
        ind_is_new = is_new
        for _ in range(max_tries):
            new_ind, log_args = func(*args, **kwargs)
            if ind_is_new(new_ind):
                return new_ind, log_args
        log.warning("Could not create a new individual from 50 iterations of {}".format(func.__name__))
        return new_ind, log_args
    return fn_new

@try_until_new
def generate_new_with_id(container, generator):
    individual = tools.initIterate(container, generator)
    individual.id = uuid.uuid4()
    return individual, []

def generate_new(*args, **kwargs):
    return generate_new_with_id(*args, **kwargs)[0]

@try_until_new
def random_valid_mutation_new(ind, toolbox, pset):
    new_ind = toolbox.clone(ind)
    (new_ind,), mut_fn = random_valid_mutation(new_ind, pset, return_function=True)
    new_ind.id = uuid.uuid4()
    log_args = [TOKENS.MUTATION, new_ind.id, ind.id, mut_fn.__name__]
    return new_ind, log_args

@try_until_new
def mate_new(ind1, ind2):
    parent1_id, parent2_id = ind1.id, ind2.id
    new_ind, _ = cxOnePoint(ind1, ind2)
    new_ind.id = uuid.uuid4()
    log_args = [TOKENS.CROSSOVER, new_ind.id, parent1_id, parent2_id]
    return new_ind, log_args

def create_from_population(pop, n, cxpb, mutpb, toolbox):
    """ Creates n new individuals based on the population. Can apply both crossover and mutation. """
    offspring = []
    for _ in range(n):
        ind1, ind2 = np.random.choice(range(len(pop)), size=2, replace=False)
        ind1, ind2 = toolbox.clone(pop[ind1]), toolbox.clone(pop[ind2])
        if np.random.random() < cxpb:
            new_ind, log_args = toolbox.mate(ind1, ind2)
            log_parseable_event(log, *log_args)
        else:
            new_ind, log_args = toolbox.mutate(ind1, toolbox)
            log_parseable_event(log, *log_args)
        offspring.append(new_ind)
    return offspring


import abc
import re
from dataclasses import dataclass
from enum import Enum
from typing import Any, Dict, Optional, Tuple

import numpy as np

from automata.core.symbol.scip_pb2 import Descriptor as DescriptorProto  # type: ignore

class SymbolDescriptor:
    """
    Wraps the descriptor component of the URI into a python object
    """

    ScipSuffix = DescriptorProto

    class PyKind(Enum):
        Local = "local"
        Module = "module"
        Class = "class"
        Method = "method"
        Value = "value"
        Meta = "meta"
        Macro = "macro"
        Parameter = "parameter"
        TypeParameter = "type_parameter"

    def __init__(
        self, name: str, suffix: DescriptorProto, disambiguator: Optional[str] = None
    ) -> None:
        self.name = name
        self.suffix = suffix
        self.disambiguator = disambiguator

    def __repr__(self) -> str:
        return f"Descriptor({self.name}, {self.suffix}" + (
            f", {self.disambiguator})" if self.disambiguator else ")"
        )

    def unparse(self) -> str:
        """Converts back into URI string"""
        escaped_name = SymbolDescriptor.get_escaped_name(self.name)
        if self.suffix == SymbolDescriptor.ScipSuffix.Namespace:
            return f"{escaped_name}/"
        elif self.suffix == SymbolDescriptor.ScipSuffix.Type:
            return f"{escaped_name}#"
        elif self.suffix == SymbolDescriptor.ScipSuffix.Term:
            return f"{escaped_name}."
        elif self.suffix == SymbolDescriptor.ScipSuffix.Meta:
            return f"{escaped_name}:"
        elif self.suffix == SymbolDescriptor.ScipSuffix.Method:
            return f"{escaped_name}({self.disambiguator})."
        elif self.suffix == SymbolDescriptor.ScipSuffix.Parameter:
            return f"({escaped_name})"
        elif self.suffix == SymbolDescriptor.ScipSuffix.TypeParameter:
            return f"[{escaped_name}]"
        else:
            raise ValueError(f"Invalid descriptor suffix: {self.suffix}")

    @staticmethod
    def get_escaped_name(name) -> str:
        def is_simple_identifier(name):
            return re.match(r"^[\w$+-]+$", name) is not None

        if not name:
            return ""
        if is_simple_identifier(name):
            return name
        return "`" + re.sub("`", "``", name) + "`"

    @staticmethod
    def convert_scip_to_python_suffix(
        descriptor_suffix: DescriptorProto,
    ) -> PyKind:
        if descriptor_suffix == SymbolDescriptor.ScipSuffix.Local:
            return SymbolDescriptor.PyKind.Local

        elif descriptor_suffix == SymbolDescriptor.ScipSuffix.Namespace:
            return SymbolDescriptor.PyKind.Module

        elif descriptor_suffix == SymbolDescriptor.ScipSuffix.Type:
            return SymbolDescriptor.PyKind.Class

        elif descriptor_suffix == SymbolDescriptor.ScipSuffix.Method:
            return SymbolDescriptor.PyKind.Method

        elif descriptor_suffix == SymbolDescriptor.ScipSuffix.Term:
            return SymbolDescriptor.PyKind.Value

        elif descriptor_suffix == SymbolDescriptor.ScipSuffix.Macro:
            return SymbolDescriptor.PyKind.Macro

        elif descriptor_suffix == SymbolDescriptor.ScipSuffix.Parameter:
            return SymbolDescriptor.PyKind.Parameter

        elif descriptor_suffix == SymbolDescriptor.ScipSuffix.TypeParameter:
            return SymbolDescriptor.PyKind.TypeParameter

        else:
            return SymbolDescriptor.PyKind.Meta

@dataclass
class SymbolPackage:
    """Wraps the package component of the URI"""

    manager: str
    name: str
    version: str

    def __repr__(self) -> str:
        return f"Package({self.unparse()})"

    def unparse(self) -> str:
        """Converts back into URI string"""
        return f"{self.manager} {self.name} {self.version}"

@dataclass
class Symbol:
    """
    Symbol is similar to a URI, it identifies a class, method, or a local variable. SymbolInformation contains rich metadata about symbols such as the docstring.

    Symbol has a standardized string representation, which can be used interchangeably with Symbol. The syntax for Symbol is the following:

    # (<x>)+ stands for one or more repetitions of <x>
    <symbol>               ::= <scheme> ' ' <package> ' ' (<descriptor>)+ | 'local ' <local-id>
    <package>              ::= <manager> ' ' <package-name> ' ' <version>
    <scheme>               ::= any UTF-8, escape spaces with double space.
    <manager>              ::= same as above, use the placeholder '.' to indicate an empty value
    <package-name>         ::= same as above
    <version>              ::= same as above
    <descriptor>           ::= <namespace> | <type> | <term> | <method> | <type-parameter> | <parameter> | <meta> | <macro>
    <namespace>            ::= <name> '/'
    <type>                 ::= <name> '#'
    <term>                 ::= <name> '.'
    <meta>                 ::= <name> ':'
    <macro>                ::= <name> '!'
    <method>               ::= <name> '(' <method-disambiguator> ').'
    <type-parameter>       ::= '[' <name> ']'
    <parameter>            ::= '(' <name> ')'
    <name>                 ::= <identifier>
    <method-disambiguator> ::= <simple-identifier>
    <identifier>           ::= <simple-identifier> | <escaped-identifier>
    <simple-identifier>    ::= (<identifier-character>)+
    <identifier-character> ::= '_' | '+' | '-' | '$' | ASCII letter or digit
    <escaped-identifier>   ::= '`' (<escaped-character>)+ '`'
    <escaped-characters>   ::= any UTF-8 character, escape backticks with double backtick.

    Examples -
    from automata.core.symbol.search.symbol_parser import parse_symbol

    symbol_class = parse_symbol(
        "scip-python python automata 75482692a6fe30c72db516201a6f47d9fb4af065 `automata.core.agent.agent_enums`/ActionIndicator#"
    )

    symbol_method = parse_symbol(
        "scip-python python automata 75482692a6fe30c72db516201a6f47d9fb4af065 `automata.core.base.tool`/ToolNotFoundError#__init__()."
    )
    """

    uri: str
    scheme: str
    package: SymbolPackage
    descriptors: Tuple[SymbolDescriptor, ...]

    def __repr__(self) -> str:
        """Converts back into URI string"""
        return f"Symbol({self.uri}, {self.scheme}, {self.package}, {self.descriptors})"

    def __hash__(self) -> int:
        """Hashes the URI string"""
        return hash(self.uri)

    def __eq__(self, other) -> bool:
        """Compares the URI string"""
        if isinstance(other, Symbol):
            return self.uri == other.uri
        elif isinstance(other, str):
            return self.uri == other
        return False

    def symbol_kind_by_suffix(self) -> SymbolDescriptor.PyKind:
        """Converts the suffix of the URI into a PyKind"""
        return SymbolDescriptor.convert_scip_to_python_suffix(self.symbol_raw_kind_by_suffix())

    def symbol_raw_kind_by_suffix(self) -> DescriptorProto:
        """Converts the suffix of the URI into a DescriptorProto"""
        if self.uri.startswith("local"):
            return SymbolDescriptor.ScipSuffix.Local
        if self.uri.endswith("/"):
            return SymbolDescriptor.ScipSuffix.Namespace
        elif self.uri.endswith("#"):
            return SymbolDescriptor.ScipSuffix.Type
        elif self.uri.endswith(")."):
            return SymbolDescriptor.ScipSuffix.Method
        elif self.uri.endswith("."):
            return SymbolDescriptor.ScipSuffix.Term
        elif self.uri.endswith(":"):
            return SymbolDescriptor.ScipSuffix.Meta
        elif self.uri.endswith(")"):
            return SymbolDescriptor.ScipSuffix.Parameter
        elif self.uri.endswith("]"):
            return SymbolDescriptor.ScipSuffix.TypeParameter
        else:
            raise ValueError(f"Invalid descriptor suffix: {self.uri}")

    def parent(self) -> "Symbol":
        """Returns the parent symbol of the current symbol"""
        parent_descriptors = list(self.descriptors)[:-1]
        return Symbol(self.uri, self.scheme, self.package, tuple(parent_descriptors))

    @property
    def dotpath(self) -> str:
        """Returns the dotpath of the symbol"""
        return ".".join([ele.name for ele in self.descriptors])

    @property
    def module_name(self) -> str:
        """Returns the module name of the symbol"""
        return self.descriptors[0].name

    @staticmethod
    def is_local(symbol: "Symbol") -> bool:
        """Returns True if the symbol is local"""
        return symbol.descriptors[0].suffix == SymbolDescriptor.ScipSuffix.Local

    @staticmethod
    def is_meta(symbol: "Symbol") -> bool:
        """Returns True if the symbol is meta"""
        return symbol.descriptors[0].suffix == SymbolDescriptor.ScipSuffix.Meta

    @staticmethod
    def is_parameter(symbol: "Symbol") -> bool:
        """Returns True if the symbol is parameter"""
        return symbol.descriptors[0].suffix == SymbolDescriptor.ScipSuffix.Parameter

    @staticmethod
    def is_protobuf(symbol: "Symbol") -> bool:
        """Returns True if the symbol is a protobuf symbol"""
        return symbol.module_name.endswith("pb2")

    @classmethod
    def from_string(cls, symbol_str: str) -> "Symbol":
        """
        Creates a Symbol instance from a string representation

        :param symbol_str: The string representation of the Symbol
        :return: A Symbol instance
        """
        # Assuming symbol_str is in the format: "Symbol({uri}, {scheme}, Package({manager} {name} {version}), [{Descriptor},...])"
        # Parse the symbol_str to extract the uri, scheme, package_str, and descriptors_str
        match = re.search(r"Symbol\((.*?), (.*?), Package\((.*?)\), \((.*?)\)\)", symbol_str)
        if not match:
            raise ValueError(f"Invalid symbol_str: {symbol_str}")
        uri, _, __, ___ = match.groups()
        # In current implementation, only the uri is used in re-construcing the symbol
        from automata.core.symbol.parser import parse_symbol

        return parse_symbol(uri)

@dataclass
class SymbolReference:
    """Represents a reference to a symbol in a file"""

    symbol: Symbol
    line_number: int
    column_number: int
    roles: Dict[str, Any]

    def __hash__(self) -> int:
        # This could cause collisions if the same symbol is referenced in different files at the same location
        return hash(f"{self.symbol.uri}-{self.line_number}-{self.column_number}")

    def __eq__(self, other) -> bool:
        if isinstance(other, SymbolReference):
            return (
                f"{self.symbol.uri}-{self.line_number}-{self.column_number}"
                == f"{other.symbol.uri}-{other.line_number}-{other.column_number}"
            )
        return False

@dataclass
class SymbolFile:
    """Represents a file that contains a symbol"""

    path: str
    occurrences: str

    def __hash__(self) -> int:
        return hash(self.path)

    def __eq__(self, other) -> bool:
        if isinstance(other, SymbolFile):
            return self.path == other.path
        elif isinstance(other, str):
            return self.path == other
        return False

class SymbolEmbedding(abc.ABC):
    """Abstract base class for different types of embeddings"""

    def __init__(self, symbol: Symbol, embedding_source: str, vector: np.ndarray):
        self.symbol = symbol
        self.embedding_source = embedding_source
        self.vector = vector

class SymbolCodeEmbedding(SymbolEmbedding):
    """Embedding for symbol code"""

    def __init__(self, symbol: Symbol, source_code: str, vector: np.ndarray):
        super().__init__(symbol, source_code, vector)

class SymbolDocEmbedding(SymbolEmbedding):
    """Embedding for symbol documents"""

    def __init__(
        self,
        symbol: Symbol,
        document: str,
        vector: np.ndarray,
        source_code: Optional[str] = None,
        summary: Optional[str] = None,
        context: Optional[str] = None,
    ) -> None:
        super().__init__(symbol, document, vector)
        # begin additional meta data
        self.source_code = source_code
        self.summary = summary
        self.context = context


import json
import os
import time
import uuid
from copy import deepcopy

import boto3
import dash
import dash_core_components as dcc
import dash_html_components as html
import requests
from dash.dependencies import Input, Output, State
from dotenv import load_dotenv, find_dotenv
from flask_caching import Cache

import dash_reusable_components as drc
from utils import STORAGE_PLACEHOLDER, GRAPH_PLACEHOLDER, \
    IMAGE_STRING_PLACEHOLDER
from utils import apply_filters, show_histogram, generate_lasso_mask, \
    apply_enhancements

DEBUG = True

app = dash.Dash(__name__)
server = app.server

if 'REDIS_URL' in os.environ:
    # Change caching to redis if hosted on dds
    cache_config = {
        'CACHE_TYPE': 'redis',
        'CACHE_REDIS_URL': os.environ["REDIS_URL"],
        'CACHE_THRESHOLD': 400
    }
# Local Conditions
else:
    # Make sure that your credentials are saved inside your .env file, as
    # given here:
    # https://devcenter.heroku.com/articles/bucketeer#environment-setup
    load_dotenv(find_dotenv())

    # Caching with filesystem when served locally
    cache_config = {
        'CACHE_TYPE': 'filesystem',
        'CACHE_DIR': 'cache-directory',
    }

# S3 Client. It is used to store user images. The bucket name
# is stored inside the utils file, the key is
# the session id generated by uuid
access_key_id = os.environ.get('ACCESS_KEY_ID')
secret_access_key = os.environ.get('SECRET_ACCESS_KEY')
bucket_name = os.environ.get('BUCKET_NAME')

s3 = boto3.client('s3',
                  endpoint_url="https://storage.googleapis.com",
                  aws_access_key_id=access_key_id,
                  aws_secret_access_key=secret_access_key)

# Caching
cache = Cache()
cache.init_app(app.server, config=cache_config)

def store_image_string(string, key_name):
    # Generate the POST attributes
    post = s3.generate_presigned_post(
        Bucket=bucket_name,
        Key=key_name
    )

    files = {"file": string}
    # Post the string file using requests
    response = requests.post(post["url"], data=post["fields"], files=files)
    return response

def serve_layout():
    # Generates a session ID
    session_id = str(uuid.uuid4())

    # Post the image to the right key, inside the bucket named after the
    # session ID
    res = store_image_string(IMAGE_STRING_PLACEHOLDER, session_id)
    print(res)

    # App Layout
    return html.Div([
        # Session ID
        html.Div(session_id, id='session-id', style={'display': 'none'}),

        # Banner display
        html.Div([
            html.H2(
                'Image Processing App',
                id='title'
            ),
            html.Img(
                src="https://s3-us-west-1.amazonaws.com/plotly-tutorials/logo/new-branding/dash-logo-by-plotly-stripe-inverted.png"
            )
        ],
            className="banner"
        ),

        # Body
        html.Div(className="container", children=[
            html.Div(className='row', children=[
                html.Div(className='five columns', children=[
                    drc.Card([
                        dcc.Upload(
                            id='upload-image',
                            children=[
                                'Drag and Drop or ',
                                html.A('Select an Image')
                            ],
                            style={
                                'width': '100%',
                                'height': '50px',
                                'lineHeight': '50px',
                                'borderWidth': '1px',
                                'borderStyle': 'dashed',
                                'borderRadius': '5px',
                                'textAlign': 'center'
                            },
                            accept='image/*'
                        ),

                        drc.NamedInlineRadioItems(
                            name='Selection Mode',
                            short='selection-mode',
                            options=[
                                {'label': ' Rectangular', 'value': 'select'},
                                {'label': ' Lasso', 'value': 'lasso'}
                            ],
                            val='select'
                        ),

                        drc.NamedInlineRadioItems(
                            name='Image Display Format',
                            short='encoding-format',
                            options=[
                                {'label': ' JPEG', 'value': 'jpeg'},
                                {'label': ' PNG', 'value': 'png'}
                            ],
                            val='jpeg'
                        ),
                    ]),

                    drc.Card([
                        drc.CustomDropdown(
                            id='dropdown-filters',
                            options=[
                                {'label': 'Blur', 'value': 'blur'},
                                {'label': 'Contour', 'value': 'contour'},
                                {'label': 'Detail', 'value': 'detail'},
                                {'label': 'Enhance Edge', 'value': 'edge_enhance'},
                                {'label': 'Enhance Edge (More)', 'value': 'edge_enhance_more'},
                                {'label': 'Emboss', 'value': 'emboss'},
                                {'label': 'Find Edges', 'value': 'find_edges'},
                                {'label': 'Sharpen', 'value': 'sharpen'},
                                {'label': 'Smooth', 'value': 'smooth'},
                                {'label': 'Smooth (More)',
                                 'value': 'smooth_more'}
                            ],
                            searchable=False,
                            placeholder='Basic Filter...'
                        ),

                        drc.CustomDropdown(
                            id='dropdown-enhance',
                            options=[
                                {'label': 'Brightness', 'value': 'brightness'},
                                {'label': 'Color Balance', 'value': 'color'},
                                {'label': 'Contrast', 'value': 'contrast'},
                                {'label': 'Sharpness', 'value': 'sharpness'}
                            ],
                            searchable=False,
                            placeholder='Enhance...'
                        ),

                        html.Div(
                            id='div-enhancement-factor',
                            style={
                                'display': 'none',
                                'margin': '25px 5px 30px 0px'
                            },
                            children=[
                                f"Enhancement Factor:",
                                html.Div(
                                    style={'margin-left': '5px'},
                                    children=dcc.Slider(
                                        id='slider-enhancement-factor',
                                        min=0,
                                        max=2,
                                        step=0.1,
                                        value=1,
                                        updatemode='drag'
                                    )
                                )
                            ]
                        ),

                        html.Button(
                            'Run Operation',
                            id='button-run-operation',
                            style={'margin-right': '10px', 'margin-top': '5px'}
                        ),

                        html.Button(
                            'Undo',
                            id='button-undo',
                            style={'margin-top': '5px'}
                        )
                    ]),

                    dcc.Graph(id='graph-histogram-colors',
                              config={'displayModeBar': False})
                ]),

                html.Div(
                    className='seven columns',
                    style={'float': 'right'},
                    children=[
                        # The Interactive Image Div contains the dcc Graph
                        # showing the image, as well as the hidden div storing
                        # the true image
                        html.Div(id='div-interactive-image', children=[
                            GRAPH_PLACEHOLDER,
                            html.Div(
                                id='div-storage',
                                children=STORAGE_PLACEHOLDER,
                                style={'display': 'none'}
                            )
                        ])
                    ]
                )
            ])
        ])
    ])

app.layout = serve_layout

# Helper functions for callbacks
def add_action_to_stack(action_stack,
                        operation,
                        operation_type,
                        selectedData):
    """
    Add new action to the action stack, in-place.
    :param action_stack: The stack of action that are applied to an image
    :param operation: The operation that is applied to the image
    :param operation_type: The type of the operation, which could be a filter,
    an enhancement, etc.
    :param selectedData: The JSON object that contains the zone selected by
    the user in which the operation is applied
    :return: None, appending is done in place
    """

    new_action = {
        'operation': operation,
        'type': operation_type,
        'selectedData': selectedData
    }

    action_stack.append(new_action)

def undo_last_action(n_clicks, storage):
    action_stack = storage['action_stack']

    if n_clicks is None:
        storage['undo_click_count'] = 0

    # If the stack isn't empty and the undo click count has changed
    elif len(action_stack) > 0 and n_clicks > storage['undo_click_count']:
        # Remove the last action on the stack
        action_stack.pop()

        # Update the undo click count
        storage['undo_click_count'] = n_clicks

    return storage

# Recursively retrieve the previous versions of the image by popping the
# action stack
@cache.memoize()
def apply_actions_on_image(session_id,
                           action_stack,
                           filename,
                           image_signature):
    action_stack = deepcopy(action_stack)

    # If we have arrived to the original image
    if len(action_stack) == 0:
        # Retrieve the url in which the image string is stored inside s3,
        # using the session ID

        url = s3.generate_presigned_url(
            ClientMethod='get_object',
            Params={
                'Bucket': bucket_name,
                'Key': session_id
            }
        )

        # A key replacement is required for URL pre-sign in gcp

        url = url.replace('AWSAccessKeyId', 'GoogleAccessId')

        response = requests.get(url)
        print(len(response.text))
        im_pil = drc.b64_to_pil(response.text)
        return im_pil

    # Pop out the last action
    last_action = action_stack.pop()
    # Apply all the previous action_stack, and gets the image PIL
    im_pil = apply_actions_on_image(
        session_id,
        action_stack,
        filename,
        image_signature
    )
    im_size = im_pil.size

    # Apply the rest of the action_stack
    operation = last_action['operation']
    selectedData = last_action['selectedData']
    type = last_action['type']

    # Select using Lasso
    if selectedData and 'lassoPoints' in selectedData:
        selection_mode = 'lasso'
        selection_zone = generate_lasso_mask(im_pil, selectedData)
    # Select using rectangular box
    elif selectedData and 'range' in selectedData:
        selection_mode = 'select'
        lower, upper = map(int, selectedData['range']['y'])
        left, right = map(int, selectedData['range']['x'])
        # Adjust height difference
        height = im_size[1]
        upper = height - upper
        lower = height - lower
        selection_zone = (left, upper, right, lower)
    # Select the whole image
    else:
        selection_mode = 'select'
        selection_zone = (0, 0) + im_size

    # Apply the filters
    if type == 'filter':
        apply_filters(
            image=im_pil,
            zone=selection_zone,
            filter=operation,
            mode=selection_mode
        )
    elif type == 'enhance':
        enhancement = operation['enhancement']
        factor = operation['enhancement_factor']

        apply_enhancements(
            image=im_pil,
            zone=selection_zone,
            enhancement=enhancement,
            enhancement_factor=factor,
            mode=selection_mode
        )

    return im_pil

@app.callback(Output('interactive-image', 'figure'),
              [Input('radio-selection-mode', 'value')],
              [State('interactive-image', 'figure')])
def update_selection_mode(selection_mode, figure):
    if figure:
        figure['layout']['dragmode'] = selection_mode
    return figure

@app.callback(Output('graph-histogram-colors', 'figure'),
              [Input('interactive-image', 'figure')])
def update_histogram(figure):
    # Retrieve the image stored inside the figure
    enc_str = figure['layout']['images'][0]['source'].split(';base64,')[-1]
    # Creates the PIL Image object from the b64 png encoding
    im_pil = drc.b64_to_pil(string=enc_str)

    return show_histogram(im_pil)

@app.callback(Output('div-interactive-image', 'children'),
              [Input('upload-image', 'contents'),
               Input('button-undo', 'n_clicks'),
               Input('button-run-operation', 'n_clicks')],
              [State('interactive-image', 'selectedData'),
               State('dropdown-filters', 'value'),
               State('dropdown-enhance', 'value'),
               State('slider-enhancement-factor', 'value'),
               State('upload-image', 'filename'),
               State('radio-selection-mode', 'value'),
               State('radio-encoding-format', 'value'),
               State('div-storage', 'children'),
               State('session-id', 'children')])
def update_graph_interactive_image(content,
                                   undo_clicks,
                                   n_clicks,
                                   selectedData,
                                   filters,
                                   enhance,
                                   enhancement_factor,
                                   new_filename,
                                   dragmode,
                                   enc_format,
                                   storage,
                                   session_id):
    t_start = time.time()

    # Retrieve information saved in storage, which is a dict containing
    # information about the image and its action stack
    storage = json.loads(storage)
    filename = storage['filename']  # Filename is the name of the image file.
    image_signature = storage['image_signature']

    # Runs the undo function if the undo button was clicked. Storage stays
    # the same otherwise.
    storage = undo_last_action(undo_clicks, storage)

    # If a new file was uploaded (new file name changed)
    if new_filename and new_filename != filename:
        # Replace filename
        if DEBUG:
            print(filename, "replaced by", new_filename)

        # Update the storage dict
        storage['filename'] = new_filename

        # Parse the string and convert to pil
        string = content.split(';base64,')[-1]
        im_pil = drc.b64_to_pil(string)

        # Update the image signature, which is the first 200 b64 characters
        # of the string encoding
        storage['image_signature'] = string[:200]

        # Posts the image string into the Bucketeer Storage (which is hosted
        # on S3)
        store_image_string(string, session_id)
        if DEBUG:
            print(new_filename, "added to Bucketeer S3.")

        # Resets the action stack
        storage['action_stack'] = []

    # If an operation was applied (when the filename wasn't changed)
    else:
        # Add actions to the action stack (we have more than one if filters
        # and enhance are BOTH selected)
        if filters:
            type = 'filter'
            operation = filters
            add_action_to_stack(
                storage['action_stack'],
                operation,
                type,
                selectedData
            )

        if enhance:
            type = 'enhance'
            operation = {
                'enhancement': enhance,
                'enhancement_factor': enhancement_factor,
            }
            add_action_to_stack(
                storage['action_stack'],
                operation,
                type,
                selectedData
            )

        # Apply the required actions to the picture, using memoized function
        im_pil = apply_actions_on_image(
            session_id,
            storage['action_stack'],
            filename,
            image_signature
        )

    t_end = time.time()
    if DEBUG:
        print(f"Updated Image Storage in {t_end - t_start:.3f} sec")

    return [
        drc.InteractiveImagePIL(
            image_id='interactive-image',
            image=im_pil,
            enc_format=enc_format,
            display_mode='fixed',
            dragmode=dragmode,
            verbose=DEBUG
        ),

        html.Div(
            id='div-storage',
            children=json.dumps(storage),
            style={'display': 'none'}
        )
    ]

# Show/Hide Callbacks
@app.callback(Output('div-enhancement-factor', 'style'),
              [Input('dropdown-enhance', 'value')],
              [State('div-enhancement-factor', 'style')])
def show_slider_enhancement_factor(value, style):
    # If any enhancement is selected
    if value:
        style['display'] = 'block'
    else:
        style['display'] = 'none'

    return style

# Reset Callbacks
@app.callback(Output('dropdown-filters', 'value'),
              [Input('button-run-operation', 'n_clicks')])
def reset_dropdown_filters(_):
    return None

@app.callback(Output('dropdown-enhance', 'value'),
              [Input('button-run-operation', 'n_clicks')])
def reset_dropdown_enhance(_):
    return None

# Running the server
if __name__ == '__main__':
    app.run_server(debug=True)


# -- coding: utf-8 --
# @Time : 01/04/2020 09:44
# @Author : LZL
# @Email: z.luan@qmul.ac.uk
# @File : atloc.py

import torch
import torch.nn as nn  # torch.nn是专门为神经网络设计的模块化接口。nn构建于autograd之上，可以用来定义和运行神经网络
import torch.nn.functional as F
import torch.nn.init  # 提供了常用的初始化方法函数
from network.att import AttentionBlock

# nn.Module：神经网络模块。是一种方便封装参数的方式，具有将参数移动到GPU、导出、加载等功能
# 在定义自已的网络的时候，需要继承nn.Module类，并重新实现构造函数__init__构造函数和forward这两个方法
class AtLoc(nn.Module):
    # 一般把网络中具有可学习参数的层（如全连接层、卷积层等）放在构造函数__init__()中
    def __init__(self, feature_extracter, droprate=0.5, pretrained=True, feat_dim=2048):
        super(AtLoc, self).__init__()  # nn.Module的子类函数必须在构造函数中执行父类的构造函数,等价与nn.Module.__init__()
        self.droprate = droprate

        # 在特征提取器中取代最后一个全连接层
        self.feature_extractor = feature_extracter
        self.feature_extractor.avgpool = nn.AdaptiveAvgPool2d(1)  # AdaptiveAvgPool2d-自适应平均池化函数，输出tensor size为1*1
        fe_out_planes = self.feature_extractor.fc.in_features
        self.feature_extractor.fc = nn.Linear(fe_out_planes, feat_dim)  # feat_dim是特征提取器最后提取的特征维度(2048)

        self.att = AttentionBlock(feat_dim)
        self.fc_xyz = nn.Linear(feat_dim, 3)  # fc_xyz为计算出的position
        self.fc_wpqr = nn.Linear(feat_dim, 3)  # fc_wpqr为计算出的rotation

        # 初始化操作
        if pretrained:
            init_modules = [self.feature_extractor.fc, self.fc_xyz, self.fc_wpqr]  # 初始化modules
        else:
            init_modules = self.modules()

        for m in init_modules:
            if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear):
                nn.init.kaiming_normal_(m.weight.data)  # kaiming正态分布初始化
                if m.bias is not None:
                    nn.init.constant_(m.bias.data, 0)  # 常数初始化

    def forward(self, x):
        x = self.feature_extractor(x)
        x = F.relu(x)

        x = self.att(x.view(x.size(0), -1))  # reshape x

        if self.droprate > 0:
            x = F.dropout(x, p=self.droprate)  # 模型随机失活

        xyz = self.fc_xyz(x)  # 获取位置参数xyz
        wpqr = self.fc_wpqr(x)  # 获取旋转参数wpqr

        return torch.cat((xyz, wpqr), 1)  # torch.cat是将两个张量（tensor）拼接在一起(根据维度1进行拼接)


from typing import Optional

from pydantic import BaseModel, Field

from utils.pyobjectid import ObjectId, PyObjectId

class User(BaseModel):
    """User Base 모델"""

    id: Optional[PyObjectId] = Field(alias="_id")
    user_name: str = Field(description="사용자의 닉네임")

class UserIn(User):
    """User DB 모델"""

    class Config:
        json_encoders = {ObjectId: str}
        schema_extra = {
            "example": {
                "id": "1",
                "user_name": "우주최강개발자 박정섭",
            }
        }

class UserOut(User):
    """User Response 모델"""

    point: int = Field(description="유저의 누적 포인트")

    class Config:
        json_encoders = {ObjectId: str}
        schema_extra = {
            "example": {
                "id": "1",
                "user_name": "우주최강개발자 박정섭",
                "point": 0,
            }
        }


import pygame
import time
from tkinter import *

class Window(Frame):
	def __init__(self,master = None):
		Frame.__init__(self,master)
		self.master = master
		self.init_window()

	def init_window(self):
		self.master.title('START GUI')
		self.pack(fill=BOTH, expand=1)
		startButton = Button(self,text="Start",command=self.client_start)
		startButton.place(x=200,y=200)
		quitButton = Button(self,text="Quit",command=self.client_quit)
		quitButton.place(x=250,y=200)

	def client_quit(self):
		root.destroy()
		root.quit()

	def client_start(self):
		pygame.init()
		display_width = 1024
		display_height = 864
		gameDisplay = pygame.display.set_mode((display_width,display_height))
		pygame.display.set_caption('WORMS - TURBO MEGA NITRO POWA version 2k17 created by ricomxp level asia master 5000')

		red = (255,0,0)
		green = (0,255,0)
		blue = (0,0,255)
		darkBlue = (0,0,128)
		white = (255,255,255)
		black = (0,0,0)
		pink = (255,200,200)
		clock = pygame.time.Clock()

		x = int(display_width*0.75)
		y = int(display_height/2)
		x2= int(display_width/4)
		y2= int(display_height/2)
		scorelist1 =[]
		scorelist2 =[]

		###########################################
		def text_objects(text, font):
			textSurface = font.render(text, True, white)
			return textSurface, textSurface.get_rect()

		def message_display(text):
			largeText = pygame.font.Font('freesansbold.ttf',30)
			TextSurf, TextRect = text_objects(text, largeText)
			TextRect.center = ((display_width/2),(display_height/12))
			gameDisplay.blit(TextSurf, TextRect)
			pygame.display.update()
			time.sleep(2)
			gameDisplay.fill(black)
			game_ON(x,y,x2,y2)

		def show_score(count1,count2):
			font = pygame.font.Font('freesansbold.ttf', 30)
			text1 = font.render("Red score: "+str(count1), True, red)
			text2 = font.render("Blue score: "+str(count2), True, blue)
			gameDisplay.blit(text2,(0,0))
			gameDisplay.blit(text1,(800,0))

		def crash():
			message_display('BOOM!HEADSHOT! ')

		def crash_w1():
			message_display('RED crashed, You Fo00oo0o0o0OL !!! ')

		def crash_w2():
			message_display('BLUE recked, You NOo0o0o00oOB !!! ')

		def show_worm(x,y):
			pygame.draw.circle(gameDisplay, red, (x,y), 1, 0)

		def show_worm2(x2,y2):
			pygame.draw.circle(gameDisplay, blue, (x2,y2), 1, 0)

		def border(x,y):
			if (x > 0 and x < display_width)  and (y > 0 and y < display_height):
				pass
			else:
				#print('BORDER CRASH - worm #1')
				scorelist2.append(1)
				crash_w1()

		def border2(x2,y2):
			if (x2 > 0 and x2 < display_width)  and (y2 > 0 and y2 < display_height):
				pass
			else:
				scorelist1.append(1)
				crash_w2()

		def worms_check(x,y,x2,y2):
			if (x == x2 and y == y2):
				#print('WORMS CROSSOVER CRASH')
				crash()
			else:
				pass

		def game_ON(x,y,x2,y2):
			clockrate = 30
			xy_list = []
			xy2_list = []
			x_change = -1
			y_change = 0
			x2_change = 1
			y2_change = 0
			gameon = True
			while gameon:
				time = pygame.time.get_ticks()
				if time >= 5000:
					clockrate+=1
				for event in pygame.event.get():
					if event.type == pygame.QUIT:
						gameon = False
						pygame.quit()
						quit()
					if event.type == pygame.KEYDOWN:
						if event.key == pygame.K_LEFT:
							x_change = -1
							y_change = 0
						elif event.key == pygame.K_RIGHT:
							x_change = 1
							y_change = 0
						elif event.key == pygame.K_DOWN:
							x_change = 0
							y_change = 1
						elif event.key == pygame.K_UP:
							x_change = 0
							y_change = -1
						elif event.key == pygame.K_a:
							x2_change = -1
							y2_change = 0
						elif event.key == pygame.K_d:
							x2_change = 1
							y2_change = 0
						elif event.key == pygame.K_s:
							x2_change = 0
							y2_change = 1
						elif event.key == pygame.K_w:
							x2_change = 0
							y2_change = -1
				if (x,y) in xy_list:
					#print ('Self crash worm #1')
					scorelist2.append(1)
					crash_w1()
				else:
					pass
				if (x2,y2) in xy2_list:
					#print ('Self crash worm #2')
					scorelist1.append(1)
					crash_w2()
				else:
					pass
				if (x,y) in xy2_list:
					#print('WORM #1 CROSSOVER CRASH ')
					scorelist2.append(1)
					print("Red crashed:",len(scorelist2))
					crash_w1()
				if (x2,y2) in xy_list:
					#print('WORM #2 CROSSOVER CRASH')
					scorelist1.append(1)
					crash_w2()
				xy2_list.insert(0, (x2,y2))
				xy_list.insert(0, (x,y))
				x+=x_change
				y+=y_change
				x2+=x2_change
				y2+=y2_change
				show_worm2(x2,y2)
				show_worm(x,y)
				border2(x2,y2)
				border(x,y)
				worms_check(x,y,x2,y2)
				show_score(len(scorelist1),len(scorelist2))
				pygame.display.update()
				clock.tick(clockrate)

		def main_loop():
			game_ON(x,y,x2,y2)
		##################################
		main_loop()
		pygame.quit()
		quit()

root = Tk()
root.geometry("400x400")
app = Window(root)
root.mainloop()


import os
import re
import json
import pytest
import pets
STUDENT_NUMBER_PATTERN = re.compile(r's\d{7}$')

class TestJSON:
    @pytest.fixture
    def data(self):
        """A special function to provide the data used in each test case with
        an argument named `data`. A new copy is created each time."""
        with open('me.json') as f:
            return json.load(f)

    def test_name(self, data):
        assert 'Name' in data, 'Name property is missing'
        assert isinstance(data['Name'], str), 'Value for Name is not a string'
        assert data['Name'].strip(), 'Value for Name is empty'

    def test_student_number(self, data):
        assert 'Student number' in data, 'Student number property is missing'
        assert isinstance(data['Student number'], str), (
            'Value for Student number is not a string')
        match = STUDENT_NUMBER_PATTERN.match(data['Student number'])
        assert match, ('Value for Student number must be a lower-case s '
                       'followed by seven digits')

    def test_workgroup(self, data):
        assert 'Workgroup' in data, 'Workgroup property is missing'
        assert isinstance(data['Workgroup'], int), (
            'Value for Workgroup must be an integer')
        assert data['Workgroup'] in (1, 2), (
            'Value for Workgroup must be 1 or 2')

class TestImage:
    def test_image(self):
        assert (os.path.isfile('me.jpg')
                or os.path.isfile('me.png')
                or os.path.isfile('me.gif')), (
            'No image me.jpg, me.png or me.gif found')

class TestText:
    def test_text(self):
        assert os.path.isfile('me.txt'), 'No file me.txt found'
        with open('me.txt') as f:
            text = f.read()
        assert text.strip(), 'me.txt is empty'

class TestPets:
    def test_cat(self):
        Pet = pets.Dog('Pablo')
        assert Pet.sound(), 'Animal does not have a sound'


from distutils.core import setup

from setuptools import find_packages

import os

# Pull project description (long) from project readme
current_directory = os.path.dirname(os.path.abspath(__file__))

# Linke to the requirements text file
requirementPath = current_directory + '/requirements.txt'
install_requires = []
if os.path.isfile(requirementPath):
    with open(requirementPath) as f:
        install_requires = f.read().splitlines()

try:

    with open(os.path.join(current_directory,'README.md'), encoding='utf-8') as f:

        long_description = f.read()

except Exception:

    long_description = ''

setup(

	# Project name:

	name='2Fast4U Computer Inventory Management Database (CIMDB)',

	# Packages to include in the distribution:

	packages=find_packages(''),

	# Project version number:

	version='1.0.0',

	# List a license for the project, eg. MIT License

	license='MIT License',

	# Short description of your library:

	description='',

	# Long description of your library:

	long_description=long_description,

	long_description_content_type='text/markdown',

	# Your name:

	author='Ali Jalilian & Asa LeHolland',

	# Your email address:

	author_email='jalilian@oregonstate.edu & hollaasa@oregonstate.edu',

	# Link to your github repository or website:

	url='https://github.com/team-cs-cats/cimdb',

	# Download Link from where the project can be downloaded from:

	download_url='https://github.com/team-cs-cats/cimdb',

	# List of keywords:

	keywords=['Python', ],

	# List project dependencies:

	install_requires=install_requires,

	# https://pypi.org/classifiers/

	classifiers=['Environment :: Win32 (MS Windows)', 'Development Status :: 5 - Production/Stable', 'Intended Audience :: End Users/Desktop', 'License :: Free For Educational Use', 'Programming Language :: Python :: 3.7', 'Topic :: Education']

)


import re

import responses

from .base import OscTest, CallbackFactory

class OriginTest(OscTest):
    def test_origin_sort_key(self):
        pattern = self.osc.origins._origin_priority_pattern

        data = (
            # project name, expected
            ('SUSE:SLE-15:Update',
             {"family": "SUSE:SLE", "major": "15", "minor": None, "tail": "Update"}),
            ('SUSE:SLE-15-SP1:Update',
             {"family": "SUSE:SLE", "major": "15", "minor": "1", "tail": "Update"}
             ),
            ('SUSE:SLE-15-SP2:Update',
             {"family": "SUSE:SLE", "major": "15", "minor": "2", "tail": "Update"}
             ),
            ('openSUSE:Leap:15.1',
             {"family": "openSUSE:Leap", "major": "15", "minor": "1", "tail": None}
             ),
            ('openSUSE:Leap:15.1:Update',
             {"family": "openSUSE:Leap", "major": "15", "minor": "1", "tail": "Update"}
             ),
            ('openSUSE:Factory', None),
            ('openSUSE:Leap:15.1:NonFree:Update',
             {"family": "openSUSE:Leap", "major": "15", "minor": "1", "tail": "NonFree:Update"}),
            ('openSUSE:Factory:NonFree', None)
        )

        for project, expected in data:
            with self.subTest(project):
                match = pattern.match(project)
                if expected is None:
                    self.assertIsNone(match)
                else:
                    self.assertEqual(match.groupdict(), expected)

    def test_family_sorter(self):
        data = (
            # input, expected
            (
                ['<devel>', 'openSUSE:Leap:15.2:MicroOS:workarounds',
                 'SUSE:SLE-15-SP2:Update:Products:MicroOS', 'SUSE:SLE-15-SP2:Update',
                 'openSUSE:Leap:15.2:Update', 'openSUSE:Factory'],
                ['<devel>', 'openSUSE:Leap:15.2:MicroOS:workarounds',
                 'SUSE:SLE-15-SP2:Update:Products:MicroOS', 'SUSE:SLE-15-SP2:Update',
                 'openSUSE:Leap:15.2:Update', 'openSUSE:Factory']
            ),
            (
                ['<devel>', 'SUSE:SLE-15:Update', 'SUSE:SLE-15-SP1:Update',
                 'SUSE:SLE-15-SP2:Update', 'openSUSE:Leap:15.1:Update', 'openSUSE:Factory'],
                ['<devel>', 'SUSE:SLE-15-SP2:Update', 'SUSE:SLE-15-SP1:Update',
                 'SUSE:SLE-15:Update', 'openSUSE:Leap:15.1:Update', 'openSUSE:Factory']
            ),
            (
                ['<devel>', 'SUSE:SLE-15-SP2:GA', 'openSUSE:Leap:15.1:Update', 'openSUSE:Leap:15.1',
                 'openSUSE:Factory'],
                ['<devel>', 'SUSE:SLE-15-SP2:GA', 'openSUSE:Leap:15.1:Update', 'openSUSE:Leap:15.1',
                 'openSUSE:Factory']
            ),
            (
                ['<devel>', 'SUSE:SLE-15-SP2:GA', 'openSUSE:Leap:15.1', 'openSUSE:Leap:15.1:Update',
                 'openSUSE:Factory'],
                ['<devel>', 'SUSE:SLE-15-SP2:GA', 'openSUSE:Leap:15.1:Update', 'openSUSE:Leap:15.1',
                 'openSUSE:Factory']
            )
        )

        for unsorted, expected in data:
            with self.subTest():
                now_sorted = list(self.osc.origins.family_sorter(unsorted))
                self.assertTrue(len(now_sorted) == len(unsorted) == len(expected))
                self.assertEqual(expected, now_sorted)

    @responses.activate
    def test_maintained_projects(self):
        def callback(headers, params, request):
            status, body = 500, ""

            if "OBS:MaintenanceProject" in "".join(params.get("match", [])):
                status = 200
                body = """
                <collection matches="1">
                    <project name="openSUSE:Maintenance"/>
                </collection>
                """
            elif "OBS:Maintained" in "".join(params.get("match", [])):
                status = 200
                body = """
                <collection matches="2">
                    <project name="openSUSE:Leap:15.1:Update"/>
                    <project name="openSUSE:Leap:15.2:Update"/>
                </collection>
                """

            return status, headers, body

        self.mock_request(
            method=responses.GET,
            url=re.compile(self.osc.url + "/search/project/id"),
            callback=CallbackFactory(callback)
        )

        with self.subTest("Maintenance Project"):
            maint_project = self.osc.origins.maintenance_project
            self.assertEqual(maint_project, "openSUSE:Maintenance")

        with self.subTest("Maintained Projects"):
            self.assertEqual(self.osc.origins.maintained_projects,
                             ["openSUSE:Leap:15.1:Update", "openSUSE:Leap:15.2:Update"])

    @responses.activate
    def test_get_project_origin_config(self):
        self.mock_request(
            method=responses.GET,
            url=re.compile(self.osc.url + "/source/openSUSE:Leap:15.2:Update/_attribute"),
            body="""
<attributes>
  <attribute name="OriginConfig" namespace="OSRT">
    <value>origins:
- <devel>: {}
- SUSE:SLE-15:Update:
    maintainer_review_initial: false
- SUSE:SLE-15-SP1:Update:
    maintainer_review_initial: false
- SUSE:SLE-15-SP2:Update:
    maintainer_review_initial: false
- openSUSE:Leap:15.1:Update:
    pending_submission_allow: true
- openSUSE:Factory:
    pending_submission_allow: true
- '*~': {}
fallback-group: 'origin-reviewers-maintenance'
    </value>
  </attribute>
  <attribute name="Maintained" namespace="OBS"/>
</attributes>"""
        )

        config = self.osc.origins.get_project_origin_config("openSUSE:Leap:15.2:Update")
        self.assertEqual(config["origins"],
                         [
                             {'<devel>': {}},
                             {'SUSE:SLE-15:Update': {'maintainer_review_initial': False}},
                             {'SUSE:SLE-15-SP1:Update': {'maintainer_review_initial': False}},
                             {'SUSE:SLE-15-SP2:Update': {'maintainer_review_initial': False}},
                             {'openSUSE:Leap:15.1:Update': {'pending_submission_allow': True}},
                             {'openSUSE:Factory': {'pending_submission_allow': True}},
                             {'*~': {}}
                         ])

    @responses.activate
    def test_expanded_origins(self):
        def callback_attr(headers, params, request):
            pattern = re.compile(r"openSUSE:Leap:15\.([12]):Update")
            status, body = 500, ""
            match = pattern.search(request.url)
            if match.group(1) == "2":
                status = 200
                body = """
<attributes>
  <attribute name="OriginConfig" namespace="OSRT">
    <value>origins:
- <devel>: {}
- SUSE:SLE-15:Update:
    maintainer_review_initial: false
- SUSE:SLE-15-SP1:Update:
    maintainer_review_initial: false
- SUSE:SLE-15-SP2:Update:
    maintainer_review_initial: false
- openSUSE:Leap:15.1:Update:
    pending_submission_allow: true
- openSUSE:Factory:
    pending_submission_allow: true
- '*~': {}
fallback-group: 'origin-reviewers-maintenance'
    </value>
  </attribute>
  <attribute name="Maintained" namespace="OBS"/>
</attributes>"""
            elif match.group(1) == "1":
                status = 200
                body = """
<attributes>
  <attribute name="OriginConfig" namespace="OSRT">
    <value>origins:
- <devel>: {}
- SUSE:SLE-15*:
    maintainer_review_initial: false
- openSUSE:Factory:
    pending_submission_allow: true
- '*~': {}
fallback-group: 'origin-reviewers-maintenance'
    </value>
  </attribute>
  <attribute name="Maintained" namespace="OBS"/>
</attributes>"""

            return status, headers, body

        def callback_proj(headers, params, request):
            status, body = 500, ""
            match_string = "".join(params.get("match", []))

            if 'OSRT:OriginConfig' in match_string:
                status = 200
                body = """
                <collection matches="2">
                    <project name="openSUSE:Leap:15.1:Update"/>
                    <project name="openSUSE:Leap:15.2:Update"/>
                </collection>
                """
            elif "starts-with" in match_string:
                status = 200
                body = """
                <collection matches="10">
                  <project name='SUSE:SLE-15-SP1:GA'/>
                  <project name='SUSE:SLE-15-SP1:Update'/>
                  <project name='SUSE:SLE-15-SP2:GA'/>
                  <project name='SUSE:SLE-15-SP2:Update'/>
                  <project name='SUSE:SLE-15-SP2:Update:Products:MicroOS'/>
                  <project name='SUSE:SLE-15-SP2:Update:Products:MicroOS:Update'/>
                  <project name='SUSE:SLE-15-SP3:GA'/>
                  <project name='SUSE:SLE-15-SP3:Update'/>
                  <project name='SUSE:SLE-15:GA'/>
                  <project name='SUSE:SLE-15:Update'/>
                </collection>
                """

            return status, headers, body

        self.mock_request(
            method=responses.GET,
            url=re.compile(self.osc.url + "/search/project/id"),
            callback=CallbackFactory(callback_proj)
        )
        self.mock_request(
            method=responses.GET,
            url=re.compile(self.osc.url + r"/source/openSUSE:Leap:15\.[12]:Update/_attribute"),
            callback=CallbackFactory(callback_attr)
        )

        self.assertEqual(
            dict(self.osc.origins.expanded_origins),
            {
                "openSUSE:Leap:15.1:Update": [
                    "<devel>", "SUSE:SLE-15-SP1:Update", "SUSE:SLE-15-SP1:GA", "SUSE:SLE-15:Update",
                    "SUSE:SLE-15:GA", "openSUSE:Factory"
                ],
                "openSUSE:Leap:15.2:Update": [
                    "<devel>", "SUSE:SLE-15-SP2:Update", "SUSE:SLE-15-SP1:Update",
                    "SUSE:SLE-15:Update", "openSUSE:Leap:15.1:Update", "openSUSE:Factory"
                ]
            }
        )


from copy import deepcopy
import itertools
from typing import Dict

import numpy as np
import pandas as pd
import pytest
from sklearn.preprocessing import StandardScaler
import torch

from pytorch_forecasting.data import EncoderNormalizer, GroupNormalizer, NaNLabelEncoder, TimeSeriesDataSet
from pytorch_forecasting.data.examples import get_stallion_data

torch.manual_seed(23)

@pytest.mark.parametrize(
    "data,allow_nan",
    itertools.product(
        [
            (np.array([2, 3, 4]), np.array([1, 2, 3, 5, np.nan])),
            (np.array(["a", "b", "c"]), np.array(["q", "a", "nan"])),
        ],
        [True, False],
    ),
)
def test_NaNLabelEncoder(data, allow_nan):
    fit_data, transform_data = data
    encoder = NaNLabelEncoder(warn=False, add_nan=allow_nan)
    encoder.fit(fit_data)
    assert np.array_equal(
        encoder.inverse_transform(encoder.transform(fit_data)), fit_data
    ), "Inverse transform should reverse transform"
    if not allow_nan:
        with pytest.raises(KeyError):
            encoder.transform(transform_data)
    else:
        assert encoder.transform(transform_data)[0] == 0, "First value should be translated to 0 if nan"
        assert encoder.transform(transform_data)[-1] == 0, "Last value should be translated to 0 if nan"
        assert encoder.transform(fit_data)[0] > 0, "First value should not be 0 if not nan"

@pytest.mark.parametrize(
    "kwargs",
    [
        dict(method="robust"),
        dict(log_scale=True),
        dict(coerce_positive=True),
        dict(center=False),
        dict(log_zero_value=0.0),
    ],
)
def test_EncoderNormalizer(kwargs):
    data = torch.rand(100)
    defaults = dict(method="standard", log_scale=False, coerce_positive=False, center=True, log_zero_value=0.0)
    defaults.update(kwargs)
    kwargs = defaults
    if kwargs["coerce_positive"] and kwargs["log_scale"]:
        with pytest.raises(AssertionError):
            normalizer = EncoderNormalizer(**kwargs)
    else:
        normalizer = EncoderNormalizer(**kwargs)
        if kwargs["coerce_positive"]:
            data = data - 0.5

        if kwargs["coerce_positive"]:
            assert (
                normalizer.inverse_transform(normalizer.fit_transform(data)) >= 0
            ).all(), "Inverse transform should yield only positive values"
        else:
            assert torch.isclose(
                normalizer.inverse_transform(normalizer.fit_transform(data)), data, atol=1e-5
            ).all(), "Inverse transform should reverse transform"

@pytest.mark.parametrize(
    "kwargs,groups",
    itertools.product(
        [
            dict(method="robust"),
            dict(log_scale=True),
            dict(coerce_positive=True),
            dict(center=False),
            dict(log_zero_value=0.0),
            dict(scale_by_group=True),
        ],
        [[], ["a"]],
    ),
)
def test_GroupNormalizer(kwargs, groups):
    data = pd.DataFrame(dict(a=[1, 1, 2, 2, 3], b=[1.1, 1.1, 1.0, 5.0, 1.1]))
    defaults = dict(
        method="standard", log_scale=False, coerce_positive=False, center=True, log_zero_value=0.0, scale_by_group=False
    )
    defaults.update(kwargs)
    kwargs = defaults
    kwargs["groups"] = groups
    kwargs["scale_by_group"] = kwargs["scale_by_group"] and len(kwargs["groups"]) > 0

    if kwargs["coerce_positive"] and kwargs["log_scale"]:
        with pytest.raises(AssertionError):
            normalizer = GroupNormalizer(**kwargs)
    else:
        if kwargs["coerce_positive"]:
            data.b = data.b - 2.0
        normalizer = GroupNormalizer(**kwargs)
        encoded = normalizer.fit_transform(data["b"], data)

        test_data = dict(
            prediction=torch.tensor([encoded.iloc[0]]),
            target_scale=torch.tensor(normalizer.get_parameters([1])).unsqueeze(0),
        )

        if kwargs["coerce_positive"]:
            assert (normalizer(test_data) >= 0).all(), "Inverse transform should yield only positive values"
        else:
            assert torch.isclose(
                normalizer(test_data), torch.tensor(data.b.iloc[0]), atol=1e-5
            ).all(), "Inverse transform should reverse transform"

def check_dataloader_output(dataset: TimeSeriesDataSet, out: Dict[str, torch.Tensor]):
    x, y = out

    # check for nans and finite
    for k, v in x.items():
        assert torch.isfinite(v).all(), f"Values for {k} should be finite"
        assert not torch.isnan(v).any(), f"Values for {k} should not be nan"
    assert torch.isfinite(y).all(), "Values for target should be finite"
    assert not torch.isnan(y).any(), "Values for target should not be nan"

    # check shape
    assert x["encoder_cont"].size(2) == len(dataset.reals)
    assert x["encoder_cat"].size(2) == len(dataset.flat_categoricals)

@pytest.mark.parametrize(
    "kwargs",
    [
        dict(min_encoder_length=0, max_prediction_length=2),
        dict(static_categoricals=["agency", "sku"]),
        dict(static_reals=["avg_population_2017", "avg_yearly_household_income_2017"]),
        dict(time_varying_known_categoricals=["month"]),
        dict(
            time_varying_known_categoricals=["special_days", "month"],
            variable_groups=dict(
                special_days=[
                    "easter_day",
                    "good_friday",
                    "new_year",
                    "christmas",
                    "labor_day",
                    "independence_day",
                    "revolution_day_memorial",
                    "regional_games",
                    "fifa_u_17_world_cup",
                    "football_gold_cup",
                    "beer_capital",
                    "music_fest",
                ]
            ),
        ),
        dict(time_varying_known_reals=["time_idx", "price_regular", "discount_in_percent"]),
        dict(time_varying_unknown_reals=["volume", "log_volume", "industry_volume", "soda_volume", "avg_max_temp"]),
        dict(
            target_normalizer=GroupNormalizer(
                groups=["agency", "sku"], log_scale=True, scale_by_group=True, log_zero_value=1.0
            )
        ),
        dict(target_normalizer=EncoderNormalizer(), min_encoder_length=2),
        dict(randomize_length=True, min_encoder_length=2, min_prediction_length=1),
        dict(predict_mode=True),
        dict(add_target_scales=True),
        dict(add_encoder_length=True),
        dict(add_encoder_length=True),
        dict(add_relative_time_idx=True),
        dict(weight="volume"),
        dict(
            scalers=dict(time_idx=GroupNormalizer(), price_regular=StandardScaler()),
            categorical_encoders=dict(month=NaNLabelEncoder()),
            time_varying_known_categoricals=["month"],
            time_varying_known_reals=["time_idx", "price_regular"],
        ),
        dict(dropout_categoricals=["month"], time_varying_known_categoricals=["month"]),
        dict(constant_fill_strategy=dict(volume=0.0), allow_missings=True),
    ],
)
def test_TimeSeriesDataSet(test_data, kwargs):

    defaults = dict(
        time_idx="time_idx",
        target="volume",
        group_ids=["agency", "sku"],
        max_encoder_length=5,
        max_prediction_length=2,
    )
    defaults.update(kwargs)
    kwargs = defaults

    if kwargs.get("allow_missings", False):
        np.random.seed(2)
        test_data = test_data.sample(frac=0.5)

    # create dataset and sample from it
    dataset = TimeSeriesDataSet(test_data, **kwargs)
    check_dataloader_output(dataset, next(iter(dataset.to_dataloader(num_workers=0))))

def test_from_dataset(test_dataset, test_data):
    dataset = TimeSeriesDataSet.from_dataset(test_dataset, test_data)
    check_dataloader_output(dataset, next(iter(dataset.to_dataloader(num_workers=0))))

def test_dataset_index(test_dataset):
    index = test_dataset.get_index()
    assert len(index) <= len(test_dataset), "Index can only be subset of dataset"

@pytest.mark.parametrize(
    "value,variable,target",
    [
        (1.0, "price_regular", "encoder"),
        (1.0, "price_regular", "all"),
        (1.0, "price_regular", "decoder"),
        ("Agency_01", "agency", "all"),
        ("Agency_01", "agency", "decoder"),
    ],
)
def test_overwrite_values(test_dataset, value, variable, target):
    dataset = deepcopy(test_dataset)

    # create variables to check against
    control_outputs = next(iter(dataset.to_dataloader(num_workers=0, train=False)))
    dataset.set_overwrite_values(value, variable=variable, target=target)

    # test change
    outputs = next(iter(dataset.to_dataloader(num_workers=0, train=False)))
    check_dataloader_output(dataset, outputs)

    if variable in dataset.reals:
        output_name_suffix = "cont"
    else:
        output_name_suffix = "cat"

    if target == "all":
        output_names = [f"encoder_{output_name_suffix}", f"decoder_{output_name_suffix}"]
    else:
        output_names = [f"{target}_{output_name_suffix}"]

    for name in outputs[0].keys():
        changed = torch.isclose(outputs[0][name], control_outputs[0][name]).all()
        if name in output_names or (
            "cat" in name and variable == "agency"
        ):  # exception for static categorical which should always change
            assert not changed, f"Output {name} should change"
        else:
            assert changed, f"Output {name} should not change"

    # test resetting
    dataset.reset_overwrite_values()
    outputs = next(iter(dataset.to_dataloader(num_workers=0, train=False)))
    for name in outputs[0].keys():
        changed = torch.isclose(outputs[0][name], control_outputs[0][name]).all()
        assert changed, f"Output {name} should be reset"
    assert torch.isclose(outputs[1], control_outputs[1]).all(), "Target should be reset"


'''
This is the only script to run the code.
It runs the main function when being called by run.sh.

It consists 4 functions.
-- process_header
-- initialization
-- time_search_ip
-- main

Details of each function as below.
-- process_header
This function reads the header line and finds which column contains ip/date/time.

-- initialization
This function returns all the data structures to store information initialized with data from the first request line.

-- time_search_ip
This function returns the time (in seconds) that a certain ip has been active.

-- main
This function
* opens the input and output file
* does initialization
* follows the algorithm shown in README

'''
import sys
from datetime import datetime

def process_header(header):
	'''
	Finds the index of ip, date, time in the header
	'''

	cols = header.split(',')
	for i,col_name in enumerate(cols):
		if col_name == 'ip':
			ip_index = i
		elif col_name == 'date':
			date_index = i
		elif col_name == 'time':
			time_index = i
		else:
			pass
	return ip_index, date_index, time_index

def initialization(line_one, inactive_period, ip_index, date_index, time_index):
	'''
	Store previous time step in t_last.
	Create ip_table and store active ip as { ip: [first_request_time, last_request_time, number_of request] }.
	Create time_table and store active ip's active time as { active_time_t: [ip (which is active for time t)]}.
	'''

	# grab data from input line
	col_one = line_one.split(',')
	ip, date, time = col_one[ip_index], col_one[date_index], col_one[time_index]

	# initialize
	t_last = datetime.strptime(date+' '+time, '%Y-%m-%d %H:%M:%S')
	ip_table = {ip: [t_last, t_last, 1]}
	ip_order = {ip: 1}
	time_table = {t_loop: [] for t_loop in range(inactive_period+1)}
	time_table[0].append(ip)

	return t_last, ip_table, ip_order, time_table

def time_search_ip(time_table, ip):
	'''
	Search how long an ip has been active.
	'''
	for prev_active_time in time_table.keys():
		if ip in time_table[prev_active_time]:
			return prev_active_time

def main(input_file, inactive_file, output_file):
	''' main '''

	# read inactive_period
	with open(inactive_file,'r') as f:
		inactive_period = int(f.read())
	f.close()

	# process input and output
	with open(input_file,'r') as fin:
		# read header
		header = fin.readline()
		ip_index, date_index, time_index = process_header(header)

		with open(output_file,'w') as fout:

			# initialization
			line_one = fin.readline()
			t_last, ip_table, ip_order, time_table = initialization(line_one, inactive_period, ip_index, date_index, time_index)

			# read input from line 2 on and process
			for num, line in enumerate(fin, 2):
				columns = line.split(',')
				ip, date, time = columns[ip_index], columns[date_index], columns[time_index]

				# UPDATE time_table
				t_now = datetime.strptime(date+' '+time, '%Y-%m-%d %H:%M:%S')

				# same time
				if t_now==t_last:
					pass
				# update time_table
				else:
					# dt in second
					dt = int((t_now - t_last).total_seconds())

					# dt < inactive_period and only some ip times up
					if dt < inactive_period+1:
						# collect time-out ip
						ip_timeout = []
						for t_timeout in range(inactive_period-dt+1, inactive_period+1):
							for ip_tmp in time_table[t_timeout]:
								ip_timeout.append(ip_tmp)
						# move time_table forward by dt
						for t_tmp in range(inactive_period, dt-1, -1):
							time_table[t_tmp] = time_table[t_tmp-dt]
						for t_tmp in range(dt):
							time_table[t_tmp] = []

					# all ip times up
					else:
						ip_timeout = ip_table.keys()
						time_table = {t_loop: [] for t_loop in range(inactive_period+1)}

					# sort with ip_order
					ip_timeout_table = { ip_tmp: ip_order[ip_tmp] for ip_tmp in ip_timeout}
					sorted_ip = sorted(ip_timeout_table, key=lambda key: ip_timeout_table[key])
					# write to output
					for ip_tmp in sorted_ip:
						first_request, last_request, n_request = ip_table[ip_tmp]
						first_datetime = first_request.strftime('%Y-%m-%d %H:%M:%S')
						last_datetime = last_request.strftime('%Y-%m-%d %H:%M:%S')
						total_time = int((last_request - first_request).total_seconds())
						fout.write(ip_tmp+','+first_datetime+','+last_datetime+','+str(total_time+1)+','+str(n_request)+'\n')
						ip_table.pop(ip_tmp)

					# update t_last
					t_last = t_now

				# UPDATE ip_table and ip_order
				if ip in ip_table.keys():
					# update ip_table
					first_request, last_request, n_request = ip_table[ip]
					ip_table[ip] = [first_request, t_now, n_request+1]
					# no need to update ip_order
					###
					# update time_table by removing previous record and adding to newly active list
					prev_active_time = time_search_ip(time_table, ip)
					time_table[prev_active_time].remove(ip)
					time_table[0].append(ip)

				# ip is new
				else:
					ip_table[ip] = [t_now, t_now, 1]
					ip_order[ip] = num
					time_table[0].append(ip)

			# remaining output after finishing reading input
			ip_timeout = ip_table.keys()
			# sort with ip_order
			ip_timeout_table = { ip_tmp: ip_order[ip_tmp] for ip_tmp in ip_timeout}
			sorted_ip = sorted(ip_timeout_table, key=lambda key: ip_timeout_table[key])
			# write to output
			for ip_tmp in sorted_ip:
				first_request, last_request, n_request = ip_table[ip_tmp]
				first_datetime = first_request.strftime('%Y-%m-%d %H:%M:%S')
				last_datetime = last_request.strftime('%Y-%m-%d %H:%M:%S')
				total_time = int((last_request - first_request).total_seconds())
				fout.write(ip_tmp+','+first_datetime+','+last_datetime+','+str(total_time+1)+','+str(n_request)+'\n')

	# close files
	fin.close()
	fout.close()

	return None

if __name__ == '__main__':
	main(sys.argv[1], sys.argv[2], sys.argv[3])


from flask import Blueprint, render_template, redirect, url_for
from instagram.posts.forms import PostForm
from instagram.posts.models import Post
from flask_login import login_required
from flask_login import current_user
import secrets, os
from instagram import app, db
from PIL import Image

posts = Blueprint('posts', __name__)

def save_pic(form_pic):
  random_hex = secrets.token_hex(8)
  _, f_ext = os.path.splitext(form_pic.filename)
  picture_fn = random_hex + f_ext
  picture_path = os.path.join(app.root_path, 'static/media', picture_fn)
  output_size = (420, 420)
  i = Image.open(form_pic)
  i.thumbnail(output_size)
  i.save(picture_path)
  return picture_fn

@posts.route('/new_post', methods=['GET', 'POST'])
@login_required
def new_post():
  form = PostForm()
  if form.validate_on_submit():
    fn = save_pic(form.image.data)
    post = Post(body=form.body.data, image=fn, user_id=current_user.id)
    db.session.add(post)
    db.session.commit()
    return redirect(url_for('main.index'))
  return render_template('new_post.html', form=form)