import re
import voluptuous as vol
import logging

from homeassistant.config_entries import (ConfigFlow, OptionsFlow)
from homeassistant.core import callback

from .const import (
  DOMAIN,

  CONFIG_MAIN_REGION,

  CONFIG_TARGET_NAME,
  CONFIG_TARGET_HOURS,
  CONFIG_TARGET_START_TIME,
  CONFIG_TARGET_END_TIME,
  CONFIG_TARGET_TYPE,
  CONFIG_TARGET_OFFSET,
  CONFIG_TARGET_ROLLING_TARGET,

  REGEX_TIME,
  REGEX_ENTITY_NAME,
  REGEX_HOURS,
  REGEX_OFFSET_PARTS,
)

_LOGGER = logging.getLogger(__name__)

def validate_target_rate_sensor(data):
  errors = {}

  matches = re.search(REGEX_ENTITY_NAME, data[CONFIG_TARGET_NAME])
  if matches == None:
    errors[CONFIG_TARGET_NAME] = "invalid_target_name"

  # For some reason float type isn't working properly - reporting user input malformed
  matches = re.search(REGEX_HOURS, data[CONFIG_TARGET_HOURS])
  if matches == None:
    errors[CONFIG_TARGET_HOURS] = "invalid_target_hours"
  else:
    data[CONFIG_TARGET_HOURS] = float(data[CONFIG_TARGET_HOURS])
    if data[CONFIG_TARGET_HOURS] % 0.5 != 0:
      errors[CONFIG_TARGET_HOURS] = "invalid_target_hours"

  if CONFIG_TARGET_START_TIME in data:
    matches = re.search(REGEX_TIME, data[CONFIG_TARGET_START_TIME])
    if matches == None:
      errors[CONFIG_TARGET_START_TIME] = "invalid_target_time"

  if CONFIG_TARGET_END_TIME in data:
    matches = re.search(REGEX_TIME, data[CONFIG_TARGET_END_TIME])
    if matches == None:
      errors[CONFIG_TARGET_END_TIME] = "invalid_target_time"

  if CONFIG_TARGET_OFFSET in data:
    matches = re.search(REGEX_OFFSET_PARTS, data[CONFIG_TARGET_OFFSET])
    if matches == None:
      errors[CONFIG_TARGET_OFFSET] = "invalid_offset"

  return errors

def get_region_options():
  return {
    "1": "North Scotland",
    "2": "South Scotland",
    "3": "North West England",
    "4": "North East England",
    "5": "Yorkshire",
    "6": "North Wales",
    "7": "South Wales",
    "8": "West Midlands",
    "9": "East Midlands",
    "10": "East England",
    "11": "South West England",
    "12": "South England",
    "13": "London",
    "14": "South East England",
    "15": "England",
    "16": "Scotland",
    "17": "Wales"
  }

class CarbonIntensityConfigFlow(ConfigFlow, domain=DOMAIN):
  """Config flow."""

  VERSION = 1

  async def __async_setup_settings_schema(self):
    regions = get_region_options()

    return vol.Schema({
      vol.Required(CONFIG_MAIN_REGION): vol.In(regions),
    })

  async def __async_setup_target_rate_schema(self):
    return vol.Schema({
      vol.Required(CONFIG_TARGET_NAME): str,
      vol.Required(CONFIG_TARGET_HOURS): str,
      vol.Required(CONFIG_TARGET_TYPE, default="Continuous"): vol.In({
        "Continuous": "Continuous",
        "Intermittent": "Intermittent"
      }),
      vol.Optional(CONFIG_TARGET_START_TIME): str,
      vol.Optional(CONFIG_TARGET_END_TIME): str,
      vol.Optional(CONFIG_TARGET_OFFSET): str,
      vol.Optional(CONFIG_TARGET_ROLLING_TARGET, default=False): bool,
    })

  async def async_step_target_rate(self, user_input):
    """Setup a target based on the provided user input"""

    errors = validate_target_rate_sensor(user_input)

    if len(errors) < 1:
      # Setup our targets sensor
      return self.async_create_entry(
        title=f"{user_input[CONFIG_TARGET_NAME]} (target)",
        data=user_input
      )

    # Reshow our form with raised errors
    data_Schema = await self.__async_setup_target_rate_schema()
    return self.async_show_form(
      step_id="target_rate", data_schema=data_Schema, errors=errors
    )

  async def async_step_user(self, user_input):
    """Setup based on user config"""

    is_settings_setup = False
    for entry in self._async_current_entries(include_ignore=False):
      if CONFIG_MAIN_REGION in entry.data:
        is_settings_setup = True
        break

    if user_input is not None:
      # We are setting up our initial stage
      if CONFIG_MAIN_REGION in user_input:
        return self.async_create_entry(
        title="Carbon Intensity",
        data=user_input
      )

      # We are setting up a target
      if CONFIG_TARGET_NAME in user_input:
        return await self.async_step_target_rate(user_input)

    if is_settings_setup:
      data_Schema = await self.__async_setup_target_rate_schema()
      return self.async_show_form(
        step_id="target_rate", data_schema=data_Schema
      )

    data_Schema = await self.__async_setup_settings_schema()
    return self.async_show_form(
      step_id="user", data_schema=data_Schema
    )

  @staticmethod
  @callback
  def async_get_options_flow(entry):
    return OptionsFlowHandler(entry)

class OptionsFlowHandler(OptionsFlow):
  """Handles options flow for the component."""

  def __init__(self, entry) -> None:
    self._entry = entry

  async def __async_setup_target_rate_schema(self, config, errors):
    start_time_key = vol.Optional(CONFIG_TARGET_START_TIME)
    if (CONFIG_TARGET_START_TIME in config):
      start_time_key = vol.Optional(CONFIG_TARGET_START_TIME, default=config[CONFIG_TARGET_START_TIME])

    end_time_key = vol.Optional(CONFIG_TARGET_END_TIME)
    if (CONFIG_TARGET_END_TIME in config):
      end_time_key = vol.Optional(CONFIG_TARGET_END_TIME, default=config[CONFIG_TARGET_END_TIME])

    offset_key = vol.Optional(CONFIG_TARGET_OFFSET)
    if (CONFIG_TARGET_OFFSET in config):
      offset_key = vol.Optional(CONFIG_TARGET_OFFSET, default=config[CONFIG_TARGET_OFFSET])

    # True by default for backwards compatibility
    is_rolling_target = True
    if (CONFIG_TARGET_ROLLING_TARGET in config):
      is_rolling_target = config[CONFIG_TARGET_ROLLING_TARGET]

    return self.async_show_form(
      step_id="target_rate",
      data_schema=vol.Schema({
        vol.Required(CONFIG_TARGET_NAME, default=config[CONFIG_TARGET_NAME]): str,
        vol.Required(CONFIG_TARGET_HOURS, default=f'{config[CONFIG_TARGET_HOURS]}'): str,
        vol.Required(CONFIG_TARGET_TYPE, default=config[CONFIG_TARGET_TYPE]): vol.In({
          "Continuous": "Continuous",
          "Intermittent": "Intermittent"
        }),
        start_time_key: str,
        end_time_key: str,
        offset_key: str,
        vol.Optional(CONFIG_TARGET_ROLLING_TARGET, default=is_rolling_target): bool,
      }),
      errors=errors
    )

  async def __async_setup_settings_schema(self, config):
    regions = get_region_options()

    return self.async_show_form(
      step_id="user", data_schema=vol.Schema({
        vol.Required(CONFIG_MAIN_REGION, default=config[CONFIG_MAIN_REGION]): vol.In(regions),
      })
    )

  async def async_step_init(self, user_input):
    """Manage the options for the custom component."""

    config = dict(self._entry.data)
    if self._entry.options is not None:
      config.update(self._entry.options)

    if CONFIG_MAIN_REGION in self._entry.data:
      return await self.__async_setup_settings_schema(config)
    elif CONFIG_TARGET_TYPE in self._entry.data:
      return await self.__async_setup_target_rate_schema(config, {})

    return self.async_abort(reason="not_supported")

  async def async_step_user(self, user_input):
    """Manage the options for the custom component."""

    if user_input is not None:
      config = dict(self._entry.data)
      config.update(user_input)
      return self.async_create_entry(title="", data=config)

    return self.async_abort(reason="not_supported")

  async def async_step_target_rate(self, user_input):
    """Manage the options for the custom component."""

    if user_input is not None:
      config = dict(self._entry.data)
      config.update(user_input)

      errors = validate_target_rate_sensor(config)

      if (len(errors) > 0):
        return await self.__async_setup_target_rate_schema(config, errors)

      return self.async_create_entry(title="", data=config)

    return self.async_abort(reason="not_supported")


# -*- coding: utf-8 -*-
"""Jupyter Notebook Diff for Unsolved and Solved.

This module parses all notebooks in an Activity folder and compares the
Unsolved notebook source code to the Solved. If the Unsolved code is not found
in the Solved folder then an error is thrown. The assumption is that the
Unsolved notebooks should only contain comments or starter code from the Solved.
Comments are stripped from the code and not compared.

Example:

    $ python nbdiff path/to/Activities

Todo:
    * Update README
    * Add to setup.py

"""
import click
import nbformat
from pathlib import Path

from io import StringIO
import tokenize

def remove_comments_and_docstrings(source):
    """
    Returns 'source' minus comments and docstrings.

    modified from https://stackoverflow.com/a/2962727
    """
    io_obj = StringIO(source)
    out = ""
    prev_toktype = tokenize.INDENT
    last_lineno = -1
    last_col = 0
    for tok in tokenize.generate_tokens(io_obj.readline):
        token_type = tok[0]
        token_string = tok[1]
        start_line, start_col = tok[2]
        end_line, end_col = tok[3]
        ltext = tok[4]
        # The following two conditionals preserve indentation.
        # This is necessary because we're not using tokenize.untokenize()
        # (because it spits out code with copious amounts of oddly-placed
        # whitespace).
        if start_line > last_lineno:
            last_col = 0
        if start_col > last_col:
            out += (" " * (start_col - last_col))
        # Remove comments:
        if token_type == tokenize.COMMENT:
            pass
        # This series of conditionals removes docstrings:
        elif token_type == tokenize.STRING:
            if prev_toktype != tokenize.INDENT:
                # This is likely a docstring; double-check we're not inside an operator:
                if prev_toktype != tokenize.NEWLINE:
                    # Note regarding NEWLINE vs NL: The tokenize module
                    # differentiates between newlines that start a new statement
                    # and newlines inside of operators such as parens, brackes,
                    # and curly braces.  Newlines inside of operators are
                    # NEWLINE and newlines that start new code are NL.
                    # Catch whole-module docstrings:
                    if start_col > 0:
                        # Unlabelled indentation means we're inside an operator
                        out += token_string
                    # Note regarding the INDENT token: The tokenize module does
                    # not label indentation inside of an operator (parens,
                    # brackets, and curly braces) as actual indentation.
                    # For example:
                    # def foo():
                    #     "The spaces before this docstring are tokenize.INDENT"
                    #     test = [
                    #         "The spaces before this string do not get a token"
                    #     ]
        else:
            out += token_string
        prev_toktype = token_type
        last_col = end_col
        last_lineno = end_line
    return out

@click.command()
@click.argument("activities_directory", default=".")
def cli(activities_directory):

    # Create Paths
    activities_path = Path(activities_directory)

    # Build activities list
    activities = [a for a in activities_path.iterdir() if a.is_dir()]

    # Iterate through each activity
    for activity in activities:
        solved_path = activity / "Solved"
        unsolved_path = activity / "Unsolved"
        unsolved_notebooks = unsolved_path.glob("**/*.ipynb")
        solved_notebooks = solved_path.glob("**/*.ipynb")

        # Grab all of the unsolved code
        unsolved_code = set()
        for notebook_path in unsolved_notebooks:
            with open(notebook_path, "r") as notebook:
                nb = nbformat.read(notebook, as_version=4)
                for cell in nb.cells:
                    if cell.cell_type == "code" and cell.source:
                        cleaned = remove_comments_and_docstrings(cell.source)
                        if cleaned.strip():
                            unsolved_code.add(cleaned)

        # Grab all of the solved code
        solved_code = set()
        for notebook_path in solved_notebooks:
            with open(notebook_path, "r") as notebook:
                nb = nbformat.read(notebook, as_version=4)
                for cell in nb.cells:
                    if cell.cell_type == "code" and cell.source:
                        cleaned = remove_comments_and_docstrings(cell.source)
                        if cleaned.strip():
                            solved_code.add(cleaned)

        if not unsolved_code <= solved_code:
            print("Error, code cells in the unsolved activity doesn't match the solved")
            print(activity.resolve())
            print(unsolved_code - solved_code)

if __name__ == '__main__':
    cli()


from typing import Tuple

from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
from django.core.paginator import Paginator
from django.db import models
from django.db.models import QuerySet

class Review(models.Model):
    """
    Review Model
    """

    RATING_CHOICES = zip(range(1, 6), range(1, 6))

    reviewer = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    book = models.ForeignKey('book.Book', on_delete=models.CASCADE)

    title = models.CharField(max_length=128)
    text = models.TextField()
    rating = models.IntegerField(choices=RATING_CHOICES)

    approved = models.BooleanField(default=False)

    def __repr__(self) -> str:
        """
        Repr method overriding
        :return: str
        """
        return self.reviewer.username + ' : ' + self.book.title

    def __str__(self) -> str:
        """
        Str method overriding
        :return: str
        """
        return self.title

    @property
    def intro(self) -> str:
        """
        Review Intro displayed in Book Details View
        :return: str
        """
        return self.text[:100] + '...'

    @property
    def owner(self) -> User:
        """
        Property for accessing the user who wrote the review.
        Added for compatibility with Comment model.
        :return: User
        """
        return self.reviewer

    def save(self, *args, **kwargs):
        """
        All comments written by superuser or moderator are approved by default
        """
        if self.reviewer.is_superuser or self.reviewer.groups.filter(
                name='moderator').exists():
            self.approved = True
        super().save(*args, **kwargs)

    @classmethod
    def get_book_reviews(cls, *, pk: int, page: int = 0,
                         paginate_by: int = 12) -> Tuple[QuerySet, Paginator]:
        """
        Return Book Reviews and Pagination object.
        Used in Book Details view.
        :param pk: int
        :param page: int = 0
        :param paginate_by: int = 12
        :return: Tuple[QuerySet, Paginator]
        """
        pag = Paginator(
            cls.objects.select_related('reviewer').filter(approved=True,
                                                          book__pk=pk),
            paginate_by)
        return pag.page(page).object_list, pag

class Comment(models.Model):
    """
    Comment Model
    """

    writer = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    review = models.ForeignKey('comment.Review', on_delete=models.CASCADE)
    replied_to = models.ForeignKey('comment.Comment', on_delete=models.CASCADE,
                                   null=True, blank=True, default=None)
    text = models.CharField(max_length=1024)
    approved = models.BooleanField(default=False)

    @property
    def owner(self) -> User:
        """
        Property for accessing the user who wrote the review.
        Added for compatibility with Review model.
        :return: User
        """
        return self.writer

    def __repr__(self) -> str:
        """
        Repr method overriding
        :return: str
        """
        return self.writer.username + " : " + str(self.review.pk)

    def __str__(self) -> str:
        """
        Str method overriding
        :return: str
        """
        return self.text[:100]

    def save(self, *args, **kwargs):
        """
        All comments written by superuser or moderator are approved by default
        """
        if self.writer.is_superuser or self.writer.groups.filter(
                name='moderator').exists():
            self.approved = True
        super().save(*args, **kwargs)

    @classmethod
    def get_review_comments(cls, *, pk: int, page: int = 0,
                            paginate_by: int = 12) \
            -> Tuple[QuerySet, Paginator]:
        """
        Return Review Comments and Pagination object.
        Used in Book Reviews view.
        :param pk: int
        :param page: int = 0
        :param paginate_by: int = 12
        :return: Tuple[QuerySet, Paginator]
        """
        pag = Paginator(cls.objects.prefetch_related('writer')
                        .filter(approved=True, review__pk=pk), paginate_by)
        return pag.page(page).object_list, pag


import json
import os

TOKEN_SIZE = 500
START_ID = 0
OUTPUT_DIR = os.path.basename(__file__).split('.')[0]

NAME = "Opti Maze ⬜"
DESC = "To be alive is not to know exactly where to go. ⬜"
IMG = "https://diewland.github.io/legendary-bassoon/assets/opti_maze.gif"
ATTRS = [
    # standard
    #{
    #  "trait_type": "Collection",
    #  "value": NAME,
    #},
    # custom
    #{
    #  "trait_type": "Rarity",
    #  "value": "Unrevealed",
    #},
]
ENGINE = "Jigsaw Engine"

metadata = {
  "name": "***",
  "description": DESC,
  "image": "***",
  "attributes": ATTRS,
  "compiler": ENGINE,
}

for id in range(0, TOKEN_SIZE):
    metadata["name"] = "{} #{}".format(NAME, id)
    metadata["image"] = IMG.format(id)
    with open("./{}/{}".format(OUTPUT_DIR, START_ID + id), "w") as f:
        json.dump(metadata, f, ensure_ascii=False)


import numpy as np
import imageio
import pyautogui
import os

def get_next_file_name(file_name):

    name, ext = os.path.splitext(file_name)
    i = 0
    while os.path.exists(file_name):
        i += 1
        file_name = f"{name}{i}{ext}"
    return file_name

screen_size = (1920, 1080)

file_name = 'record.mp4'
file_name = get_next_file_name(file_name)
writer = imageio.get_writer(file_name, fps=30)

# Başlangıç mesajı yazdırın
print("Screen recording started. Use 'Ctrl+C' key combination to stop recording.")

try:
    while True:
        img = pyautogui.screenshot()
        frame = np.array(img)
        writer.append_data(frame)

except KeyboardInterrupt:
    print("\nScreen recording stopped.")

finally:
    writer.close()


# -*- coding: utf-8 -*-
import os
import sqlite3
import time
import subprocess

from PyQt5 import uic
from PyQt5.QtCore import QDateTime, Qt, QDate, QTime
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QMainWindow

class ODBC():
    def __init__(self):
        self.path = os.path.join(os.getcwd(),  'datas.db')

    def GetConnect(self):
        self.con = sqlite3.connect(self.path)
        cur = self.con.cursor()
        if not cur:
            raise (NameError, '连接数据库失败')
        else:
            return cur

    def add_info(self, table_name, data):
        # 添加信息
        value_len = ','.join('?' * len(data[0]))  # 计算数据长度
        sql = 'INSERT INTO %s VALUES (%s)' % (str(table_name), value_len)
        cur = self.GetConnect()
        cur.executemany(sql, data)
        self.con.commit()

    def del_info(self, table_name, name):
        # 添加信息
        sql = 'delete from %s where Time2 like "%s"' % (str(table_name), str(name))

        cur = self.GetConnect()
        cur.execute(sql)
        self.con.commit()

    def xiugai_info(self, table_name, text, data):
        # 添加信息
        self.del_info(table_name, text)
        self.add_info(table_name, data)

    def search_all_info(self, table_name):
        # 添加信息
        sql = 'select * from %s' % str(table_name)

        cur = self.GetConnect()
        datas = cur.execute(sql)
        return [i for i in datas]

    def search_info_by(self):
        # 添加信息
        pass
    def creat_(self):
        # 使用 cursor（）方法创建一个游标对象 cursor
        cursor = self.GetConnect()
        sql = '''CREATE TABLE Bwl
                           (
                           Texts          CHAR(100),
                           Time1          CHAR(100)  ,
                            Time2     CHAR(300));'''
        try:
            cursor.execute(sql)
            # 提交到数据库执行
            self.con.commit()
            print('CREATE [user_info] TABLE SUCCESS.')
        # 捕获与数据库相关的错误
        except:
            print('CREATE TABLE FAILED')
            # 如果发生错误就回滚
            self.con.rollback()
        finally:
            # 关闭数据库连接
            self.con.close()
ms = ODBC()

class UILoader(QMainWindow):
    def __init__(self):
        # 加载UI文件
        # uiFile = QFile("console.ui")
        # uiFile.open(QFile.ReadOnly)
        # uiFile.close()
        self.ms = ODBC()

        # 从ui文件中动态创建一个相应的窗口对象
        self.mainWindow = uic.loadUi("console.ui")
        # 左侧的按钮
        self.showModelBtn = self.mainWindow.showModelBtn
        self.showModelBtn.clicked.connect(lambda value: self.OpenShowModel())
        self.soundsEffectBtn = self.mainWindow.soundsEffectBtn
        self.soundsEffectBtn.setChecked(True)

        self.freeMoveBtn = self.mainWindow.freeMoveBtn
        self.freeMoveBtn.clicked.connect(lambda value: self.OpenFreeMove())
        self.freeMoveBtn = self.mainWindow.freeMoveBtn

        self.soundsEffectBtn = self.mainWindow.soundsEffectBtn
        self.soundsEffectBtn.clicked.connect(lambda value: self.OpenSoundEffect())
        self.soundsEffectBtn = self.mainWindow.soundsEffectBtn

        # 控制移动速度的滑块
        self.dialogLifeLable = self.mainWindow.dialogLifeLable
        self.dialogLifeSlider = self.mainWindow.dialogLifeSlider
        self.dialogLifeSlider.valueChanged.connect(
            lambda value: self.ShowValue(self.dialogLifeLable,
                                         "Duration of the dialog box: " + str(self.dialogLifeSlider.value())))
        self.dialogLifeSlider.valueChanged.connect(lambda value: self.SetDialogLife(self.dialogLifeSlider.value()))

        #控制移动频率的滑块
        self.actFrequencyLabel = self.mainWindow.ActFrequencyLable
        self.actFrequencySlider = self.mainWindow.ActFrequencySlider
        self.actFrequencySlider.valueChanged.connect(
            lambda value: self.ShowValue(self.actFrequencyLabel,
                                         "activity frequency: " + str(self.actFrequencySlider.value())))
        self.actFrequencySlider.valueChanged.connect(lambda value: self.SetActivityFrequency(self.actFrequencySlider.value()))
        self.activityFrequency = 6

        # 控制音量大小的滑块
        self.soundValueLabel = self.mainWindow.soundValueLabel
        self.soundValueSlider = self.mainWindow.soundValueSlider
        self.soundValueSlider.valueChanged.connect(
            lambda value: self.ShowValue(self.soundValueLabel,
                                         "sound value: " + str(self.soundValueSlider.value())))
        self.soundValueSlider.valueChanged.connect(lambda value: self.SetSoundValue(self.soundValueSlider.value()))
        self.soundValue = 25

        # 主界面右下角两个按钮
        self.resourceFileBtn = self.mainWindow.resourceFileBtn
        self.resourceFileBtn.clicked.connect(lambda value: self.OpenResourceFile())
        self.resetBtn = self.mainWindow.resetBtn
        self.resetBtn.clicked.connect(lambda value: self.Reset())

        # 日程界面的操作栏
        self.dateEdit = self.mainWindow.dateEdit
        self.dateEdit.setDate(QDateTime.currentDateTime().date())
        self.timeEdit = self.mainWindow.timeEdit
        self.timeEdit.setTime(QDateTime.currentDateTime().time())
        self.scheduleEdit = self.mainWindow.ScheduleEdit
        self.addScheduleBtn = self.mainWindow.addScheduleBtn
        self.addScheduleBtn.clicked.connect(
            lambda value: self.addSchedule(self.dateEdit.date(), self.timeEdit.time(), self.scheduleEdit.text()))
        self.scheduleListModel = QStandardItemModel()

        # 日程界面按钮
        self.clearFinishTaskBtn = self.mainWindow.clearFinishTaskBtn
        self.clearFinishTaskBtn.clicked.connect(lambda value: self.clearTask())
        self.openRemindingBtn = self.mainWindow.openRemindingBtn
        self.openRemindingBtn.clicked.connect(lambda value: self.getRemindstate())
        self.stateLable = self.mainWindow.stateLable
        self.stateLable.setStyleSheet("color: red;")

        datas = self.ms.search_all_info('bwl')
        row = len(datas)
        for i in range(row):
            time_to_add = datas[i][1]
            s = str.split(time_to_add, " ")
            d = str.split(s[0], "-")
            qdate = QDate(int(d[0]), int(d[1]), int(d[2]))
            t = str.split(s[1], ":")
            qtime = QTime(int(t[0]), int(t[1]), int(t[2]))
            self.addSchedule1(qdate, qtime, datas[i][0])

    def closeEvent(self, event):
        event.ignore()

    def OpenShowModel(self):
        if self.showModelBtn.isChecked():
            return 1
        else: return 0

    def SetShowModel(self, boolean):
        if boolean:
            self.showModelBtn.setChecked(Qt.Checked)
        else: self.showModelBtn.setChecked(Qt.Unchecked)

    def OpenFreeMove(self):
        if self.freeMoveBtn.isChecked():
            return True
        return False

    def SetFreeMove(self, boolean):
        if boolean:
            self.freeMoveBtn.setChecked(Qt.Checked)
        else: self.freeMoveBtn.setChecked(Qt.Unchecked)

    def OpenSoundEffect(self):
        if self.soundsEffectBtn.isChecked():
            return True
        else: False

    def ShowValue(self, target, textContent):
        target.setText(textContent)

    def SetDialogLife(self, setValue):
        print('set value:',setValue)
        pass

    def GetDialogLife(self):
        return self.dialogLifeSlider.value() * 1000

    def SetSoundValue(self, setValue):
        self.soundValue = setValue

    def GetSoundValue(self):
        if self.OpenSoundEffect():
            return self.soundValue/100
        else: return 0

    def SetActivityFrequency(self, setValue):
        self.activityFrequency = setValue

    def GetActivityFrequency(self):
        return self.activityFrequency*100

    def OpenResourceFile(self):
        current_dir = os.path.dirname(os.path.abspath(__file__))
        subprocess.run(["explorer.exe", current_dir])

    # 获取日程表，返回Qdate, QTime, 是否勾选, 日程内容
    def getSchedule(self):
        itemList = []
        for row in range(0, self.scheduleListModel.rowCount() - 1):
            datetimeItem = self.scheduleListModel.item(row, column=0)
            getDateTime = QDateTime.fromString(datetimeItem.text(), Qt.ISODate)
            QDate = getDateTime.date()
            QTime = getDateTime.time()
            is_checked = bool(datetimeItem.checkState())
            taskContent = self.scheduleListModel.item(row, column=1).text()
            itemList.append([QDate, QTime, is_checked, taskContent])
        return itemList

    #设置日程
    def addSchedule(self, Qdate, Qtime, TaskContent):

        def validation(date, time):

            # 该方法返回自1970年1月1日以来的毫秒数
            setDateTime = QDateTime(date,time).toSecsSinceEpoch()

            currentDateTime = QDateTime.currentSecsSinceEpoch()

            if self.scheduleEdit.text() == '':
                self.stateLable.setText('please enter your task')
                return False
            if currentDateTime > setDateTime:
                self.stateLable.setText('please enter a correct time')
                return False
            return True

        def changeFormat(inputNum):
            if inputNum < 10:
                inputNum = '0'+str(inputNum)
            return inputNum

        if validation(Qdate, Qtime):

            self.stateLable.setText('') # 隐藏按钮侧边的文字
            Qdate = Qdate.getDate()
            timeItem = QStandardItem('%s-%s-%s %s:%s:%s' % (changeFormat(Qdate[0]), changeFormat(Qdate[1]), changeFormat(Qdate[2])
                                                              ,changeFormat(Qtime.hour()),changeFormat(Qtime.minute()),changeFormat(Qtime.second())))

            timeItem.setCheckable(True)
            timeItem.setCheckState(Qt.Unchecked)  # 设置复选框的默认状态
            taskItem = QStandardItem(TaskContent)
            self.scheduleListModel.setHorizontalHeaderLabels(['time','Task'])
            self.scheduleListModel.appendRow([timeItem,taskItem])
            # 显示列表前对时间进行排序
            self.scheduleListModel.sort(0, Qt.AscendingOrder)
            self.mainWindow.ScheduleListView.show()
            self.mainWindow.ScheduleListView.setModel(self.scheduleListModel)
            time_to_add = timeItem.text()
            s = str.split(time_to_add, " ")
            d = str.split(s[0], "-")
            qdate = QDate(int(d[0]), int(d[1]), int(d[2]))
            t = str.split(s[1], ":")
            qtime = QTime(int(t[0]), int(t[1]), int(t[2]))
            t = QDateTime(qdate, qtime)
            self.ms.add_info('Bwl',
                             [[TaskContent, t.toString(Qt.ISODate).replace('T', ' '), str(t.toMSecsSinceEpoch())[0:-3]]])

            return True

        return False  # 格式不对返回False

    def addSchedule1(self, Qdate, Qtime, TaskContent):

        def changeFormat(inputNum):
            if inputNum < 10:
                inputNum = '0'+str(inputNum)
            return inputNum

        self.stateLable.setText('')  # 隐藏按钮侧边的文字
        Qdate = Qdate.getDate()
        timeItem = QStandardItem(
            '%s-%s-%s %s:%s:%s' % (changeFormat(Qdate[0]), changeFormat(Qdate[1]), changeFormat(Qdate[2])
                                   , changeFormat(Qtime.hour()), changeFormat(Qtime.minute()),
                                   changeFormat(Qtime.second())))

        timeItem.setCheckable(True)
        timeItem.setCheckState(Qt.Unchecked)  # 设置复选框的默认状态
        taskItem = QStandardItem(TaskContent)

        self.scheduleListModel.setHorizontalHeaderLabels(['time', 'Task'])
        self.scheduleListModel.appendRow([timeItem, taskItem])
        # 显示列表前对时间进行排序
        self.scheduleListModel.sort(0, Qt.AscendingOrder)

        self.mainWindow.ScheduleListView.show()
        self.mainWindow.ScheduleListView.setModel(self.scheduleListModel)
        return 1

    # 通过行数删除日程，行数和getSchedule返回的列表索引相同
    def deleteTask(self, row):
        if row in range(0, self.scheduleListModel.rowCount()):
            self.scheduleListModel.removeRow(row)
            # self.ms.del_info('bwl', data[2])

    # 设置任务已完成
    def setChecked(self, row):
        if row in range(0, self.scheduleListModel.rowCount()):
            checkBox = self.scheduleListModel.item(row, column=0)
            checkBox.setCheckState(Qt.Checked)

    def getRemindstate(self):
        if self.openRemindingBtn.isChecked():
            return True
        else: return False

    # 清除已经勾选（完成）的任务
    def clearTask(self):
        print('work')
        # 反向遍历，避免删除时行数改变
        for row in range(self.scheduleListModel.rowCount()-1, -1, -1):
            # 获取每一行的勾选框
            checkBox_state = self.scheduleListModel.item(row, column=0).checkState()
            time_to_add = self.scheduleListModel.item(row, column=0).text()
            s = str.split(time_to_add, " ")
            d = str.split(s[0], "-")
            qdate = QDate(int(d[0]), int(d[1]), int(d[2]))
            t = str.split(s[1], ":")
            qtime = QTime(int(t[0]), int(t[1]), int(t[2]))
            t = QDateTime(qdate, qtime)
            self.ms.del_info('bwl', str(t.toMSecsSinceEpoch())[0:-3])
            if checkBox_state:
                self.scheduleListModel.removeRow(row)

    def Reset(self):
        self.showModelBtn.setChecked(Qt.Checked)
        self.freeMoveBtn.setChecked(Qt.Checked)
        self.soundsEffectBtn.setChecked(Qt.Checked)

        self.dialogLifeSlider.setValue(3)
        self.actFrequencySlider.setValue(3)
        self.soundValueSlider.setValue(50)

# app = QApplication()
# window = UILoader()
# window.mainWindow.show()
# app.exec_()


import numpy as np
import matplotlib.pyplot as plt

from scipy.optimize import curve_fit
from sklearn.metrics import r2_score

from pyplm.utilities import tools
from helpers import add_fit_to_ax

def saturation_split_iD(ax, df, observable, run_iD, **pltkwargs):
    subset_df = df.loc[df['iD'] == run_iD]
    x = subset_df['B']
    y = subset_df[observable]
    ax.plot(x, y, **pltkwargs)

def saturation_fit(ax, df, observable, threshold_B, **pltkwargs):
    df = df.groupby(['B'], as_index=True).mean()
    df = df.reset_index()
    ax.plot(df['B'], df[observable], c='k', marker='d', ls='none', alpha=1)
    df = df.loc[df['B'] >= threshold_B]

    x = df['B']
    y = df[observable]
    func = tools.arctan
    popt, _ = curve_fit(tools.arctan, x, y, p0 = [0.1, 0.1])
    xfit = np.linspace(0, x.max(), 1000)
    yfit = func(xfit, *popt)
    ax.plot(xfit, yfit, **pltkwargs)
    popt[0] = (popt[0] * np.pi) / 2
    popt[1] = 1 / popt[1]
    popt = np.append(popt, np.nanmax(y))
    return popt

def N_error(ax):
    x = np.array([50, 100, 200, 400, 800])
    y = np.array([0.14320461, 0.20949111, 0.29832886, 0.44711299, 0.69887905])
    mus = np.array([0.5, 0.4, 0.4, 0.6, 0.2])
    sigmas = np.array([1, 1, 1, 1, 1])
    Ts = np.array([1.1, 1.175, 1.1, 1.1, 1.25])

    # so if I plot simga mu n against thingy it should be a straight line going
    # throguh the origin right?

    # lbl = (r'$\varepsilon _{\mathrm{min}}(N) = A N ^\gamma$')

    # lbl = (r'$\varepsilon _{\mathrm{min}}(N) = A N ^{1/2}$')
    ax.plot(x, y, ls='none', color='#4053d3')

    xs = np.linspace(0, 1000, 1000)
    fc = 'grey'
    lbl = (r'$\varepsilon (N) = A N ^\gamma$')
    add_fit_to_ax(
        ax, x, y, tools.pure_power, xfit=xs, err_col=fc, show_error=False,
        marker=',', c='k', ls='-', label=lbl)
    fc = '#00beff'
    lbl = (r'$\varepsilon (N) = A N ^{1/2} + c$')
    print('-0-')
    add_fit_to_ax(
        ax, x, y, tools.sqrt_x, xfit=xs, err_col=fc, show_error=False,
        marker=',', c='r', ls='-', label=lbl)
    print('-0-')
    # fc = '#fb49b0'

    ax.set(
        # ylim=[y.min() * 0.97, y.max() * 1.03],
        ylim=[0, None],
        xlim=[0, 805],
        xlabel=r'$N$',
        ylabel=r'$\varepsilon$')
    ax.legend()

def error_scaling_explanation(ax):
    # xs = np.array([50, 100, 200, 400, 800])
    Ns = np.array([50, 100, 200, 400, 800])
    errs = np.array([0.14320461, 0.20949111, 0.29832886, 0.44711299, 0.69887905])
    mus = np.array([0.5, 0.4, 0.4, 0.6, 0.2])
    sigmas = np.array([1, 1, 1, 1, 1])
    Ts = np.array([1.1, 1.175, 1.1, 1.1, 1.25])

    xs = (Ns ** 0.5)
    xs = xs * (Ts ** -1)
    xs_uncorrected = np.copy(xs)
    sqrt_term = np.sqrt( ((mus ** 2) / Ns) + (sigmas ** 2))
    xs = xs * sqrt_term
    ys = errs

    # explain this somehow! So acutally the error is a pretty
    # bad measure of the phase diagram thingy. Really
    # I should do my B subsmapling to figure it out i think!
    # this comes from numerics....
    # I should plot my error surface for a fixed thing!

    # lbl = (r'$\varepsilon _{\mathrm{min}}(N) = A N ^{1/2}$')
    ax.plot(xs, ys, ls='none', color='#4053d3')
    ax.plot(xs_uncorrected, ys, ls='none', color='#b51d14')

    # xfits = np.linspace(0, 1000, 1000)
    xfits = np.linspace(xs.min(), xs.max(), 1000)
    fc = 'grey'
    lbl = (r'$\varepsilon (N) = A N ^\gamma$')
    add_fit_to_ax(
        ax, xs, ys, tools.linear, xfit=xfits, err_col=fc, show_error=True,
        marker=',', c='k', ls='-', label=lbl)
    # fc = '#00beff'
    # lbl = (r'$\varepsilon (N) = A N ^{1/2} + c$')
    # print('-0-')
    # add_fit_to_ax(
    #     ax, x, y, tools.sqrt_x, xfit=xs, err_col=fc, show_error=False,
    #     marker=',', c='r', ls='-', label=lbl)
    # print('-0-')
    # # fc = '#fb49b0'

    ax.set(
        # ylim=[y.min() * 0.97, y.max() * 1.03],
        # ylim=[0, None],
        # xlim=[0, 805],
        xlabel=r'$N$',
        ylabel=r'$\varepsilon$')
    ax.legend()

def error_test():
    E = 1
    N = 200
    # Ts = np.linspace(0.3, 2, 100)
    T = 1
    mu = 0.1
    sigma = 1
    # err = [err_analytical(E, N, T, mu, sigma) for T in Ts]
    # plt.plot(Ts, err)
    Ns = np.linspace(10, 200, 20)
    plt.plot(Ns, err_analytical(E, Ns, T, mu, sigma))
    plt.show()
    pass

def err_analytical(E, N, T, mu, sigma):
    err = E * (N ** -0.5) * (T ** -1)
    sqrt_term = np.sqrt( ((mu ** 2) / N) + (sigma ** 2))
    err = err * sqrt_term
    return err

def plot_with_fit(ax, x, y, fitfunc, color, label):
    xrange = abs(x.max() - x.min())
    x_lim_offset = xrange * 0.02
    xlim_min = x.min() - x_lim_offset
    xlim_max = x.max() + x_lim_offset

    yrange = abs(y.max() - y.min())
    y_lim_offset = yrange * 0.02
    ylim_min = y.min() - y_lim_offset
    ylim_max = y.max() + y_lim_offset

    xfit = np.linspace(xlim_min, xlim_max, 200)
    # x = np.log10(x)
    # y = np.log10(y)
    # xfit = np.log10(xfit)
    # popt, _ = curve_fit(fitfunc, x, y)
    # yfit = fitfunc(xfit, *popt)
    # score = r2_score(y, fitfunc(x, *popt))
    # score = 1 - ((1-score) * ((y.size - 1) / (y.size - popt.size - 1)))
    # print(popt, score)
    # x = 10 ** x
    # y = 10 ** y
    # xfit = 10 ** xfit
    # yfit = 10 ** yfit
    # ax.plot(xfit, yfit, c='k', marker=',', ls='-')
    add_fit_to_ax(
        ax, x, y, fitfunc, xfit=xfit, err_col=color, show_error=False,
        marker=',', c='k', ls='-')
    ax.set(xlim=[xlim_min, xlim_max], ylim=[ylim_min, ylim_max])
    line, = ax.plot(x, y, c=color, ls='none', label=label, zorder=50)
    return line


from flask import Flask, Blueprint, session, redirect, url_for
from flask_session import Session
from pathlib import Path
import config as dev_config
import os, logging

"""
Instructions for running the app:

LINUX/OSX - in a terminal window, type the following:
=======================================================
    export FLASK_APP=authenticate_users_in_my_tenant.py
    export FLASK_ENV=development
    export FLASK_DEBUG=1
    export FLASK_RUN_CERT=adhoc
    flask run

WINDOWS - in a command window, type the following:
====================================================
    set FLASK_APP=authenticate_users_in_my_tenant.py
    set FLASK_ENV=development
    set FLASK_DEBUG=1
    set FLASK_RUN_CERT=adhoc
    flask run

You can also use "python -m flask run" instead of "flask run"
"""

def create_app(name='authenticate_users_in_my_org', root_path=Path(__file__).parent, config_dict=None):
    app = Flask(name, root_path=root_path)
    app.logger.info(f"Environment set in app.config is {app.config.get('ENV')}")
    if app.config.get('ENV') == 'production':
        app.logger.level=logging.INFO
        app.logger.error("ARE YOU SURE?")
        # if you are certain you want to run in prod,
        # supply a production config and remove this line:
        raise ValueError('This app is not meant to run in production. Run it according to instructions at top of this file.')
    elif app.config.get('ENV') == 'development':
        app.logger.level=logging.DEBUG
        app.config.from_object(dev_config)
    else:
        raise ValueError('production and development are the only options')

    if config_dict is not None:
            app.config.from_mapping(config_dict)

    # init the serverside session on the app
    Session(app)

    # We have to push the context before registering auth endpoints blueprint
    app.app_context().push()

    # this is where our auth-related endpoints are defined:
    import auth_endpoints

    # register the auth endpoints! These are:
    # sign-in status
    # token details
    # redirect
    # sign in
    # sign out
    # post sign-out
    app.register_blueprint(auth_endpoints.auth)

    # add the default route (/)
    # redirect user to page to see their sign-in status
    @app.route('/')
    def index():
        return redirect(url_for('auth.sign_in_status'))

    return app

if __name__ == '__main__':
    app=create_app()
    # the param value in the following line creates an adhoc ssl cert and allows the app to serve HTTPS on loopback (127.0.0.1).
    # WARNING 1: Use a real certificate in production
    # WARNING 2: Don't use app.run in production - use a production server!
    app.run(ssl_context='adhoc')


__author__           = "Dilawar Singh"
__copyright__        = "Copyright 2019-, Dilawar Singh"
__maintainer__       = "Dilawar Singh"
__email__            = "dilawars@ncbs.res.in"

# read a tiff file and return frames.

from PIL import Image
import numpy as np

def readTiff(filepath):
    img = Image.open(filepath)
    data, frames = [], []
    for i in range(img.n_frames):
        img.seek(i)
        f = np.array(img)
        frames.append(f)
        line = [chr(x) for x in f[0,:]]
        data.append(''.join(line))
    return frames, data


from minisom import MiniSom
from pylab import pcolor, colorbar, plot
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
import os
import pandas as pd
import math
import numpy as np

# utils
def shape_view(title : str, dataframe):
    shape = dataframe.shape
    c = 17
    c2 = 7
    c3 = 5
    try:
        records = shape[0]
        pixel_h = shape[1]
        pixel_v = shape[2]
        channel = shape[3]
        total = pixel_h * pixel_v
        print(f'Titulo: {title}{(c-len(title))*" "}|| Registros: {records}{(c2-len(str(records)))*" "}|| Pixels: {pixel_h} X {pixel_h} = {total}{(c3-len(str(total)))*" "}|| Canais: {channel}')
    except:
        try:
            records = shape[0]
            pixel_h = shape[1]
            pixel_v = shape[2]
            total = pixel_h * pixel_v
            print(f'Titulo: {title}{(c-len(title))*" "}|| Registros: {records}{(c2-len(str(records)))*" "}|| Pixels: {pixel_h} X {pixel_h} = {total}')
        except:
            records = shape[0]
            try:
                columns = shape[1]
            except:
                columns = 0
            print(f'Titulo: {title}{(c-len(title))*" "}|| Registros: {records}{(c2-len(str(records)))*" "}|| Colunas: {columns}')

def column_null_view(column : str):
    shape_view(f'Coluna Nula - {column}',base.loc[pd.isnull(base[column])])

def drop_columns(columns : list):
    for column in columns:
        base.drop(column, axis=1, inplace=True)
    return base

def column_value_view(column : str):
    return base[column].value_counts()

def graphic_view():
    plt.axis('off')
    plt.show()

def drop_lines_na():
    # axis = 0 ele apaga a linha
    base.dropna(axis=0, inplace=True)

def calculate_neurons(qty_records):
    square = math.sqrt(qty_records)
    value = 5 * square
    matrix_value = math.ceil(math.sqrt(value))
    return (matrix_value, matrix_value)

def qty_atributes(data):
    try:
        return data.shape[1]
    except:
        return len(data[0])

def qty_records(data):
    try:
        return data.shape[0]
    except:
        return len(data)

# lendo os dados
PATH_DATA = os.path.join(os.path.dirname(__file__),'data')

# Carregamento da base de dados
base = pd.read_csv(PATH_DATA + '\\credit_data.csv')
shape_view('Base Inicial', base)

# Pré processamento
drop_lines_na()
shape_view('Nulos Removidos', base)

## preenchendo idades negativas com a média das idades que não são negativas
base.loc[base.age < 0, 'age'] = base.loc[base.age > 0, 'age'].mean()
shape_view('Idades Negativas',base.loc[base.age < 0, 'age'])

## separando os dados
X = base.iloc[:, 0:4].values
y = base.iloc[:, 4].values

## normalizando os dados entre 0 e 1
normalizador = MinMaxScaler(feature_range=(0, 1))
X = normalizador.fit_transform(X)

# construindo o mapa organizavel
x_matrix, y_matrix = calculate_neurons(qty_records(X))
som = MiniSom(
    x=x_matrix,
    y=y_matrix,
    input_len=qty_atributes(X),
    random_seed=0
)

# inicializar os pesos
som.random_weights_init(X)

# realizar o treinamento
som.train_random(
    data= X,
    num_iteration=100
)

# Se o gráfico estiver com um ponto mais para o amarelo
# Quer dizer que eles são muito diferentes dos outros, ou seja, são outliers
# para o nosso exemplo, indica que eles possuem uma probabilidade alta de fraude
# visualizando o mapa
pcolor(som.distance_map().T)
colorbar()

# criando os marcadores
markers = ['o', 's']

# criando as cores
colors = ['r', 'b']

# criando o mapa auto organizavel
for i, x in enumerate(X):
    w = som.winner(x)
    plot(
        w[0] + 0.5,
        w[1] + 0.5,
        markers[y[i]],
        markerfacecolor='None',
        markersize=10,
        markeredgecolor=colors[y[i]],
        markeredgewidth=2
    )
graphic_view()

# buscando clientes que realmente podem ser fraudulentos
# no mapeamento vai indicar quais registros representam qual neuronio
mapeamento = som.win_map(X)

# pegando os suspeitos
# suspeitos são os que estão na base amarela do mapa
# no mapa as linhas são horizontais e as colunas nas verticais
"""
lista de suspeitos:
LINHA X COLUNA
(5, 5)
(13,7)
"""
suspeitos = np.concatenate((mapeamento[(5, 5)], mapeamento[(13,7)]), axis=0)

# desnormalizando o dado
suspeitos = normalizador.inverse_transform(suspeitos)

# criando os valores da classe
# verificando quem são os usuários e a que classe pertencem e armazenando numa lista
classe = []
for i in range(qty_records(base)):
    for j in range(qty_records(suspeitos)):
        if base.iloc[i, 0] == int(round(suspeitos[j, 0])):
            classe.append(base.iloc[i, 4])

# transformando a lista em um array do numpy para manipulação
classe = np.asarray(classe)

# juntando os dados da classe com os suspeitos
suspeitos_final = np.column_stack((suspeitos, classe))

# colocando em ordem crescente
suspeitos_final = suspeitos_final[suspeitos_final[:, 4].argsort()]
print(suspeitos_final)


import sys
from collections import deque

## 입력 및 그래프 생성
M, N = map(int, sys.stdin.readline().split())

graph = []
for _ in range(M): graph.append(list(map(int, sys.stdin.readline().split())))

## 탐색 메서드 정의
def search(x, y):
    graph[x][y] = -1

    ## 큐 생성
    que = deque()
    que.append((x, y))

    ## 반복
    while que:
        a, b = que.popleft()

        ## 탐색 방향 정의
        dx = [1, -1, 0, 0, 1, 1, -1, -1]
        dy = [0, 0, 1, -1, 1, -1, 1, -1]

        for i in range(8):
            nx = a + dx[i]
            ny = b + dy[i]

            if (0 <= nx < M) and (0 <= ny < N):
                if graph[nx][ny] == 1:
                    graph[nx][ny] = -1
                    que.append((nx, ny))

## 탐색
cnt = 0

for i in range(M):
    for j in range(N):
        if graph[i][j] == 1:
            cnt += 1
            search(i, j)

print(cnt)


import pytest
from typing import Tuple
from datetime import timedelta, date

from app.model import Batch, OrderLine, allocate, OutOfStock

today = date.today()
tomorrow = today + timedelta(days=1)
next_week = today + timedelta(weeks=1)

def test_prefers_current_stock_batches_over_shipments():
    in_stock_batch = Batch("in-stock-batch", "RETRO-CLOCK", 100, eta=None)
    shipment_batch = Batch("shipment", "RETRO-CLOCK", 100, eta=tomorrow)
    line = OrderLine("oref", "RETRO-CLOCK", 10)

    allocate(line, [in_stock_batch, shipment_batch])

    assert in_stock_batch.available == 90
    assert shipment_batch.available == 100

def test_prefers_earlier_batch():
    today_batch = Batch("first", "SPOON", 100, eta=today)
    tomorrow_batch = Batch("second", "SPOON", 100, eta=tomorrow)
    next_week_batch = Batch("third", "SPOON", 100, eta=next_week)
    line = OrderLine("order1", "SPOON", 10)

    allocate(line, [today_batch, tomorrow_batch, next_week_batch])

    assert today_batch.available == 90
    assert tomorrow_batch.available == 100
    assert next_week_batch.available == 100

def test_returns_allocated_batch_ref():
    in_stock_batch = Batch("in-stock-ref", "WORLD-MAP", 100, eta=None)
    shipment_batch = Batch("shipment-ref", "WORLD-MAP", 100, eta=tomorrow)
    line = OrderLine("oref", "WORLD-MAP", 10)
    allocation = allocate(line, [in_stock_batch, shipment_batch])
    assert allocation == in_stock_batch.ref

def test_raises_out_of_stock_exception_if_cannot_allocate():
    batch = Batch("batch1", "FORK", 10, eta=today)
    allocate(OrderLine("order1", "FORK", 10), [batch])

    with pytest.raises(OutOfStock, match="FORK"):
        allocate(OrderLine("order1", "FORK", 10), [batch])


#____________________DENSE OPTICAL FLOW______________________________________

import numpy as np
import cv2
cap = cv2.VideoCapture("/home/paython/Videos/4K Video Downloader/Walking through Japan in 1 min.mp4")

ret, first_frame = cap.read()
previous_gray = cv2.cvtColor(first_frame,cv2.COLOR_BGR2GRAY)

hsv = np.zeros_like(first_frame)
hsv[...,1]=255

while True:
    ret, frame = cap.read()
    next = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
    flow  = cv2.calcOpticalFlowFarneback(previous_gray,next,None,0.5,3,15,3,5,1.2,0)
    magnitude, angle = cv2.cartToPolar(flow[...,0],flow[...,1])
    hsv[...,0] = angle * (180/(np.pi/2))
    hsv[...,2] = cv2.normalize(magnitude,None,0,255,cv2.NORM_MINMAX)
    final = cv2.cvtColor(hsv,cv2.COLOR_HLS2BGR)
    cv2.imshow('Dense optical flow',final)
    if cv2.waitKey(1)==13:
        break
    previous_gray=next

cv2.destroyAllWindows()
cap.release()


import logging
import re

from praw.exceptions import ClientException as RedditClientException
from praw.models import Comment as RedditComment
from tor_core.helpers import send_to_slack

from tor.core.admin_commands import process_override
from tor.core.admin_commands import process_command
from tor.core.mentions import process_mention
from tor.core.user_interaction import process_claim
from tor.core.user_interaction import process_coc
from tor.core.user_interaction import process_done
from tor.core.user_interaction import process_thanks

MOD_SUPPORT_PHRASES = [
    re.compile('fuck', re.IGNORECASE),
    re.compile('unclaim', re.IGNORECASE),
    re.compile('undo', re.IGNORECASE),
    re.compile('(?:good|bad) bot', re.IGNORECASE),
]

def forward_to_slack(item, config):
    send_to_slack(
        f'Unhandled inbox reply by *{item.author}* -- *{item.subject}*: '
        f'{item.body}', config)

    logging.info(
        f'Received unhandled inbox message from {item.author}. \n Subject: '
        f'{item.subject}\n\nBody: {item.body} '
    )

def process_mod_intervention(post, config):
    """
    Triggers an alert in slack with a link to the comment if there is something
    offensive or in need of moderator intervention
    """
    if not isinstance(post, RedditComment):
        # Why are we here if it's not a comment?
        return

    # Collect all offenses (noted by the above regular expressions) from the
    # original
    phrases = []
    for regex in MOD_SUPPORT_PHRASES:
        matches = regex.search(post.body)
        if not matches:
            continue

        phrases.append(matches.group())

    if len(phrases) == 0:
        # Nothing offensive here, why did this function get triggered?
        return

    # Wrap each phrase in double-quotes (") and commas in between
    phrases = '"' + '", "'.join(phrases) + '"'

    send_to_slack(
        f':rotating_light::rotating_light: Mod Intervention Needed '
        f':rotating_light::rotating_light: '
        f'\n\nDetected use of {phrases} <{post.submission.shortlink}>',
        config
    )

def process_reply(reply, config):
    # noinspection PyUnresolvedReferences
    try:
        if any([regex.search(reply.body) for regex in MOD_SUPPORT_PHRASES]):
            process_mod_intervention(reply, config)
            reply.mark_read()
            return

        if 'i accept' in reply.body.lower():
            process_coc(reply, config)
            reply.mark_read()
            return

        if 'claim' in reply.body.lower():
            process_claim(reply, config)
            reply.mark_read()
            return

        if 'done' in reply.body.lower():
            process_done(reply, config)
            reply.mark_read()
            return

        if 'thank' in reply.body.lower():  # trigger on "thanks" and "thank you"
            process_thanks(reply, config)
            reply.mark_read()
            return

        if '!override' in reply.body.lower():
            process_override(reply, config)
            reply.mark_read()
            return

        # If we made it this far, it's something we can't process automatically
        forward_to_slack(reply, config)
        reply.mark_read()  # no spamming the slack channel :)

    except (AttributeError, RedditClientException):
        # the only way we should hit this is if somebody comments and then
        # deletes their comment before the bot finished processing. It's
        # uncommon, but common enough that this is necessary.
        pass

def check_inbox(config):
    """
    Goes through all the unread messages in the inbox. It has two
    loops within this section, each one dealing with a different type
    of mail. Also deliberately leaves mail which does not fit into
    either category so that it can be read manually at a later point.

    The first loop handles username mentions.
    The second loop sorts out and handles comments that include 'claim'
        and 'done'.
    :return: None.
    """
    # Sort inbox, then act on it
    # Invert the inbox so we're processing oldest first!
    for item in reversed(list(config.r.inbox.unread(limit=None))):
        # Very rarely we may actually get a message from Reddit itself.
        # In this case, there will be no author attribute.
        if item.author is None:
            send_to_slack(
                f'We received a message without an author. Subject: '
                f'{item.subject}', config
            )
            item.mark_read()

        elif item.author.name == 'transcribot':
            item.mark_read()

        elif item.author.name in config.redis.smembers('blacklist'):
            logging.info(
                f'Skipping inbox item from {item.author.name} who is on the '
                f'blacklist '
            )
            item.mark_read()
            continue

        elif item.subject == 'username mention':
            logging.info(f'Received mention! ID {item}')

            # noinspection PyUnresolvedReferences
            try:
                process_mention(item)
            except (AttributeError, RedditClientException):
                # apparently this crashes with an AttributeError if someone
                # calls the bot and immediately deletes their comment. This
                # should fix that.
                continue
            item.mark_read()

        elif item.subject in ('comment reply', 'post reply'):
            process_reply(item, config)

        elif item.subject[0] == '!':
            # Handle our special commands
            process_command(item, config)
            item.mark_read()
            continue

        else:
            item.mark_read()
            forward_to_slack(item, config)


import json
import datetime
import pandas as pd
from flask import Flask, request, abort
from flask_cors import CORS, cross_origin
from flask_restful import Resource, Api
from flask_jsonpify import *
from flask.json import JSONEncoder
from datetime import date, datetime, timedelta
from sqlalchemy import and_, cast, extract, Integer

from .model.connection_pg import Connection_pg
from .model.models.municipios_brasil import Municipio
from .model.models.an_municipio import \
    AnClimMonthlyByCity, AnMonthlyByCity, \
         AnClimDailyByCity, AnDailyByCity

class CustomJSONEncoder(JSONEncoder):
    def default(self, obj):
        try:
            if isinstance(obj, date):
                return obj.isoformat()
            iterable = iter(obj)
        except TypeError:
            pass
        else:
            return list(iterable)
        return JSONEncoder.default(self, obj)

app = Flask(__name__)
app.json_encoder = CustomJSONEncoder
api = Api(app)
CORS(app)

@app.route("/")
def hello():
    return jsonify({'text':'Hello World!!!'})

def length(obj):
    aux, i = None, 0
    while True:
        try: aux, i = obj[i], i + 1
        except: break
    return i

def getDayMonths(date):
    dias = {1:31,2:0,3:31,4:30,5:31,6:30,7:31,8:31,9:30,10:31,11:30,12:31}
    ano = date.year
    bissexto = None
    if ano % 4 == 0:
        if ano % 100 == 0:
            if ano % 400 == 0: bissexto = True
            else: bissexto = False
        else: bissexto = True
    else: bissexto = False
    if bissexto: dias[2] = 29
    else: dias[2] = 28
    return dias.get(date.month, 0)

class States(Resource):
    def get(self):
        try:
            data = json.loads(open("api_flask/model/models/states.json").read())
            return jsonify(data)
        except:
            return abort(404, description = { 'info' : 'Impossível fazer a leitura'})

class CitiesByState(Resource):
    def get(self):
        try:
            conectar = Connection_pg("chuva")
            municipios = conectar.createSession(Municipio) \
                .filter_by(uf = str(request.args['uf']).upper()) \
                    .order_by(Municipio.nome1)
            data = { "municipios" : [] }
            for municipio in municipios:
                data.get("municipios").append(
                    {
                        "nome_municipio" : municipio.nome1,
                        "geocodigo" : municipio.geocodigo,
                        "latitude" : municipio.latitude,
                        "longitude" : municipio.longitude
                    }
                )
            conectar.closeAll()
            return jsonify(data)
        except:
            return abort(404, description = { 'info' : 'uf não existe ou selecione um estado ?uf=SP' })

class ClimDailyByCity(Resource):
    def get(self):
        try:
            geocodigo = str(request.args['geocodigo'])
            month = str(request.args['month'])
            day = str(request.args['day'])

            conectar = Connection_pg("chuva")

            municipio = conectar.createSession(Municipio) \
                .filter_by(geocodigo = geocodigo) \
                    .first()

            clim_daily_result = conectar.createSession(AnClimDailyByCity) \
                .filter_by(fid = municipio.fid) \
                    .filter(
                        and_(
                            extract('month', AnClimDailyByCity.execution_date) == month,
                            extract('day', AnClimDailyByCity.execution_date) == day
                        )
                    ).order_by(AnClimDailyByCity.execution_date) \
                        .first()

            data = {
                "query" : {
                    "nome_municipio" : municipio.nome1,
                    "geocodigo" : geocodigo,
                    "date" : clim_daily_result.execution_date,
                    "timeline" : {
                        "start" : 1998,
                        "end" : 2019
                    },
                },
                "climatologico" : {
                    "clim_maxima" : clim_daily_result.maxima,
                    "clim_media" : clim_daily_result.media
                }
            }

            conectar.closeAll()

            return jsonify(data)
        except:
            return abort(404, description = {
                'info' :
                    'Impossível ler o geocodigo, ' +
                    'falta atributos de busca como mes e dia como no exemplo ' +
                    '?geocodigo=3549904&month=01&day=01'
            })

class AnalysisDailyByCity(Resource):
    def get(self):
        try:
            geocodigo = str(request.args['geocodigo'])
            start_date = datetime.strptime(str(request.args['start_date']), "%Y-%m-%d")
            format_date = datetime.strptime(str(request.args['end_date']), "%Y-%m-%d")
            end_date = format_date + timedelta(days = 1)

            conectar = Connection_pg("chuva")

            municipio = conectar.createSession(Municipio) \
                    .filter_by(geocodigo = geocodigo) \
                        .first()

            an_daily = conectar.createSession(AnDailyByCity) \
                .filter_by(fid = municipio.fid) \
                    .filter(
                        and_(AnDailyByCity.execution_date >= start_date, AnDailyByCity.execution_date <= end_date)
                    ).order_by(AnDailyByCity.execution_date)

            result = []
            for i in range(length(an_daily)):

                clim_daily_result = conectar.createSession(AnClimDailyByCity) \
                    .filter_by(fid = municipio.fid) \
                        .filter(
                            and_(
                                extract('month', AnClimDailyByCity.execution_date) == an_daily[i].execution_date.month,
                                extract('day', AnClimDailyByCity.execution_date) == an_daily[i].execution_date.day
                            )
                        ).order_by(AnClimDailyByCity.execution_date) \
                                .first()

                result.append(
                    {
                        "date" : an_daily[i].execution_date,
                        "climatologico" : {
                            "clim_maxima" : clim_daily_result.maxima,
                            "clim_media" : clim_daily_result.media
                        },
                        "prec_maxima" : an_daily[i].maxima,
                        "prec_media" : an_daily[i].media,
                        "anomalia" : an_daily[i].anomalia
                    }
                )

            data = {
                "query" : {
                    "nome_municipio" : municipio.nome1,
                    "geocodigo" : geocodigo,
                    "timeline" : {
                        "start_date" : start_date,
                        "end_date" : format_date
                    }
                },
                "result" : result
            }

            conectar.closeAll()

            return jsonify(data)
        except:
            return abort(404, description = {
                'info' : 'Impossível ler o geocodigo,' +
                'falta atributos de busca como data inicial e final como no exemplo ' +
                '?geocodigo=3549904&start_date=2010-01-01&end_date=2010-12-31'
            })

class ClimMonthlyByCity(Resource):
    def get(self):
        try:
            geocodigo = str(request.args['geocodigo'])
            month = int(request.args['month'])

            conectar = Connection_pg("chuva")

            municipio = conectar.createSession(Municipio) \
                .filter_by(geocodigo = geocodigo) \
                    .first()

            clim_monthly_result = conectar.createSession(AnClimMonthlyByCity) \
                    .filter_by(fid = municipio.fid) \
                        .filter(extract('month', AnClimMonthlyByCity.execution_date) == month) \
                            .order_by(AnClimMonthlyByCity.execution_date) \
                                .first()

            data = {
                "query" : {
                    "nome_municipio" : municipio.nome1,
                    "geocodigo" : geocodigo,
                    "date" : clim_monthly_result.execution_date,
                    "timeline" : {
                        "start" : 1998,
                        "end" : 2019
                    },
                },
                "climatologico" : {
                    "clim_maxima" : clim_monthly_result.maxima * getDayMonths(
                        clim_monthly_result.execution_date
                    ),
                    "clim_media" : clim_monthly_result.media * getDayMonths(
                        clim_monthly_result.execution_date
                    )
                }
            }

            conectar.closeAll()

            return jsonify(data)
        except:
            return abort(404, description = {
                'info' :
                    'Impossível ler o geocodigo,' +
                    'selecione um geocodigo e um mes como no exemplo ' +
                    '?geocodigo=3549904&month=01'
            })

class AnalysisMonthlyByCity(Resource):
    def get(self):
        try:
            geocodigo = str(request.args['geocodigo'])
            start_date = datetime.strptime(str(request.args['start_date']), "%Y-%m")
            format_date = datetime.strptime(str(request.args['end_date']), "%Y-%m")
            end_date = format_date + timedelta(days = getDayMonths(format_date))

            conectar = Connection_pg("chuva")

            municipio = conectar.createSession(Municipio) \
                    .filter_by(geocodigo = geocodigo) \
                        .first()

            an_monthly = conectar.createSession(AnMonthlyByCity) \
                .filter_by(fid = municipio.fid) \
                    .filter(
                        and_(AnMonthlyByCity.execution_date >= start_date, AnMonthlyByCity.execution_date <= end_date)
                    ).order_by(AnMonthlyByCity.execution_date)

            result = []
            for i in range(length(an_monthly)):

                clim_monthly_result = conectar.createSession(AnClimMonthlyByCity) \
                    .filter_by(fid = municipio.fid) \
                        .filter(extract('month', AnClimMonthlyByCity.execution_date) == an_monthly[i].execution_date.month) \
                            .order_by(AnClimMonthlyByCity.execution_date) \
                                .first()

                result.append(
                    {
                        "date" : an_monthly[i].execution_date,
                        "climatologico" : {
                            "clim_maxima" : clim_monthly_result.maxima * getDayMonths(
                                clim_monthly_result.execution_date
                            ),
                            "clim_media" : clim_monthly_result.media * getDayMonths(
                                clim_monthly_result.execution_date
                            )
                        },
                        "prec_maxima" : an_monthly[i].maxima * getDayMonths(
                            an_monthly[i].execution_date
                        ),
                        "prec_media" : an_monthly[i].media * getDayMonths(
                            an_monthly[i].execution_date
                        ),
                        "anomalia" : an_monthly[i].anomalia * getDayMonths(
                            an_monthly[i].execution_date
                        )
                    }
                )

            data = {
                "query" : {
                    "nome_municipio" : municipio.nome1,
                    "geocodigo" : geocodigo,
                    "timeline" : {
                        "start_date" : start_date,
                        "end_date" : format_date
                    }
                },
                "result" : result
            }

            conectar.closeAll()

            return jsonify(data)
        except:
            return abort(404, description = {
                'info' :
                    'Impossível ler o geocodigo,' +
                    'falta atributos de busca como data inicial e final como no exemplo ' +
                    '?geocodigo=3549904&start_date=2015-01&end_date=2015-12'
            })

api.add_resource(States, '/api-flask/states')
api.add_resource(CitiesByState, '/api-flask/cities')
api.add_resource(ClimDailyByCity, '/api-flask/clim-daily-by-city')
api.add_resource(AnalysisDailyByCity,'/api-flask/analysis-daily-by-city')
api.add_resource(ClimMonthlyByCity, '/api-flask/clim-monthly-by-city')
api.add_resource(AnalysisMonthlyByCity, '/api-flask/analysis-monthly-by-city')


import numpy as np
import warnings
from pathlib import Path
import datetime
from cyclesgym.managers.operation import OperationManager
from cyclesgym.envs.utils import date2ydoy

__all__ = ['Fertilizer', 'FixedRateNFertilizer']

class Implementer(object):

    def year2opyear(self, year: int) -> int:
        """
        Convert normal year to years used in operation.

        Cycles and cyclesgym use normal years (e.g. 1980) but, in the operation
        file, they start counting from the first simulation year, i.e., if the
        sim start in 1980, this is indicated as year 1 in the operation file.
        """
        return year - self.start_year + 1

    def _is_new_action(self, year: int, doy: int, *args, **kwargs) -> bool:
        raise NotImplementedError

    def reset(self) -> bool:
        raise NotImplementedError

    def _check_valid_action(self, action_details: dict):
        raise NotImplementedError

    def _update_operation(self, year, doy, operation, *args, **kwargs):
        raise NotImplementedError

    def _check_collision(self, year, doy, operation_details):
        raise NotImplementedError

    def _get_operation_key(self, year, doy, operation_details):
        raise NotImplementedError

    def _create_new_operation(self, year, key, operation_details):
        raise NotImplementedError

    def implement_action(self, date: datetime.date, operation_details: dict) -> bool:
        """
        Write new operation file.

        We check if the new action is different from the previously specified
        one. If it is, we overwrite the operation file and we indicate cycles
        should be rerun. Othwerwise, we do nothing.

        Parameters
        ----------
        date: datetime.date
            Date
        operation_details: dict
                    details of the operation to be implemented

        Returns
        -------
        rerun_cycles: bool
            if true, it means the operation file has been changed and cycles
            should be rerun.
        """
        # Check all and only affected crops are specified
        self._check_valid_action(operation_details)

        year, doy = date2ydoy(date)
        year = self.year2opyear(year)

        if self._is_new_action(year, doy, operation_details):
            # Check for collision
            operation, collision = self._check_collision(year, doy, operation_details)
            if collision:
                # Take existing operations and update masses
                op = self._update_operation(year, doy, operation, operation_details)
            else:
                op = self._create_new_operation(year, doy, operation_details)

            # Insert and write operation
            self.operation_manager.insert_new_operations(op, force=True)
            self.operation_manager.save(self.operation_fname)

            rerun_cycles = True
        else:
            # If not new action, don't update operation and don't rerun
            rerun_cycles = False
        return rerun_cycles

class Fertilizer(Implementer):
    valid_nutrients = ['C_Organic', 'C_Charcoal', 'N_Organic', 'N_Charcoal',
                       'N_NH4', 'N_NO3', 'P_Organic', 'P_CHARCOAL',
                       'P_INORGANIC', 'K', 'S']

    def __init__(self,
                 operation_manager: OperationManager,
                 operation_fname: Path,
                 affected_nutrients: list,
                 start_year: int) -> None:
        self.operation_manager = operation_manager
        self.operation_fname = operation_fname
        assert all([nutrient in self.valid_nutrients for nutrient in
                    affected_nutrients]), f'Affected nutrients should be in ' \
                                          f'{self.valid_nutrients}. Got' \
                                          f'{affected_nutrients} instead.'
        self.affected_nutrients = affected_nutrients
        self.start_year = start_year

    def _is_new_action(self, year: int, doy: int, new_masses: dict) -> bool:
        """
        Is the fertilization recommended for (year, doy) different from previous one?

        Parameters
        ----------
        year: int
            Year
        doy: int
            Day of the year
        new_masses: dict
            keys are nutrients and values are masses in Kg

        Returns
        -------
        new: bool
        """
        # Get old operation
        old_op = self.operation_manager.op_dict.get(
            (year, doy, 'FIXED_FERTILIZATION'))
        if old_op is None:
            old_masses = {n: 0 for n in self.affected_nutrients}
        else:
            total_mass = old_op['MASS']
            old_masses = {n: v * total_mass for n, v in old_op.items()
                          if n in self.affected_nutrients}
        return old_masses != new_masses

    def _get_operation_key(self, year, doy):
        return (year, doy, 'FIXED_FERTILIZATION')

    def _check_valid_action(self, action_details: dict):
        assert all(n in self.affected_nutrients for n in
                   action_details.keys()), f'You can only specify fertilization masses for {self.affected_nutrients}'
        assert all(n in action_details.keys() for n in
                   self.affected_nutrients), f'You must specify fertilization masses for all {self.affected_nutrients}'

    def _create_new_operation(self, year, doy, masses: dict) -> dict:
        key = self._get_operation_key(year, doy)
        op_val = {
            'SOURCE': 'Unknown',
            'MASS': 0,
            'FORM': 'Unknown',
            'METHOD': 'Unknown',
            'LAYER': 1,  # TODO: Handle different layers
        }

        # Initialize all nutrients to zero
        op_val.update({n: 0 for n in self.valid_nutrients})

        # Update value for affected nutrients
        total_mass = sum([m for m in masses.values()])
        op_val.update({'MASS': total_mass})
        op_val.update({nutrient: mass / total_mass for
                       (nutrient, mass) in masses.items()})
        op = {key: op_val}
        return op

    def reset(self) -> bool:
        """
        Set to zero the masses for all the affected nutrients.

        Before starting a new simulation, we want to make sure that the only
        way the affected nutrients can be provided is through our
        fertilization. This is why we reset everything to zero (overwriting
        during the unrolling of the env may not be sufficient as there may be
        fertilization event happening between t and t + delta that we could not
        overwrite.
        """
        doy = 1
        rerun_cycles = False
        for op_k, op_v in self.operation_manager.op_dict.items():
            if op_k[-1] == 'FIXED_FERTILIZATION':
                start_year = op_k[0]
                doy = op_k[1]
                new_op = self._update_operation(start_year, doy,
                                                old_op = op_v,
                                                new_masses={n: 0 for n in self.affected_nutrients},
                                                mode='absolute')
                self.operation_manager.insert_new_operations(
                    new_op, force=True)
                rerun_cycles = True
        self.operation_manager.save(self.operation_fname)
        return rerun_cycles

    def _check_collision(self, year, doy, operation_details):
        key = (year, doy, 'FERTILIZATION')
        operation = self.operation_manager.op_dict.get(key)
        collision = operation is not None
        return operation, collision

    def _update_operation(self, year, doy,
                          old_op: dict, new_masses: dict,
                          mode: str = 'absolute') -> dict:
        """
        Update the masses of a fertilization operation.

        Parameters
        ----------
        old_op: dict
            Dictionary of fertilization operation to update
        new_masses: dict
            Dict of new masses. Keys should be possible nutrients
        mode: str
            With 'increment' the mass is added to the one already present
            in the operation. With 'absolute' we use the new mass regardless of
            what was already there
        Returns
        -------
        new_op: dict
            Dictionary of updated fertilization operation
        """

        # TODO: include mode in the call of the Abstract class in some way. Right now abstract class has no "mode"

        key = self._get_operation_key(year, doy) # TODO: Weird we get a dict with massese as input and return a dict {k: masses} as output. Can't we add the key later?

        assert mode in ['increment', 'absolute'], \
            'Can only update in increment or absolute mode'

        assert all(k in self.valid_nutrients for k in new_masses.keys()), \
            f'New masses can only specify valid nutrients ' \
            f'({self.valid_nutrients}) as keys'

        # Copy operation
        new_op = old_op.copy()

        # Init final masses
        final_masses = {k: 0 for k in self.valid_nutrients}

        for i, n in enumerate(self.valid_nutrients):
            # Update with new masses when specified
            if n in new_masses.keys():
                final_masses[n] = new_masses[n] if mode == 'absolute' else \
                    new_masses[n] + old_op[n]
            # Copy old masses otherwise
            else:
                final_masses[n] = old_op[n] * old_op['MASS']

        # Update dict
        total_mass = sum(final_masses.values())
        new_op['MASS'] = total_mass
        if total_mass > 0:
            new_op.update(
                {nutrient: final_mass / total_mass for (nutrient, final_mass)
                 in final_masses.items()})
        else:
            new_op.update({nutrient: 0 for nutrient in self.valid_nutrients})

        return {key: new_op}

class FixedRateNFertilizer(Fertilizer):
    def __init__(self, operation_manager: OperationManager,
                 operation_fname: Path,
                 rate: float,
                 start_year: int):
        nutrients = ['N_NH4', 'N_NO3']
        super(FixedRateNFertilizer, self).__init__(operation_manager,
                                                   operation_fname,
                                                   nutrients,
                                                   start_year)
        assert 0 <= rate <= 1, f'Rate must be in [0, 1]. It is {rate} instead'
        self.rate = rate

    def convert_mass(self, mass):
        masses = {'N_NH4': mass * self.rate,
                  'N_NO3': mass * (1 - self.rate)}
        return masses

    # TODO: This should have same signature as parent method!
    def implement_action(self, date: datetime.date, mass: float):
        masses = self.convert_mass(mass)
        return super(FixedRateNFertilizer, self).implement_action(date, masses)

class Planter(Implementer):
    valid_crops = ['CornRM.90', 'CornRM.100', 'CornRM.110', 'CornSilageRM.90', 'CornSilageRM.100', 'CornSilageRM.110',
                   'SorghumSS', 'SorghumMS', 'SorghumLS', 'SweetSorghum', 'SoybeanMG.3', 'SoybeanMG.4', 'SoybeanMG.5',
                   'OatsGrain', 'OatsHay', 'RygrassAnnualGrazing', 'SpringWheat', 'SpringBarley', 'WinterWheat',
                   'WinterBarley', 'Chickpea', 'SpringPeas', 'WinterPeas', 'SpringLentils', 'WinterLentils',
                   'SpringCanola', 'WinterCanola', 'Potatoes', 'Millet', 'Sesame_Beta', 'Teff_Beta', 'Alfalfa',
                   'LotusCorniculatus', 'WhiteClover', 'Orchardgrass', 'TallFescueGrazing', 'Switchgrass', 'Miscanthus',
                   'Willow', 'C3_weed', 'C4_weed']

    def __init__(self,
                 operation_manager: OperationManager,
                 operation_fname: Path,
                 affected_crops: list,
                 start_year: int) -> None:
        self.operation_manager = operation_manager
        self.operation_fname = operation_fname
        assert all([crop in self.valid_crops for crop in
                    affected_crops]), f'Affected crops should be in ' \
                                          f'{self.valid_crops}. Got' \
                                          f'{affected_crops} instead.'
        self.affected_crops = affected_crops
        self.start_year = start_year

    def _is_new_action(self, year: int, doy: int, crop_details: dict) -> bool:
        """
        Is the crop recommended for (year, doy) different from previous one?

        Parameters
        ----------
        year: int
            Year
        doy: int
            Day of the year
        crop: str
            crop to be planted

        Returns
        -------
        new: bool
        """
        # Get old operation
        old_op = self.operation_manager.op_dict.get(
            (year, doy, 'PLANTING'))
        if old_op is None:
            return True
        else:
            return old_op['CROP'] == crop_details['CROP']

    def _check_valid_action(self, action_details: dict):
        assert (action_details['CROP'] in self.valid_crops), f'You can only specify planting date for ' \
                                                             f'{self.valid_crops}'

    def reset(self) -> bool:
        """
        Set to zero the planting events of all the affected crops.

        Before starting a new simulation, we want to make sure that the only
        way the affected crops can be planted is using the planter implementer.
        This is why we delete all the operations that involve planting affected
        crops
        """
        rerun_cycles = True
        op_to_delete = []
        for op_k, op_v in self.operation_manager.op_dict.items():
            if op_k[-1] == 'PLANTING' and op_v['CROP'] in self.affected_crops:
                op_to_delete.append(op_k)

        self.operation_manager.delete_operations(op_to_delete)
        self.operation_manager.save(self.operation_fname)
        # TODO: Understand when we can avoid rerunning cycles
        return rerun_cycles

    def _get_operation_key(self, year, doy):
        return (year, doy, 'PLANTING')

    def _check_collision(self, year, doy, operation_details):
        planting_events = [key for key in self.operation_manager.op_dict.keys() if key[2] == 'PLANTING']
        operation = [key for key in planting_events if key[0] == year]
        collision = False
        if len(operation) > 0:
            operation = operation[0]
            operation = {operation: self.operation_manager.op_dict[operation]}
            collision = True
        return operation, collision

    def _update_operation(self, year, doy, old_operation, operation_details) -> dict:
        """
        Update the planting operation.

        Parameters
        ----------
        old_operation: dict
            Dictionary of operation detials of old op
        operation_details: dict
            Dict of new operation.
        -------
        new_op: dict
            Dictionary of updated planting operation
        """

        assert operation_details['CROP'] in self.valid_crops, \
            f'New planting operation can only specify valid crops \n' \
            f'({self.valid_crops}) as planted crop \n' \
            f"Given {operation_details['CROP']}"

        doy = operation_details.pop('DOY')
        for item in old_operation.values():
            item.update(operation_details)

        old_key = list(old_operation.keys())[0]
        new_key = (old_key[0], doy, old_key[2])
        old_operation[new_key] = old_operation.pop(old_key)

        self.operation_manager.op_dict.pop(old_key)
        return old_operation

    def _create_new_operation(self, year, doy, operation_details: dict) -> dict:
        key = self._get_operation_key(year, doy)
        op_val = {'DOY': 1,
                  'END_DOY': -999,
                  'MAX_SMC': -999,
                  'MIN_SMC': 0.0,
                  'MIN_SOIL_TEMP': 0.0,
                  'CROP': None,
                  'USE_AUTO_IRR': 0,
                  'USE_AUTO_FERT': 0,
                  'FRACTION': 1.0,
                  'CLIPPING_START': 1,
                  'CLIPPING_END': 366
                  }

        # Initialize all nutrients to zero
        op_val.update(operation_details)
        key = (key[0], op_val.pop('DOY'), key[2])
        return {key: op_val}

class RotationPlanter(Planter):
    def __init__(self, operation_manager: OperationManager,
                 operation_fname: Path,
                 rotation_crops: list,
                 start_year: int):
        super(RotationPlanter, self).__init__(operation_manager,
                                              operation_fname,
                                              rotation_crops,
                                              start_year)

    def convert_action_to_dict(self, crop_categorical: int, doy: int,
                               end_doy: int, max_smc: int):
        """crop_categorical: integer representing the crop in the crop rotation
        doy: integer lower bound of the planting date, as number of weeks starting from the
            first of march
        end_doy: upper bound of the planting date, as number of weeks from the doy
        max_smc: maximum moisture for automated planting"""

        doy = 90 + doy * 7
        end_doy = doy + end_doy * 7
        max_smc = float((1 + max_smc) / 10.)

        operation_det = {'DOY': doy,
                         'CROP': self.affected_crops[crop_categorical],
                         'END_DOY': end_doy,
                         'MAX_SMC': max_smc}
        return operation_det

    def implement_action(self, date: datetime.date, crop_categorical: int, doy: int,
                         END_DOY: int, MAX_SMC: int):
        operation_det = self.convert_action_to_dict(crop_categorical, doy, END_DOY, MAX_SMC)
        return super(RotationPlanter, self).implement_action(date, operation_det)

class RotationPlanterFixedPlanting(RotationPlanter):

    def implement_action(self, date: datetime.date, crop_categorical: int, doy: int):
        operation_det = self.convert_action_to_dict(crop_categorical, doy, 0, 0)
        operation_det['END_DOY'] = -999
        operation_det['MAX_SMC'] = -999
        return super(RotationPlanter, self).implement_action(date, operation_det)


import time
import math

import pytest
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec

link = 'https://stepik.org/lesson/{}/step/1'
pages = ['236895', '236896', '236897', '236898', '236899', '236903', '236904', '236905']

@pytest.fixture
def browser():
    options = ChromeOptions()
    options.add_argument('--headless')
    browser = webdriver.Chrome(options=options)
    browser.implicitly_wait(2)
    yield browser
    browser.quit()

class TestCases:

    @pytest.mark.parametrize('page', pages)
    def test_pages_for_alien_messages(self, browser, page):
        browser.get(link.format(page))
        element = WebDriverWait(browser, 10).until(
            ec.visibility_of_element_located((By.CSS_SELECTOR, 'textarea.string-quiz__textarea'))
        )
        answer = math.log(int(time.time()))
        element.send_keys(f'{answer}')

        browser.find_element_by_class_name('submit-submission').click()

        result_text = browser.find_element_by_class_name('smart-hints__hint').text

        assert result_text == 'Correct!', result_text


#!/usr/bin/env python
__author__ = "Lukas Klein & Thomas Zimmermann"
__copyright__ = "Copyright 2021, The MUDCake Project"
__credits__ = "Hauke Presig, Jack Drillisch, Jan Gruchott, Lukas Klein, Robert Fendrich, Thomas Zimmermann"

__license__ = """MIT License

                     Copyright (c) 2021 MUDCake Project

                     Permission is hereby granted, free of charge, to any person obtaining a copy
                     of this software and associated documentation files (the "Software"), to deal
                     in the Software without restriction, including without limitation the rights
                     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                     copies of the Software, and to permit persons to whom the Software is
                     furnished to do so, subject to the following conditions:

                     The above copyright notice and this permission notice shall be included in all
                     copies or substantial portions of the Software.

                     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                     SOFTWARE."""

__version__ = "1.0.0"
__maintainer__ = "Lukas Klein & Thomas Zimmermann"
__email__ = "mudcake@gmail.com"
__status__ = "Development"

import smtplib
import ssl
from email.message import EmailMessage
from EmailServices.MessageType import MessageType
from EmailServices.FileReader import FileReader
import socket

class EmailSender:
    """Basic class for interaction to User via email"""
    def __init__(self, user_email, user_id: str):
        """
        Constructor for email Sender to initiate needed parameters

        :param user_email: email of the recipient - String
        :param token: Token to verify User after email confirmation - String
        """
        self.user_email = user_email
        self.msg = EmailMessage()
        self.user_id = user_id
        self.e_mail = "mudcakegame@gmail.com"

    def __send_via_server_context(self, msg: EmailMessage):
        """Method to send emails via google gMail

        :param msg: preconfigured message in external method - EmailMessage
        """
        with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=ssl.create_default_context()) as server:
            server.login(self.e_mail, bytes(b'$9PLnJ5NsB#!').decode('utf8', 'strict'))
            server.send_message(msg)

    def send_email(self, m_type: MessageType):
        """Prepares email with given context and initializes sending

        :param m_type: type of message to be send - MessageType
        """
        host_name = socket.gethostname()
        local_ip = socket.gethostbyname(host_name)

        if m_type == MessageType.registration:
            content_reader = FileReader("EmailServices/confirmationEmailTemplates/content")
            content = content_reader.read()
            host_name = socket.gethostname()

            if str(socket.gethostbyname(host_name)) != "193.196.53.67":
                content = content.replace("{Server}", "localhost:4200")
            else:
                content = content.replace("{Server}", "193.196.54.98")

            self.msg.set_content(content.replace("{UserToken}", self.user_id))
            self.msg["Subject"] = content_reader.overwrite_name("EmailServices/confirmationEmailTemplates/subject").read()
            self.msg["From"] = self.e_mail
            self.msg["To"] = self.user_email
            self.__send_via_server_context(self.msg)

        if m_type == MessageType.reset_password:
            content_reader = FileReader("EmailServices/resetPasswordEmailTemplates/content")
            content = content_reader.read()
            host_name = socket.gethostname()

            if str(socket.gethostbyname(host_name)) != "193.196.53.67":
                content = content.replace("{Server}", "localhost:4200")
            else:
                content = content.replace("{Server}", "193.196.54.98")

            self.msg.set_content(content.replace("{UserToken}", str(self.user_id)))
            self.msg["Subject"] = content_reader.overwrite_name(
                "EmailServices/resetPasswordEmailTemplates/subject").read()
            self.msg["From"] = self.e_mail
            self.msg["To"] = self.user_email
            self.__send_via_server_context(self.msg)