import logging
import discord
from discord import app_commands, Interaction
from discord.app_commands import Range
from discord.ext import commands

from handler.context import Context
from handler.view import DropdownView
from pepebot import PepeBot

from datetime import timedelta

import typing
from typing import Optional, Union, Literal

from handler.utils import (
    string_to_delta,
    get_place_holders,
    get_key_pair,
    to_string_list, get_relative_time, records_to_dict)
from handler.database import (
    reinitialise_channel_settings,
    reinitialise_guild_settings,
    get_channel_settings,
    get_guild_settings)

_log = logging.getLogger("pepebot")

async def setup(bot: PepeBot) -> None:
    await bot.add_cog(Configuration(bot))

class Configuration(commands.Cog):
    def __init__(self, bot: PepeBot) -> None:
        self.bot = bot
        self.Database = bot.database

    async def set_channel_settings(self, guild_id, channel_id, **options):
        """
        Edit channel settings that is stored in the database
        """
        options_names = list(options.keys())
        options_names.insert(0, "guild_id")
        options_names.insert(1, "channel_id")
        names_str_list = to_string_list(options_names)
        placeholders_str = get_place_holders(len(options_names))
        options_names.remove("guild_id")
        options_names.remove("channel_id")
        key_value_str = get_key_pair(options_names, 3)
        values = list(options.values())
        values.insert(0, guild_id)
        values.insert(1, channel_id)

        await self.Database.insert(
            *values,
            table="peep.channel_settings",
            columns=names_str_list,
            values=placeholders_str,
            on_conflicts=
            "(guild_id,channel_id) DO UPDATE SET "
            f"{key_value_str}")
        # insert new settings to the guild cache,
        cache_new_settings = reinitialise_channel_settings(
            bot=self.bot, channel_id=channel_id, guild_id=guild_id)
        self.bot.loop.create_task(cache_new_settings)

    async def set_guild_settings(self, guild_id, **options):
        """ change guild settings from the database"""
        options_names = list(options.keys())
        options_names.insert(0, "guild_id")
        names_str_list = to_string_list(options_names)
        placeholders = get_place_holders(len(options_names))
        values = list(options.values())
        values.insert(0, guild_id)
        options_names.remove("guild_id")
        key_value_str = get_key_pair(options_names, 2)
        await self.bot.database.insert(
            *values,
            table="peep.guild_settings",
            columns=f"{names_str_list}",
            values=placeholders,
            on_conflicts=
            "(guild_id) DO UPDATE SET "
            f"{key_value_str}")
        cache_new_settings = reinitialise_guild_settings(
            bot=self.bot, guild_id=guild_id)
        self.bot.loop.create_task(cache_new_settings)

    def get_valid_emoji_string(self, string: str) -> Optional[str]:
        custom = string.replace(" ", "")
        firstEmoji = custom[0]
        # it is a default emoji
        if self.bot.emoji.is_default_emoji(custom):
            emoji = firstEmoji
        else:
            # check if given emoji emoji is custom emoji and is usable
            emoji_id = self.bot.emoji.get_emoji_id(custom)
            custom_emoji = None
            if emoji_id:
                custom_emoji = self.bot.get_emoji(int(emoji_id))
            if custom_emoji is None or not custom_emoji.is_usable():
                return None
            emoji = custom_emoji
        return emoji

    setup = app_commands.Group(
        name='setup',
        description='some configuration commands related to memes',
        guild_only=True,
        default_permissions=discord.Permissions(administrator=True)
    )

    @setup.command(name="guild_settings")
    @app_commands.checks.has_permissions(manage_guild=True)
    async def edit_guild_settings(
            self, interaction: discord.Interaction,
            option: Literal["Auto Thread", "Meme listener",
                            "Oc Listener", 'economy'],
            turn: Literal['on', 'off']
    ):

        """
         configuration commands related to server.

        Parameters
        ----------
        option: :class:`typing.Literal`
            choose a setting that you want to change
        turn: typing.Literal
            turn on or turn off setting
        """

        # change the str value to boolean value
        turned = True if turn == 'on' else False
        settings_map = {
            "Auto Thread": "thread_lstnr",
            "Meme listener": "reaction_lstnr",
            "Oc Listener": "oc_lstnr",
            "economy": "economy"
        }
        setting = settings_map[option]
        await self.set_guild_settings(
            guild_id=interaction.guild.id, **{setting: turned})

        embed = discord.Embed()
        embed.title = "``config update``"
        embed.description = (f">>> {self.bot.emoji.right} {option}"
                             f" has been turned ``{turn}``")
        await interaction.response.send_message(
            embed=embed, ephemeral=True)

    @setup.command(name="channel_settings")
    @app_commands.checks.has_permissions(manage_guild=True)
    async def edit_channel_settings(
            self, interaction: Interaction, channel: discord.TextChannel,
            option: typing.Literal["Auto thread", "Meme listener",
                                   "Oc listener"],
            turn: typing.Literal["on", "off"]
    ):

        """
        change channel related settings.

        Parameters
        ----------
        channel:
            channel that you want change settings
        option:
            what settings you want to change
        turn:
            turn on or off setting
        """
        embed = discord.Embed(title="``updated Config``")
        permission = channel.permissions_for(interaction.guild.me)

        bot_required_permissions = [
            permission.manage_messages, permission.view_channel,
            permission.read_messages, permission.add_reactions]
        permission_string = [
            "manage_messages", "view_channel",
            "read_messages", "add_reactions"]
        if not all(bot_required_permissions):
            embed.title = f"**invalid permissions**"
            embed.description = (
                f">>> {self.bot.emoji.right} bot missing following "
                f"permissions in {channel.mention} channel"
                f":{channel.mention} \n"
                f" \n".join(permission_string)
            )
            await interaction.response.send_message(
                embed=embed, ephemeral=True)
            return
        turned = True if turn == "on" else False
        settings_map = {
            "Auto thread": "is_threadChannel",
            "Meme listener": "is_memeChannel",
            "Oc listener": "is_ocChannel"
        }
        setting = settings_map[option]
        await self.set_channel_settings(
            channel_id=channel.id, guild_id=channel.guild.id,
            **{setting: turned})
        embed.title = "**updated config**"
        embed.description = (
            f">>> {option} for {channel.mention} has been turned {turn}")
        await interaction.response.send_message(
            embed=embed, ephemeral=True)

    @setup.command(name="edit_meme_channel")
    @app_commands.checks.has_permissions(manage_guild=True)
    async def set_meme_channel_settings(
            self, interaction: Interaction,
            meme_channel: discord.TextChannel, duration: Optional[str] = None,
            max_likes: Optional[int] = None,
            next_gallery_channel: Optional[discord.TextChannel] = None
    ):

        """
        configure meme channel related settings.

        Parameters
        ----------
        meme_channel:
            the meme channel that you want to configure
        duration:
            example:1d,1s,1w etc. and 0 for no time limit.
        max_likes:
            maximum likes needed to go to next gallery
        next_gallery_channel:
            the channel where memes will be moved
            when reaching max likes
        """
        response = interaction.response
        embed = discord.Embed(title=f"``command failed``")
        # get configurations realted to the channel
        channel_settings = await get_channel_settings(
            bot=self.bot, channel=meme_channel)
        # send a error message if given channel is not a meme channel
        if channel_settings is None or not channel_settings["is_memechannel"]:
            embed.description = (
                f">>> {self.bot.emoji.right} {meme_channel.mention} channel"
                f" is not a meme channel, pls run command ``/config "
                f"{meme_channel.name}`` to check if the channel is a "
                f"meme channel or ``/serverconfig`` to see list of "
                f"meme channels in the gild "
            )
            await response.send_message(embed=embed, ephemeral=True)
            return
        delta_time = 'NULL' if duration == '0' else duration
        if delta_time is not None and duration != '0':
            try:
                delta_time = string_to_delta(duration)
            except Exception as error:
                embed.description = (
                    f">>> {self.bot.emoji.right} the time ``{duration}`` is "
                    f"invalid it must be in formate of"
                    f"```1(h|hr|hour|hours) \n 1(m|min|minute) \n"
                    f" 1(s|second|secs)``` "
                )
                await response.send_message(embed=embed, ephemeral=True)
                _log.error(error)
                return
        args = {}
        if max_likes is not None:
            if max_likes <= 1:
                embed.description = (
                    f">>> {self.bot.emoji.right} max likes must be greater than 1")
                await response.send_message(embed=embed, ephemeral=True)
                return
            elif max_likes > 1:
                args["max_like"] = max_likes
        if (next_gallery_channel is not None and
                next_gallery_channel.id == interaction.channel_id):
            embed.description = "the channel is not valid choose another " \
                                "channel "
            await response.send_message(embed=embed)
        if delta_time is not None:
            args["voting_time"] = delta_time if delta_time != 'NULL' else None

        if next_gallery_channel is not None:
            args["Nextlvl"] = next_gallery_channel.id

        await self.set_channel_settings(
            interaction.guild_id, interaction.channel_id, **args)

        embed.title = f"``updated config``"
        is_delta_time = isinstance(delta_time, timedelta)
        is_next_gallery_channel = "none"
        if next_gallery_channel is not None:
            is_next_gallery_channel = next_gallery_channel.mention
        human_relative_time = "none"
        if is_delta_time:
            human_relative_time = get_relative_time(delta_time)
        embed.description = f"""
        >>> {self.bot.emoji.right} **channel**:{meme_channel.mention}
        **maximum likes**: {max_likes}
        **gallery**: {is_next_gallery_channel}
        **time limit**: {human_relative_time}
        """
        await response.send_message(embed=embed)

    @app_commands.checks.has_permissions(administrator=True)
    @app_commands.checks.bot_has_permissions(manage_roles=True)
    @setup.command(name="meme_admin_role")
    async def add_meme_admin_role(
            self, interaction: Interaction, role: discord.Role,
            remove: Literal['true'] = None
    ):
        """
        add a role to manage economy related commands.

        Parameters
        ----------
        role:
            role that you wanted to manage commands
        """
        response = interaction.response
        embed = discord.Embed()
        is_owner = await self.bot.is_owner(interaction.user)
        if not is_owner and interaction.guild.owner_id != interaction.user.id:
            embed.title = "``command failed!``"
            embed.description = "only owner can execute this command"
            await interaction.response.send_message(
                embed=embed, ephemeral=True)
            return
        role_id = None if remove == 'true' else role.id
        await self.set_guild_settings(
            guild_id=interaction.guild.id, MemeAdmin=role_id)
        embed.title = f"``role {'added' if role_id else 'removed'}``"
        embed.description = (
            f">>> {self.bot.emoji.right} now members with "
            f"{role.mention} will be able to execute "
            f"all meme and economy commands")
        if role_id is None:
            embed.description = (
                f">>> {self.bot.emoji.right} meme admin role has been removed, "
                f"economy and meme commands are now owner only")
        await response.send_message(embed=embed, ephemeral=True)

    @app_commands.checks.has_permissions(administrator=True)
    @setup.command(name="like")
    async def setup_like_emoji(
            self, interaction: Interaction, meme_channel: discord.TextChannel,
            custom_emoji: str = None, setdefault: Literal['true'] = None,
            remove: Literal['true'] = None
    ):
        """
        settings related bot reactions

        Parameters
        ----------
        custom_emoji:
            specify the emoji you want the bot to react with
        setdefault:
            change the reaction emoji to default one that bot use
        remove:
            stop the bot to react like emoji on memes
        """
        response = interaction.response
        embed = discord.Embed(title="``command failed``")
        emoji = ""
        channel_settings = await get_channel_settings(
            bot=self.bot, channel=meme_channel)

        if (channel_settings is None or
                not channel_settings["is_memechannel"]):
            embed.description = (
                f">>> {self.bot.emoji.right} channel {meme_channel.mention} is "
                f"not a meme channel please run commands ``/channelconfig`` to "
                f"see list of meme channels")
            await response.send_message(embed=embed, ephemeral=True)
            return

        if not any([custom_emoji, setdefault, remove]):
            embed.description = (
                f">>> {self.bot.emoji.right}"
                f" you must specify at least one setting")
            await response.send_message(embed=embed, ephemeral=True)
            return

        if custom_emoji is not None:
            emoji = self.get_valid_emoji_string(string=custom_emoji)
            if emoji is None:
                embed.description = (
                    f">>> {self.bot.emoji.right}"
                    f"given emoji is not a valid emoji :``{custom_emoji}``")
                await response.send_message(embed=embed, ephemeral=True)
                return
            elif custom_emoji == channel_settings["dislike_emoji"]:
                embed.description = (
                    f">>> {self.bot.right} "
                    f"like emoji should not be same as dislike emoji"
                )
                await response.send_message(
                    embed=embed, ephemeral=True
                )
                return
        elif setdefault is not None:
            emoji = self.bot.emoji.like
            custom_emoji = emoji
        elif remove is not None:
            emoji = '0'
        await self.set_channel_settings(
            guild_id=interaction.guild_id, channel_id=meme_channel.id,
            like_emoji=str(emoji))
        embed.title = "``updated config``"
        embed.description = f"now bot will react to {custom_emoji}"
        if emoji == '0':
            embed.description = f"now bot will not able to like messages"
        await response.send_message(embed=embed, ephemeral=True)

    @app_commands.checks.has_permissions(administrator=True)
    @setup.command(name="dislike")
    async def setup_dislike_emoji(
            self, interaction: Interaction, meme_channel: discord.TextChannel,
            custom_emoji: str = None, setdefault: Literal['true'] = None,
            remove: Literal['true'] = None
    ):
        """
        settings related bot reactions

        Parameters
        ----------
        custom_emoji:
            specify the emoji you want the bot to react with
        setdefault:
            change the reaction emoji to default one that bot use
        remove:
            stop the bot to react like emoji on memes
        """

        response = interaction.response
        embed = discord.Embed(title="``command failed``")
        emoji = ""
        channel_settings = await get_channel_settings(
            bot=self.bot, channel=meme_channel)

        if (channel_settings is None or
                not channel_settings["is_memechannel"]):
            embed.description = (
                f">>> {self.bot.emoji.right} channel {meme_channel.mention} is "
                f"not a meme channel")
            await response.send_message(embed=embed, ephemeral=True)
            return

        if not any([custom_emoji, setdefault, remove]):
            embed.description = (
                f">>> {self.bot.emoji.right}"
                f" you must specify at least one setting")
            await response.send_message(embed=embed, ephemeral=True)
            return

        if custom_emoji is not None:
            emoji = self.get_valid_emoji_string(string=custom_emoji)
            if emoji is None:
                embed.description = (
                    f">>> {self.bot.emoji.right}"
                    f" given emoji is not a valid emoji :``{custom_emoji}``")
                await response.send_message(embed=embed, ephemeral=True)
                return
            elif custom_emoji == channel_settings["like_emoji"]:
                embed.description = (
                    f">>> {self.bot.right} "
                    f"dislike emoji should not be same as like emoji"
                )
                await response.send_message(
                    embed= embed, ephemeral= True
                )
                return
        elif setdefault is not None:
            emoji = self.bot.emoji.like
            custom_emoji = emoji
        elif remove is not None:
            emoji = '0'
        await self.set_channel_settings(
            guild_id=interaction.guild_id, channel_id=meme_channel.id,
            like_emoji=str(emoji))
        embed.title = "``updated config``"
        embed.description = f"now bot will react to {custom_emoji}"
        if emoji == '0':
            embed.description = f"now bot will not able to dislike messages"

        await response.send_message(embed=embed, ephemeral=True)

    @app_commands.checks.has_permissions(administrator=True)
    @setup.command(name="reward_role")
    async def setup_reward_role(
            self, interaction: discord.Interaction, at_likes: Range[int, 1],
            give_role: discord.Role
    ):
        """
        setup reward role for specific likes

        Parameters
        ----------
        at_likes
            at what like levels will user be given the role
        give_role
            the role that will be given to user
        """
        embed = discord.Embed(title="``command failed``")
        if not give_role.is_assignable():
            embed.description = (
                f">>> {self.bot.emoji.right} the bot can't"
                f"assign this role, whether the role is above "
                f"bot role or bot lack permissions to assign it")
            await interaction.response.send_message(embed=embed)
            return

        insert_role = self.bot.database.insert(
            interaction.guild_id,
            at_likes,
            give_role.id,
            table="peep.rolerewards",
            columns="guild_id,likes,role_id",
            values="$1,$2,$3",
            on_conflicts="(guild_id,likes) DO UPDATE SET "
                         "role_id = $3")
        embed.title = "``role added!``"
        embed.description = (
            f"{give_role.mention} will be assigned to user after "
            f"reaching ``{at_likes}`` {self.bot.emoji.like}"
        )
        await interaction.response.send_message(embed=embed)
        self.bot.loop.create_task(insert_role)

    @setup.command(name="logging")
    @app_commands.checks.has_permissions(administrator=True)
    async def configure_logging(
            self, interaction: discord.Interaction,
            shop_log: discord.TextChannel,
            remove: Literal['true'] = None,
            dm_member_on_accept: Literal['true', 'false'] = 'false',
    ):
        """
        setup logging for the purchased items by the user.

        Parameters
        ----------
        shop_log:
            the channel that you want the bot to send messages in
        dm_member_on_accept:
            whether dm user after accepting the item or not
        """

        bot_reactions = shop_log.permissions_for(shop_log.guild.me)
        required_permission = [
            bot_reactions.send_messages,
            bot_reactions.view_channel,
            bot_reactions.read_message_history,
        ]
        embed = discord.Embed(title="``command failed``")
        if not any(required_permission):
            embed.description = (
                f"the bot missing permission in channel "
                f"{shop_log.mention} check if bot has following permissions "
                f"in the channel\n"
                f"[read messages, send messages,read message history]")
            await interaction.response.send_message(embed=embed)
            return
        shop_log_channel = shop_log.id
        guild_settings = await get_guild_settings(
            bot=self.bot, guild_id=interaction.guild_id)
        if remove == 'true':
            if guild_settings["shoplog"] is None:
                embed.description = "there is already no shop log channel"
                await interaction.response.send_message(embed=embed)
                return
            shop_log_channel = None

        dm_member_on_accept = True if dm_member_on_accept == 'true' else False
        await self.set_guild_settings(
            guild_id=interaction.guild_id,
            shoplog=shop_log_channel,
            dm_on_accept=dm_member_on_accept
        )
        embed.title = "``logging configuration``"
        embed.description = (
            f">>> {self.bot.emoji.right}"
            f"bot will now send item logs to channel {shop_log.mention} "
        )
        if remove == 'true':
            embed.description = f">>> {self.bot.emoji.right}" \
                                f"logging channel has been removed"
        await interaction.response.send_message(
            embed=embed)

    @setup.command(name="thread_msg")
    @app_commands.checks.has_permissions(administrator=True)
    async def set_thread_message(
            self, interaction: discord.Interaction,
            thread_channel: discord.TextChannel,
            thread_message: app_commands.Range[str, 1, 500],
            set_default: Literal['true'] = None):

        channel_settings = await get_channel_settings(
            bot=self.bot, channel=thread_channel)
        embed = discord.Embed(title="command failed")
        if channel_settings is None or not channel_settings["is_threadchannel"]:
            embed.description = (
                f"Channel {thread_channel} is not a thread channel"
                f"please run ``/channelconfig`` to see list of channels "
                f"in the guild ")
            await interaction.response.send_message(embed=embed)
            return

        if set_default is not None:
            thread_message = None

        await self.edit_channel_settings(
            channel=interaction.channel,
            thread_msg=thread_message
        )
        embed.title = "``config update``"
        embed.description = (
            f"thread message for the channel :{interaction.channel.mention}"
            f"has been updated to message: ``{thread_message}``"
        )
        await interaction.response.send_message(
            embed=embed
        )

    @setup.command(name="prefix")
    @app_commands.checks.has_permissions(administrator=True)
    async def setup_prefix(
        self, interaction:discord.Interaction, prefix: app_commands.Range[str,0,10],
        set_default: Literal['true'] = None
    ):
        await self.set_guild_settings(
            guild_id=interaction.guild_id,
            prefix=prefix if not set_default else '$')
        if set_default == 'true':
            prefix = '$'
        embed = discord.Embed()
        embed.description = (
            f""">>> {self.bot.right} bot prefix has changed to {prefix}"""
        )
        await interaction.response.send_message(
            embed=embed
        )

    youtube = app_commands.Group(
        name='youtube',
        description='configuration commands related to youtube',
        guild_only=True,
        default_permissions=discord.Permissions(administrator=True)
    )

    @youtube.command(name="subscribe")
    @app_commands.checks.has_permissions(administrator=True)
    async def setup_youtube(
        self, interaction: discord.Interaction, youtube_channel: str,
        send_to: discord.TextChannel = None
    ):

        """
        set up a notification channel for youtube video.

        Parameters
        ----------
        youtube_channel:
            the YouTube channel that you want to get notification for, can be a
            link or username
        send_to:
            provide the channel where you want the notification to send to
        """

        channel_id = self.bot.youtube.get_id_from_url(
            url=youtube_channel)
        channel_username = self.bot.youtube.get_username_from_url(
            url=youtube_channel)
        embed = discord.Embed(title="``command failed``")
        await interaction.response.defer(thinking=True, ephemeral=True)

        subscribed_channels = await self.bot.youtube.get_subscribed_for_guilds(
            guild_id=interaction.guild_id
        )

        if subscribed_channels is not None:
            embed.description = (
                "subscribing to multiple channel is currently not available. "
                "to subscribe to new channel you must remove older one. "
                "for that please run command "
                "``/setup youtube_notification remove:true`` "
            )
            await interaction.followup.send(embed=embed)
            return

        if not channel_id and not channel_username:
            channel_username = youtube_channel
            channel_id = await self.bot.youtube.get_channel_id(
                channel_username)
            if channel_id is None:
                embed.description = (
                    f">>> {self.bot.emoji.right} channel url/username ``{youtube_channel}`` is invalid "
                    "please provide any of following types: \n"
                    "``yourusername`` \n"
                    "``https://youtube.com/c/yourcustomusername`` \n"
                    "``https://youtube.com/@yourusername`` \n"
                    "``https://youtube.com/user/yourusername`` \n"
                    "``https://youtube.com/channel/yourchannelid`` \n"
                    " for more info please visit [this link]"
                    "(https://support.google.com/youtube/answer/6180214?hl=en)"
                )
                await interaction.followup.send(
                    embed=embed)
                return
        elif channel_id is None and channel_username:
            channel_id = await self.bot.youtube.get_channel_id(
                channel_username)
            if channel_id is None:
                embed.description = "the url or username you provided is " \
                                    "invalid "
                await interaction.followup.send(
                    embed=embed)
                return

        if send_to is not None:
            # check permissions for the log channel
            channel_permissions = send_to.permissions_for(interaction.guild.me)
            required_permissions = [
                channel_permissions.send_messages,
                channel_permissions.embed_links,
                channel_permissions.view_channel]
            if not all(required_permissions):
                embed.description = (
                    f"bot missing permission in channel: ``{send_to.mention}`` : \n"
                    f"make sure bot has following permissions in the channel: "
                    f"``send message,send embed links, view channel``")
                await interaction.followup.send(embed=embed)
                return
            await self.set_guild_settings(
                guild_id=interaction.guild_id, upload_channel=send_to.id
            )
        await self.bot.youtube.subscribe_to_channel(
            channel_id=channel_id,
            guild_id=interaction.guild_id,
            upload_id=None
        )
        embed.title = "``subscribed``"
        embed.description = (
            f">>> {self.bot.emoji.right} "
            f"subscribed to channel ``{channel_username}``\n")
        await interaction.followup.send(embed=embed)

    @youtube.command(name="unsubscribe")
    async def youtube_unsubscribe(
        self, interaction: discord.Interaction, channel: Literal['all']
    ):
        """
        unsubscribe to channel.
        """
        await interaction.response.defer(thinking=True, ephemeral=True)
        embed = discord.Embed(title="command failed")
        subscribed_channels = await self.bot.youtube.get_subscribed_for_guilds(
            guild_id=interaction.guild_id
        )
        if subscribed_channels is None:
            embed.description = "you are already not subscribed to any channel"
            await interaction.followup.send(
                embed=embed, ephemeral=True)
            return
        await self.bot.youtube.unsubscribe_to_channel(
            guild_id=interaction.guild_id
        )
        embed.title = None
        embed.description = (
            f""">>> {self.bot.emoji.right} unsubscribed to {channel} channels""")
        await interaction.followup.send(embed=embed, ephemeral=True)

    @youtube.command(name="notification_channel")
    async def youtube_notification_channel(
        self, interaction: discord.Interaction, channel: discord.TextChannel
    ):
        """
        setup a youtube notification channel.

        Parameters
        ----------
        channel:
            the channel you want to set to notification channel.
        """
        channel_permissions = channel.permissions_for(interaction.guild.me)
        required_permissions = [
            channel_permissions.view_channel,
            channel_permissions.send_messages,
            channel_permissions.embed_links,
        ]
        embed = discord.Embed(title="``command failed``")
        if not all(required_permissions):
            embed.description = (
                f">>> {self.bot.emoji.right}"
                f"bot missing any of following permissions in the channel"
                f"{channel.mention} : \n"
                f"``view channel, send message, embed links`` "
            )
            await interaction.response.send_message(
                embed=embed,ephemeral=True
            )
            return
        await self.set_guild_settings(
            guild_id=interaction.guild_id, upload_channel=channel.id)

        embed.title = None
        embed.description = (
            f">>> "
            f"{self.bot.emoji.right} all youtube channel "
            f"notifications will send to "
            f"{channel.mention} channel")
        await interaction.response.send_message(
            embed=embed, ephemeral=True
        )

    @youtube.command(name="upload_ping")
    async def youtube_upload_ping(
        self, interaction: discord.Interaction, role: discord.Role,
            remove: Literal['true'] = None):
        """
        setup a ping role for notifications.

        Parameters
        ----------
        role:
            the role that you want to set to upload ping role.
        remove:
            remove already set ping roles.
        """
        embed = discord.Embed()
        await interaction.response.defer(thinking=True, ephemeral=True)
        if remove == 'true':
            await self.set_guild_settings(
                guild_id=interaction.guild_id,
                upload_ping=None
            )
            embed.title = None
            embed.description = f">>> {self.bot.emoji.right} removed ping role"
            await interaction.followup.send(embed=embed)
            return
        await self.set_guild_settings(
            guild_id=interaction.guild_id,
            upload_ping=role.id
        )
        embed.title = None
        embed.description = f">>> {self.bot.emoji.right} " \
                            f"role {role.mention} will be pinged " \
                            f"in youtube notification"
        await interaction.followup.send(embed=embed, ephemeral=True)

    @youtube.command(name='help')
    async def youtube_help(self, interaction: discord.Interaction):
        """
        get help related to setting up youtube notification
        """
        embed = discord.Embed(title=f"{self.bot.emoji.discord_emoji} youtube help")
        embed.description = (
            f"{self.bot.right} run following commands to setup "
            f"youtube notification \n \n"
            f">>> {self.bot.slash} **youtube subscribe ``channel:``youtube "
            f"channel:** "
            f"subscribe to a channel, you can only subscribe one channel per "
            f"guild \n "
            f"{self.bot.slash} **youtube notification_channel ``channel``: "
            f"text channel :** "
            f" the channel where you want to get the notification to send "
            f"into \n "
            f"{self.bot.slash} **youtube upload_ping ``role``: a role:**"
            f"if you want a role to pinged when received upload notification \n"
            f"{self.bot.slash} **youtube unsubscribe**: unsubscribe to "
            f"youtube notifications "
        )
        await interaction.response.send_message(embed=embed, ephemeral=True)

    @setup.command(name="help")
    async def send_test(self, interaction: Interaction):
        """
        get helps related to setup commands
        """
        view = DropdownView()
        embed = discord.Embed(title=f"{self.bot.emoji.discord_emoji} Bot help")
        embed.description = (
            "To view the setup instructions for a particular setting,"
            " simply select the one of the option from the dropdown menu."
        )
        await interaction.response.send_message(
            embed=embed, view=view)


import cv2
import numpy as np

cap = cv2.VideoCapture(0)

while(1):

    # Take each frame
    _, frame = cap.read()
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    lower_red = np.array([30,150,50])
    upper_red = np.array([255,255,180])

    mask = cv2.inRange(hsv, lower_red, upper_red)
    res = cv2.bitwise_and(frame,frame, mask= mask)

    laplacian = cv2.Laplacian(frame,cv2.CV_64F)
    sobelx = cv2.Sobel(frame,cv2.CV_64F,1,0,ksize=5)
    sobely = cv2.Sobel(frame,cv2.CV_64F,0,1,ksize=5)

    cv2.imshow('Original',frame)
    cv2.imshow('Mask',mask)
    cv2.imshow('laplacian',laplacian)
    cv2.imshow('sobelx',sobelx)
    cv2.imshow('sobely',sobely)

    if cv2.waitKey(5) & 0xFF == ord('q'):

        break
cv2.imwrite('soblex.png', sobelx)
cv2.imwrite('laplacian.png' , laplacian)
cv2.imwrite('sobley.png', sobely)
cv2.imwrite('frame.png', frame)

cv2.destroyAllWindows()
cap.release()


from simpful import *
import matplotlib.pylab as plt
from numpy import linspace, array

FS = FuzzySystem()

S_1 = FuzzySet(function=Triangular_MF(a=0, b=0, c=5), term="poor")
S_2 = FuzzySet(function=Triangular_MF(a=0, b=5, c=10), term="good")
S_3 = FuzzySet(function=Triangular_MF(a=5, b=10, c=10), term="excellent")
FS.add_linguistic_variable("Service", LinguisticVariable([S_1, S_2, S_3], concept="Service quality", universe_of_discourse=[0,10]))

F_1 = FuzzySet(function=Triangular_MF(a=0, b=0, c=10), term="rancid")
F_2 = FuzzySet(function=Triangular_MF(a=0, b=10, c=10), term="delicious")
FS.add_linguistic_variable("Food", LinguisticVariable([F_1, F_2], concept="Food quality", universe_of_discourse=[0,10]))

T_1 = FuzzySet(function=Triangular_MF(a=0, b=0, c=10), term="small")
T_2 = FuzzySet(function=Triangular_MF(a=0, b=10, c=20), term="average")
T_3 = FuzzySet(function=Trapezoidal_MF(a=10, b=20, c=25, d=25), term="generous")
FS.add_linguistic_variable("Tip", LinguisticVariable([T_1, T_2, T_3], universe_of_discourse=[0,25]))

R1 = "IF (Service IS poor) OR (Food IS rancid) THEN (Tip IS small)"
R2 = "IF (Service IS good) THEN (Tip IS average)"
R3 = "IF (Service IS excellent) OR (Food IS delicious) THEN (Tip IS generous)"
FS.add_rules([R1, R2, R3])

# Plotting surface
xs = []
ys = []
zs = []
DIVs = 20
for x in linspace(0,10,DIVs):
    for y in linspace(0,10,DIVs):
        FS.set_variable("Food", x)
        FS.set_variable("Service", y)
        tip = FS.inference()['Tip']
        xs.append(x)
        ys.append(y)
        zs.append(tip)
xs = array(xs)
ys = array(ys)
zs = array(zs)

from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

xx, yy = plt.meshgrid(xs,ys)

ax.plot_trisurf(xs,ys,zs, vmin=0, vmax=25, cmap='gnuplot2')
ax.set_xlabel("Food")
ax.set_ylabel("Service")
ax.set_zlabel("Tip")
ax.set_title("Simpful", pad=20)
ax.set_zlim(0, 25)
plt.tight_layout()
plt.show()


import pathlib
from os.path import join
import itertools
from typing import Any

import matplotlib.pyplot as plt
from pytorch_lightning.utilities.types import STEP_OUTPUT
import pytorch_lightning as pl
import torch.nn as nn

from .observable_layers import ObservableLayersChans
from general.utils import max_min_norm, save_json

class PlotWeightsBiSE(ObservableLayersChans):

    def __init__(self, *args, freq: int = 100, **kwargs):
        super().__init__(*args, freq=freq, **kwargs)
        self.last_weights = []

    def on_train_batch_end_layers_chans(
        self,
        trainer: 'pl.Trainer',
        pl_module: 'pl.LightningModule',
        outputs: "STEP_OUTPUT",
        batch: "Any",
        batch_idx: int,
        dataloader_idx: int,
        layer: "nn.Module",
        layer_idx: int,
        chan_input: int,
        chan_output: int,
    ):
        # if isinstance(layer, (BiSE, COBiSE, BiSEC, COBiSEC)):
        #     trainer.logger.experiment.add_figure(f"weights/Normalized_{layer_idx}", self.get_figure_normalized_weights(
        #         layer._normalized_weight, layer.bias, layer.activation_P), trainer.global_step)
        # trainer.logger.experiment.add_figure(f"weights/Raw_{layer_idx}", self.get_figure_raw_weights(layer.weight), trainer.global_step)

        weights = layer.weights[chan_output, chan_input]
        weights_norm = layer._normalized_weight[chan_output, chan_input]
        trainer.logger.experiment.add_figure(
            f"weights_normalized/layer_{layer_idx}_chin_{chan_input}_chout_{chan_output}",
            self.get_figure_normalized_weights(weights_norm, layer.bias_bise[chan_output, chan_input], layer.activation_P_bise[chan_output, chan_input]),
            trainer.global_step
        )
        trainer.logger.experiment.add_figure(
            f"weights_raw/layer_{layer_idx}_chin_{chan_input}_chout_{chan_output}",
            self.get_figure_raw_weights(weights),
            trainer.global_step
        )

    def on_train_end(self, trainer: 'pl.Trainer', pl_module: 'pl.LightningModule') -> None:
        for layer_idx, layer in enumerate(pl_module.model.layers):
            to_add = {"weights": layer.weights, "bias_bise": layer.bias_bise, "activation_P_bise": layer.activation_P_bise}
            to_add["normalized_weights"] = layer._normalized_weight
            self.last_weights.append(to_add)

    def save(self, save_path: str):
        final_dir = join(save_path, self.__class__.__name__)
        pathlib.Path(final_dir).mkdir(exist_ok=True, parents=True)
        for layer_idx, layer_dict in enumerate(self.last_weights):
            for key, weight in layer_dict.items():
                for chan_output in range(weight.shape[0]):
                    for chan_input in range(weight.shape[1]):
                        if key == "normalized_weights":
                            fig = self.get_figure_normalized_weights(weight[chan_output, chan_input],
                            bias=layer_dict['bias_bise'][chan_output, chan_input], activation_P=layer_dict['activation_P_bise'][chan_output, chan_input])
                        elif key == "weights":
                            fig = self.get_figure_raw_weights(weight[chan_output, chan_input])
                        fig.savefig(join(final_dir, f"{key}_layer_{layer_idx}_chin_{chan_input}_chout_{chan_output}.png"))

        return self.last_weights

    @staticmethod
    def get_figure_normalized_weights(weights, bias, activation_P):
        weights = weights.cpu().detach()
        figure = plt.figure(figsize=(8, 8))
        plt.title(f"bias={bias.item()}  act_P={activation_P.item()}")
        plt.imshow(weights, interpolation='nearest', cmap=plt.cm.gray)
        plt.colorbar()
        plt.clim(0, 1)

        # Use white text if squares are dark; otherwise black.

        for i, j in itertools.product(range(weights.shape[0]), range(weights.shape[1])):
            color = "white" if weights[i, j] < .5 else "black"
            plt.text(j, i, round(weights[i, j].item(), 2), horizontalalignment="center", color=color)

        plt.tight_layout()
        return figure

    @staticmethod
    def get_figure_raw_weights(weights):
        weights = weights.cpu().detach()
        weights_normed = max_min_norm(weights)
        figure = plt.figure(figsize=(8, 8))
        plt.imshow(weights_normed, interpolation='nearest', cmap=plt.cm.gray)
        plt.colorbar()
        # plt.clim(0, 1)

        # Use white text if squares are dark; otherwise black.
        threshold = weights_normed.max() / 2.

        for i, j in itertools.product(range(weights.shape[0]), range(weights.shape[1])):
            color = "white" if weights_normed[i, j] < threshold else "black"
            plt.text(j, i, round(weights[i, j].item(), 2), horizontalalignment="center", color=color)

        plt.tight_layout()
        return figure

class PlotParametersBiSE(ObservableLayersChans):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.last_params = {}

    def on_train_batch_end_layers_chans(
        self,
        trainer: 'pl.Trainer',
        pl_module: 'pl.LightningModule',
        outputs: "STEP_OUTPUT",
        batch: "Any",
        batch_idx: int,
        dataloader_idx: int,
        layer: "nn.Module",
        layer_idx: int,
        chan_input: int,
        chan_output: int,
    ):
        metrics = {}
        last_params = {}

        metrics[f'params/weight_P/layer_{layer_idx}_chin_{chan_input}_chout_{chan_output}'] = layer.weight_P_bise[chan_output, chan_input]
        metrics[f'params/activation_P/layer_{layer_idx}_chin_{chan_input}_chout_{chan_output}'] = layer.activation_P_bise[chan_output, chan_input]
        metrics[f'params/bias_bise/layer_{layer_idx}_chin_{chan_input}_chout_{chan_output}'] = layer.bias_bise[chan_output, chan_input]

        trainer.logger.log_metrics(metrics, trainer.global_step)
        self.last_params[layer_idx] = last_params

        trainer.logger.experiment.add_scalars(
            f"comparative/weight_P/layer_{layer_idx}_chout_{chan_output}",
            {f"chin_{chan_input}": layer.weight_P_bise[chan_output, chan_input]},
            trainer.global_step
        )
        trainer.logger.experiment.add_scalars(
            f"comparative/activation_P/layer_{layer_idx}_chout_{chan_output}",
            {f"chin_{chan_input}": layer.activation_P_bise[chan_output, chan_input]},
            trainer.global_step
        )
        trainer.logger.experiment.add_scalars(
            f"comparative/bias_bise/layer_{layer_idx}_chout_{chan_output}",
            {f"chin_{chan_input}": layer.bias_bise[chan_output, chan_input]},
            trainer.global_step
        )

    def save(self, save_path: str):
        final_dir = join(save_path, self.__class__.__name__)
        pathlib.Path(final_dir).mkdir(exist_ok=True, parents=True)
        save_json({k1: {k2: str(v2) for k2, v2 in v1.items()} for k1, v1 in self.last_params.items()}, join(final_dir, "parameters.json"))
        return self.last_params


import argparse
import json
from pathlib import Path
from typing import Any

import torch
from torch.utils.data import DataLoader
from transformers import AdamW, BertConfig, BertForMaskedLM

from hparams import Hyperparameter
from preprocess import Preprocessor

import torch
import torch.nn as nn
import wandb

def get_acc(scores, y):
    predictions = (scores > 0.5).long()
    num_correct += (predictions == y).sum()
    num_samples += predictions.size(0)

    acc = num_correct/num_samples
    return acc

def validate(model: Any, loader: DataLoader, device: torch.device, Sigmoid, criterion):  # type: ignore[type-arg]
    model.eval()
    total_loss = 0
    num_correct = 0
    num_samples = 0

    for batch in loader:
        input_ids, label_ids = batch
        input_ids = input_ids.to(device)
        label_ids = label_ids.to(device)

        with torch.no_grad():
            output = model(input_ids)
            prediction = output.logits[:,-1,:].squeeze()
            if len(prediction.shape) < 1: # for batch size = 1
                prediction = prediction.reshape(1)

            scores = Sigmoid(prediction)
            loss = criterion(scores.float(), label_ids.float())

            predictions = (scores > 0.5).long()
            num_correct += (predictions == label_ids).sum()
            num_samples += predictions.size(0)

        total_loss += loss.item()

    avg_loss = total_loss / len(loader)
    avg_acc  = (num_correct / num_samples).item()
    return avg_loss, avg_acc

def test(model_save_path, model, test_loader, device, Sigmoid, criterion):
    print("[Testing]")
    dic = torch.load(model_save_path)
    model.load_state_dict(dic["model_state_dict"])
    test_loss, test_acc = validate(model, test_loader, device, Sigmoid, criterion)
    test_dict = {"test_loss": test_loss, "test_acc":test_acc}
    return test_dict

def train(args: argparse.Namespace) -> None:
    wandb.login(key="")
    run = wandb.init(
        name = "[New Data] Paper Accpetance", ## Wandb creates random run names if you skip this field
        reinit = True, ### Allows reinitalizing runs when you re-run this cell
        # run_id = ### Insert specific run id here if you want to resume a previous run
        # resume = "must" ### You need this to resume previous runs, but comment out reinit = True when using this
        project = "IDL", ### Project should be created in your wandb account
        #config = configParaser ### Wandb Config for your run
        entity = "gyuseoklee"
    )

    # Hyperparameters
    hp = Hyperparameter()
    device = f"cuda:{args.gpu_num}" if torch.cuda.is_available() else "cpu"
    save_dir = Path(hp.save_dir) / args.task / hp.pretrained_model

    # Preprocessor
    config = BertConfig.from_pretrained(hp.pretrained_model)
    preprocessor = Preprocessor(hp, config=config)

    # train/dev/test dataset
    train_dataset, dev_dataset, test_dataset = preprocessor(args.task)

    train_loader = DataLoader(
        train_dataset, batch_size=hp.batch_size, shuffle=True, collate_fn=preprocessor.collate_fn_accept
    )
    dev_loader = DataLoader(
        dev_dataset, batch_size=hp.batch_size, shuffle=False, collate_fn=preprocessor.collate_fn_accept
    )
    test_loader = DataLoader(
        test_dataset, batch_size=hp.batch_size, shuffle=False, collate_fn=preprocessor.collate_fn_accept
    )

    # Model
    model = BertForMaskedLM.from_pretrained(hp.pretrained_model, config=config).to(device)
    model.cls.predictions.decoder = nn.Linear(768,1).to(device)

    # check
    # x,y = next(iter(train_loader))
    # output = model(x.to(device))
    # print("output", output.logits.shape) # 8 x 512 x 1

    # Optimizer
    no_decay = ["bias", "LayerNorm.weight"]
    optimizer_grouped_parameters = [
        {
            "params": [
                p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)
            ],
            "weight_decay": 1e-4,
        },
        {
            "params": [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)],
            "weight_decay": 0,
        },
    ]
    optimizer = AdamW(optimizer_grouped_parameters, lr=hp.learning_rate, eps=1e-8)  # type: ignore[arg-type]
    criterion = nn.BCELoss().to(device)
    Sigmoid = nn.Sigmoid().to(device)

    if args.test_only:
        test_dict = test(args.model_pretrain_path, model, test_loader, device, Sigmoid, criterion)
        print(test_dict)
        wandb.log(test_dict)
        return

    min_dev_loss = float("inf")
    global_step = 0
    patience = 0
    early_stopping = False

    for epoch in range(10): #hp.max_epochs):
        print(f"[Epoch : {epoch}]")
        model.train()
        num_correct = 0
        num_samples = 0
        total_loss = 0

        print("[Training]")
        for idx, batch in enumerate(train_loader):
            input_ids, label_ids = batch
            input_ids = input_ids.to(device)
            label_ids = label_ids.to(device)

            output = model(input_ids)
            prediction = output.logits[:,-1,:].squeeze()
            if len(prediction.shape) < 1: # for batch size = 1
                prediction = prediction.reshape(1)

            scores = Sigmoid(prediction)
            loss = criterion(scores.float(), label_ids.float())
            total_loss += loss.item()

            predictions = (scores > 0.5).long()
            num_correct += (predictions == label_ids).sum()
            num_samples += predictions.size(0)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            global_step += 1

            train_step_loss = total_loss / (idx+1)
            train_step_acc = (num_correct / num_samples).item()

            # validate
            dev_loss, dev_acc = None, None
            if global_step % 1 == 0:
                #print("[Validating]")
                dev_loss, dev_acc = validate(model, dev_loader, device, Sigmoid, criterion)
                #print(f"[Epoch:{epoch}] Step {global_step} | Dev loss: {dev_loss:.6f} Dev acc: {dev_acc:.6f}")

                if dev_loss < min_dev_loss:
                    min_dev_loss = dev_loss

                    # Save model
                    model.save_pretrained(save_dir)
                    config.save_pretrained(save_dir)
                    with open(save_dir / "hp.json", "w") as f:
                        json.dump(hp.dict(), f, indent=4, ensure_ascii=False)

                    status = {"global_step": global_step, "epoch": epoch}
                    with open(save_dir / "status.json", "w") as f:
                        json.dump(status, f, indent=4, ensure_ascii=False)

                    # Notice model saved
                    model_save_path = args.model_save_path
                    torch.save({"model_state_dict" : model.state_dict()}, model_save_path)

                    print(f"Model saved at step {global_step} / epoch {epoch}")
                    patience = 0

                # else:
                #     patience += hp.validation_steps
                #     if patience > hp.patience_steps:
                #         print(f"Early stopping: No improvement in {patience} epochs")
                #         early_stopping = True
                        #break
            # result
            step_result = {"train_step_acc" : train_step_acc,
                           "train_step_loss": train_step_loss,
                           "valid_acc" : dev_acc,
                           "valid_loss": dev_loss}

            print(f"[Epoch : {epoch:04d}] global_step : {global_step}")
            print(step_result)
            wandb.log(step_result)

        # epoch_log
        epoch_result = {"train_epoch_loss": train_step_loss,
                        "train_epoch_acc": train_step_acc}
        wandb.log(epoch_result)

        # if early_stopping:
        #     break

    # Test
    test_dict = test(model_save_path, model, test_loader, device, Sigmoid, criterion)
    print(test_dict)
    wandb.log(test_dict)
    run.finish()
    return

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--task",
        type=str,
        choices=["tldr", "strength", "weakness", "accepted"],
        help="Task to train",
        default = "accepted"
    )
    parser.add_argument("--gpu_num", type = int, default = 0, help = "the number of gpu")
    parser.add_argument("--model_pretrain_path", type = str, default = "./checkpoints/accepted/accepted.pth")
    parser.add_argument("--model_save_path", type = str, default = "./checkpoints/accepted/accepted.pth")
    parser.add_argument("--test_only", type = bool, default = False, help = "Whether only test mode or not")

    args = parser.parse_args()

    print("[Starting]")
    print(args)

    train(args)

    print(args)
    print("[Ending]")


import json
from datetime import datetime

class Flow():
    def __init__(self, timestamp=None, clientIP=None, serverIP=None, clientPort=None, serverPort=None, duration=None, _id=None, initFwdLength=-1, initBwdLength=-1, fwdHeaderLength=0, bwdHeaderLength=0, fwdBytes=0, bwdBytes=0, fwdPackets=0, bwdPackets=0, fwdPacketMin=float("inf"), fwdPacketMax=0, bwdPacketMin=float("inf"), bwdPacketMax=0, fwdFIN=0, fwdSYN=0, fwdRST=0, fwdPSH=0, fwdACK=0, fwdURG=0, fwdECE=0, fwdCWR=0, bwdFIN=0, bwdSYN=0, bwdRST=0, bwdPSH=0, bwdACK=0, bwdURG=0, bwdECE=0, bwdCWR=0, label='BENIGN'):
        self.timestamp = timestamp
        self.clientIP = clientIP
        self.serverIP = serverIP
        self.clientPort = clientPort
        self.serverPort = serverPort
        self.duration = duration
        self.id = self.getID() if not _id else _id

        self.initFwdLength = initFwdLength
        self.initBwdLength = initBwdLength

        self.fwdHeaderLength = fwdHeaderLength
        self.bwdHeaderLength = bwdHeaderLength
        self.fwdBytes = fwdBytes
        self.bwdBytes = bwdBytes
        self.fwdPackets = fwdPackets
        self.bwdPackets = bwdPackets

        self.fwdPacketMin = fwdPacketMin
        self.fwdPacketMax = fwdPacketMax
        self.bwdPacketMin = bwdPacketMin
        self.bwdPacketMax = bwdPacketMax

        self.fwdFIN = fwdFIN
        self.fwdSYN = fwdSYN
        self.fwdRST = fwdRST
        self.fwdPSH = fwdPSH
        self.fwdACK = fwdACK
        self.fwdURG = fwdURG
        self.fwdECE = fwdECE
        self.fwdCWR = fwdCWR

        self.bwdFIN = bwdFIN
        self.bwdSYN = bwdSYN
        self.bwdRST = bwdRST
        self.bwdPSH = bwdPSH
        self.bwdACK = bwdACK
        self.bwdURG = bwdURG
        self.bwdECE = bwdECE
        self.bwdCWR = bwdCWR

        self.label = label

    def load(self, flowDict):
        self.timestamp = flowDict['timestamp']
        self.clientIP = flowDict['clientIP']
        self.serverIP = flowDict['serverIP']
        self.clientPort = flowDict['clientPort']
        self.serverPort = flowDict['serverPort']
        self.duration = flowDict['duration']
        self.id = flowDict['id']

        self.initFwdLength = flowDict['initFwdLength']
        self.initBwdLength = flowDict['initBwdLength']

        self.fwdHeaderLength = flowDict['fwdHeaderLength']
        self.bwdHeaderLength = flowDict['bwdHeaderLength']
        self.fwdBytes = flowDict['fwdBytes']
        self.bwdBytes = flowDict['bwdBytes']
        self.fwdPackets = flowDict['fwdPackets']
        self.bwdPackets = flowDict['bwdPackets']

        self.fwdPacketMin = flowDict['fwdPacketMin']
        self.fwdPacketMax = flowDict['fwdPacketMax']
        self.bwdPacketMin = flowDict['bwdPacketMin']
        self.bwdPacketMax = flowDict['bwdPacketMax']

        self.fwdFIN = flowDict['fwdFIN']
        self.fwdSYN = flowDict['fwdSYN']
        self.fwdRST = flowDict['fwdRST']
        self.fwdPSH = flowDict['fwdPSH']
        self.fwdACK = flowDict['fwdACK']
        self.fwdURG = flowDict['fwdURG']
        self.fwdECE = flowDict['fwdECE']
        self.fwdCWR = flowDict['fwdCWR']

        self.bwdFIN = flowDict['bwdFIN']
        self.bwdSYN = flowDict['bwdSYN']
        self.bwdRST = flowDict['bwdRST']
        self.bwdPSH = flowDict['bwdPSH']
        self.bwdACK = flowDict['bwdACK']
        self.bwdURG = flowDict['bwdURG']
        self.bwdECE = flowDict['bwdECE']
        self.bwdCWR = flowDict['bwdCWR']

        self.label = flowDict['label']

    def getID(self):
        try:
            client = ':'.join([self.clientIP, str(self.clientPort)])
            server = ':'.join([self.serverIP, str(self.serverPort)])
            return '@'.join(['->'.join([client, server]), str(self.timestamp)])
        except(TypeError):
            return None

    def increment(self, packet, isLastPacket=False):
        if isLastPacket:
            start = datetime.strptime(self.timestamp, '%Y-%m-%d %H:%M:%S.%f')
            end = datetime.strptime(packet['time'], '%Y-%m-%d %H:%M:%S.%f')
            self.duration = (end - start).total_seconds()

        if self.isClient(packet['src']):
            self.incrementClient(packet)

        if self.isServer(packet['src']):
            self.incrementServer(packet)

    def updateLabel(self, packet):
        if self.label == 'BENIGN' and packet['label'] != 'BENIGN':
            self.label = packet['label']

        #ensure no flow has packets with differing non-benign labels
        if self.label != 'BENIGN' and self.label != packet['label']:
            #logging.debug('Differing non-benign labels discovered in packet {}'.format(packet.__dict__))
            pass

    def incrementClient(self, packet):
        if not self.seenClientPacket():
            self.initFwdLength = packet['length']

        self.updateLabel(packet)

        self.fwdPackets += 1
        self.fwdHeaderLength += packet['headerLength']
        self.fwdBytes += packet['length']

        self.fwdFIN += 1 if packet['FIN'] else 0
        self.fwdSYN += 1 if packet['SYN'] else 0
        self.fwdRST += 1 if packet['RST'] else 0
        self.fwdPSH += 1 if packet['PSH'] else 0
        self.fwdACK += 1 if packet['ACK'] else 0
        self.fwdURG += 1 if packet['URG'] else 0
        self.fwdECE += 1 if packet['ECE'] else 0
        self.fwdCWR += 1 if packet['CWR'] else 0

        if self.fwdPacketMin > packet['length']:
            self.fwdPacketMin = packet['length']

        if self.fwdPacketMax < packet['length']:
            self.fwdPacketMax = packet['length']

    def incrementServer(self, packet):
        if not self.seenServerPacket():
            self.initBwdLength = packet['length']

        self.updateLabel(packet)

        self.bwdPackets += 1
        self.bwdHeaderLength += packet['headerLength']
        self.bwdBytes += packet['length']

        self.bwdFIN += 1 if packet['FIN'] else 0
        self.bwdSYN += 1 if packet['SYN'] else 0
        self.bwdRST += 1 if packet['RST'] else 0
        self.bwdPSH += 1 if packet['PSH'] else 0
        self.bwdACK += 1 if packet['ACK'] else 0
        self.bwdURG += 1 if packet['URG'] else 0
        self.bwdECE += 1 if packet['ECE'] else 0
        self.bwdCWR += 1 if packet['CWR'] else 0

        if self.bwdPacketMin > packet['length']:
            self.bwdPacketMin = packet['length']

        if self.bwdPacketMax < packet['length']:
            self.bwdPacketMax = packet['length']

    def isClient(self, srcIP):
        return self.clientIP == srcIP

    def isServer(self, srcIP):
        return self.serverIP == srcIP

    def seenClientPacket(self):
        return self.fwdPackets > 0

    def seenServerPacket(self):
        return self.bwdPackets > 0

    def toCSV(self):
        values = [str(value) for attr, value in self.__dict__.items()]
        return ','.join(values)

    def toHeader(self):
        values = [attr for attr, value in self.__dict__.items()]
        return ','.join(values)

    def toJSON(self):
        return json.dumps(self, default=lambda x: x.__dict__)


import matplotlib.pyplot as plt
# 그래프 생성/이미지화 모듈

x = [1,4,9,16,25,36,49,64]
y = [i for i in range(1, 9)]

plt.plot(x, y, 'r')
# 문자열은 색상/반환타입의 속성이다

plt.title('matplotlib sample')
plt.xlabel('x value')
plt.ylabel('y value')
plt.show()


import pytorch_lightning as pl
from pytorch_lightning.callbacks import (DeviceStatsMonitor, ModelCheckpoint,
                                          LearningRateMonitor)
from pytorch_lightning.callbacks.progress import TQDMProgressBar
from pytorch_lightning import Trainer, loggers as pl_loggers

from configs import *
from utils import *
from models import *
from dataset import *
from pl_modules import *
from augmentations import *

## setting seeds for reproducable results.
pl.seed_everything(hash("setting random seeds") % 2**32 - 1)

## Inserting keypoint in single tube images.
insert_dummy_keypoints(TRAIN_PATH)
insert_dummy_keypoints(TEST_PATH)

#defining loss function
criterion = nn.MSELoss()

## defining the callbacks
tb_logger = pl_loggers.TensorBoardLogger(logdir, name="glue_tube_length_model")

checkpoint_callback = ModelCheckpoint(dirpath=ckptdir,
                                      every_n_epochs=10,
                                      save_top_k=2,
                                      monitor = "val_loss",
                                      filename='{epoch}-{val_loss:.2f}')

progressbar_callback = TQDMProgressBar(refresh_rate= 3)
lr_monitor_callback = LearningRateMonitor()
callbacks = [lr_monitor_callback,progressbar_callback,checkpoint_callback]

## Configuring the trainer for pytorch lightning
if torch.cuda.is_available():
    trainer = Trainer(max_epochs = EPOCHS,
                  num_sanity_val_steps=1,
                  log_every_n_steps = 6,
                  weights_summary='top',
                  gpus = -1,
                  precision = 16,
                  logger=tb_logger,
                  callbacks = callbacks,
                  accumulate_grad_batches=4,
                  auto_lr_find = True,
                  reload_dataloaders_every_n_epochs = True
                  )
else:
    trainer = Trainer(max_epochs = EPOCHS,
                  num_sanity_val_steps=1,
                  log_every_n_steps = 6,
                  weights_summary='top',
                  logger=tb_logger,
                  callbacks = callbacks,
                  auto_lr_find = True,
                  reload_dataloaders_every_n_epochs = True
                  )

network = SimpleKeypointModel() # creating the model object
pl_model = plModel(network, criterion, learning_rate = LR) # creating the PL model wrapper object

base_dataset = TubeLengthDataset(TRAIN_PATH) # creating the base dataset object
test_dataset = TubeLengthDataset(TEST_PATH,transform = test_transforms) #creating the test dataset object
#creating the pl datamodule
pl_datamodule = plDataModule(base_dataset,test_dataset,transforms_dict,val_split = VAL_SPLIT, batch_size = BATCH_SIZE)

#fit the model
trainer.fit(model = pl_model,datamodule= pl_datamodule)

#evaluate the model
val_score = trainer.test(model = pl_model,datamodule= pl_datamodule)
print(val_score)

#save only the weight separately
torch.save(pl_model.network.state_dict(),"./model/tube_length_model.pt")


import click
import json
import os
from LapisParser import *

@click.command()
@click.option('--source')
@click.option('--name')
@click.option('--method')
@click.option('--out')
def main(source, name, method, out):
    ans = dict()
    ans['data'] = dict()
    ans['succeed'] = False
    ans['errors'] = list()

    source = str(source)
    name = str(name)
    method = str(method)
    out = str(out)

    ans['errors'] = [{'line': 1, 'col': 1, 'errno': 0, 'msg': 'Format Error'}]
    with open(out, 'w') as f:
        json.dump(ans, f)

    try:
        ret1 = addDocFromFile(source)

        assert ret1 is True
        ret2 = parseAPI()

        assert ret2 is True
        ret3 = generateRandomDataFromAPISchema(name, method)

        if ret3 is not None:
            ans['data'] = ret3
            ans['succeed'] = True
        ans['errors'] = list()
        if len(getErrors()) > 0:
            ans['errors'] = getErrors()
    except Exception:
        ans['errors'] = getErrors()

    with open(out, 'w') as f:
        json.dump(ans, f)

if __name__ == '__main__':
    main()


"""
图像处理
    更改颜色空间
    图像的几何变换
    图像阈值
    平滑图像
    形态转换
    图像梯度
    Canny边缘检测
    图像金字塔
    OpenCV中的轮廓
    OpenCV中的直方图
    OpenCV中的图像转换
    模板匹配
    霍夫线变换
    霍夫圆变换
    基于分水岭算法的图像分割
    基于GrabCut算法的交互式前景提取

"""
import cv2
import numpy as np

"""
 ████
▒▒███
 ▒███
 ▒███
 ▒███
 ▒███
 █████
▒▒▒▒▒
更改颜色空间
"""
#%%
"""
cv.cvtColor(input_image, flag)
两个最常使用的方法，
    BGR 到 Gray: cv.COLOR_BGR2GRAY
    BGR 到 HSV: cv.COLOR_BGR2HSV
"""
import cv2 as cv
# flags = [i for i in dir(cv) if i.startswith('COLOR_')]
# print( flags )

img = cv.imread('data/images/1.jpg')
img_gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
img_hsv = cv.cvtColor(img, cv.COLOR_RGB2HSV_FULL)

cv.imshow('img gray', img_gray)
cv.imshow('img hsv', img_hsv)
cv.waitKey(0)
cv.destroyAllWindows()

#%% 目标追踪
import cv2 as cv
import numpy as np
cap = cv.VideoCapture(0)

# 通过rbg值追踪hsv值
def get_mask(color):
    color_rgb = np.uint8([[[0, 0, 0]]])
    if color == 'blue':
        color_rgb = np.uint8([[[0, 0, 255]]])
    elif color == 'green':
        color_rgb = np.uint8([[[0, 255, 0]]])

    color_hsv = cv.cvtColor(color_rgb, cv.COLOR_RGB2HSV).reshape(-1)
    blue_mask = cv.inRange(hsv,
                           np.asarray([color_hsv[0] - 30, 100, 100]),
                           np.asarray([color_hsv[0] + 30, 255, 255]))
    return blue_mask

while(True):
    _, frame = cap.read()
    # rgb转hsv
    hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
    # define range of blue color in HSV
    # lower_blue = np.array([100,100,100])
    # upper_blue = np.array([130,255,255])
    # Threshold the HSV image to get only blue colors
    # blue_mask = cv.inRange(hsv, lower_blue, upper_blue)
    blue_mask = get_mask('blue')
    green_mask = get_mask('green')
    # Bitwise-AND mask and original image
    blue_res = cv.bitwise_and(frame, frame, mask=blue_mask)
    green_res = cv.bitwise_and(frame, frame, mask=green_mask)

    cv.imshow('frame', frame)
    cv.imshow('blue mask', blue_mask)
    cv.imshow('blue res', blue_res)
    cv.imshow('green mask', green_mask)
    cv.imshow('green res', green_res)

    k = cv.waitKey(5) & 0xFF
    if k == 27:
        break
cv.destroyAllWindows()

#%%
"""
  ████████
 ███▒▒▒▒███
▒▒▒    ▒███
   ███████
  ███▒▒▒▒
 ███      █
▒██████████
▒▒▒▒▒▒▒▒▒▒

图像的几何变换
    如平移、旋转、仿射变换等。
"""

#%%
# 两个转换函数，
# cv.warpAffine 和 cv.warpPerspective，可以进行各种转换

import numpy as np
import cv2 as cv

img = cv.imread('data/images/1.jpg')
# interpolation :插值方法
# 对于下采样(图像上缩小)，最合适的插值方法是 cv.INTER_AREA
# 对于上采样(放大),最好的方法是 cv.INTER_CUBIC （速度较慢）和 cv.INTER_LINEAR (速度较快)。
# 默认情况下，所使用的插值方法都是 cv.INTER_AREA 。
# 下采样
res1 = cv.resize(img, None, fx=0.5, fy=0.5, interpolation=cv.INTER_AREA)
res2 = cv.resize(img, None, fx=2, fy=2, interpolation=cv.INTER_CUBIC)

cv.imshow('res1', res1)
cv.imshow('res2', res2)
cv.waitKey(0)
cv.destroyAllWindows()

#%% 平移变换
img = cv.imread('data/images/1.jpg')
rows,cols, _ = img.shape

"""
平移变换是物体位置的移动。如果知道 （x，y） 方向的偏移量，
假设为 (t_x,t_y)**，则可以创建如下转换矩阵 **M：

    M = [ 1 0 tx ]
        [ 0 1 ty ]
"""
M = np.float32([[1,0,100], [0,1,50]])
# cv.warpAffine 函数的第三个参数是输出图像的大小，其形式应为（宽度、高度）。
# 记住宽度=列数，高度=行数。
dst = cv.warpAffine(img, M, (cols, rows))

cv.imshow('dst', dst)
cv.waitKey(0)
cv.destroyAllWindows()

#%% 旋转
img = cv.imread('data/images/1.jpg')

rows,cols,_ = img.shape
# cols-1 and rows-1 are the coordinate limits.
M = cv.getRotationMatrix2D(((cols-1)/2.0,(rows-1)/2.0),45,1)
dst = cv.warpAffine(img,M,(cols,rows))

cv.imshow('dst', dst)
cv.waitKey(0)
cv.destroyAllWindows()

#%%
"""
  ████████
 ███▒▒▒▒███
▒▒▒    ▒███
   ██████▒
  ▒▒▒▒▒▒███
 ███   ▒███
▒▒████████
 ▒▒▒▒▒▒▒▒
图像阈值
"""
#%% 简单阈值法
"""
如果像素值大于阈值，则会被赋为一个值（可能为白色），否则会赋为另一个值（可能为黑色）。
使用的函数是 cv.threshold。
    第一个参数是源图像，它应该是灰度图像。
    第二个参数是阈值，用于对像素值进行分类。
    第三个参数是 maxval，它表示像素值大于（有时小于）阈值时要给定的值。
    opencv 提供了不同类型的阈值，由函数的第四个参数决定。类型有：
        cv.THRESH_BINARY
            二值化阈值处理：在对8位灰度图像进行二值化时，如果将阈值设定为127，那么：
                         所有大于127的像素点会被处理为255。其余值会被处理为0。
        cv.THRESH_BINARY_INV
            反二值化阈值处理：对于灰度值大于阈值的像素点，将其值设定为0。
                         对于灰度值小于或等于阈值的像素点，将其值设定为255。
        cv.THRESH_TRUNC
            截断阈值化处理：将图像中大于阈值的像素点的值设定为阈值，小于或等于该阈值的像素点的值保持不变
        cv.THRESH_TOZERO
            低阈值零处理：将图像中小于或等于阈值的像素点的值处理为0，大于阈值的像素点的值保持不变。
        cv.THRESH_TOZERO_INV
            超阈值零处理：超阈值零处理会将图像中大于阈值的像素点的值处理为0，小于或等于该阈值的像素点的值保持不变。
"""
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread('data/images/1.jpg', 0)
_, thresh1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
_, thresh2 = cv.threshold(img, 127, 255, cv.THRESH_BINARY_INV)
_, thresh3 = cv.threshold(img, 127, 255, cv.THRESH_TRUNC)
_, thresh4 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO)
_, thresh5 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO_INV)

titles = ['Original', 'BINARY', 'BINARY_INV',
          'TRUNC', 'TOZERO', 'TOZERO_INV']

images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]

for i in range(6):
    plt.subplot(2,3,i+1),
    plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])

plt.show()

#%% 自适应阈值
"""
自适应阈值算法计算图像的一个小区域的阈值。
因此，我们得到了同一图像不同区域的不同阈值，对于不同光照下的图像，得到了更好的结果。

"""

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread('data/images/digital.jpg', 0)
# 使用中值滤波器模糊图像。
# 该函数使用中值滤波器平滑图像ksize × ksize光圈。
# 多通道图像的每个通道都是独立处理的。支持就地操作。
# img = cv.medianBlur(img, 5)

ret, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)

th2 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_MEAN_C,
                           cv.THRESH_BINARY,11,2)
th3 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C,
                           cv.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
            'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']

images = [img, th1, th2, th3]

for i in range(4):
    plt.subplot(2,2,i+1),plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

#%%
"""
 █████ █████
▒▒███ ▒▒███
 ▒███  ▒███ █
 ▒███████████
 ▒▒▒▒▒▒▒███▒█
       ▒███▒
       █████
      ▒▒▒▒▒
平滑图像
"""

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread('data/images/opencv-logo.png')
kernel = np.ones((5,5),np.float32)/25
print(img)
print(kernel)

dst = cv.filter2D(img,-1,kernel)

plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(dst),plt.title('Averaging')
plt.xticks([]), plt.yticks([])
plt.show()

#%%
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('data/images/digital.jpg',0)
laplacian = cv.Laplacian(img,cv.CV_64F)
sobelx = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
sobely = cv.Sobel(img,cv.CV_64F,0,1,ksize=5)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()

#%%
"""
 ██████████
▒███▒▒▒▒▒▒█
▒███     ▒
▒█████████
▒▒▒▒▒▒▒▒███
 ███   ▒███
▒▒████████
 ▒▒▒▒▒▒▒▒
形态转换
"""

#%% 腐蚀 膨胀
# 假设前景色为白色，通过卷积，可以消除白色噪音
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread(r'E:\cao\pytorchProject\data\images\i.png')

# 有当内核下的所有像素都为 1 时，原始图像中的像素（1 或 0）才会被视为 1，否则会被侵蚀（变为零）。
blur = cv.blur(img, (4,4))
# 这里，如果内核下至少有一个像素为“1”，则像素元素为“1”。
# 所以它会增加图像中的白色区域，或者增加前景对象的大小。
dilation = cv.dilate(blur,(3,3),iterations = 1)

plt.subplot(131), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])

plt.subplot(132), plt.imshow(blur), plt.title('blur')
plt.xticks([]), plt.yticks([])

plt.subplot(133), plt.imshow(dilation), plt.title('dilation')
plt.xticks([]), plt.yticks([])

plt.show()

#%% 开运算（腐蚀）  闭运算（膨胀后腐蚀）
# 开运算用于去除噪点
# 闭运算用于填充前景上的小孔

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread(r'E:\cao\pytorchProject\data\images\i.png')
kernel = (15,15)
opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)
closing = cv.morphologyEx(opening, cv.MORPH_CLOSE, kernel)
# 形态梯度 它是图像的膨胀和腐蚀之间的差值。 结果将类似于对象的轮廓。
gradient = cv.morphologyEx(closing, cv.MORPH_GRADIENT, kernel)
# 顶帽 原图像和原图像开运算结果的差值
tophat = cv.morphologyEx(closing, cv.MORPH_TOPHAT, kernel)
# 7、黑帽 它是原图像和原图像的闭的差值
blackhat = cv.morphologyEx(closing, cv.MORPH_BLACKHAT, kernel)

plt.subplot(231), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])

plt.subplot(232), plt.imshow(opening), plt.title('opening')
plt.xticks([]), plt.yticks([])

plt.subplot(233), plt.imshow(closing), plt.title('closing')
plt.xticks([]), plt.yticks([])

plt.subplot(234), plt.imshow(gradient), plt.title('gradient')
plt.xticks([]), plt.yticks([])

plt.subplot(235), plt.imshow(tophat), plt.title('tophat')
plt.xticks([]), plt.yticks([])

plt.subplot(236), plt.imshow(blackhat), plt.title('blackhat')
plt.xticks([]), plt.yticks([])

plt.show()

"""
  ████████
 ███▒▒▒▒███
▒███   ▒▒▒
▒█████████
▒███▒▒▒▒███
▒███   ▒███
▒▒████████
 ▒▒▒▒▒▒▒▒
图像梯度
"""

#%%
# OpenCv 提供三种类型的梯度滤波器或高通滤波器，Sobel、Scharr 和 Laplacian。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread('data/images/digital.jpg', 0)
laplacian = cv.Laplacian(img, cv.CV_64F)

sobelx = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=5)
sobely = cv.Sobel(img, cv.CV_64F, 0, 1, ksize=5)

plt.subplot(221), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.xticks([]), plt.yticks([])

plt.subplot(222), plt.imshow(laplacian, cmap='gray'), plt.title('Laplacian')
plt.xticks([]), plt.yticks([])

plt.subplot(223), plt.imshow(sobelx, cmap='gray'), plt.title('Sobel X')
plt.xticks([]), plt.yticks([])

plt.subplot(224), plt.imshow(sobely, cmap='gray'), plt.title('Sobel Y')
plt.xticks([]), plt.yticks([])
plt.show()

"""
 ██████████
▒███▒▒▒▒███
▒▒▒    ███
      ███
     ███
    ███
   ███
  ▒▒▒
Canny边缘检测
"""

#%%
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# img = cv.imread('data/images/canny.png',0)
# 有噪声
img = cv.imread('data/images/canny2.png',0)
# 降噪

gaussian_blur = cv.GaussianBlur(img, (7, 7), 0)

edges = cv.Canny(gaussian_blur,100,200)
plt.subplot(131),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132),plt.imshow(gaussian_blur,cmap = 'gray')
plt.title('gaussian_blur'), plt.xticks([]), plt.yticks([])
plt.subplot(133),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

"""
  ████████
 ███▒▒▒▒███
▒███   ▒███
▒▒████████
 ███▒▒▒▒███
▒███   ▒███
▒▒████████
 ▒▒▒▒▒▒▒▒

"""


from django.urls import path
from .views import PostsList, PostDetail, PostCreate, PostUpdate, PostDelete, PostSearch, CategoryListView, subscribe, unsubscribe
from django.views.decorators.cache import cache_page

urlpatterns = [
   path('', cache_page(60)(PostsList.as_view()), name='posts_list'),
   path('<int:pk>', cache_page(60*5)(PostDetail.as_view()), name='post_detail'),
   path('search/', PostSearch.as_view(), name='post_search'),
   path('create/', PostCreate.as_view(), name='post_create'),
   path('<int:pk>/update/', PostUpdate.as_view(), name='post_update'),
   path('<int:pk>/delete/', PostDelete.as_view(), name='post_delete'),
   path('categories/<int:pk>', CategoryListView.as_view(), name='category_list'),
   path('categories/<int:pk>/subscribe', subscribe, name='subscribe'),
   path('categories/<int:pk>/unsubscribe', unsubscribe, name='unsubscribe'),
]


import os
import json
import collections

from pathlib import Path
from appdirs import AppDirs

from typing import Any, Optional, Iterable, Dict, Tuple

from ..extra.utils import nested_dict_update, load_playlist

class Model:
    def __init__(
        self,
        state_fname: Optional[Path] = None,
        log_fname: Optional[Path] = None
    ) -> None:
        self.adirs = AppDirs('vydia', 'kpj')

        self.STATE_FILE: Path = state_fname \
            or Path(self.adirs.user_data_dir) / 'state.json'
        self.LOG_FILE: Path = log_fname \
            or Path(self.adirs.user_log_dir) / 'log.txt'

        self._ensure_dir(str(self.LOG_FILE))

    def get_playlist_list(self) -> Iterable[str]:
        return sorted(self._load_state().keys())

    def get_playlist_info(self, pid: str) -> Dict[str, str]:
        cur = self._load_state()[pid]
        cur.update({'name': pid})
        return cur

    def delete_playlist_by_name(self, name: str) -> None:
        _state = self._load_state()
        _state.pop(name)
        self._save_state(_state)

    def get_current_video(self, pid: str) -> Optional[Dict[str, str]]:
        _state = self._load_state()
        return _state[pid].get('current', None)

    def add_new_playlist(self, plid: str) -> Optional[Tuple[str, str]]:
        try:
            plugin_name, pl = load_playlist(plid)
        except ValueError:
            print(f'No plugin found for "{plid}"')
            return None

        self.update_state(pl.title, {'id': pl.id, 'episodes': {}})
        return pl.title, plugin_name

    def update_state(
        self,
        pid: str, data: Dict[str, Any]
    ) -> None:
        _state = self._load_state()

        if pid in _state:
            _state[pid] = nested_dict_update(_state[pid], data)
        else:
            _state[pid] = data

        self._save_state(_state)

    def _ensure_dir(self, fname: str) -> None:
        """ Make sure that directory exists
        """
        dir_ = os.path.dirname(fname)
        if not os.path.isdir(dir_):
            os.makedirs(dir_)

    def _load_state(self, fn: Optional[str] = None) -> Dict[Any, Any]:
        """ Load state
        """
        fname = str(fn or self.STATE_FILE)
        self._ensure_dir(fname)

        if not os.path.isfile(fname):
            res: Dict[Any, Any] = {}
        else:
            try:
                with open(fname) as fd:
                    res = json.load(fd)
            except json.decoder.JSONDecodeError:
                print(f'Invalid state file: "{fname}"')
                exit(-1)

        return collections.defaultdict(dict, res)

    def _save_state(
        self,
        state: Dict[Any, Any], fn: Optional[str] = None
    ) -> None:
        fname = str(fn or self.STATE_FILE)
        self._ensure_dir(fname)

        with open(fname, 'w') as fd:
            json.dump(state, fd)


"""
https://leetcode.com/problems/maximum-subarray/

did this before but still ran into nails..
kind of subtle.. not sure if I can come up with this in a stressful interview environment
"""

from typing import List

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:

        prefixSum = nums[0]
        s = nums[0]
        for num in nums[1:]:
            s = max(s, prefixSum+num, num) # either w/ or w/o prefixSum
            prefixSum = max(prefixSum+num, num, 0) # either taking current num+prefix, or only current num, or nothing

        return s

"""
lets see the divide and conquer approach? prefix sum again?

  [5,4,-1,7,8]
[0,5,9,8,15,23]

  [-2,1,-3,4,-1,2,1,-5,4]
[0,-2,-1,-4,0,-1,1,2,-3,1] the max - min max is to the right of min..

not divide and conquer

I guess divide and conquer if finding left max subarray and right max subarray
and the max subarray cross middle line??

yes

https://leetcode.com/problems/maximum-subarray/discuss/1595195/C%2B%2BPython-7-Simple-Solutions-w-Explanation-or-Brute-Force-%2B-DP-%2B-Kadane-%2B-Divide-and-Conquer

class Solution:
    def maxSubArray(self, nums):
        def maxSubArray(A, L, R):
            if L > R: return -inf
            mid, left_sum, right_sum, cur_sum = (L + R) // 2, 0, 0, 0
            for i in range(mid-1, L-1, -1):
                left_sum = max(left_sum, cur_sum := cur_sum + A[i])
            cur_sum = 0
            for i in range(mid+1, R+1):
                right_sum = max(right_sum, cur_sum := cur_sum + A[i])
            return max(maxSubArray(A, L, mid-1), maxSubArray(A, mid+1, R), left_sum + A[mid] + right_sum)
        return maxSubArray(nums, 0, len(nums)-1)

the cross mid, sum is just adding everything together and see how big it can retain
            for i in range(mid-1, L-1, -1):
                left_sum = max(left_sum, cur_sum := cur_sum + A[i])
            cur_sum = 0
            for i in range(mid+1, R+1):
                right_sum = max(right_sum, cur_sum := cur_sum + A[i])

also it can be precomputed.. pretty smart...

"""


import scipy.integrate as integrate
import scipy.stats as stats
import numpy as np
from numpy import sqrt, pi, sin, radians
import matplotlib.pyplot as plt

# Constructive-destructive-interference
t = np.arange(0, 1+0.01, 0.01)
y1 = sqrt(2)*sin(2*pi*t)
energy1 = integrate.quad(lambda t: (sqrt(2)*sin(2*pi*t))**2, 0, 1)
theta_degree = np.arange(-180, 180+10, 10)
theta = radians(theta_degree)
energy2 = [integrate.quad(lambda t: (1/sqrt(2)*(sqrt(2)*sin(2*pi*t) + sqrt(2)*sin(2*pi*t - theta_point)))**2, 0, 1)
           for theta_point in theta]
energy2_first = [energy2_tuple[0] for energy2_tuple in energy2]

plt.plot(theta_degree, energy1[0]*np.ones(theta_degree.size), 'k--',
         theta_degree, energy2_first, 'b')
plt.legend(('One signal', 'Two signals'))
plt.xlabel('Phase difference (degrees)')
plt.ylabel('Energy')
plt.xlim([-180, 180])
plt.ylim([0, 2])

# Channel capacity
# Gaussian distribution
mu = 0
variance = 1
sigma = sqrt(variance)
x = np.linspace(mu-5*sigma, mu+5*sigma, 100)
plt.plot(x, stats.norm.pdf(x, mu, sigma))
plt.xlabel('x')
plt.ylabel('Probability density function')
plt.xlim([-5, 5])
plt.ylim([0, 0.4])
# Complex gaussian distribution
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
sigma2 = sigma/sqrt(2)
x_r = np.linspace(-5, 5, 100)
x_i = np.linspace(-5, 5, 100)
x_r, x_i = np.meshgrid(x_r, x_i)
ax.plot_surface(x_r, x_i, stats.norm.pdf(x_r, mu, sigma2) * stats.norm.pdf(x_i, mu, sigma2), color='r', cmap=plt.cm.Spectral)
ax.set_xlabel('Real part')
ax.set_xlim(-5, 5)
ax.set_ylabel('Imaginary part')
ax.set_ylim(-5, 5)
ax.set_zlabel('Probability density function')
ax.set_zlim(0, 0.3)
# Capacity behaviours
B = np.linspace(0, 100, 100)
plt.plot(SNR, np.log2(1+5*10**6*B))