"""
快捷导入:

- `CommandGroup` -> {ref}`nonebot.command.group.CommandGroup`

此模块定义了一个内部细节可能会频繁变更而不在文档公开的 `Command` 类型，用户如果需要使用应该自行考虑不兼容更新产生的风险。
"""
import re
import asyncio
import warnings
from datetime import datetime, timedelta
from functools import partial
from typing import (NoReturn, Tuple, Union, Iterable, Any, Optional, List, Dict,
                    Awaitable, Pattern, Type)

from aiocqhttp import Event as CQEvent
from aiocqhttp.message import Message

from nonebot import NoneBot
from nonebot.command.argfilter import ValidateError
from nonebot.helpers import context_id, send, render_expression
from nonebot.log import logger
from nonebot.session import BaseSession
from nonebot.typing import (CommandName_T, CommandArgs_T, CommandHandler_T,
                            Message_T, PermissionPolicy_T, State_T, Filter_T, Patterns_T)
from nonebot.permission import check_permission

# key: context id
# value: CommandSession object
_sessions = {}  # type: Dict[str, "CommandSession"]

class CommandInterrupt(Exception):
    """INTERNAL API"""
    pass

class _YieldException(CommandInterrupt):
    """
    Raised by command.run(), session indicating that the waiting session
    will resume and current execution path should return immediately.
    """
    pass

class _PauseException(CommandInterrupt):
    """
    Raised by session.pause() indicating that the command session
    should be paused to ask the user for some arguments.
    """
    pass

class _FinishException(CommandInterrupt):
    """
    Raised by session.finish() indicating that the command session
    should be stopped and removed.
    """

    def __init__(self, result: bool = True):
        """
        :param result: succeeded to call the command
        """
        self.result = result

class SwitchException(CommandInterrupt):
    """
    INTERNAL API

    Raised by session.switch() indicating that the command session
    should be stopped and replaced with a new one (going through
    handle_message() again).

    Since the new message will go through handle_message() again,
    the later function should be notified. So this exception is
    intended to be propagated to handle_message().
    """

    def __init__(self, new_message: Message):
        """
        :param new_message: new message which should be placed in event
        """
        self.new_message = new_message

class Command:
    """
    INTERNAL API

    Note ... (Ellipsis) is a valid value for field expire_timeout and run_timeout.
    However I cannot type it until Python 3.10. see bugs.python.org/issue41810.
    """
    __slots__ = ('name', 'func', 'only_to_me', 'privileged', 'args_parser_func',
                 'permission', 'expire_timeout', 'run_timeout', 'session_class')

    def __init__(self, *, name: CommandName_T, func: CommandHandler_T,
                 only_to_me: bool, privileged: bool,
                 permission: PermissionPolicy_T,
                 expire_timeout: Optional[timedelta],
                 run_timeout: Optional[timedelta],
                 session_class: Optional[Type['CommandSession']]):
        self.name = name
        self.func = func
        self.only_to_me = only_to_me
        self.privileged = privileged
        self.args_parser_func: Optional[CommandHandler_T] = None
        self.permission = permission  # includes EllipsisType
        self.expire_timeout = expire_timeout  # includes EllipsisType
        self.run_timeout = run_timeout  # includes EllipsisType
        self.session_class = session_class

    async def run(self,
                  session: 'CommandSession',
                  *,
                  check_perm: bool = True,
                  dry: bool = False) -> bool:
        """
        Run the command in a given session.

        :param session: CommandSession object
        :param check_perm: should check permission before running
        :param dry: just check any prerequisite, without actually running
        :return: the command is finished (or can be run, given dry == True)
        """
        has_perm = await self._check_perm(session) if check_perm else True
        if self.func and has_perm:
            if dry:
                return True

            if session.current_arg_filters is not None and \
                    session.current_key is not None:
                # argument-level filters are given, use them
                arg = session.current_arg
                config = session.bot.config
                for f in session.current_arg_filters:
                    try:
                        res = f(arg)
                        if isinstance(res, Awaitable):
                            res = await res
                        arg = res
                    except ValidateError as e:
                        # validation failed
                        if config.MAX_VALIDATION_FAILURES > 0:
                            # should check number of validation failures
                            session.state['__validation_failure_num'] = \
                                session.state.get(
                                    '__validation_failure_num', 0) + 1

                            if session.state['__validation_failure_num'] >= \
                                    config.MAX_VALIDATION_FAILURES:
                                # noinspection PyProtectedMember
                                session.finish(
                                    render_expression(
                                        config.
                                        TOO_MANY_VALIDATION_FAILURES_EXPRESSION
                                    ), **session._current_send_kwargs)

                        failure_message = e.message
                        if failure_message is None:
                            failure_message = render_expression(
                                config.DEFAULT_VALIDATION_FAILURE_EXPRESSION)
                        # noinspection PyProtectedMember
                        session.pause(failure_message,
                                      **session._current_send_kwargs)

                # passed all filters
                session.state[session.current_key] = arg
            else:
                # fallback to command-level args_parser_func
                if self.args_parser_func:
                    await self.args_parser_func(session)
                if session.current_key is not None and \
                        session.current_key not in session.state:
                    # args_parser_func didn't set state, here we set it
                    session.state[session.current_key] = session.current_arg
            if session.waiting:
                session._future.set_result(True)
                raise _YieldException()
            else:
                await self.func(session)
            return True
        return False

    async def _check_perm(self, session: 'CommandSession') -> bool:
        """
        Check if the session has sufficient permission to
        call the command.

        :param session: CommandSession object
        :return: the session has the permission
        """
        return await check_permission(session.bot, session.event,
            self.permission if self.permission is not ...
                else session.bot.config.DEFAULT_COMMAND_PERMISSION)

    def args_parser(self, parser_func: CommandHandler_T) -> CommandHandler_T:
        """
        Decorator to register a function as the arguments parser of
        the corresponding command.
        """
        self.args_parser_func = parser_func
        return parser_func

    def __repr__(self):
        return f'<Command, name={self.name.__repr__()}>'

    def __str__(self):
        return self.__repr__()

class CommandManager:
    """全局命令管理器。

    版本: 1.6.0+
    """
    _commands = {}  # type: Dict[CommandName_T, Command]
    _aliases = {}  # type: Dict[str, Command]
    _switches = {}  # type: Dict[Command, bool]
    _patterns = {}  # type: Dict[Pattern[str], Command]

    def __init__(self):
        # TODO: don't copy
        # https://github.com/nonebot/nonebot/projects/2#card-69531150
        self.commands: Dict[CommandName_T, Command] = CommandManager._commands.copy()
        """{anno}`Dict[nonebot.typing.CommandName_T, Command]` 命令字典。"""
        self.aliases: Dict[str, Command] = CommandManager._aliases.copy()
        """命令别名字典。"""
        self.switches: Dict[Command, bool] = CommandManager._switches.copy()
        """命令开关状态字典。"""
        self.patterns: Dict[Pattern[str], Command] = CommandManager._patterns.copy()
        """
        命令正则匹配字典。
        版本: 1.7.0+
        """

    @classmethod
    def add_command(cls, cmd_name: CommandName_T, cmd: Command) -> None:
        """注册一个 `Command` 对象。

        参数:
            cmd_name (nonebot.typing.CommandName_T): 命令名称
            cmd: 命令对象

        用法:
            ```python
            cmd = Command(...)
            CommandManager.add_command(name, cmd)
            ```
        """
        if cmd_name in cls._commands:
            warnings.warn(f"Command {cmd_name} already exists")
            return
        cls._switches[cmd] = True
        cls._commands[cmd_name] = cmd

    @classmethod
    def reload_command(cls, cmd_name: CommandName_T, cmd: Command) -> None:
        """更新一个已存在的命令。

        参数:
            cmd_name (nonebot.typing.CommandName_T): 命令名称
            cmd: 命令对象

        用法:
            ```python
            cmd = Command(...)
            CommandManager.reload_command(name, cmd)
            ```
        """
        if cmd_name not in cls._commands:
            warnings.warn(
                f"Command {cmd_name} does not exist. Please use add_command instead"
            )
            return

        cmd_ = cls._commands[cmd_name]
        if cmd_ in cls._switches:
            del cls._switches[cmd_]

        cls._switches[cmd] = True
        cls._commands[cmd_name] = cmd

    @classmethod
    def remove_command(cls, cmd_name: CommandName_T) -> bool:
        """移除一个已存在的命令。

        参数:
            cmd_name (nonebot.typing.CommandName_T): 命令名称

        返回:
            bool: 是否成功移除命令

        用法:
            ```python
            CommandManager.remove_command(("test", ))
            ```
        """
        if cmd_name in cls._commands:
            cmd = cls._commands[cmd_name]
            for alias in list(
                    filter(lambda x: cls._aliases[x] == cmd,
                           cls._aliases.keys())):
                del cls._aliases[alias]
            del cls._commands[cmd_name]
            if cmd in cls._switches:
                del cls._switches[cmd]
            return True
        return False

    @classmethod
    def switch_command_global(cls,
                              cmd_name: CommandName_T,
                              state: Optional[bool] = None) -> None:
        """根据 `state` 更改 command 的全局状态。

        参数:
            cmd_name (nonebot.typing.CommandName_T): 命令名称
            state:
                - `None(default)`: 切换状态，即 开 -> 关、关 -> 开
                - `bool`: 切换至指定状态，`True` -> 开、`False` -> 关

        用法:
            ```python
            from nonebot import message_preprocessor

            # 全局关闭命令test, 对所有消息生效
            CommandManager.switch_command_global(("test", ), state=False)

            @message_preprocessor
            async def processor(bot: NoneBot, event: CQEvent, plugin_manager: PluginManager):
                plugin_manager.cmd_manager.switch_command_global(("test", ), state=False)
            ```
        """
        cmd = cls._commands[cmd_name]
        cls._switches[cmd] = not cls._switches[cmd] if state is None else bool(
            state)

    @classmethod
    def add_aliases(cls, aliases: Union[Iterable[str], str], cmd: Command) -> None:
        """为 `Command` 添加命令别名。

        参数:
            aliases: 命令别名列表

        用法:
            ```python
            cmd = Command(...)
            CommandManager.add_aliases({"别名", "test"}, cmd)
            ```
        """
        if isinstance(aliases, str):
            aliases = (aliases,)
        for alias in aliases:
            if not isinstance(alias, str):
                warnings.warn(f"Alias {alias} is not a string! Ignored")
                return
            elif alias in cls._aliases:
                warnings.warn(f"Alias {alias} already exists")
                return
            cls._aliases[alias] = cmd

    @classmethod
    def add_patterns(cls, patterns: Patterns_T, cmd: Command) -> None:
        """Register command alias(es)

        Args:
            patterns (Union[Iterable[Pattern], Pattern, Iterable[str], str]): Command patterns
            cmd (Command): Matched command
        """
        if isinstance(patterns, (str, Pattern)):
            patterns = (patterns,)
        for pattern in patterns:
            if isinstance(pattern, str):
                pattern = re.compile(pattern)
            if not isinstance(pattern, Pattern):
                warnings.warn(
                    f"Pattern {pattern} is not a regex or string! Ignored")
                continue
            elif pattern in cls._patterns:
                warnings.warn(f"Pattern {pattern} already exists")
                continue
            cls._patterns[pattern] = cmd

    def _find_command(self, name: Union[str,
                                        CommandName_T]) -> Optional[Command]:
        cmd_name = (name,) if isinstance(name, str) else name
        if not cmd_name:
            return None

        cmd = {
            name: cmd
            for name, cmd in self.commands.items()
            if self.switches.get(cmd, True)
        }.get(cmd_name)
        return cmd

    def parse_command(
            self, bot: NoneBot,
            cmd_string: str) -> Tuple[Optional[Command], Optional[str]]:
        logger.debug(f'Parsing command: {repr(cmd_string)}')

        matched_start = None
        for start in bot.config.COMMAND_START:
            # loop through COMMAND_START to find the longest matched start
            curr_matched_start = None
            if isinstance(start, str):
                if cmd_string.startswith(start):
                    curr_matched_start = start
            elif isinstance(start, Pattern):
                m = start.search(cmd_string)
                if m and m.start(0) == 0:
                    curr_matched_start = m.group(0)

            if curr_matched_start is not None and \
                    (matched_start is None or
                    len(curr_matched_start) > len(matched_start)):
                # a longer start, use it
                matched_start = curr_matched_start

        if matched_start is None:
            # it's not a command
            logger.debug('It\'s not a command')
            return None, None

        logger.debug(f'Matched command start: '
                     f'{matched_start}{"(empty)" if not matched_start else ""}')
        full_command = cmd_string[len(matched_start):].lstrip()

        if not full_command:
            # command is empty
            return None, None

        cmd_name_text, *cmd_remained = full_command.split(maxsplit=1)

        cmd_name = None
        for sep in bot.config.COMMAND_SEP:
            # loop through COMMAND_SEP to find the most optimized split
            curr_cmd_name = None
            if isinstance(sep, str):
                curr_cmd_name = tuple(cmd_name_text.split(sep))
            elif isinstance(sep, Pattern):
                curr_cmd_name = tuple(sep.split(cmd_name_text))

            if curr_cmd_name is not None and \
                    (not cmd_name or len(curr_cmd_name) > len(cmd_name)):
                # a more optimized split, use it
                cmd_name = curr_cmd_name

        if not cmd_name:
            cmd_name = (cmd_name_text,)
        logger.debug(f'Split command name: {cmd_name}')

        cmd = self._find_command(cmd_name)  # type: ignore
        if not cmd:
            logger.debug(f'Command {cmd_name} not found. Try to match aliases')
            cmd = {
                name: cmd
                for name, cmd in self.aliases.items()
                if self.switches.get(cmd, True)
            }.get(cmd_name_text)

        if not cmd:
            logger.debug(f'Alias {cmd_name} not found. Try to match patterns')
            patterns = {
                pattern: cmd
                for pattern, cmd in self.patterns.items()
                if self.switches.get(cmd, True)
            }
            for pattern in patterns:
                if pattern.search(full_command):
                    cmd = patterns[pattern]
                    logger.debug(
                        f'Pattern {pattern} of command {cmd.name} matched, function: {cmd.func}'
                    )
                    # if command matched by regex, it will use the full_command as the current_arg of the session
                    return cmd, full_command

        if not cmd:
            return None, None

        logger.debug(f'Command {cmd.name} found, function: {cmd.func}')
        return cmd, ''.join(cmd_remained)

    def switch_command(self,
                       cmd_name: CommandName_T,
                       state: Optional[bool] = None) -> None:
        """根据 `state` 更改 command 的状态。仅对当前消息有效。

        参数:
            cmd_name (nonebot.typing.CommandName_T): 命令名称
            state:
                - `None(default)`: 切换状态，即 开 -> 关、关 -> 开
                - `bool`: 切换至指定状态，`True` -> 开、`False` -> 关

        用法:
            ```python
            from nonebot import message_preprocessor

            # 关闭命令test, 仅对当前消息生效
            @message_preprocessor
            async def processor(bot: NoneBot, event: CQEvent, plugin_manager: PluginManager):
                plugin_manager.cmd_manager.switch_command(("test", ), state=False)
            ```
        """
        cmd = self.commands[cmd_name]
        self.switches[cmd] = not self.switches[cmd] if state is None else bool(
            state)

class CommandSession(BaseSession):
    """
    继承自 `BaseSession` 类，表示命令 Session。

    参数:
        bot:
        event:
        cmd:
        current_arg:
        args (Optional[nonebot.typing.CommandArgs_T]):
    """

    __slots__ = ('cmd', 'current_key', 'current_arg_filters',
                 '_current_send_kwargs', 'current_arg', '_current_arg_text',
                 '_current_arg_images', '_state', '_last_interaction',
                 '_running', '_run_future', '_future')

    def __init__(self,
                 bot: NoneBot,
                 event: CQEvent,
                 cmd: Command,
                 *,
                 current_arg: Optional[str] = '',
                 args: Optional[CommandArgs_T] = None):
        super().__init__(bot, event)
        self.cmd = cmd  # Command object

        # unique key of the argument that is currently requesting (asking)
        self.current_key: Optional[str] = None
        """命令会话当前正在询问用户的参数的键（或称参数的名字）。第一次运行会话时，该属性为 `None`。"""

        # initialize current argument filters
        self.current_arg_filters: Optional[List[Filter_T]] = None

        self._current_send_kwargs: Dict[str, Any] = {}

        # initialize current argument
        self.current_arg: Optional[str] = ''  # with potential CQ codes
        """命令会话当前参数。实际上是 酷 Q 收到的消息去掉命令名的剩下部分，因此可能存在 CQ 码。"""
        self._current_arg_text = None
        self._current_arg_images = None
        self.refresh(event, current_arg=current_arg)  # fill the above

        self._run_future = partial(asyncio.run_coroutine_threadsafe,
                                   loop=bot.loop)

        self._state: State_T = {}
        if args:
            self._state.update(args)

        self._last_interaction = None  # last interaction time of this session
        self._running = False
        self._future: Optional[asyncio.Future] = None

    @property
    def state(self) -> State_T:
        """
        {anno}`nonebot.typing.State_T`
        命令会话的状态数据（包括已获得的所有参数）。

        属性本身只读，但属性中的内容可读写。

        版本: 1.2.0+

        用法:
            ```python
            if not session.state.get('initialized'):
                # ... 初始化工作
                session.state['initialized'] = True
            ```

            在命令处理函数的开头进行**每次命令调用只应该执行一次的初始化操作**。
        """
        return self._state

    @property
    def running(self) -> bool:
        """INTERNAL API"""
        return self._running

    @running.setter
    def running(self, value) -> None:
        """INTERNAL API"""
        if self._running is True and value is False:
            # change status from running to not running, record the time
            self._last_interaction = datetime.now()
        self._running = value

    @property
    def waiting(self) -> bool:
        """INTERNAL API"""
        return self._future is not None and not self._future.done()

    @property
    def expire_timeout(self) -> Optional[timedelta]:
        """INTERNAL API"""
        if self.cmd.expire_timeout is not ...:
            return self.cmd.expire_timeout
        return self.bot.config.SESSION_EXPIRE_TIMEOUT

    @property
    def run_timeout(self) -> Optional[timedelta]:
        """INTERNAL API"""
        if self.cmd.run_timeout is not ...:
            return self.cmd.run_timeout
        return self.bot.config.SESSION_RUN_TIMEOUT

    @property
    def is_valid(self) -> bool:
        """
        INTERNAL API

        Check whether the session has expired or not.
        """
        tm = self.expire_timeout
        if tm and self._last_interaction and \
            datetime.now() - self._last_interaction > tm:
            return False
        return True

    @property
    def is_first_run(self) -> bool:
        """命令会话是否第一次运行。"""
        return self._last_interaction is None

    @property
    def current_arg_text(self) -> str:
        """`current_arg` 属性的纯文本部分（不包含 CQ 码），各部分使用空格连接。"""
        if self._current_arg_text is None:
            self._current_arg_text = Message(
                self.current_arg).extract_plain_text()
        return self._current_arg_text

    @property
    def current_arg_images(self) -> List[str]:
        """`current_arg` 属性中所有图片的 URL 的列表，如果参数中没有图片，则为 `[]`。"""
        if self._current_arg_images is None:
            self._current_arg_images = [
                s.data['url']
                for s in Message(self.current_arg)
                if s.type == 'image' and 'url' in s.data
            ]
        return self._current_arg_images

    @property
    def argv(self) -> List[str]:
        """
        命令参数列表，类似于 `sys.argv`，本质上是 `session.state.get('argv', [])`，**需要搭配 `on_command(..., shell_like=True)` 使用**。

        用法:
            ```python
            @on_command('some_cmd', shell_like=True)
            async def _(session: CommandSession):
                argv = session.argv
            ```
        """
        return self.state.get('argv', [])

    def refresh(self,
                event: CQEvent,
                *,
                current_arg: Optional[str] = '') -> None:
        """
        INTERNAL API

        Refill the session with a new message event.

        :param event: new message event
        :param current_arg: new command argument as a string
        """
        self.event = event
        self.current_arg = current_arg
        self._current_arg_text = None
        self._current_arg_images = None

    def get(self,
            key: str,
            *,
            prompt: Optional[Message_T] = None,
            arg_filters: Optional[List[Filter_T]] = None,
            **kwargs) -> Any:
        r"""
        从 `state` 属性获取参数，如果参数不存在，则暂停当前会话，向用户发送提示，并等待用户的新一轮交互。

        如果需要暂停当前会话，则命令处理器中，此函数调用之后的语句将不会被执行（除非捕获了此函数抛出的特殊异常）。

        注意，一旦传入 `arg_filters` 参数（参数过滤器），则等用户再次输入时，_command_func._`args_parser` 所注册的参数解析函数将不会被运行，而会在对 `current_arg` 依次运行过滤器之后直接将其放入 `state` 属性中。

        :::tip
        推荐使用 `aget` 方法。
        :::

        参数:
            key: 参数的键
            prompt (Optional[nonebot.typing.Message_T]): 提示的消息内容
            arg_filters (Optional[List[nonebot.typing.Filter_T]]) {version}`1.2.0+`: 用于处理和验证用户输入的参数的过滤器
            kwargs: 其它传入 `BaseSession.send()` 的命名参数

        返回:
            Any: 参数的值

        用法:
            ```python
            location = session.get('location', prompt='请输入要查询的地区')
            ```

            获取位置信息，如果当前还不知道，则询问用户。

            ```python
            from nonebot.command.argfilter import extractors, validators

            time = session.get(
                'time', prompt='你需要我在什么时间提醒你呢？',
                arg_filters=[
                    extractors.extract_text,  # 取纯文本部分
                    controllers.handle_cancellation(session),  # 处理用户可能的取消指令
                    str.strip,  # 去掉两边空白字符
                    # 正则匹配输入格式
                    validators.match_regex(r'^\d{4}-\d{2}-\d{2}$', '格式不对啦，请重新输入')
                ]
            )
            ```

            获取时间信息，如果当前还不知道，则询问用户，等待用户输入之后，会依次运行 `arg_filters` 参数中的过滤器，以确保参数内容和格式符合要求。
        """
        if key in self.state:
            return self.state[key]

        self.current_key = key
        self.current_arg_filters = arg_filters
        self._current_send_kwargs = kwargs
        self.pause(prompt, **kwargs)

    async def aget(self,
                   key: str = ...,
                   *,
                   prompt: Optional[Message_T] = None,
                   arg_filters: Optional[List[Filter_T]] = None,
                   force_update: bool = ...,
                   **kwargs) -> Any:
        r"""
        从 `state` 属性获取参数，如果参数不存在，则异步地暂停当前会话，向用户发送提示，并等待用户的进一步交互。

        当用户再次输入时，不会重新运行命令处理器，而是回到此函数调用之处继续执行。

        注意，一旦传入 `arg_filters` 参数（参数过滤器），则等用户再次输入时，_command_func._`args_parser` 所注册的参数解析函数将不会被运行，而会在对 `current_arg` 依次运行过滤器之后直接将其放入 `state` 属性中。

        版本: 1.8.0+

        参数:
            key: 参数的键，若不传入则使用默认键值
            prompt (Optional[nonebot.typing.Message_T]): 提示的消息内容
            arg_filters (Optional[List[nonebot.typing.Filter_T]]): 用于处理和验证用户输入的参数的过滤器
            force_update: 是否强制获取用户新的输入，若是，则会忽略已有的当前参数，若 `key` 不传入则为真，否则默认为假
            kwargs: 其它传入 `BaseSession.send()` 的命名参数

        返回:
            Any: 参数的值

        用法:
            ```python
            from nonebot.command.argfilter import extractors, validators

            note = await session.aget(
                'note', prompt='你需要我提醒你什么呢',
                arg_filters=[
                    extractors.extract_text,  # 取纯文本部分
                    controllers.handle_cancellation(session),  # 处理用户可能的取消指令
                    str.strip  # 去掉两边空白字符
                ]
            )

            time = await session.aget(
                'time', prompt='你需要我在什么时间提醒你呢？',
                arg_filters=[
                    extractors.extract_text,  # 取纯文本部分
                    controllers.handle_cancellation(session),  # 处理用户可能的取消指令
                    str.strip,  # 去掉两边空白字符
                    # 正则匹配输入格式
                    validators.match_regex(r'^\d{4}-\d{2}-\d{2}$', '格式不对啦，请重新输入')
                ]
            )
            ```

            连续获取多个参数，如果当前还不知道，则询问用户，等待用户输入之后，会依次运行 `arg_filters` 参数中的过滤器，以确保参数内容和格式符合要求。
        """
        if key is ...:
            key = '__default_argument'
            force_update = True
        if key in self.state:
            if force_update is True:
                del self.state[key]
            else:
                return self.state[key]

        self.current_key = key
        self.current_arg_filters = arg_filters
        self._current_send_kwargs = kwargs
        await self.apause(prompt, **kwargs)

        return self.state.get(key, self.current_arg)

    def pause(self, message: Optional[Message_T] = None, **kwargs) -> NoReturn:
        """
        暂停当前命令会话，并发送消息。此函数调用之后的语句将不会被执行（除非捕获了此函数抛出的特殊异常）。

        :::tip
        推荐使用 `apause` 方法。
        :::

        参数:
            message (Optional[nonebot.typing.Message_T]): 要发送的消息，若不传入则不发送
            kwargs: 其它传入 `BaseSession.send()` 的命名参数

        用法:
            ```python
            if session.is_first_run:
                session.pause('请发送要处理的图片，发送 done 结束')
            if session.current_arg_text.strip() == 'done':
                session.finish('处理完成')
            process_images(session.current_arg_images)
            session.pause('请继续发送要处理的图片，发送 done 结束')
            ```

            需要连续接收用户输入，并且过程中不需要改变 `current_key` 时，使用此函数暂停会话。
        """
        if message:
            self._run_future(self.send(message, **kwargs))
        self._raise(_PauseException())

    async def apause(self, message: Optional[Message_T] = None, **kwargs) -> None:
        """
        异步地暂停当前命令会话，并发送消息。

        当用户再次输入时，不会重新运行命令处理器，而是回到此函数调用之处继续执行。

        版本: 1.8.0+

        参数:
            message (Optional[nonebot.typing.Message_T]): 要发送的消息，若不传入则不发送
            kwargs: 其它传入 `BaseSession.send()` 的命名参数

        用法:
            ```python
            await session.apause('请发送要处理的图片，发送 done 结束')
            while True:
                if session.current_arg_text.strip() == 'done':
                    session.finish('处理完成')
                process_images(session.current_arg_images)
                await session.apause('请继续发送要处理的图片，发送 done 结束')
            ```

            需要连续接收用户输入，并且过程中不需要改变 `current_key` 时，使用此函数暂停会话。
        """
        if message:
            self._run_future(self.send(message, **kwargs))
        if self.waiting:
            return
        while True:
            try:
                self._future = asyncio.get_event_loop().create_future()
                self.running = False
                timeout_opt = self.expire_timeout
                timeout = timeout_opt.total_seconds() if timeout_opt else None
                await asyncio.wait_for(self._future, timeout)
                break
            except _PauseException:
                continue
            except (_FinishException, asyncio.TimeoutError):
                raise

    def finish(self, message: Optional[Message_T] = None, **kwargs) -> NoReturn:
        """
        结束当前命令会话，并发送消息。此函数调用之后的语句将不会被执行（除非捕获了此函数抛出的特殊异常）。

        调用此函数后，命令将被视为已经完成，当前命令会话将被移除。

        参数:
            message (Optional[nonebot.typing.Message_T]): 要发送的消息，若不传入则不发送
            kwargs: 其它传入 `BaseSession.send()` 的命名参数

        用法:
            ```python
            session.finish('感谢您的使用～')
            ```
        """
        if message:
            self._run_future(self.send(message, **kwargs))
        self._raise(_FinishException())

    def switch(self, new_message: Message_T) -> NoReturn:
        """
        结束当前会话，改变当前消息事件中的消息内容，然后重新处理消息事件。

        此函数可用于从一个命令中跳出，将用户输入的剩余部分作为新的消息来处理，例如可实现以下对话:

        ```
        用户: 帮我查下天气
        Bot: 你要查询哪里的天气呢？
        用户: 算了，帮我查下今天下午南京到上海的火车票吧
        Bot: 今天下午南京到上海的火车票有如下班次: blahblahblah
        ```

        这里进行到第三行时，命令期待的是一个地点，但实际发现消息的开头是「算了」，于是调用 `switch('帮我查下今天下午南京到上海的火车票吧')`，结束天气命令，将剩下来的内容作为新的消息来处理（触发火车票插件的自然语言处理器，进而调用火车票查询命令）。

        参数:
            new_message (nonebot.typing.Message_T): 要覆盖消息事件的新消息内容

        用法:
            ```python
            @my_cmd.args_parser
            async def _(session: CommandSession)
                if not session.is_first_run and session.current_arg.startswith('算了，'):
                    session.switch(session.current_arg[len('算了，'):])
            ```

            使用「算了」来取消当前命令，转而进入新的消息处理流程。这个例子比较简单，实际应用中可以使用更复杂的 NLP 技术来判断。
        """
        if self.is_first_run:
            # if calling this method during first run,
            # we think the command is not handled
            raise _FinishException(result=False)

        if not isinstance(new_message, Message):
            new_message = Message(new_message)
        self._raise(SwitchException(new_message))

    def _raise(self, e: Exception) -> NoReturn:
        """Raise an exception from the main execution path"""
        if self.waiting:
            self._future.set_exception(e)
            raise _YieldException
        raise e

async def handle_command(bot: NoneBot, event: CQEvent,
                         manager: CommandManager) -> Optional[bool]:
    """
    INTERNAL API

    Handle a message as a command.

    This function is typically called by "handle_message".

    :param bot: NoneBot instance
    :param event: message event
    :param manager: command manager
    :return: the message is handled as a command
    """
    cmd, current_arg = manager.parse_command(bot, str(event.message).lstrip())
    is_privileged_cmd = cmd and cmd.privileged
    if is_privileged_cmd and cmd.only_to_me and not event['to_me']:
        is_privileged_cmd = False
    disable_interaction = bool(is_privileged_cmd)

    if is_privileged_cmd:
        logger.debug(f'Command {cmd.name} is a privileged command')

    ctx_id = context_id(event)

    if not is_privileged_cmd:
        # wait for 1.5 seconds (at most) if the current session is running
        retry = 5
        while retry > 0 and \
                _sessions.get(ctx_id) and _sessions[ctx_id].running:
            retry -= 1
            await asyncio.sleep(0.3)

    check_perm = True
    session: Optional[CommandSession] = _sessions.get(
        ctx_id) if not is_privileged_cmd else None

    if session is not None:
        if session.running:
            logger.warning(f'There is a session of command '
                           f'{session.cmd.name} running, notify the user')
            asyncio.ensure_future(
                send(bot, event,
                     render_expression(bot.config.SESSION_RUNNING_EXPRESSION)))
            # pretend we are successful, so that NLP won't handle it
            return True

        if session.is_valid:
            logger.debug(f'Session of command {session.cmd.name} exists')
            # since it's in a session, the user must be talking to me
            event['to_me'] = True
            session.refresh(event, current_arg=str(event['message']))
            # there is no need to check permission for existing session
            check_perm = False
        else:
            # the session is expired, remove it
            logger.debug(f'Session of command {session.cmd.name} has expired')
            if ctx_id in _sessions:
                del _sessions[ctx_id]
            session = None

    if session is None:
        if not cmd:
            logger.debug('Not a known command, ignored')
            return False
        if cmd.only_to_me and not event['to_me']:
            logger.debug('Not to me, ignored')
            return False
        SessionClass = cmd.session_class or CommandSession
        session = SessionClass(bot, event, cmd, current_arg=current_arg)
        logger.debug(f'New session of command {session.cmd.name} created')

    assert isinstance(session, CommandSession)
    return await _real_run_command(session,
                                   ctx_id,
                                   check_perm=check_perm,
                                   disable_interaction=disable_interaction)

async def call_command(bot: NoneBot,
                       event: CQEvent,
                       name: Union[str, CommandName_T],
                       *,
                       current_arg: str = '',
                       args: Optional[CommandArgs_T] = None,
                       check_perm: bool = True,
                       disable_interaction: bool = False) -> Optional[bool]:
    """从内部直接调用命令。可用于在一个插件中直接调用另一个插件的命令。

    参数:
        bot: NoneBot 对象
        event: 事件对象
        name (Union[str, nonebot.typing.CommandName_T]): 要调用的命令名
        current_arg: 命令会话的当前输入参数
        args (Optional[nonebot.typing.CommandArgs_T]): 命令会话的（初始）参数（将会被并入命令会话的 `state` 属性）
        check_perm: 是否检查命令的权限，若否，则即使当前事件上下文并没有权限调用这里指定的命令，也仍然会调用成功
        disable_interaction: 是否禁用交互功能，若是，则该命令的会话不会覆盖任何当前已存在的命令会话，新创建的会话也不会保留

    返回:
        bool: 命令是否调用成功

    用法:
        ```python
        await call_command(bot, event, 'say', current_arg='[CQ:face,id=14]', check_perm=False)
        ```

        从内部调用 `say` 命令，且不检查权限。
    """
    cmd = CommandManager()._find_command(name)
    if not cmd:
        return False
    SessionClass = cmd.session_class or CommandSession
    session = SessionClass(bot, event, cmd, current_arg=current_arg, args=args)
    return await _real_run_command(session,
                                   context_id(session.event),
                                   check_perm=check_perm,
                                   disable_interaction=disable_interaction)

async def _real_run_command(session: CommandSession,
                            ctx_id: str,
                            disable_interaction: bool = False,
                            **kwargs) -> Optional[bool]:
    if not disable_interaction:
        # override session only when interaction is not disabled
        _sessions[ctx_id] = session
    try:
        logger.debug(f'Running command {session.cmd.name}')
        session.running = True
        future = asyncio.ensure_future(session.cmd.run(session, **kwargs))
        timeout_opt = session.run_timeout
        timeout = timeout_opt.total_seconds() if timeout_opt else None

        try:
            await asyncio.wait_for(future, timeout)
            handled = future.result()
        except asyncio.TimeoutError:
            handled = True
        except CommandInterrupt:
            raise
        except Exception as e:
            logger.error(f'An exception occurred while '
                         f'running command {session.cmd.name}:')
            logger.exception(e)
            handled = True
        raise _FinishException(handled)
    except _PauseException:
        session.running = False
        if disable_interaction:
            # if the command needs further interaction, we view it as failed
            return False
        logger.debug(f'Further interaction needed for '
                     f'command {session.cmd.name}')
        # return True because this step of the session is successful
        return True
    except _YieldException:
        return True
        # return True because this step of the session is successful
    except (_FinishException, SwitchException) as e:
        session.running = False
        logger.debug(f'Session of command {session.cmd.name} finished')
        if not disable_interaction and ctx_id in _sessions:
            # the command is finished, remove the session,
            # but if interaction is disabled during this command call,
            # we leave the _sessions untouched.
            del _sessions[ctx_id]

        if isinstance(e, _FinishException):
            return e.result
        elif isinstance(e, SwitchException):
            # we are guaranteed that the session is not first run here,
            # which means interaction is definitely enabled,
            # so we can safely touch _sessions here.
            if ctx_id in _sessions:
                # make sure there is no session waiting
                del _sessions[ctx_id]
            logger.debug(f'Session of command {session.cmd.name} switching, '
                         f'new message: {e.new_message}')
            raise e  # this is intended to be propagated to handle_message()

def kill_current_session(event: CQEvent) -> None:
    """强行移除当前已存在的任何命令会话，即使它正在运行。该函数可用于强制移除执行时间超过预期的命令，以保证新的消息不会被拒绝服务。

    参数:
        event: 事件对象

    用法:
        ```python
        @on_command('kill', privileged=True)
        async def _(session: CommandSession):
            kill_current_session(session.event)
        ```

        在特权命令 `kill` 中强行移除当前正在运行的会话。
    """
    ctx_id = context_id(event)
    if ctx_id in _sessions:
        if _sessions[ctx_id].waiting:
            _sessions[ctx_id]._future.set_exception(_FinishException())
        del _sessions[ctx_id]

from nonebot.command.group import CommandGroup

__all__ = [
    'CommandManager',
    'CommandSession',
    'call_command',
    'kill_current_session',
    'CommandGroup',
]

__autodoc__ = {
    "CommandInterrupt": False,
    "SwitchException": False,
    "Command": False,
    "CommandManager.add_patterns": False,
    "CommandManager.parse_command": False,
    "CommandSession.running": False,
    "CommandSession.waiting": False,
    "CommandSession.expire_timeout": False,
    "CommandSession.run_timeout": False,
    "CommandSession.is_valid": False,
    "CommandSession.refresh": False,
    "handle_command": False
}


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

from xls_operation import read_xls_file, save_xls_file

# 首先读取test1.xlsx文件中内容
target_file = u"test1.xlsx"

# 列名：0项目代码 1项目名称 2总经费 3已投入经费 4完成时间 5承制单位
meta_data = read_xls_file(target_file)  # 得到目标文件的第一个表格的数据，列表类型

# 去掉第一行，标题行
meta_data = meta_data[1:]
# print(meta_data)

# 若最后一列为空时（没有承制单位），需要补齐一个空白
empty_blank = ["", "", "", "", "", ""]
for row in range( len(meta_data[:]) ) :
    if( len( meta_data[row] ) < 6 ): # 若长度小于5，则用“”补齐
        meta_data[row][ len( meta_data[row] ) : ] =  empty_blank[ len(meta_data[row]) : ]
# print(meta_data)

for row in meta_data:
    if len(row[5]) > 0 :
        split_mark = row[5].split("、")  # 取出承制单位，用顿号进行切分
        for temp_mark in split_mark :
            print(temp_mark)
            # 针对每个公司进行查找其公司编码，在数据库中查找，还是在Excel中查找
            # 。。。。

    # print("string = %s, Mark Num=%d" % (row[5], len(split_mark)) )


import Tkinter
import random
import math

import numpy as np

from PIL import Image,ImageTk,ImageOps, ImageEnhance, ImageDraw

class App():
	def __init__(self):

		self.seed_value = np.random.randint(2 ** 32)

		print (self.seed_value)

		np.random.seed(self.seed_value)

		self.grid_x = 384 * 2
		self.grid_y = 384 * 2

		self.display_x = 700
		self.display_y = 700

		self.save_images = False

		if self.save_images:
			try:
				os.makedirs("pics/" + str(self.seed_value))
			except OSError as exc:
				if exc.errno == errno.EEXIST and os.path.isdir(path):
					pass

		self.top = Tkinter.Tk()
		self.w = Tkinter.Canvas(self.top, width = self.display_x * 2, height = self.display_y);self.w.pack()
		self.im = Image.new("RGB",(self.grid_x,self.grid_y),"white")
		self.p = ImageTk.PhotoImage(self.im)

		self.im_graph = Image.new("RGB",(5000,1000),"white")
		self.im_graph_draw = ImageDraw.Draw(self.im_graph)
		self.p_graph = ImageTk.PhotoImage(self.im_graph.resize((700,700),Image.ANTIALIAS))

		self.timer = None
		self.reset()
		self.bit = self.w.create_image(0,0, image = self.p, anchor=Tkinter.NW)
		self.bit_graph = self.w.create_image(self.display_x,0, image = self.p_graph,anchor=Tkinter.NW)
		self.b = Tkinter.Button(text="Start",command = self.calc_rules);self.b.pack()

	def reset(self):

		self.sets = {}

		self.generation = 0
		self.last_generation = 0

		self.old_rule_count = 0

		self.old_sums = None

		if self.timer:
			self.top.after_cancel(self.timer)
			self.timer = None

		self.grid_and_rules = np.zeros( (self.grid_x,self.grid_y,19), dtype=np.uint8)

		d = [0.0,
			 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,
			 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]

		do = [1.0,
			 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,
			 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]

		s = self.grid_x / 4 #np.random.randint(6,7)

		for x in xrange(s,self.grid_x ,s * 2):
			for y in xrange(s,self.grid_y ,s * 2):

				for r in xrange(19):
					d[r] = np.random.random() * do[r]

				for r in xrange(19):
					self.grid_and_rules[(x - s):(x + s),(y - s):(y + s),r] =  np.random.random((s * 2,s * 2)) < d[r]

		self.color_me()

	def calc_rules(self):

		for counter in xrange(1):

			accum = np.zeros( (self.grid_x,self.grid_y,19), dtype=np.uint8 )

			temp = self.grid_and_rules

			for x in xrange(1,19):
				temp[:,:,x] *= temp[:,:,0]

			for dy in [-1,0,1]:
				for dx in [-1,0,1]:

					left = np.roll(temp,dx,axis = 0)
					up = np.roll(left,dy,axis = 1)

					accum += up

			grid = accum[:,:,0]

			for x in xrange(1,19):

				temp[:,:,x] = (accum[:,:,x] > 0) & (grid > 0) & (((accum[:,:,x] == (grid )) )| ((temp[:,:,0] == 0)))

			on_rules = temp[:,:,1:10]
			off_rules = temp[:,:,10:]

			accum2 = np.zeros( (self.grid_x,self.grid_y), dtype=np.uint8 )

			for x in xrange(0,9):
				accum2 |= (grid == (on_rules[:,:,x] * (x + 1))) & (temp[:,:,0] == 1) & (grid > 0)
				accum2 |= ((grid + 1) == (off_rules[:,:,x] * (x + 1))) & (temp[:,:,0] == 0)

			self.grid_and_rules[:,:,0] = accum2
			self.grid_and_rules[:,:,1:10] = on_rules
			self.grid_and_rules[:,:,10:] = off_rules

		self.generation += 1

		self.color_me()

		self.timer = self.top.after_idle(self.calc_rules)

	def color_me(self):

		new_keys = 0

		colors = np.zeros( (self.grid_x,self.grid_y,3), dtype=np.uint8 )

		lookup = [0,
					10,11,12,1,2,3,
					13,14,15,4,5,6,
					16,17,18,7,8,9]

		color_lookups = [tuple([0,0,0])] * 19

		for x in xrange(1,7):
			colors[:,:,1] += self.grid_and_rules[:,:,lookup[x]] * 2 ** (x )
			color_lookups[lookup[x]] = tuple([0,42 * (x),0])

		for x in xrange(7,13):
			colors[:,:,2] += self.grid_and_rules[:,:,lookup[x]] * 2 ** (x - 6)
			color_lookups[lookup[x]] = tuple([0,0,42 * (x - 6)])

		for x in xrange(13,19):
			colors[:,:,0] += self.grid_and_rules[:,:,lookup[x]] * 2 ** (x - 12)
			color_lookups[lookup[x]] = tuple([42 * (x - 12),0,0])

		colors[:,:,0] *= self.grid_and_rules[:,:,0] + 1
		colors[:,:,1] *= self.grid_and_rules[:,:,0] + 1
		colors[:,:,2] *= self.grid_and_rules[:,:,0] + 1

		if self.generation % 50 == 0:

			rule_sets = self.grid_and_rules[:,:]
			rule_sets = rule_sets.reshape(-1,rule_sets.shape[2])
			unique_rule_sets = np.unique(rule_sets.view(rule_sets.dtype.descr * rule_sets.shape[1]))

			sums = [0] * 19

			if self.old_rule_count > 0:

				gen_diff = self.generation - self.last_generation

				gen_mod = self.generation % 5000

				if gen_mod < (self.last_generation % 5000):
					self.im_graph_draw.rectangle((0,0,5000,1000),fill="white")

				self.im_graph_draw.line((gen_mod - gen_diff, 1000 - self.old_rule_count,gen_mod,1000 - len(unique_rule_sets)),fill="purple")

				for x in xrange(19):
					sums[x] = (np.sum(self.grid_and_rules[:,:,x]) * 1000) / (self.grid_x * self.grid_y)

					self.im_graph_draw.line((gen_mod - gen_diff, 1005 - self.old_sums[x] ,gen_mod,1005 - sums[x]),fill=color_lookups[x])
			else:
				for x in xrange(19):
					sums[x] = (np.sum(self.grid_and_rules[:,:,x]) * 1000) / (self.grid_x * self.grid_y)

			self.old_rule_count = len(unique_rule_sets)

			self.old_sums = sums

			self.last_generation = self.generation

			self.p_graph = ImageTk.PhotoImage(self.im_graph.resize((700,700),Image.ANTIALIAS))

			self.bit_graph = self.w.create_image(self.display_x,0, image = self.p_graph,anchor=Tkinter.NW)

		temp = self.im

		colors[(colors == 0).all(axis = -1)] = [0,0,0]

		self.im = Image.fromarray(colors)

		temp2 = Image.blend(temp,self.im,1.0)

		self.p = ImageTk.PhotoImage(temp2.resize((self.display_x,self.display_y),Image.ANTIALIAS))

		self.im = Image.blend(temp,self.im,1.0)

		self.bit = self.w.create_image(0,0, image = self.p, anchor=Tkinter.NW)

		if self.save_images and self.generation % 5 == 0:
			self.im.resize((384*2,384*2),Image.ANTIALIAS).save("pics/" + str(self.seed_value) + "image." + str(self.generation/5) + ".jpeg")

app=App()
app.top.mainloop()


from flask import Blueprint, render_template, request, url_for, redirect
from models import Total
from exts import db
from .forms import AddForm
from sqlalchemy import or_

bp = Blueprint("operation", __name__, url_prefix="/operation")

# Display the operation page.
@bp.route('/ope')
def operation():
    return render_template("operation.html")

# Display the add page.
@bp.route('/add')
def add():
    return render_template("add.html")

# Add the assessment
@bp.route('/add_assessment', methods=['GET', 'POST'])
def add_assessment():
    # Check whether its method is "get" or "post"
    if request.method == 'POST':
        form = AddForm(request.form)
        # Check whether it pass the validator
        if form.validate():
            title = form.title.data
            description = form.description.data
            ddl = form.ddl.data
            finish = form.finish.data
            module_id = form.module_id.data
            assessment = Total(title=title, description=description, ddl=ddl, finish=finish, module_id=module_id)
            db.session.add(assessment)
            db.session.commit()
            return redirect(url_for("display.display"))
        else:
            return redirect(url_for("operation.add_assessment"))
    else:
        return render_template("add.html")

# Display the mark page.
@bp.route('/mark')
def mark():
    total = Total.query.all()
    return render_template("mark.html", total=total)

# Display the delete page.
@bp.route('/delete')
def delete():
    total = Total.query.all()
    return render_template("delete.html", total=total)

# Execute the operation of deleting.
@bp.route('/delete_assessment/<int:id>')
def delete_assessment(id):
    total = Total.query.filter_by(id=id)[0]
    db.session.delete(total)
    db.session.commit()
    return redirect(url_for('operation.delete'))

# Execute the operation of marking.
@bp.route('/search_mark/<int:id>')
def search_mark(id):
    total = Total.query.filter_by(id=id)[0]
    if total.finish == "True":
        total.finish = "False"
    else:
        total.finish = "True"
    db.session.commit()
    return redirect(url_for('operation.mark'))

# Execute the operation of searching.(which in the top left corner of the page)
@bp.route('/search')
def search():
    search = request.args.get("search")
    total = Total.query.filter(or_(Total.title.contains(search),
                                   Total.description.contains(search),
                                   Total.ddl.contains(search),
                                   Total.finish.contains(search),
                                   Total.module_id.contains(search)))
    return render_template('display.html', total=total)


class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        # minor optimization
        # nums.sort()
        dp = [0 for i in range(target+1)]
        dp[0] = 1

        for comb_sum in range(target+1):

            for num in nums:
                if comb_sum - num >= 0:
                    dp[comb_sum] += dp[comb_sum-num]
                # minor optimization, early stopping.
                # else:
                #    break
        return dp[target]


# -*- coding: utf-8 -*-
"""
Created on Fri Jan 21 23:00:03 2022

@author: Mohammed Amine Farez
"""

# Import the necessary libraries:
import cv2
import mediapipe as mp
import math

# Initialise MediaPipe solution for Hand Tracking:
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_hands = mp.solutions.hands

# Import the Encodings of the Gestures of Interest:
Question =  [71.84, 69.43, 51.87, 38.95, 85.09, 67.78, 40.8, 37.11, 147.56, 77.88, 47.3, 42.2, 182.02, 72.99, 44.72, 39.05, 183.7, 57.56, 34.93, 32.28]
Tired =  [57.31, 55.15, 39.82, 30.89, 74.24, 62.97, 44.55, 35.51, 146.33, 73.68, 47.52, 38.47, 165.56, 71.7, 45.4, 36.35, 162.59, 54.74, 34.53, 28.28]
Critical =  [76.49, 68.45, 47.71, 32.65, 129.25, 66.1, 38.83, 33.73, 136.34, 77.67, 53.0, 34.79, 112.93, 79.16, 51.0, 33.11, 103.58, 67.12, 36.88, 24.6]
Sick = [60.22, 57.25, 44.72, 35.44, 66.71, 76.12, 45.88, 32.65, 141.6, 88.28, 47.76, 33.12, 158.07, 82.73, 46.17, 32.7, 156.7, 66.61, 37.66, 29.61]

encodings = [Question, Tired, Critical, Sick]
encodings_name = ["Question", "Tired", "Critical", "Sick"]

# For video input:
#cap = cv2.VideoCapture(r'THE PATH TO THE FILE')

# For webcam input:
cap = cv2.VideoCapture(0)

# Obtain the number of frames in Video:
len_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

# Count the frames:
count = 0

# Launch the MediaPipe Hand Tracking with 0.5 Confidence:
with mp_hands.Hands(
    model_complexity=0,
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5) as hands:

    # Iterate over each Frame:
    while cap.isOpened():
        success, image = cap.read()
        count += 1

        # Neglect the first 30 Frames for better accuracy:
        if count < 30:
            continue

        # Break if the Video comes to an end:
        if count == len_frames:
            break

        # Neglect empty Frames:
        if not success:
            print("Ignoring empty camera frame.")
            continue

        # Mark the image as not writeable to improve performance:
        image.flags.writeable = False

        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        results = hands.process(image)

        # Draw the hand annotations on the image:
        image.flags.writeable = True
        image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

        # Initialise the lists for 2D pixel values:
        x_encoding = []
        y_encoding = []
        lml = []
        xl = []
        yl = []

        # Initialise the list for the number of matches obtained for each Hand Gesture class:
        matches = []

        # When the Hand is detected:
        if results.multi_hand_landmarks:
            # Draw the Landmarks:
            for hand_landmarks in results.multi_hand_landmarks:
                mp_drawing.draw_landmarks(
                image,
                hand_landmarks,
                mp_hands.HAND_CONNECTIONS,
                mp_drawing_styles.get_default_hand_landmarks_style(),
                mp_drawing_styles.get_default_hand_connections_style())
            # Transform the 3D points to 2D pixel values:
            for id, lm in enumerate(results.multi_hand_landmarks[0].landmark):
                #  Obtain the Height of Width of the Frame:
                h, w, _ = image.shape
                # Append the x,y coordinates for each point of the landmark:
                xc, yc = int(lm.x * w), int(lm.y * h)
                lml.append([id, xc, yc])
                xl.append(xc)
                yl.append(yc)

            # Calculate the distances between x values, and between y values:
            for i in range(0,len(xl)-1):
                x_encoding.append(abs(xl[i]-xl[i+1]))
                y_encoding.append(abs(yl[i]-yl[i+1]))

            # Calculate the Euclidean distance between each point of the Landmark:
            dist = [round(math.sqrt(i**2 +j**2),2) for i, j in zip(x_encoding, y_encoding)]

            # Compare the distances obtained with the pretrained encodings:
            for encoding in encodings:
                mat = 0

                # Normalize the distances by the first distance between point 0 and point 1:
                result = [round((i*encoding[0])/(j*dist[0]),2) for i, j in zip(dist, encoding)]

                # For each comparison, if the result is between 0.71 and 1.21, we have a match:
                for i in range(0,len(result)):
                    test = result[i]
                    if test < 1.21 and test > 0.71:
                        mat += 1
                        print(mat)

                # Append the number of matched distances with each Hand Gesture class:
                matches.append(mat)

            # Obtain the Class with the highest number of matches:
            gesture_class = max(matches)

            # Obtain the name of the Hand Gesture:
            hand_gesture = encodings_name[matches.index(gesture_class )]

            # Display the result if at least Half the distances match (10/20):
            if gesture_class > 10:
                cv2.putText(image,hand_gesture,(100, 150),cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),2)

        # Flip the image horizontally for a selfie-view display:
        #cv2.imshow('MediaPipe Hands', cv2.flip(image, 1))
        cv2.imshow('MediaPipe Hands', image)

        # Break when Q is pressed:
        if cv2.waitKey(10) & 0xFF == ord('q'):
            break

# Destroy windows and release the camera when the process has finished:
cv2.destroyAllWindows()
cap.release()


import os
import unittest
from datetime import datetime
from unittest import TestLoader, TestSuite
from pyunitreport import HTMLTestRunner
from __VerifyYouTubeTab import VerifyYouTubeTabs
from __VerifyMediaPlayer import VerifyMediaPlayer
from __VerifyPlayerFunctions import PlayerFunction
from __VerfiyLoginFunction import Login
from __Utils import get_logpath, parsetestsuits
from __Utils import parseCapabilities

if __name__ == '__main__':

    testsuitlist = parsetestsuits()
    suite = unittest.TestSuite()

    for x in testsuitlist:
        if x == "test_HomeTab":
            suite.addTest(VerifyYouTubeTabs('test_HomeTab'))
        elif x == "test_TrendingTab":
            suite.addTest(VerifyYouTubeTabs('test_TrendingTab'))
        elif x == "test_SubscriptionTab":
            suite.addTest(VerifyYouTubeTabs('test_SubscriptionTab'))
        elif x == "test_InboxTab":
            suite.addTest(VerifyYouTubeTabs('test_InboxTab'))
        elif x == "test_LibrartTab":
            suite.addTests(VerifyYouTubeTabs('test_TrendingTab'))
        elif x == "test_pause":
            suite.addTest(VerifyMediaPlayer('test_pause'))
        elif x == "test_nextvideoplayback":
            suite.addTest(VerifyMediaPlayer('test_nextvideoplayback'))
        elif x == "test_function":
            suite.addTest(PlayerFunction('test_function'))
        elif x == "test_login":
            suite.addTest(Login('test_login'))

    kwargs = {
        "output": get_logpath(),
        "report_name": datetime.now().strftime('YouTube_Automation_Report_%H_%M_%d_%m_%Y'),
        "failfast": True
    }
    runner = HTMLTestRunner(**kwargs)
    runner.run(suite)


alfavit_EU =  'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ'
smeshenie = int(input('Шаг шифровки: '))
message = input("Сообщение для дешифровки: ").upper()

itog = ''
for i in message:
    mesto = alfavit_EU.find(i)		 # Меняем знак + на знак -
    new_mesto = mesto - smeshenie
    if i in alfavit_EU:
        itog += alfavit_EU[new_mesto]
    else:
        itog += i
print(itog)


import sys

value = []
for line in sys.stdin:
	res = line.split()
	print(res)
	if '->' in res:
		#num = ord(res[2])
		res[2] = res[0]
		print(res[2])
	if 'AND' in res:
		var1 = res[0]
		print(var1)
		res[4] = res[0]&[2]
		print(res[4])


import csv
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib import pyplot as plt
from scipy.signal import find_peaks
import numpy as np

def get_column_data(numcol, numrows, datacsv):
    datatotal = []
    for i in range(1, numrows):
        data = datacsv[i][numcol]
        if data != '':
            data = float(data)
            datatotal.append(data)
    return np.asarray(datatotal).astype(float)

datacsv = []
csvfile = open('values.csv')
reader = csv.reader(csvfile, delimiter=',')
for row in reader:
    datacsv.append(row)
csvfile.close()
numrows = len(datacsv)
numcolumns = len(datacsv[1])

xdata = get_column_data(0, numrows, datacsv)
ydata = get_column_data(1, numrows, datacsv)

peaks, _ = find_peaks(ydata, height=3)

print(peaks)
print(xdata[peaks])
maxspacings = xdata[peaks]

dspacings = [x - maxspacings[i - 1] for i, x in enumerate(maxspacings)][1:]
print("d spacing (nm): ", np.nanmedian(dspacings))

plt.plot(xdata, ydata)
plt.plot(xdata[peaks], ydata[peaks], "x")
plt.tight_layout()
plt.show()


import pickle
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import itertools
import os

results = None

# matplotlib.rcParams.update({'font.size': 12})

color_list = plt.cm.tab10(np.linspace(0, 1, 10))
colors = {'lstm': color_list[0], 'pf_e2e': color_list[1], 'pf_ind_e2e': color_list[2], 'pf_ind': color_list[3]}
labels = {'lstm': 'LSTM', 'pf_e2e': 'DPF (e2e)', 'pf_ind_e2e': 'DPF (ind+e2e)', 'pf_ind': 'DPF (ind)', 'ff': 'FF', 'odom': 'Odom. baseline'}
# conditions = ['normal', 'no_motion_likelihood', 'learn_odom', 'no_proposer']
# conditions = ['normal', 'learn_odom', 'no_inject']
# clabels = {'normal': 'Default', 'no_motion_likelihood': 'W/o motion likelihood', 'learn_odom': 'Learned odometry', 'no_proposer': 'W/o particle proposer', 'no_inject': "No inject"}

if False:
    test_conditions = ['odom5_imgTG', 'odom10_imgTG']

    conditions = ['orig_odom5_imgTG', 'odom5_imgTG_odom5_imgTG',
                  'odom10_imgTG_odom5_imgTG', 'odom5_imgTG_odom10_imgTG',
                    'odom10_imgTG_odom10_imgTG', 'orig_odom10_imgTG']

    clabels = {'orig_odom5_imgTG':'5',
               'odom5_imgTG_odom5_imgTG':'mo5,me5',
               'odom5_imgTG_odom10_imgTG':'mo5,me10',
               'orig_odom10_imgTG':'10',
               'odom10_imgTG_odom10_imgTG': 'mo10,me10',
               'odom10_imgTG_odom5_imgTG': 'mo10,me5',
               'odom5_imgTG': '5',
               'odom10_imgTG': '10',
                }
    xlabels = ['A', 'B']
    exp = 'swapmo'
else:

    test_conditions = ['odom10_imgG', 'odom10_imgTG']

    conditions = ['orig_odom10_imgG', 'odom10_imgG_odom10_imgG', 'odom10_imgG_odom10_imgTG',
                  'odom10_imgTG_odom10_imgG', 'odom10_imgTG_odom10_imgTG', 'orig_odom10_imgTG']

    # clabels = {'orig_odom10_imgG':'G',
    #            'odom10_imgG_odom10_imgG':'meG,moG',
    #            'odom10_imgG_odom10_imgTG':'meG,moTG',
    #            'orig_odom10_imgTG':'TG',
    #            'odom10_imgTG_odom10_imgTG': 'meTG,moTG',
    #            'odom10_imgTG_odom10_imgG': 'meTG,moG',
    #            'odom10_imgG': 'G',
    #            'odom10_imgTG': 'TG',
    #             }

    clabels = {'orig_odom10_imgG':'A(A)*',
           'odom10_imgG_odom10_imgG':'A(A)',
           'odom10_imgG_odom10_imgTG':'A(B)',
           'orig_odom10_imgTG':'B(B)*',
           'odom10_imgTG_odom10_imgTG': 'B(B)',
           'odom10_imgTG_odom10_imgG': 'B(A)',
           'odom10_imgG': 'A',
           'odom10_imgTG': 'B',
            }
    xlabels = ['C', 'D']
    exp = 'swapme'

vmax = 0.4

task = 'nav02'
# methods = ['pf_ind', 'pf_e2e', 'pf_ind_e2e', 'lstm']
methods = ['pf_ind_e2e']

# load results
results = dict()

count = 0
for cond in conditions:
    # log_path = '/home/rbo/Desktop/log/'+task+'_ab1'
    log_path = '../log/'+exp+'/'+cond
    for filename in [f for f in os.listdir(log_path) if os.path.isfile(os.path.join(log_path, f))]:
        full_filename = os.path.join(log_path, filename)
        print('loading {}:'.format(count) + full_filename + ' ...')
        try:
            # if 'DeepThought' not in filename:
            # if 'DeepThought' in filename:
            with open(full_filename, 'rb') as f:
                result = pickle.load(f)
                # result_name = result['task'][0] + '/' + result['method'][0] + '/' + str(result['num_episodes'][0]) + '/' + result['condition'][0]
                result_name = cond #+ '_' + result['exp_params'][0]['file_ending'] #result['exp_params'][0]['task'] + '/' + result['exp_params'][0]['method'] + '/' + str(result['exp_params'][0]['num_episodes']) + '/' + result['exp_params'][0]['ab_cond']
                print(result_name)
                if result_name not in results.keys():
                    results[result_name] = result
                else:
                    for key in result.keys():
                        if key in results[result_name].keys():
                            results[result_name][key] += result[key]
                        else:
                            results[result_name][key] = result[key]
                        # print(result_name, key)
                count += 1
        except Exception as e:
            print(e)
            print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')

print()
for result_name, r in results.items():
    print(result_name, len(r['exp_params']))
    # print(result_name, len(r['test_odom5_imgTG_mse']))

print('Loaded {} results'.format(count))

task = 'nav02'
step = 3

episodes = [1000]
# episodes = [1000]

means = []
ses = []

for c, condition in enumerate(conditions):

    means.append(np.zeros([len(test_conditions), 5]))
    ses.append(np.zeros([len(test_conditions), 5]))

    for tc, test_condition in enumerate(test_conditions):

        result_name = condition
        if result_name in results.keys():
            result = results[result_name]

            hist = np.array([[h[i] for i in range(0, 50, 10)] for h in result['test_'+test_condition+'_hist' ]])  # result x time x sqe [.0, 0.1, .., 10.0]
            err = 1. - np.sum(hist[:,:,:10], axis=-1) # sqe < 1.0
            # err = np.sum(hist[:,:,:10], axis=-1) # sqe < 1.0
            print(result_name, err)
            means[c][tc] = np.mean(err, axis=0)
            ses[c][tc] = np.std(err, axis=0, ddof=1) / np.sqrt(len(err))

        else:
            print(result_name + 'not found')
            means[tc] *= np.nan
            ses[tc] *= np.nan

# means[num_episodes]['min'] = np.stack([means[num_episodes][method] for method in methods], axis=0).min(axis=1)

# ax = fig.add_subplot(111)
# # Turn off axis lines and ticks of the big subplot
# ax.spines['top'].set_color('none')
# ax.spines['bottom'].set_color('none')
# ax.spines['left'].set_color('none')
# ax.spines['right'].set_color('none')
# ax.tick_params(labelcolor='w', top='off', bottom='off', left='off', right='off')

m = np.array(means)[:, :, step].T
s = np.array(ses)[:, :, step].T

for i in range(2):
    z = m[i, :]
    y = s[i, :]
    x = [[z[0]] * 2 + [z[2]] * 2,
         [z[1]] * 2 + [z[2]] * 2,
         [z[3]] * 2 + [z[5]] * 2,
         [z[3]] * 2 + [z[4]] * 2]
    plt.figure(i, [1.35,1.35])
    plt.imshow(x, interpolation='nearest', vmin=-0.33*vmax, vmax=vmax, cmap='viridis_r')
    plt.plot([-0.5, 3.5],[1.5, 1.5], '-w', linewidth=0.5)
    plt.plot([1.5, 1.5],[-0.5, 3.5], '-w', linewidth=0.5)

    for j, x_coord, y_coord, value, s_value in [
            (0, 0.5, 0, z[0], y[0]),
            (1, 0.5, 1, z[1], y[1]),
            (2, 2.5, 0.5, z[2], y[2]),
            (3, 0.5, 2.5, z[3], y[3]),
            (5, 2.5, 2, z[5], y[5]),
            (4, 2.5, 3, z[4], y[4])]:
        if j == 0 or j == 5:
            # text = '{:.4s}*\n+-{:.4s}'.format('{:.3f}'.format(value)[1:],'{:.2f}'.format(s_value)[1:])
            text = ' {:.4s}*'.format('{:.3f}'.format(value)[1:],'{:.2f}'.format(s_value)[1:])
        else:
            # text = '{:.4s}\n+-{:.4s}'.format('{:.3f}'.format(value)[1:],'{:.2f}'.format(s_value)[1:])
            text = '{:.4s}'.format('{:.3f}'.format(value)[1:],'{:.2f}'.format(s_value)[1:])

        plt.text(x_coord, y_coord, text, va='center', ha='center', color='white', fontweight='normal')

    plt.gca().set_aspect('equal')

    plt.xlabel('Motion model')
    plt.xticks([0.5, 2.5], xlabels)
    plt.ylabel('Measurem. model')
    plt.yticks([0.5, 2.5], xlabels)
    plt.tight_layout(0.0, 0.0, 0.0)
    print('saving')
    plt.savefig('../plots/cr/'+exp+'%s.pdf' % i, bbox_inches="tight", transparent=True, dpi=600, frameon=True, facecolor='w', pad_inches=0.01)

plt.figure('colorbar', [0.6, 1.35])
a = np.array([[0.0, 0.3]])
img = plt.imshow(a, cmap="viridis_r", vmin=-0.33*vmax, vmax=vmax)
plt.gca().set_visible(False)
cax = plt.axes([0.0, 0.2, 0.1, 0.65])
plt.colorbar(orientation="vertical", cax=cax, label='Error rate', boundaries=np.linspace(0,0.4,100), ticks=np.linspace(0.0, 0.4, 5))

plt.savefig('../plots/cr/colorbar.pdf'.format(s), transparent=True, dpi=600, frameon=False, facecolor='w', pad_inches=0.01)

plt.show()


# https://leetcode.com/problems/single-number

from typing import List

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        ans = 0
        for num in nums:
            ans ^= num
        return ans

def main() -> int:
    nums = [2, 2, 1]
    print(Solution().singleNumber(nums))
    return 0

if __name__ == '__main__':
    main()


from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
from collections import defaultdict
import os
from utils import deserialize_features
import numpy as np
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

def create_cluster_artists(artist_dict: defaultdict):
    v = np.vstack([artist_dict[k] for k in artist_dict.keys()])     #Stack vertically the arrays belonging to an artist
    y = []      #labels
    for k in artist_dict.keys():
        y = y + ([k.split('.')[0]] * len(artist_dict[k]))
    return v, y

def create_cluster_movements(artist_dict, cluster_name):
    v = np.vstack([artist_dict[k] for k in artist_dict.keys()])
    y = [cluster_name] * v.shape[0]
    return v, y

def create_scatter_plot(v, y):
    """
    Create a scatter plot to represent in a bidimensional space the paintings.
    """
    pca = PCA(n_components=100, random_state=42)
    pca_embedding = pca.fit_transform(v)
    labels = [0 if y[i] == y[0] else 1 for i in range(len(y))]      # Convert textual labels to numeric labels, which
                                                            # is what the 'c' parameter of the scatter function expects
    y = list(set(y))
    scatter = plt.scatter(*zip(*pca_embedding[:, :2]), c=labels, cmap='BrBG')
    plt.legend(handles=scatter.legend_elements()[0], labels=y)
    plt.show()

if __name__ == '__main__':
    artists_dict = defaultdict()
    dir = 'merged'

    '''
    #With this cycle you check the similarities of every possible artists pairing
    artists = os.listdir(dir)
    l = []
    for i in range(len(artists)):
        artists_dict = defaultdict()
        ar1 = artists[i].split('.')[0]
        artists_dict[ar1] = deserialize_features(os.path.join(dir, ar1+'.npy'))
        j = i+1
        while j < len(artists):
            ar2 = artists[j].split('.')[0]
            artists_dict[ar2] = deserialize_features(os.path.join(dir, ar2 + '.npy'))
            (v, y) = create_cluster_artists(artists_dict)
            l.append((silhouette_score(v, y), ar1, ar2))
            artists_dict.pop(ar2)
            j += 1
    l.sort()
    print(l)
    '''
    '''
    #Cycle to compare couples of artists
    artists = ['Vincent_van_Gogh', 'Titian']
    for artist in artists:
        artists_dict[artist] = deserialize_features(os.path.join(dir, artist+'.npy'))
    (v, y) = create_cluster_artists(artists_dict)

    '''
    #Comparison between artistic movements
    renaissance_artists = ['Sandro_Botticelli', 'Titian', 'Leonardo_da_Vinci', 'Michelangelo', 'Raphael']
    impressionism_artists = ['Claude_Monet', 'Edouard_Manet', 'Camille_Pissarro', 'Edgar_Degas', 'Pierre-Auguste_Renoir']
    artist_dict_ren = defaultdict()
    artist_dict_impr = defaultdict()

    for artist in renaissance_artists:
        artist_dict_ren[artist] = deserialize_features(os.path.join(dir, artist+'.npy'))

    for artist in impressionism_artists:
        artist_dict_impr[artist] = deserialize_features(os.path.join(dir, artist+'.npy'))

    (v0, y0) = create_cluster_movements(artist_dict_ren, 'Renaissance')
    (v1, y1) = create_cluster_movements(artist_dict_impr, 'Impressionism')
    v = np.vstack([v0, v1])
    y = y0 + y1

    create_scatter_plot(v, y)
    print(davies_bouldin_score(v, y))
    print(calinski_harabasz_score(v, y))
    print(silhouette_score(v, y))


import numpy as np
import re
from pyparsing import Word, hexnums, WordEnd, Optional, alphas, alphanums, printables, ParseException
import os

MAX_OPCODE_LENGHT = 64
NUMBER_OF_INSTRUCTIONS = 3200

regex = re.compile(r'[\n\r\t\?\;\_\'\-]')
hex_integer = Word(hexnums) + WordEnd()
search = Optional('.') + Word(alphas,alphanums)('section') + ":" + hex_integer('adress') + Optional((hex_integer*(1,))("instructions") + Word(alphas,alphanums)("opcode")) + Optional(Word(alphas,alphanums)('first_operant')) + Optional("," + Word(printables)('second_operant'))

PROCESS_VERSION = 3

empty_files_counter = 0
empty_file_ids = []

small_files_counter = 0
small_file_ids = []

super_small_files_counter = 0
super_small_file_ids = []

def pad(A, length):
    arr = np.zeros(length)
    arr[:len(A)] = A
    return arr

def pad2D(A, height, width):
    c_height, c_width = A.shape
    return np.pad(A, ((0,(height-c_height)),(0,width-c_width)), 'constant')

def asm_to_np(file):
    RESULT = []
    counter = 0
    counter_2 = 0
    for line in file.readlines():
            line = regex.sub(" ", line)
            try:
                line = search.parseString(line)
                section = line.section.encode(encoding = 'ASCII', errors = 'ignore')
                adress = bytes.fromhex(''.join(line.adress.asList() if not line.adress == None else ''))
                opcode = line.opcode.encode(encoding = 'ASCII', errors = 'ignore') if not line.opcode == '' else ' '.encode(encoding = 'ASCII', errors = 'ignore')
                operants = f'{line.first_operant},{line.second_operant}'.encode(encoding = 'ASCII', errors = 'ignore')
                instructions = bytes.fromhex(''.join(line.instructions.asList() if not line.instructions == '' else ''))
                byteline = section[1].to_bytes(1, byteorder='big')+adress[2:]+instructions[:4]+opcode[0].to_bytes(1, byteorder='big')+operants
                np_array = np.frombuffer(byteline, dtype='uint8')
                np_array = pad(np_array[0:int(MAX_OPCODE_LENGHT/8)],int(MAX_OPCODE_LENGHT/8))
                RESULT.append(np_array)
                counter += 1
                if counter >= NUMBER_OF_INSTRUCTIONS: break
            except ParseException as e:
                continue
            except ValueError as e:
                continue
            counter_2 += 1
            if counter_2 >= 12000:
                break
    RESULT = pad2D(np.asarray(RESULT)[:NUMBER_OF_INSTRUCTIONS,:int(MAX_OPCODE_LENGHT/8)],NUMBER_OF_INSTRUCTIONS,int(MAX_OPCODE_LENGHT/8))
    return RESULT

def preprocess(file_name:str, origin_folder:str, dest_folder:str):
    asm_path = f"{origin_folder}/{file_name}.asm"
    vec_path = f"{dest_folder}/{PROCESS_VERSION}/{file_name}_{NUMBER_OF_INSTRUCTIONS}_{int(MAX_OPCODE_LENGHT/8)}.npy"
    if not os.path.exists(vec_path):
        f = open(asm_path, "r", encoding = 'unicode_escape', errors='ignore')
        output = asm_to_np(f)
        np.save(vec_path,output)
    else:
        output = np.load(vec_path)
    return output


from db.database import db
from models.models import Card, Deck, Rating, User,DeckStat,Participation,Performance
from sqlalchemy import *
import random
import hashlib
from datetime import datetime
import os
import time
#import matplotlib.pyplot as plt
#import base64
#from io import BytesIO

def sha3512(password):
    m = hashlib.sha3_512()
    m.update(password.encode("utf-8"))
    return str(m.hexdigest())

def username_exists(username):
    user = db.session.query(User).filter(User.username==username).first()
    if user == None:
        return False
    else:
        return True

def email_exists(email):
    user = db.session.query(User).filter(User.email==email).first()
    if user == None:
        return False
    else:
        return True

def get_user_by_username(username):
    user = db.session.query(User).filter(User.username==username).first()
    return user

def get_user_by_id(user_id):
    user = db.session.query(User).filter(User.id==user_id).first()
    return user

def get_deck_by_deck_id(deck_id):
    deck = db.session.query(Deck).filter(Deck.deck_id==deck_id).first()
    return deck
def get_deckstat(user_id,deck_id):
    stat = db.session.query(DeckStat).filter(DeckStat.deck_id==deck_id,DeckStat.user_id==user_id).first()
    return stat

def get_decks_for_dashboard(user_id):
    deckstats = db.session.query(DeckStat).filter(DeckStat.user_id==user_id).all()
    deck_list = []
    for deckstat in deckstats:
        number_of_cards = countcards(deckstat.deck_id)
        deck = get_deck_by_deck_id(deckstat.deck_id)
        owner = get_user_by_id(deck.owner).username
        visibility = deck.visibility
        name = deck.name
        deck_id=deck.deck_id
        description = deck.description
        average_score = deckstat.average_score
        rating=get_rating(deckstat.deck_id)
        times_reviewed = deckstat.times_reviewed
        last_reviewed = format_datetime(deckstat.last_reviewed)
        deck_obj = {'owner':owner,
        'visibility':visibility,
        'name':name,
        'description':description,
        'average_score':average_score,
        'rating':rating,
        'times_reviewed':times_reviewed,
        'last_reviewed':last_reviewed,
        'deck_id':deck_id,
        'number_of_cards':number_of_cards}
        if str(deck.owner) == str(user_id) or deck.visibility == "Public":
            deck_list.append(deck_obj)
    return deck_list

def countcards(deck_id):
    cards = db.session.query(Card).filter(Card.deck_id==deck_id).all()
    return len(cards)

def get_decks_for_user(user_id,purpose):
    if purpose == 'all':
        decks = db.session.query(Deck).filter(or_(Deck.owner==user_id,Deck.visibility=='Public')).all()
    elif purpose == 'restricted':
        decks = db.session.query(Deck).filter(Deck.owner==user_id).all()
    deck_list = []
    for deck in decks:
        deckstat = db.session.query(DeckStat).filter(DeckStat.deck_id==deck.deck_id).filter(DeckStat.user_id==user_id).first()
        if deckstat==None:
            last_reviewed='Never'
        else:
            last_reviewed=format_datetime(deckstat.last_reviewed)
        deck_id = deck.deck_id
        number_of_cards = countcards(deck.deck_id)
        owner = get_user_by_id(deck.owner).username
        visibility = deck.visibility
        name = deck.name
        description = deck.description
        deck_obj = {'owner':owner,
        'visibility':visibility,
        'name':name,
        'description':description,
        'number_of_cards':number_of_cards,
        'deck_id':deck_id,
        'last_reviewed':last_reviewed}
        deck_list.append(deck_obj)
    return deck_list

def get_cards_by_deck(deck_id):
    deck = db.session.query(Deck).filter(Deck.deck_id==deck_id).first()
    cards = db.session.query(Card).filter(Card.deck_id==deck_id).all()
    card_list = []
    deck_name = deck.name
    for card in cards:
        card_id = card.card_id
        question = card.question
        hint = card.hint
        answer = card.answer
        card_obj = {'deck_id':deck_id,
        'card_id':card_id,
        'question':question,
        'hint':hint,
        'answer':answer,
        'deck_name':deck_name
        }
        card_list.append(card_obj)
    return card_list

def add_card(card):
    cards = db.session.add(card)
    db.session.commit()

def delete_card(card_id):
    db.session.query(Card).filter(Card.card_id==card_id).delete()
    db.session.commit()

def add_deck(deck):
    db.session.add(deck)
    db.session.commit()

def update_deck(deck_id,deck_name,deck_description,deck_visibility):
    db.session.query(Deck).filter(Deck.deck_id==deck_id).update({"name":deck_name,"description":deck_description,'visibility':deck_visibility})
    db.session.commit()

def delete_deck(deck_id):
    delete_rating(deck_id)
    delete_deckstats(deck_id)
    for card in get_cards_by_deck(deck_id):
        delete_card(card['card_id'])
    db.session.query(Deck).filter(Deck.deck_id==deck_id).delete()
    db.session.commit()

def check_if_deck_owner(deck_id,user_id):
    deck = db.session.query(Deck).filter(Deck.deck_id==deck_id).first()
    if deck:
        print(deck.owner,user_id)
        if str(deck.owner) == str(user_id):
            return True,deck.name,deck.description,deck.visibility
        else:
            return False,deck.name,deck.description,deck.visibility
    else:
        return False,None

def load_decks_quiz_selector(user_id):
    deck_list = []
    temp_decks = db.session.query(Deck).all()
    for deck in temp_decks:
        count = countcards(deck.deck_id)
        if count > 0:
            deckstat = db.session.query(DeckStat).filter(DeckStat.deck_id==deck.deck_id).filter(DeckStat.user_id==user_id).first()
            if deckstat==None:
                last_reviewed='Never'
            else:
                last_reviewed=format_datetime(deckstat.last_reviewed)
            deck_id = deck.deck_id
            number_of_cards = count
            owner = get_user_by_id(deck.owner).username
            visibility = deck.visibility
            name = deck.name
            description = deck.description
            deck_obj = {'owner':owner,
            'visibility':visibility,
            'name':name,
            'description':description,
            'number_of_cards':number_of_cards,
            'deck_id':deck_id,
            'last_reviewed':last_reviewed}
            #print("deck is owned : ",str(deck.owner)==str(user_id))
            #print(deck.owner)
            #print(user_id)
            #print("deck is public : ",deck.visibility=='Public')
            if str(deck.owner) == str(user_id) or deck.visibility == 'Public':
                deck_list.append(deck_obj)
    return deck_list

def format_datetime(unix):
    return datetime.utcfromtimestamp(int(unix)).strftime('%Y-%m-%d %H:%M:%S')

def question_gen(deck_id):
    cards = db.session.query(Card).filter(Card.deck_id==deck_id).all()
    #deck = db.session.query(Deck).filter(Deck.deck_id==deck_id).first()
    questions_set = []
    with open('data/wordlist.txt') as f:
        W = f.readlines()
        n = len(W)
        index = 1
        for card in cards:
            options = [card.answer]
            while len(options) < 4:
                idx = random.randint(0,n-1)
                print(idx)
                if W[idx] not in options and W[idx]:
                    options.append(W[idx].capitalize())
            random.shuffle(options)
            question_obj = {
            'card_id':card.card_id,
            'deck_id':card.deck_id,
            'index':index,
            'question':card.question,
            'hint':card.hint,
            'options':options}
            questions_set.append(question_obj)
    return questions_set
            #print("options generated : ",options)

def export_deck(user_id,deck_id,endpoint):
    cards = db.session.query(Card).filter(Card.deck_id==deck_id).all()
    if len(cards) > 0:
        lines = []
        for card in cards:
            question = card.question
            hint = card.hint
            answer = card.answer
            lines.append("{},{},{}\n".format(question,hint,answer))
        with open("proc/{}.csv".format(endpoint), 'a+') as f:
            for line in lines:
                f.write(line)

def quick_quiz_score(deck_id,submission):
    cards = db.session.query(Card).filter(Card.deck_id==deck_id).all()
    number_of_cards = len(cards)
    correct_cards = 0
    for sub in submission:
        card_id = sub["card_id"]
        reply = sub["reply"]
        card = [card for card in cards if card.card_id == card_id][0]
        if card.answer == reply:
            correct_cards+=1
    return correct_cards,number_of_cards

def update_rating(deck_id,rating):
    rating_dict={"Easy":"0","Medium":"1","Hard":"2"}
    rating_val = rating_dict[rating]
    rating_obj = Rating(deck_id=deck_id,rating=rating_val)
    db.session.add(rating_obj)
    db.session.commit()

def delete_rating(deck_id):
    db.session.query(Rating).filter(Rating.deck_id==deck_id).delete()
    db.session.commit()

def get_rating(deck_id):
    ratings = db.session.query(Rating).filter(Rating.deck_id==deck_id).all()
    if len(ratings) == 0:
        return "No rating yet"
    rating_val = sum([int(rating.rating) for rating in ratings])/len(ratings)
    if rating_val<0.7:
        rating_string="Easy"
    elif rating_val<1.4:
        rating_string="Medium"
    else:
        rating_string="Hard"
    return rating_string

def update_deckstats(user_id,deck_id,last_reviewed,score):
    deckstat = db.session.query(DeckStat).filter(DeckStat.user_id==user_id).filter(DeckStat.deck_id==deck_id).first()
    if deckstat == None:
        new_deckstat = DeckStat(user_id=user_id,deck_id=deck_id,last_reviewed=str(last_reviewed),average_score=str(score),times_reviewed="1")
        db.session.add(new_deckstat)
        db.session.commit()
    else:
        new_average_score = str(round((float(deckstat.average_score)*int(deckstat.times_reviewed)+float(score))/(int(deckstat.times_reviewed)+1),2))
        new_times_reviewed = str(int(deckstat.times_reviewed)+1)
        db.session.query(DeckStat).filter(DeckStat.user_id==user_id).filter(DeckStat.deck_id==deck_id).update({'last_reviewed':last_reviewed,'average_score':new_average_score,'times_reviewed':new_times_reviewed})
        db.session.commit()

def delete_deckstats(deck_id):
    db.session.query(DeckStat).filter(DeckStat.deck_id==deck_id).delete()
    db.session.commit()

def update_participation(user_id):
    older = db.session.query(Participation).filter(Participation.user_id==user_id).first()
    now = str(int(time.time()))
    if older == None:
        participation = Participation(user_id=user_id,last_revised=now)
        db.session.add(participation)
        db.session.commit()
    else:
        db.session.query(Participation).filter(Participation.user_id==user_id).update({'last_revised':now})
        db.session.commit()

def to_remind(user_id):
    participation = db.session.query(Participation).filter(Participation.user_id==user_id).first()
    if participation == None:
        return true
    else:
        now = int(time.time())
        last_revised = int(participation.last_revised)
        if now-last_revised>=61200:
            return true
        else:
            return false

def get_all_user_ids():
    users = db.session.query(User).all()
    return users


import requests
import settings

def get_request(number):
    # Делаем запрос к api
    try:
        params_req = {"get" : "num", "format" : "json", "num" : number}
        req = requests.get(settings.url_api, params=params_req)
        res = req.json()
        return res["0"]["operator"]
    except Exception as e:
        print(e)
        return "no data"

def get_data_from_file():
    # Получаем данные из файла с номерами
    with open(settings.name_file, encoding="utf-8") as f:
        list_data = []
        for elem in f.readlines():
            list_data.append([elem.split(',')[0], elem.split(',')[1].replace('\n','')])
        return list_data

def check_data(data):
    # Проверяем совпадает ли данные из базы с госреестром
    count_true_data = 0
    count_error_data = 0
    result_list = []
    for elem in data:
        res = get_request(elem[0])
        flag_position = 0
        if res == elem[1]:
            count_true_data += 1
            flag_position = 1
        elif res == "no data":
            count_error_data += 1
            flag_position = 2
        result_list.append(",".join([elem[0], elem[1], res, str(flag_position)]))
    print(f"Checked phones: {len(data)} Is true: {count_true_data} Is false: {len(data) - count_true_data - count_error_data} Undefined: {count_error_data}")
    return result_list

def main():
    data_from_file = get_data_from_file()
    result = check_data(data_from_file)
    with open('result.txt', 'a+', encoding="utf-8") as f:
        for elem in result:
            f.write(elem + '\n')

if __name__ == "__main__":
    main()


"""
Problem: Given a matrix A, return the transpose of A. The transpose of a matrix is the matrix flipped over it's main diagonal, switching the row and column indices of the matrix.

Sample input:
    [[1,2,3],
     [4,5,6],
     [7,8,9]]

Sample output:
    [[1,4,7],
     [2,5,8],
     [3,6,9]]
"""

class Solution:
    def transpose(self, A: List[List[int]]) -> List[List[int]]:
        transpose=[]
        k=0
        for p in range(len(A[0])):
            temp=[]
            for i in A:
                while k<len(i):
                    temp.append(i[k])
                    break
            transpose.append(temp)
            k+=1
        return transpose


#Nested class

class Army():
  def __init__(self):
    self.name = 'Lucky'
    self.g = self.Gun()#inner class object
  def show(self):
    print("name:",self.name)

  class Gun:
    def __init__(self):
      self.name = 'AK-47'
      self.capacity = '75 Rounds'
      self.length = '100 cm'
    def disp(self):
      print("name :",self.name)
      print("capacity:",self.capacity)
      print("length :",self.length)

a = Army()
a.show()
g = Army().Gun()#object of nested class
g.disp()
print(a.g.name)#Accecing member


import sys
f = sys.stdin
#f = open("input.txt", "r")
a = f.readline().strip()
prev = a[0]
count = 0
k = 0
for i in a[1:]:
    if i != prev:
        count += 1
        k = 0
    elif i == prev and k < 4:
        k += 1
    elif i == prev and k == 4:
        count += 1
        k = 0
    prev = i
print(count+1)


# (c) 2021 Okawo
# This code is licensed under MIT license (see LICENSE for details)

import logging
import os
import stat
import enum

class log_levels(enum.Enum):
    DEBUG = logging.DEBUG
    INFO = logging.INFO
    WARNING = logging.WARNING
    ERROR = logging.ERROR
    CRITICAL = logging.CRITICAL

def setup_custom_logger(
    name,
    level,
    file,
    format="[%(asctime)s] %(name)s %(levelname)s - %(message)s",
    console_logging=False,
):
    logger = logging.getLogger(name)
    logger.setLevel(level)

    log_path = os.path.normpath(os.path.join(os.path.dirname(__file__), file))
    dir_p = os.path.dirname(log_path)
    if not os.path.exists(dir_p):
        os.mkdir(dir_p)

    # logging entry format
    logger_formatter = logging.Formatter(
        "[%(asctime)s] %(name)s %(levelname)s - %(message)s"
    )

    # if i don't this bullshit, it creates root owned log files in some cases
    os.chmod(dir_p, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
    if not os.path.exists(log_path):
        with open(log_path, 'wt') as f:
            f.write('fuck you')
    os.chmod(log_path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)

    logger_handler = logging.FileHandler(log_path)
    logger_handler.setLevel(level)
    logger_handler.setFormatter(logger_formatter)
    logger.addHandler(logger_handler)

    if console_logging:
        console_logger_handler = logging.StreamHandler()
        console_logger_handler.setLevel(level)
        console_logger_handler.setFormatter(logger_formatter)
        logger.addHandler(console_logger_handler)

    return logger


#!/usr/bin/env python3
""" AirBnB Console """
import cmd
import sys
import models
from models.engine.file_storage import FileStorage
from models.user import User
from datetime import datetime
from models.city import City
from models.state import State
from models.amenity import Amenity
from models.place import Place
from models.review import Review
from models import storage
from models.base_model import BaseModel

class HBNBCommand(cmd.Cmd):
    """ class to read a command """
    prompt = '(hbnb) '
    classes = {"BaseModel": BaseModel, "User": User,
               "Place": Place, "State": State, "City": City,
               "Amenity": Amenity, "Review": Review}

    def emptyline(self):
        """ in case there is an empty line """
        pass

    def do_quit(self, args):
        """ quit command """
        return (True)

    def do_EOF(self, args):
        """ EOF command """
        print()
        return (True)

    def do_create(self, args):
        """ creates a new instance of base models """
        if not args:
            print("** class name missing **")
            return
        elif args in HBNBCommand.classes:
                new_inst = HBNBCommand.classes.get(args)()
                new_inst.save()
                print("{}".format(new_inst.id))
        else:
            print("** class doesn't exist **")

    def do_show(self, args):
        """ prints the string representation of an instance """
        tok = args.split()
        try:
            if len(tok) == 0:
                print("** class name missing **")
                return
            if tok[0] in HBNBCommand.classes:
                if len(tok) > 1:
                    key = tok[0] + "." + tok[1]
                    if key in models.storage.all():
                        print(models.storage.all()[key])
                    else:
                        print("** no instance found **")
                else:
                    print("** instance id missing **")
            else:
                print("** class doesn't exist **")
        except:
            print("** instance id missing **")

    def do_destroy(self, args):
        """ deletes an instance """
        tok = args.split(" ")
        if not args:
            print("** class name missing **")
            return
        elif tok[0] not in HBNBCommand.classes:
            print("** class doesn't exist **")
            return
        elif len(tok) == 1:
            print("** instance id missing **")
            return
        else:
            try:
                key = tok[0] + "." + tok[1]
                storage.all().pop(key)
                storage.save()
            except:
                print("** no instance found **")

    def do_all(self, args):
        """ prints string representation off all instnaces """
        inst = []
        if not args:
            inst = list(storage.all().values())
            print(list(map(lambda x: str(x), inst)))
        elif args in HBNBCommand.classes:
            inst = list(storage.all().values())
            inst = filter(lambda x: type(x) is
                          HBNBCommand.classes.get(args), inst)
            print(list(map(lambda x: str(x), inst)))
        else:
            print("** class doesn't exist **")

    def do_update(self, args):
        """ will update an instance """
        tok = args.split(" ")
        if not args:
            print("** class name missing **")
        elif tok[0] not in HBNBCommand.classes:
            print("** class doesn't exist **")
        elif len(tok) == 1:
            print("** instance id missing **")
        elif ("{}.{}".format(tok[0], tok[1])
              not in storage.all().keys()):
            print("** no instance found **")
        elif len(tok) == 2:
            print("** attribute name missing **")
        elif len(tok) == 3:
            print("** value missing **")
        else:
            inst = storage.all()
            key = "{}.{}".format(tok[0], tok[1])
            if key in inst.keys():
                atr = getattr(inst[key], tok[2], "")
                setattr(inst[key], tok[2], type(atr)(tok[3]))
                inst[key].save()

if __name__ == '__main__':
    HBNBCommand().cmdloop()


n = int(input())
stars = [[" " for _ in range(n + n)] for i in range(n)]

def chopStar(top, bottom, center):
    height = bottom - top
    if height > 3:
        temp = height // 2
        chopStar(top, top + temp, center)
        chopStar(top + temp, bottom, center - temp)
        chopStar(top + temp, bottom, center + temp)
        return
    stars[top][center] = "*"
    stars[top + 1][center - 1] = "*"
    stars[top + 1][center + 1] = "*"
    for i in range(3):
        stars[top + 2][center + i] = "*"
        stars[top + 2][center - i] = "*"
    return

chopStar(0, n, n - 1)

for row in stars:
    print(''.join(row))