import threading
import requests
import json
import time
import urllib.parse
from dotmap import DotMap

from models.types import Event, Message
from models.errors import APIError
import asyncio
import aiohttp
import traceback
from pprint import pprint

class UpdateManager():

    def __init__(self, **kwargs):
        self.loop = asyncio.get_event_loop()

        self.session = kwargs.get("session")
        self.token = kwargs.get("token")
        self.callback = kwargs.get("callback")

        self.poll_timeout = 100

        self.URL = f"https://api.telegram.org/bot{self.token}/"

        self.last_update = None
        self.command_queue = []

    async def update_loop(self):
        print("Starting Poll Update Loop")

        while True:
            await self.poll_updates(self.last_update)

    async def poll_updates(self, offset=None):

        url = f"{self.URL}getUpdates?timeout={self.poll_timeout}"

        if offset:
            url += f"&offset={offset}"

        async with self.session.get(url) as resp:
            data = await resp.json()
            result = data['result']

            if result:
                self.command_queue.extend(result)
                self.last_update = max(x["update_id"] for x in result) + 1
                print(f"Poll Successful: {self.last_update}")
                await self.callback()

class NatsukoClient():

    def __init__(self, token, **kwargs):
        self.token = token
        self.API_URL = f"https://api.telegram.org/bot{self.token}/"

        self.commands = {}
        self.usercache = {}

        self.loop = asyncio.get_event_loop()
        self.session = aiohttp.ClientSession(loop=self.loop)
        self.manager = UpdateManager(token=self.token, session=self.session, callback=self.process)

    def run(self):

        self.loop.run_until_complete(self._run())

    async def _run(self):

        task = asyncio.ensure_future(self.manager.update_loop())
        await task

    async def process(self):
        print(f"Callback Called: {self}")

        while self.manager.command_queue:
            _cmd = DotMap(self.manager.command_queue.pop(0))
            command = Event(self, _cmd)
            self.parse_command(command)

    def parse_command(self, event):

        for entity in event.message.entities:
            if entity.is_command:
                command = entity.text[1:]
                print(f"Identified as Bot Command: {entity}")

                if command in self.commands:
                    func = self.commands[command]["function"]
                    asyncio.ensure_future(func(event))

        user = event.message.author
        if not user.username in self.usercache:
            self.usercache[user.username] = user

        #
        # not sure that any of this shit is necessary
        #
        # if "message" in event and not raw_command.message.keys():
            # if not username in self.usercache:
                # self.usercache[username] = raw_command.message["from"]

        # elif "inline_query" in raw_command:
            # if not username in self.usercache:
                # self.usercache[username] = raw_command.inline_query["from"]

        # elif "chat" in raw_command:
            # if not username in self.usercache:
                # self.usercache[username] = raw_command.chat["from"]

    # Command Decorator
    def command(self, name, **options):

        def deco(f):
            command = {"function": f}
            command["no_error"] = False if "no_error" not in options else options["no_error"]

            self.commands[name] = command
            print(f'\tLOAD_OK: {f.__name__}: on_command @ {name}')

            return f

        return deco

    async def _api_send(self, url, apiq):
        print(f"APISEND: {apiq}")

        async with self.session.get(url, params=apiq) as resp:
            content = await resp.json()

            if not content["ok"]:
                raise APIError(content)

            return content["result"]

    async def send_message(self, chat_id, message, **kwargs):
        """
            Use this method to send text messages. On success, the sent Message is returned.
            (Optional parameters are keyword arguments)

            Parameters                  Type    Required    Description
            chat_id                     Int/Str Yes         Unique identifier for the target chat or username of
                                                            the target channel (in the format @channelusername)
            text                        String  Yes         Text of the message to be sent
            parse_mode*                 String  Optional    Send Markdown or HTML, if you want Telegram apps to
                                                            show bold, italic, fixed-width text or inline URLs
                                                            in your bot's message.
            disable_web_page_preview*   Boolean Optional    Disables link previews for links in this message
            disable_notification*       Boolean Optional    Sends the message silently. Users will receive a
                                                            notification with no sound.
            reply*                      Integer Optional    If the message is a reply, ID of the original message
            reply_markup*               Json    Optional    Additional interface options. A JSON-serialized
                                                            object for an inline keyboard, custom reply keyboard,
                                                            instructions to remove reply keyboard or to force a
                                                            reply from the user.
        """

        endpoint = "sendMessage"

        url = self.API_URL + endpoint
        args = {"chat_id": chat_id, "text": message, **kwargs}
        return await self._api_send(url, args)

    async def forward_message(self, target_cid, source_cid,
                    message_id, **kwargs):
        """
        Use this method to forward messages of any kind. On success, the sent Message is returned.
        (Optional parameters are keyword arguments)

        Parameters              Type    Required    Description
        chat_id                 Int/Str Yes         Unique identifier for the target chat or username of the
                                                    target channel (in the format @channelusername)
        from_chat_id            Int/Str Yes         Unique identifier for the chat where the original message
                                                    was sent (or channel username in the format
                                                    @channelusername)
        disable_notification    Boolean Optional    Sends the message silently. Users will receive a
                                                    notification with no sound.
        message_id              Integer Yes         Message identifier in the chat specified in from_chat_id
        """

        endpoint = "forwardMessage"

        url = self.API_URL + endpoint
        args = {'chat_id': target_cid, 'from_chat_id': source_cid, 'message_id': message_id, **kwargs}

        return await self._api_send(url, args)

    async def send_photo(self, chat_id, photo, **kwargs):
        """Use this method to send photos. On success, the sent Message is returned.
        (Optional parameters are keyword arguments)

        Parameters              Type        Required    Description
        chat_id                 Int/Str     Yes         Unique identifier for the target chat or username of
                                                        the target channel (in the format @channelusername)
        photo                   File/Str    Yes         Photo to send. Pass a file_id or a binary filestream.
        caption                 String      Optional    Photo caption (may also be used when resending photos
                                                        by file_id), 0-200 characters
        disable_notification    Boolean     Optional    Sends the message silently. Users will receive a
                                                        notification with no sound.
        reply                   Integer     Optional    If the message is a reply, ID of the original message
        reply_markup            Json        Optional    Additional interface options. A JSON-serialized object
                                                        for an inline keyboard, custom reply keyboard,
                                                        instructions to remove reply keyboard or to
                                                        force a reply from the user.
        """

        endpoint = 'sendPhoto'
        url = self.API_URL + endpoint

        if isinstance(photo, str):
            if photo.startswith('http'):
                photo = urllib.parse.quote(photo)

            args = {"chat_id": chat_id, "photo": photo, **kwargs}
            return await self._api_send(url, args)

        else:
            args = {"chat_id": chat_id, **kwargs}
            response = await self.session.post(url, data=dict(photo=photo), params=args)
            return response.json()

    async def send_audio(self, chat_id, audio, **kwargs):
        """Use this method to send audio, if you want Telegram clients to display them in the music
        player. Your audio must be in the .mp3 format. On success, the sent Message is returned.
        Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the
        future. On success, the sent Message is returned.
        (Optional parameters are keyword arguments)

        Parameters              Type        Required    Description
        chat_id                 Int/Str     Yes         Unique identifier for the target chat or username of
                                                        the target channel (in the format @channelusername)
        audio                   File/Str    Yes         Audio file to send. Pass a file_id or a
                                                        binary filestream.
        caption                 String      Optional    Audio Caption, 0-200 characters
        duration                Integer     Optional    Duration of the audio in seconds
        performer               String      Optional    Performer
        title                   String      Optional    Track name
        disable_notification    Boolean     Optional    Sends the message silently. Users will receive a
                                                        notification with no sound.
        reply                   Integer     Optional    If the message is a reply, ID of the original message
        reply_markup            Json        Optional    Additional interface options. A JSON-serialized object
                                                        for an inline keyboard, custom reply keyboard,
                                                        instructions to remove reply keyboard or to
                                                        force a reply from the user.
        """

        endpoint = "sendAudio"

        url = self.API_URL + endpoint

        if isinstance(audio, str):
            args = {'chat_id': chat_id, 'audio': audio, **kwargs}
            return await self._api_send(url, args)

        else:
            args = {'chat_id': chat_id, **kwargs}
            response = await self.session.post(url, data=dict(audio=audio), params=args)
            return response.json()

    async def send_document(self, chat_id, document, **kwargs):
        """Use this method to send documents.  Bots can currently send files of any type of up to 50 MB
        in size, this limit may be changed in the future. On success, the sent Message is returned.
        (Optional parameters are keyword arguments)

        Parameters              Type        Required    Description
        chat_id                 Int/Str     Yes         Unique identifier for the target chat or username of
                                                        the target channel (in the format @channelusername)
        document                File/Str    Yes         File to send. Pass a file_id or a
                                                        binary filestream.
        caption                 String      Optional    Caption, 0-200 characters
        disable_notification    Boolean     Optional    Sends the message silently. Users will receive a
                                                        notification with no sound.
        reply                   Integer     Optional    If the message is a reply, ID of the original message
        reply_markup            Json        Optional    Additional interface options. A JSON-serialized object
                                                        for an inline keyboard, custom reply keyboard,
                                                        instructions to remove reply keyboard or to
                                                        force a reply from the user.
        """

        endpoint = "sendDocument"

        if isinstance(document, str):
            args = {'chat_id': chat_id, 'document': document, **kwargs}
            return await self._api_send(url, args)

        else:
            args = {'chat_id': chat_id, **kwargs}
            response = await self.session.post(url, data=dict(document=document), params=args)
            return response.json()

    async def send_video(self, chat_id, video, **kwargs):
        """Use this method to send videos. Telegram clients support mp4 videos
        (other formats may be sent as Document). On success, the sent Message is returned.
        Bots can currently send video files of up to 50 MB in size, this limit may be changed
        in the future.
        (Optional parameters are keyword arguments)

        Parameters              Type        Required    Description
        chat_id                 Int/Str     Yes         Unique identifier for the target chat or username of
                                                        the target channel (in the format @channelusername)
        video                   File/Str    Yes         File to send. Pass a file_id or a
                                                        binary filestream.
        caption                 String      Optional    Caption, 0-200 characters
        duration                Integer     Optional    Duration of sent video in seconds
        width                   Integer     Optional    Video width
        height                  Integer     Optional    Video height
        disable_notification    Boolean     Optional    Sends the message silently. Users will receive a
                                                        notification with no sound.
        reply                   Integer     Optional    If the message is a reply, ID of the original message
        reply_markup            Json        Optional    Additional interface options. A JSON-serialized object
                                                        for an inline keyboard, custom reply keyboard,
                                                        instructions to remove reply keyboard or to
                                                        force a reply from the user.
        """

        endpoint = "sendVideo"

        url = self.API_URL + endpoint

        if isinstance(video, str):
            args = {"chat_id": chat_id, "video": video, **kwargs}
            return await self._api_send(url, args)

        else:
            args = {"chat_id": chat_id, **kwargs}
            response = await self.session.post(url, data=dict(video=video), params=args)
            return response.json()

    async def send_voice(self, chat_id, voice, **kwargs):
        """Use this method to send voice files. if you want Telegram clients to display the file as a
        playable voice message. For this to work, your audio must be in an .ogg file encoded with OPUS
        (other formats may be sent as Audio or Document). On success, the sent Message is returned.
        Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in
        the future.
        (Optional parameters are keyword arguments)

        Parameters              Type        Required    Description
        chat_id                 Int/Str     Yes         Unique identifier for the target chat or username of
                                                        the target channel (in the format @channelusername)
        voice                   File/Str    Yes         File to send. Pass a file_id or a
                                                        binary filestream.
        caption                 String      Optional    Caption, 0-200 characters
        disable_notification    Boolean     Optional    Sends the message silently. Users will receive a
                                                        notification with no sound.
        reply                   Integer     Optional    If the message is a reply, ID of the original message
        reply_markup            Json        Optional    Additional interface options. A JSON-serialized object
                                                        for an inline keyboard, custom reply keyboard,
                                                        instructions to remove reply keyboard or to
                                                        force a reply from the user.
        """

        endpoint = "sendVoice"

        url = self.API_URL + endpoint

        if isinstance(voice, str):
            args = {"chat_id": chat_id, "voice": voice, **kwargs}
            return await self._api_send(apiq)

        else:
            args = {"chat_id": chat_id, **kwargs}
            response = await self.session.post(url, data=dict(voice=voice), params=args)
            return response.json()

    async def send_video_note(self, chat_id, v_note, **kwargs):
        """As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long.
        Use this method to send video messages. On success, the sent Message is returned.
        (Optional parameters are keyword arguments)

        Parameters              Type        Required    Description
        chat_id                 Int/Str     Yes         Unique identifier for the target chat or username of
                                                        the target channel (in the format @channelusername)
        video                   File/Str    Yes         File to send. Pass a file_id or a
                                                        binary filestream.
        caption                 String      Optional    Caption, 0-200 characters
        duration                Integer     Optional    Duration of sent video in seconds
        disable_notification    Boolean     Optional    Sends the message silently. Users will receive a
                                                        notification with no sound.
        reply                   Integer     Optional    If the message is a reply, ID of the original message
        reply_markup            Json        Optional    Additional interface options. A JSON-serialized object
                                                        for an inline keyboard, custom reply keyboard,
                                                        instructions to remove reply keyboard or to
                                                        force a reply from the user.
        """

        endpoint = "sendVideoNote"
        url = self.API_URL + endpoint

        if isinstance(v_note, str):
            args = {"chat_id": chat_id, "video_note": v_note}
            return await self._api_send(url, args)

        else:
            args = {"chat_id": chat_id}
            response = await self.session.post(url, data=dict(video_note=v_note), params=args)
            return response.json()

    async def send_location(self, chat_id, long, lat, **kwargs):
        """Use this method to send point on the map. On success, the sent Message is returned.
        (Optional parameters are keyword arguments)

        Parameters              Type        Required    Description
        chat_id                 Int/Str     Yes         Unique identifier for the target chat or username of
                                                        the target channel (in the format @channelusername)
        chat_id                 Int/Str     Yes         Unique identifier for the target chat or username of
                                                        the target channel (in the format @channelusername)
        latitude                Float       Yes         Latitude of location
        longitude               Float       Yes         Longitude of location
        disable_notification    Boolean     Optional    Sends the message silently. Users will receive a
                                                        notification with no sound.
        reply                   Integer     Optional    If the message is a reply, ID of the original message
        reply_markup            Json        Optional    Additional interface options. A JSON-serialized object
                                                        for an inline keyboard, custom reply keyboard,
                                                        instructions to remove reply keyboard or to
                                                        force a reply from the user.
        """

        endpoint = 'sendLocation'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, "latitude": lat, "longitutde": long, **kwargs}
        return await self._api_send(url, args)

    async def send_venue(self, chat_id, lat, long, title, addr, **kwargs):
        """Use this method to send information about a venue. On success, the sent Message is returned.
        (Optional parameters are keyword arguments)

        Parameters              Type        Required    Description
        chat_id                 Int/Str     Yes         Unique identifier for the target chat or username of
                                                        the target channel (in the format @channelusername)
        latitude                Float       Yes         Latitude of location
        longitude               Float       Yes         Longitude of location
        title                   String      Yes         Name of the venue
        address                 String      Yes         Address of the venue
        foursquare_id           String      Optional    Foursquare identifier of the venue
        disable_notification    Boolean     Optional    Sends the message silently. Users will receive a
                                                        notification with no sound.
        reply                   Integer     Optional    If the message is a reply, ID of the original message
        reply_markup            Json        Optional    Additional interface options. A JSON-serialized object
                                                        for an inline keyboard, custom reply keyboard,
                                                        instructions to remove reply keyboard or to
                                                        force a reply from the user.
        """

        endpoint = "sendVenue"
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'latitude': lat, "longitude": long, 'title': title, 'address': addr}
        return await self._api_send(url, args)

    async def send_contact(self, chat_id, phone_number, first_name, **kwargs):
        """Use this method to send information about a venue. On success, the sent Message is returned.
        (Optional parameters are keyword arguments)

        Parameters              Type        Required    Description
        chat_id                 Int/Str     Yes         Unique identifier for the target chat or username of
                                                        the target channel (in the format @channelusername)
        phone_number            String      Yes         Contact's phone number
        first_name              String      Yes         Contact's first name
        last_name               String      Optional    Contact's last name
        disable_notification    Boolean     Optional    Sends the message silently. Users will receive a
                                                        notification with no sound.
        reply                   Integer     Optional    If the message is a reply, ID of the original message
        reply_markup            Json        Optional    Additional interface options. A JSON-serialized object
                                                        for an inline keyboard, custom reply keyboard,
                                                        instructions to remove reply keyboard or to
                                                        force a reply from the user.
        """

        endpoint = 'sendContact'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name, **kwargs}
        return await self._api_send(url, args)

    async def send_chat_action(self, chat_id, action):
        """Use this method when you need to tell the user that something is happening on the bot's side.
        The status is set for 5 seconds or less (when a message arrives from your bot,
        Telegram clients clear its typing status). Returns True on success.

        Example: The ImageBot needs some time to process a request and upload the image.
        Instead of sending a text message along the lines of “Retrieving image, please wait…”,
        the bot may use sendChatAction with action = upload_photo. The user will see a “sending photo”
        status for the bot.

        We only recommend using this method when a response from the bot will take a noticeable
        amount of time to arrive.
        (Optional parameters are keyword arguments)

        Parameters              Type        Required    Description
        chat_id                 Int/Str     Yes         Unique identifier for the target chat or username of
                                                        the target channel (in the format @channelusername)
        action                  String      Yes         Type of action to broadcast. Choose one, depending on
                                                        what the user is about to receive:
                                                        'typing' for text messages,
                                                        'upload_photo' for photos,
                                                        'record_video' or 'upload_video' for videos,
                                                        'record_audio' or 'upload_audio' for audio files,
                                                        'upload_document' for general files,
                                                        'find_location' for 'location data',
                                                        'record_video_note' or 'upload_video_note'
                                                            for video notes.
        """

        endpoint = "sendChatAction"
        url = self.API_URL + endpoint

        args = {'chat_id': str(chat_id), 'action': action}
        return await self._api_send(url, args)

    async def get_user_profile_photos(self, user_id, **kwargs):
        """Use this method to get a list of profile pictures for a user.
        Returns a UserProfilePhotos Dictionary.
        (Optional parameters are keyword arguments)

        Parameters              Type        Required    Description
        user_id                 Int/Str     Yes         Unique identifier for the target user.
        offset                  Integer     Optional    Sequential number of the first photo to be returned.
                                                        By default, all photos are returned.
        limit                   Integer     Optional    Limits the number of photos to be retrieved.
                                                        Values between 1—100 are accepted. Defaults to 100
        """

        endpoint = 'getUserProfilePhotos'
        url = self.API_URL + endpoint

        args = {'user_id': user_id, **kwargs}
        return await self._api_send(url, args)

    async def get_file(self, file_id):

        endpoint = 'getFile'
        url = self.API_URL + endpoint

        args = {'file_id': file_id}
        return await self._api_send(url, args)

    async def get_file_url(self, file_obj):

        return f"https://api.telegram.org/file/bot{self.token}/{file_obj.file_path}"

    async def ban_chat_memeber(self, chat_id, user_id, **kwargs):

        endpoint = "kickChatMember"
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'user_id': user_id, **kwargs}
        return await self._api_send(url, args)

    async def unban_chat_memeber(self, chat_id, user_id):

        endpoint = 'unbanChatMember'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'user_id': user_id}
        return await self._api_send(url, args)

    async def restrict_chat_memeber(self, chat_id, user_id, **kwargs):

        endpoint = 'restrictChatMember'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'user_id': user_id, **kwargs}
        return await self._api_send(url, args)

    async def promote_chat_memeber(self, chat_id, user_id, **kwargs):

        endpoint = 'promoteChatMember'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'user_id': user_id, **kwargs}
        return await self._api_send(url, args)

    async def export_invite_link(self, chat_id):

        endpoint = 'exportChatInviteLink'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id}
        return await self._api_send(url, args)

    async def set_chat_photo(self, chat_id, photo):

        endpoint = 'sendChatPhoto'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id}
        response = await self.session.post(apiq, data=dict(photo=photo), params=args)
        return response.json()

    async def delete_chat_photo(self, chat_id):

        endpoint = 'deleteChatPhoto'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id}
        return await self._api_send(url, args)

    async def set_chat_title(self, chat_id, title):

        endpoint = 'setChatTitle'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'title': title}
        return await self._api_send(url, args)

    async def set_chat_description(self, chat_id, desc):

        endpoint = 'setChatDescription'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'description': desc}
        return await self._api_send(url, args)

    async def pin_chat_message(self, chat_id, message_id, **kwargs):

        endpoint = 'pinChatMessage'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'message_id': message_id, **kwargs}
        return await self._api_send(url, args)

    async def unpin_chat_message(self, chat_id):

        endpoint = 'unpinChatMessage'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id}
        return await self._api_send(url, args)

    async def get_chat(self, chat_id):

        endpoint = 'getChat'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id}
        return await self._api_send(url, args)

    async def get_chat_administrators(self, chat_id):

        endpoint = 'getChatAdministrators'

        url = self.API_URL + endpoint

        args = {'chat_id': chat_id}
        return await self._api_send(url, args)

    async def get_chat_member_count(self, chat_id):

        endpoint = 'getChatMembersCount'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id}
        return await self._api_send(url, args)

    async def get_chat_member(self, chat_id, user_id):

        endpoint = 'getChatMember'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id}
        return await self._api_send(url, args)

    # Updating Messages

    async def edit_message_text(self, chat_id, msg_id, text, **kwargs):

        endpoint = 'editMessageText'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'text': text, 'message_id': msg_id, **kwargs}
        return await self._api_send(url, args)

    async def edit_message_caption(self, chat_id, msg_id, caption, **kwargs):

        endpoint = 'editMessageCaption'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'message_id': msg_id, 'caption': caption, **kwargs}
        return await self._api_send(url, args)

    async def edit_message_reply_markup(self, chat_id, msg_id, markup, **kwargs):

        endpoint = 'editMessageReplyMarkup'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'message_id': msg_id, 'markup': markup, **kwargs}
        return await self._api_send(url, args)

    async def delete_message(self, chat_id, msg_id):

        endpoint = 'deleteMessage'
        url = self.API_URL + endpoint

        args = {'chat_id': chat_id, 'message_id': msg_id}
        return await self._api_send(url, args)


'''def mensagem(msg):
    print('~*'*30)
    print(f'{msg:^60}')
    print('~*'*30)

mensagem('SISTEMA DE ALUNOS')'''

'''def soma(x, y):
    print(x-y)

soma(y=4, x=5)
soma(8, 9)'''

'''def contador(*x):
    for v in x:
        print(v, end=' ')
    print(f'Recebi {len(x)} valores.')
    print()

contador(2, 1, 7)
contador(8, 0)
contador(4, 5, 7, 3)'''

def soma(*valores):
    s = 0
    for num in valores:
        s += num
    print(f'A soma dos valores {valores} é {s}.')

soma(5, 2)
soma(3, 4, 11)


'''Manipulando chaves e valores em dicionarios'''

pessoa = {}

chave = 'nome'

pessoa[chave] = 'MAx Carvalho'
pessoa['sobrenome'] = 'Carvalho'

print(pessoa[chave])

pessoa[chave] = 'Joao'

del pessoa['sobrenome']
print(pessoa)
print(pessoa['nome'])

if pessoa.get('sobrenome'):
    print('Nao Existe')
else:
    print(pessoa['sobrenome'])


import argparse
import matplotlib.pyplot as plt
import pandas as pd
import datetime as dt

def get_args_parser():
    parser = argparse.ArgumentParser(
        'analysis script', add_help=False)
    parser.add_argument('--model-name')
    parser.add_argument('--levit-model-dir')
    parser.add_argument('--ffcv-model-dir')
    return parser

def get_time_acc(df: pd.DataFrame, version):
    df['train_dur'] = pd.to_timedelta(df['train_dur'])
    val_time = pd.to_timedelta(df['val_eval_dur']).mean()
    sums = [(df['train_dur'].iloc[range(i+1)].sum() + val_time).total_seconds() / 60 for i in range(df.shape[0])]
    m_acc, argmax = 0, 0
    accs = df['val_acc1'].values
    # for i, acc in enumerate(accs):
    #     m_acc, argmax = (acc, i) if acc > m_acc else (m_acc, argmax)
    # sums = sums[:argmax+1]
    # accs = df['val_acc1'].values[:argmax+1]
    plt.scatter(sums, accs)
    plt.plot(sums, accs, label=version)
    return df[['train_dur', 'val_acc1']].dropna()

def get_deltatimes(durations):
    print(durations)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
            'analysis script', parents=[get_args_parser()])
    args = parser.parse_args()
    levit_df = pd.read_csv(f'{args.levit_model_dir}/logged_data.csv')
    ffcv_df = pd.read_csv(f'{args.ffcv_model_dir}/logged_data.csv')
    get_time_acc(levit_df.dropna(), 'LeViT')
    get_time_acc(ffcv_df.dropna(), 'FFCV')
    plt.title(f'training time - LeViT_{args.model_name}')
    plt.xlabel('time (minutes)')
    plt.ylabel('accuracy (%)')
    plt.grid()
    plt.legend()
    plt.savefig(f'{args.model_name}_comparison.png')


import logging
import traceback
from typing import List, Optional
from fastapi import Depends
from domain.administration_module.dto.schemaAsist import SchemaAttendanceCreate
from domain.administration_module.dto.schemaEmployee import SchemaEmployee, SchemaEmployeeCreate
from domain.administration_module.entity.Attendance import Attendance
from domain.administration_module.entity.Employee import Empleado
from domain.administration_module.entity.modelRol import Rol
from domain.administration_module.dto.schemaRol import SchemaRolCreate

class AdminService:
    def __init__(self, repository_rol, repository_empleado, repository_asist) -> None:
        self._repository_rol = repository_rol
        self._repository_empleado = repository_empleado
        self._repository_asist = repository_asist
    ################################################################### ROLES ####################################################

    def create_rol(self, rol_data: SchemaRolCreate):
        new_rol = Rol(**rol_data.__dict__)
        logging.warning("accediendo al servicio")
        self._repository_rol.create(new_rol)
        return new_rol

    def get_rol_by_id(self, rol_id: int) -> Optional[Rol]:
        logging.warning("entrando al repositorio")
        rol = self._repository_rol.get_by_id(rol_id)
        return rol

    def get_all_rols(self) -> List[Rol]:
        rols = self._repository_rol.get_all()
        return list(rols)

    def delete_rol(self, rol_id: int) -> None:
        return self._repository_rol.delete_by_id(rol_id)

    ################################################################### EMPLEADOS ####################################################

    def create_empleado(self, empleado_data: SchemaEmployeeCreate):
        new_empleado = Empleado(**empleado_data.__dict__)
        logging.warning("accediendo al servicio")
        self._repository_empleado.create(new_empleado)
        return new_empleado

    def get_empleado_by_id(self, empleado_id: int) -> Optional[Empleado]:
        logging.warning("entrando al repositorio")
        empleado = self._repository_empleado.get_by_id(empleado_id)
        return empleado

    def get_all_empleados(self) -> List[Empleado]:
        empleados = self._repository_empleado.get_all()
        return empleados

    def delete_empleado(self, empleado_id: int) -> None:
        return self._repository_empleado.delete_by_id(empleado_id)

    def create_asistence(self, asistencia_data: SchemaAttendanceCreate):
        empleado_asist = Attendance(**asistencia_data.__dict__)
        self._repository_asist.create(empleado_asist)
        return empleado_asist

    def get_asistence_by_id(self, asistence_id: int) -> Optional[Attendance]:
        asistencia = self._repository_asist.get_by_id(asistence_id)
        return asistencia

    def deleted_asistence(self, asist_id: int) -> None:
        return self._repository_asist.delete(asist_id)

    def get_all_empleados_asist(self) -> List[Attendance]:
        asistencias = self._repository_asist.get_all()
        return list(asistencias)

    def asing_rol(self, empleado_id: int, rol_id: int):
        try:
            empleado = self.get_empleado_by_id(empleado_id)

            if not empleado:

                print("Empleado no encontrado. ID:", empleado_id)
                return {
                    "error": "Empleado no encontrado"
                }

            rol = self.get_rol_by_id(rol_id)

            if not rol:
                # Agrega registros de depuración
                print("Rol no encontrado. ID:", rol_id)
                return {
                    "error": "Rol no encontrado"
                }

            empleado.rol_id = rol.id

            try:
                self._repository_empleado.update(empleado)

                print("Actualización exitosa")
                return {

                    "asignacion ejecutada con exito"
                }
            except Exception as e:

                error_message = f"Error al asignar rol: {str(e)}"
                traceback.print_exc()
                return {
                    "error": error_message
                }

        except Exception as e:

            error_message = f"Error general: {str(e)}"
            traceback.print_exc()
            return {
                "error": error_message
            }

    ################################################################### MESAS ####################################################


# -*- coding: utf-8 -*-
# @Time    : 2023/7/18 17:36
# @Author  : Chen GangQiang
# @Email   : uoaoo@163.com
# @File    : BackTest.py
# @Software: PyCharm

import time, datetime
import backtrader as bt
from utils.tushareFeed import TushareData

class BackTestForSingleStock:
    def __init__(self, strategy: bt.Strategy, code: str, start_date=None, end_date=None, cash=1000000.0,
                 commission=1 / 10000, isPlot=True):
        """
        回测主类
        :param strategy: 回测类名
        :param code: 股票代码
        :param start_date: 开始日期，不填写为730天之前
        :param end_date:结束日期，不填写为当日
        :param cash:初始资金，默认1000000
        :param commission:手续费，默认万1
        :param isPlot:是否显示图表，默认显示
        """
        self._strategy = strategy
        self._code = code
        self._cash = cash
        self._commission = commission
        self._isPlot = isPlot

        # 如果日期为空，获取1年之间的日期
        today = datetime.date.today()
        if start_date is None:
            self._start_date = today + datetime.timedelta(days=-730)
        else:
            self._start_date = self.StrToDate(start_date)
        if end_date is None:
            self._end_date = today
        else:
            self._end_date = self.StrToDate(end_date)

    def run(self):
        cerebro = bt.Cerebro()
        cerebro.addstrategy(self._strategy)

        # 获取tushare数据
        data = TushareData(dataname=self._code, fromdate=self._start_date, todate=self._end_date, )
        cerebro.adddata(data)

        cerebro.broker.setcash(self._cash)
        cerebro.broker.setcommission(commission=1 / 10000)

        print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
        cerebro.run()
        print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())

        if self._isPlot:
            # 绘制蜡烛图
            params = dict(style='candle', barup='red', bardown='green', volup='red', voldown='green', )
            cerebro.plot(**params)

    def StrToDate(self, dateString):
        """
        字符串转日期格式
        :param dateString:
        :return:
        """
        date_str = dateString
        fmt = '%Y%m%d'
        time_tuple = time.strptime(date_str, fmt)
        year, month, day = time_tuple[:3]
        a_date = datetime.date(year, month, day)
        return a_date

class BaseStrategy(bt.Strategy):
    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))

    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            return

        # Check if an order has been completed
        # Attention: broker could reject order if not enough cash
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))

                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
            else:  # Sell
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))

            self.bar_executed = len(self)

        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')

        self.order = None

    def notify_trade(self, trade):
        if not trade.isclosed:
            return

        self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                 (trade.pnl, trade.pnlcomm))


import random
response="y"
a=1
while a==1:
    while response=="y":
        n=random.randint(1,6)
        if n==1:
            print("[     ]\n[  0  ]\n[     ]")
        if n==2:
            print("[     ]\n[ 0 0 ]\n[     ]")
        if n==3:
            print("[     ]\n[0 0 0]\n[     ]")
        if n==4:
            print("[0   0]\n[     ]\n[0   0]")
        if n==5:
            print("[0   0]\n[  0  ]\n[0   0]")
        if n==6:
            print("[0   0]\n[0   0]\n[0   0]")
        response="n"
    response=input("Press 'y' to cont., 'n' to exit- ")
    print("\n")


"""
File: shrink.py
Name: Po Kai Feng
-------------------------------
Create a new "out" image half the width and height of the original.
Set pixels at x=0 1 2 3 in out , from x=0 2 4 6 in original,
and likewise in the y direction.
"""

from simpleimage import SimpleImage

def shrink(filename):
    """
    :param filename: str, the location of the picture
    :return: img, the shrink picture
    """
    img = SimpleImage(filename)
    new_img = SimpleImage.blank((img.width+1) // 2, (img.height+1) // 2)
    for x in range(0, img.width, 2):
        for y in range(0, img.height, 2):
            pixel = img.get_pixel(x, y)
            new_pixel = new_img.get_pixel(x//2, y//2)
            """
            For every pixel(x, y) in img, assigns the average RGB of pixel(x, y), pixel(x+1, y),
            pixel(x, y+1) and pixel(x+1, y+1) to new_pixel(x//2, y//2) in new_img.
            """
            if ((img.width+1) % 2 == 0 and x == img.width - 1) or ((img.height + 1) % 2 == 0 and y == img.height - 1):
                # It's the end of img.width or img.height.
                new_pixel.red = pixel.red
                new_pixel.green = pixel.green
                new_pixel.blue = pixel.blue
            else:
                pixel1 = img.get_pixel(x+1, y)
                pixel2 = img.get_pixel(x, y+1)
                pixel3 = img.get_pixel(x, y+1)
                new_pixel.red = (pixel.red + pixel1.red + pixel2.red + pixel3.red) // 4
                new_pixel.green = (pixel.green + pixel1.green + pixel2.green + pixel3.green) // 4
                new_pixel.blue = (pixel.blue + pixel1.blue + pixel2.blue + pixel3.blue) // 4
    return new_img

def main():
    """
    First show the original picture. Then show the shrinked picture.
    """
    original = SimpleImage("images/poppy.png")
    original.show()
    after_shrink = shrink("images/poppy.png")
    after_shrink.show()

if __name__ == '__main__':
    main()


from flask import Flask
import csv
from selenium import webdriver
from selenium.webdriver.common.by import By
from datetime import datetime, timedelta
from flask import Flask, request, send_file
from flask import render_template

def date_extractor(date):
    current_date = datetime.now()
    strin = date
    x = strin.split("left")
    x = x[0]
    if 'day' in x:
      x = x.split("day")
      x = x[0]
      x = int(x)
      final_date = current_date + timedelta(days=x)
      return final_date.strftime("%d-%m-%Y")
    elif 'days' in x:
      x = x.split("days")
      x = x[0]
      x = int(x)
      final_date = current_date + timedelta(days=x)
      return final_date.strftime("%d-%m-%Y")
    elif 'hours' in x:
      x = x.split("hours")
      x = x[0]
      final_date = current_date + timedelta(hours=int(x))
      return final_date.strftime("%d-%m-%Y")
    else:
      if 'months' in x:
        x = x.split("months")
      else:
        x = x.split("month")
      x = x[0]
      x = int(x)
      final_date = current_date + timedelta(days=x * 30)
      return final_date.strftime("%d-%m-%Y")

def scrape_website(url):
    # All required info:
    url = 'https://unstop.com/hackathons?filters=,all,open,all&types=teamsize,payment,oppstatus,eligible'
    path_driver = 'C://chromedriver.exe'
    xpath_endpage = "//div[@class='click_here mt-20 ng-star-inserted']"
    xpath_main = '//app-opportunity-listbox[@class="ng-star-inserted"]'
    xpath_names = './/h2[@class="double-wrap ng-star-inserted"]'
    xpath_inst = './/h3[@class="double-wrap ng-star-inserted"]'

    # create the webdriver object and specify the path to chromedriver
    driver = webdriver.Chrome(executable_path=path_driver)

    # open the website
    driver.get(url)
    driver.maximize_window()
    while True:
        try:
            driver.find_element(By.XPATH, xpath_endpage)
            break
        except:
            driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

    hackathon_list_element = driver.find_element(By.XPATH, xpath_main)

    # Find the elements containing the hackathon names
    hackathon_name_elements = hackathon_list_element.find_elements(By.XPATH, xpath_names)

    # extract the text from each element and store it in a list
    hackathon_names = [element.text for element in hackathon_name_elements]

    links = driver.find_elements(By.CLASS_NAME, 'listing')
    apply_links = []

    # Iterate through the list of elements and extract the links
    for link in links:
        temp_link = link.get_attribute('href')
        apply_links.append(temp_link)

    institutions = driver.find_elements(By.XPATH, xpath_inst)

    # Create a list to store the institution names
    institution_names = [element.text for element in institutions]
    registration_deadlines = []

    date = []
    time_left_elements = driver.find_elements(By.XPATH, './/strong[@class="ml-5"]')
    time_left = []

    count_time_left = 0
    for element in time_left_elements:
        time_left.append(element.text)

    count_date = 0
    for i in range(1, len(time_left), 2):
        temp = time_left[i]
        new_date = date_extractor(temp)
        date.append(new_date)

    date.append('Not Found')
    competitions = [["Hackathons", "Institutions", "Links", "Registration Deadline"]]
    for i in range(len(hackathon_names)):
        competitions.append([hackathon_names[i], institution_names[i], apply_links[i], date[i]])
    with open("hackathons.csv", "w", newline="") as f:
        writer = csv.writer(f)
        writer.writerows(competitions)

    return "hackathons.csv"
    # close the browser
    driver.close()

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/scrape', methods=['POST'])
def scrape():
  # get the URL from the form
  url = request.form['url']

  # call your function to scrape the website and generate the CSV file
  csv_file = scrape_website(url)
  return send_file(csv_file, as_attachment=True, download_name='hackathons.csv')

@app.route('/download', methods=['POST'])
def download():
  # get the CSV file that was generated by the scrape function
  file = scrape()

  # send the CSV file as a response
  return send_file(file, as_attachment=True, download_name='hackathons.csv')

if __name__ == '__main__':
  app.run()


# BFS 기본

from collections import deque

def bfs(graph, start, visited):

    queue = deque([start]) # 큐 구현을 위해 deque 라이브러리 사용

    visited[start] = True

    while queue:
        v = queue.popleft() # 큐에서 하나의 원소를 뽑아 출력
        print(v, end='')

        for i in graph[v]: # 해당 원소와 연결된, 아직 방문하지 않은 원소들을 큐에 삽입
            if not visited[i]:
                queue.append(i)
                visited[i] = True

graph = [
    [],
    [2, 3, 8],
    [1, 7],
    [1, 4, 5],
    [3, 5],
    [3, 4],
    [7],
    [2, 6, 8],
    [1, 7]
]

visited = [False] * 9

bfs(graph, 1, visited)


# Prolog
# Author:  Sophie Nguyen
# Email:  nnguyen177@student.gsu.edu
# Section: 034

import pandas as pd

# Read in hmeq.csv with the first 100 rows
hmeq = pd.read_csv('hmeq.csv').head(100)

# Create a new data frame with the rows with missing values dropped
hmeqDelete = hmeqDelete = hmeq.dropna()

# Create a new data frame with the missing values filled in by the mean of the column
hmeqReplace = hmeq.fillna(hmeq.mean())

# Print the means of the columns for each new data frame
print("Means for hmeqDelete are ", hmeqDelete.mean())

print("Means for hmeqReplace are ", hmeqReplace.mean())


"""
	What if gravitational and inertial mass would be different?
	http://worldbuilding.stackexchange.com/questions/3396/different-gravitational-and-inertial-mass
"""

from numpy import empty, sqrt
from matplotlib.pyplot import subplots, show

mass = [
	# gravitational; inertial
	(1., 1., 'normal'),
	(5., 1., 'new iron'),
	(1., 5., 'new copper'),
]

g = 0.1
fig, ax = subplots()

for grav, inert, label in mass:
	x, y = 0, 0
	px, py = sqrt(2 / inert), sqrt(2 / inert)
	Xs, Ys = [x], [y]
	for t in range(1000):
		Fg = - g * grav
		a = Fg / inert
		py += a
		x += px
		y += py
		Xs.append(x)
		Ys.append(y)
		if y < 0:
			break
	ax.plot(Xs, Ys, label = label)
	ax.set_yticks([])

ax.legend()

if __name__ == '__main__':
	show()


"""
test_spark_submit_mongo.py
~~~~~~~~~~~~~~
This module contains unit tests for the transformation steps of the ETL
job defined in spark_submit_mongo.py. It makes use of a local version of PySpark
that is bundled with the PySpark package.
"""
import unittest
import json
import sys
from pyspark.sql.functions import mean
from pyspark.sql import SparkSession, SQLContext, Row
from pyspark.sql.functions import explode, udf, collect_list, struct
from pyspark.sql.types import StructField, StructType, IntegerType, StringType, ArrayType

# build
spark = SparkSession.builder.appName("Preprocessing App").getOrCreate()

# making sure the module is imported to both the python context and spark context
sys.path.append('/home/ubuntu/jobs')
import spark_submit_mongo as sp
spark.sparkContext.addPyFile('/home/ubuntu/ETL/spark_submit_mongo.py')

class TestSpark(unittest.TestCase):
    def setUp(self):
        """Start Spark, define config and path to test data
        """
        self.spark = spark
        self.sc = spark.sparkContext
        self.sqlContext = SQLContext(self.sc)
        self.config = json.loads("""{"S3_root": "/home/ubuntu/unittest/S3"}""")

    def tearDown(self):
        """Stop Spark
        """
        self.spark.stop()

    def test_transform(self):
        expected_data = (self.spark.read.parquet('/home/ubuntu/unittest/report'))
        expected_rows = expected_data.count()
        # test
        folder_data = self.sqlContext.read.json(self.config['S3_root'] + "/Unzipped")
        folder_data.registerTempTable("tweets")
        extracted_SQL_table = self.sqlContext.sql("SELECT distinct id, created_at, lang, entities.hashtags FROM tweets WHERE lang = 'en' AND size(entities.hashtags) > 0")
        result = sp.transform_Data(extracted_SQL_table)
        self.assertEqual(result.count(), expected_rows)

if __name__ == '__main__':
    unittest.main()


#최대값과 최소값(programmers)
#입력으로 들어오는 문자열을 리스트로 임시변환한다.
#모두 정수형으로 처리하고 min, max를 사용해서 최대값 그리고 최소값을 찾는다.
#위에서 찾은 두 값을 문자열 형태로 다시 연결하고 반환한다.

def solution(s):
    answer = ''

    info = list(s.split())
    temp = []
    for i in info:
        temp.append(int(i))
    A = max(temp)
    B = min(temp)

    answer += str(B)
    answer += " "
    answer += str(A)

    return answer


# coding=utf-8
import logging
from typing import List

from app.helpers.catalog.product_utils import Utilities
from app.models import ProductSQL
from app.repositories.es.product import ProductElasticRepo
from app.repositories.es import ingestion

__author__ = 'LongHB'
_logger = logging.getLogger(__name__)

def update_stocks(products: List[ProductSQL]):
    es_products = [{
        'stock': {
            'in_stock': product.stock,
            'in_stock_sortable': 0 if not product.stock else 1
        },
        'sku': product.sku
    } for product in products]
    ingestion.upsert_products(es_products)

def find_by_sku(sku: str):
    product_es = ProductElasticRepo()
    responses = product_es.search({'skus': [sku]})
    products = extract_only_products_from_response(responses)
    return products[0] if products else None

def get_result_search(args):
    args = Utilities.reformat_product_search_params(args)
    product_es = ProductElasticRepo()
    response = product_es.search(args)
    return extract_product_data_from_response(args, response)

def extract_only_products_from_response(responses):
    if not responses:
        return []
    hits = responses['hits']['hits']
    products = [item['_source'] for item in hits]
    return products

def extract_product_data_from_response(args, responses):
    result = {
        'data': {
            'total': get_total_products(responses),
            'products': extract_only_products_from_response(responses)
        }
    }
    aggregations = args.get('aggregations') or []
    aggregations_result = {}
    if 'brand' in aggregations:
        aggregations_result['brand'] = get_brand_aggregation(responses)
    if 'category' in aggregations:
        aggregations_result['category'] = get_category_aggregation(responses)

    if aggregations_result:
        result['data']['aggregations'] = aggregations_result

    return result

def get_total_products(response):
    total = response.get('hits') or {}
    total = total.get('total') or {}
    total = total.get('value') or 0
    return total

def get_brand_aggregation(response):
    aggregation = response.get('aggregations') or {}
    brand = aggregation.get('brands') or {}
    buckets = brand.get('buckets') or []
    return extract_buckets(buckets)

def get_category_aggregation(response):
    aggregation = response.get('aggregations') or {}
    category = aggregation.get('categories') or {}
    data = category.get('data') or {}
    buckets = data.get('buckets') or []
    return extract_category_buckets(buckets)

def extract_category_buckets(buckets):
    responses = []
    for bucket in buckets:
        try:
            category_code = bucket.get('key')
            from app.helpers.catalog import categories_data
            category = categories_data.get(category_code)
            name = category.get('name')
            responses.append({
                'code': category_code,
                'name': name,
                'count': bucket.get('doc_count')
            })
        except Exception:
            pass
    return responses

def extract_buckets(buckets):
    responses = []
    for bucket in buckets:
        try:
            keys = bucket.get('key').split('|')
            responses.append({
                'code': keys[0],
                'name': keys[1],
                'count': bucket.get('doc_count')
            })
        except Exception:
            pass
    return responses


# -*- coding: utf-8 -*-
# Thida Aung
# Lab1 COEN 146 Wed
# TA Arman Clahi

def multiplex(messages):
        num = 0
        with open('muxed_stream', 'w') as f:
          for i in range(10):
            for k in range (len(messages)):
                for j in range(i*5, (i+1)*5):
                   if (j < len(messages[k])) :
                        #for as long as k elements in the messages list, if j count is less than each row (messages[k]) append j start from 0 to i+1 up to 5 items for 10 times(i)
                        f.write("6040{} : {}\n".format(k, messages[k][j]))
                        num += 1
                   else:
                        f.write("6040{} : No Messages\n".format(k))
                        #for value in messages:#msg = [list(msgs) for msgs in messages]
        return  num

                # We have three rows and flags row1_done, row2_done, row3_done
                # messages_left = not (row1_done and row2_done and row3_done) = not row1_done or not row2_done or not row3_done
                # a = not row1_done or not row2_done
                # b = not row3_done
                # c = a or b

def demultiplex(input_file='muxed_stream'):
        lists = [[]*5]
        with open('muxed_stream', 'r') as f:
            for line in f:
                #print(line)
                if(line[0:5] == '60400'): #left is inclusive right is exclusive
                    lists[0].append(line[7:])
        print(lists)
        return tuple(lists)

## YOU DON'T NEED TO EDIT ANYTHING BELOW HERE

def test_case_1():
    """Testing Multiplexing with tons of messages."""

    # we have to keep the type immutable and then create a copy or else pass by argument will
    # mutate the variable
    messages = (
        ('1', '2', '3', '4', '5', '6', '7', '8', '9'),
        ('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17'),
        ('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'),
        ('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16'),
        ('1', '2', '3', '4'),
        ('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20')
    )

    # this will get consumed if we pass as list of lists so make a copy
    messages_copy = [list(msgs) for msgs in messages]

    multiplex(messages_copy)
    messages_received = demultiplex('muxed_stream')

    assert len(messages_received[0]) == 9
    assert len(messages_received[1]) == 17
    assert len(messages_received[2]) == 20
    assert len(messages_received[3]) == 16
    assert len(messages_received[4]) == 24

def test_case_2():
    """Testing multiplexing with tons of lost messages."""

    # we have to keep the type immutable and then create a copy or else pass by argument will
    # mutate the variable
    messages = (
        ("This ain't", "no intro,", " this the entree"),
        (),
        (),
        (),
        ("Hit that", "intro with Kanye ", "and sound like Andre"),
        ("Tryna", "turn", "my", "baby", "mama", "to", "my", "fiancee"),
        ("She like"," music, she from", "Houston",  "like Auntie Yonce"),
        ("Man my daughter couldn't", "have a better mother"),
        ("If she ever", "find another, he", "better", "love", "her")
    )

    # this will get consumed if we pass as list of lists so make a copy
    messages_copy = [list(msgs) for msgs in messages]

    multiplex(messages_copy)
    messages_received = demultiplex('muxed_stream')

    assert len(messages_received[0]) == 3
    assert len(messages_received[1]) == 0
    assert len(messages_received[2]) == 0
    assert len(messages_received[3]) == 0
    assert len(messages_received[4]) == 22

if __name__ == '__main__':
    test_case_1()
    test_case_2()


# # Q1. Write a program to find all pairs of an integer array whose sum is equal to a given number?

def to_find_pairs(array,target):
    pairs = []

    for i in range(len(array)):
        for j in range(i+1,len(array)):
            if array[i] +array[j] == target:
                pairs.append((array[i],array[j]))
    return pairs

lst = list(map(int,input("Enter List elements seprated by a comma: ").split(",")))
num = int(input("Enter Target number: "))

result = to_find_pairs(lst,num)
print(result)


import os
import shutil

downloads = "/home/michaelexe/Downloads"
pictures = "/home/michaelexe/Pictures"
videos = "/home/michaelexe/Videos"
music = "/home/michaelexe/Music"
os.chdir(downloads)

for files in os.listdir():
	file_name, file_ext = os.path.splitext(files)

	if file_ext == ".jpg" or file_ext == ".jpeg" or file_ext == ".png":
		shutil.move(files, pictures)

	if file_ext == ".mp4":
		shutil.move(files, videos)

	if file_ext == ".mp3":
		shutil.move(files, music)


def read_input():
    return input().rstrip(), input().rstrip()

def print_occurrences(output):
    print(" ".join(map(str, output)))

def get_occurrences(pattern, text):
    return [
        i
        for i in range(len(text) - len(pattern) + 1)
        if text[i : i + len(pattern)] == pattern
    ]

class RabinKarp:
    _multiplier = 263
    _prime = 1000000007

    def __init__(self, pattern, text):
        self.text = text
        self.pattern = pattern

        self.n = len(self.pattern)
        self._pattern_hash = self._hash_func(self.pattern)

    def _hash_func(self, string):
        res = 0
        for c in reversed(string):
            res = (res * self._multiplier + ord(c)) % self._prime
        return res

    def _precompute_hashes(self, text, pattern_size):
        hashes = [0 for _ in range(len(text) - pattern_size + 1)]

        i_start = len(text) - pattern_size
        i_end = len(text)
        last_substring = text[i_start:i_end]

        hashes[len(text) - pattern_size] = self._hash_func(last_substring)

        y = 1
        for _ in range(pattern_size):
            y = (y * self._multiplier) % self._prime

        for i in range(len(text) - pattern_size - 1, -1, -1):
            hashes[i] = (
                self._multiplier * hashes[i + 1]
                + ord(text[i])
                - ord(text[i + pattern_size]) * y
            ) % self._prime

        return hashes

    def find_occurrences(self):
        """
        Print all the positions of the occurrences of pattern in text
        in the ascending order. Use 0-based indexing of positions in
        the text.
        """
        result = []

        hashes = self._precompute_hashes(self.text, len(self.pattern))

        for i in range(len(self.text) - len(self.pattern) + 1):
            substring = self.text[i : (i + self.n)]
            substring_hash = hashes[i]

            if substring_hash != self._pattern_hash:
                continue
            else:
                if substring == self.pattern:
                    result.append(i)
        return result

if __name__ == "__main__":
    pattern, text = read_input()
    rkp = RabinKarp(pattern, text)
    print_occurrences(rkp.find_occurrences())


def max_in_list(lista):
    return max(lista)

lista=[]
print("This program will return the highest number in a list you introduce. Write 'break' to finish")
while True:
    num=input("Introduce a number: ")
    if num=="break":
        break
    else:
        num=int(num)
        lista.append(num)
print (max_in_list(lista))


from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from volunteers.forms import Add_volunteer
from volunteers.models import Volunteer, EKnight
import datetime

def home(request):
	form = Add_volunteer()
	if 'volunteer' in request.GET and request.GET['volunteer']:
		name = request.GET['volunteer']
		users = Volunteer.objects.filter(name__icontains=name, arrived__lt=datetime.datetime.now())
		return render(request, 'home.html', {'searched': name, 'users': users, 'form': form})
	elif 'user_name' in request.GET and request.GET['user_name']:
		volunteer_id = Volunteer.objects.get(id=request.GET['user_name']).save()
	elif 'project' in request.GET and request.GET['project']:
		name = request.GET['project']
		projects = EKnight.objects.filter(name__icontains=name)
		return render(request, 'home.html', {'form': form, 'search_project': name, 'projects': projects})
	elif request.method == 'POST':
		form = Add_volunteer(request.POST, request.user)
		if form.is_valid():
			clean_form = form.cleaned_data
			added = Volunteer(name=clean_form['name'], email=clean_form['email'], phone=clean_form['phone'])
			added.save()
	return render(request, 'home.html', {'form': form})

def arrived(request):

	arrived = Volunteer.objects.filter(arrived=datetime.date.today())
	return render(request, 'arrived.html', {'arrived': arrived })


# coding: utf-8
import random
import os
import io
import sys
import _thread
import threading
import time
import requests
import xml.dom.minidom
import struct
import simplejson
from const import BCommand
import socket
import parser

mutex = threading.Lock()
DANMAKUs = []
TO_ENGINE=False
PRINT_JSON=False
DEBUG=False

def debug(msg):
    if DEBUG:
        print(msg)

def info(msg):
    print(msg)

def warn(msg):
    print(msg)

def error(msg):
    print(msg)

def syn_danmu_msg(msg):
    if msg=='':
        return
    if TO_ENGINE:
        mutex.acquire()
        DANMAKUs.append(msg)
        mutex.release()

def print_json(json_data):
    if PRINT_JSON:
        print('☘ ☘ Json format☘ ☘')
        print(json_data)
        print('\n')

def _tcp_start():
    print('Engine thread starting')
    serverName = '172.18.95.25'
    #serverName = '127.0.0.1'
    serverPort = 18090
    clientSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    clientSocket.settimeout(5)
    while True:
        try:
            clientSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            clientSocket.settimeout(5)
            clientSocket.connect((serverName,serverPort))
            break
        #except (ConnectionRefusedError, TimeoutError):
        except:
            print('Engine side may not running, please check!!')
            time.sleep(2)
            continue

    while True:
        mutex.acquire()
        held_lock=True
        if len(DANMAKUs)>0:
            try:
                msg = {'component':'DANMAKU', 'message':DANMAKUs[0]}
                clientSocket.send(simplejson.dumps(msg).encode())
                del DANMAKUs[0]
                ack = clientSocket.recv(512)
                debug('From Server:' + ack.decode())
            except (BrokenPipeError,ConnectionResetError):
            #except (ConnectionResetError):
                mutex.release()
                held_lock=False
                print('pipe broken, trying to re-connect')
                while True:
                    try:
                        clientSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
                        clientSocket.settimeout(5)
                        clientSocket.connect((serverName,serverPort))
                        break
                    #except (ConnectionRefusedError,ConnectionResetError,ConnectTimeoutError):
                    except:
                        print('failed to connect, trying to re-connect')
                        time.sleep(2)
                        continue
        if held_lock:
            mutex.release()
        time.sleep(0.03)
    clientSocket.close()

def _heartbeat(self):
    while True:
        time.sleep(30)
        #heartbeat_pack = struct.pack('!IHHII', length, magic_num, version, msg_type, data_exchange_pack)
        heartbeat_pack = struct.pack('!IHHII', 16, 16, 1, 2, 1)
        self.socket_client.send(heartbeat_pack + "".encode('utf-8'))
        print('❤ ❤ ❤ ❤ ❤ ❤ ❤ ❤ ❤ ❤ ❤\n')
        #\u2665

class DMJBot(object):
    def __init__(self, room_id, u_id=0):
        self.room_id = room_id
        self.api_room_detail_url = 'https://api.live.bilibili.com/api/player?id=cid:{}'.format(room_id)
        self.dm_host = None
        self.socket_client = self._set_up()
        self._uid = u_id or int(100000000000000.0 + 200000000000000.0 * random.random())
        self.magic = 16
        self.ver = 1
        self.into_room = 7
        self.package_type = 1
        self.max_data_length = 65495

    def _set_up(self):
        room_detail_xml_string = self._http_get_request(self.api_room_detail_url)
        xml_string = ('<root>' + room_detail_xml_string.strip() + '</root>').encode('utf-8')
        root = xml.dom.minidom.parseString(xml_string).documentElement
        dm_server = root.getElementsByTagName('dm_server')
        self.dm_host = dm_server[0].firstChild.data
        #self.dm_host = '120.92.112.150'

        # tcp_socket return
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        s.connect((self.dm_host, 2243))
        print(self.dm_host)
        return s

    def _http_get_request(self, url):
        s = requests.session()
        response = s.get(url)
        return response.text

    def _pack_socket_data(self, data_length, data):
        _data = data.encode('utf-8')
        _send_bytes = struct.pack('!IHHII', data_length, self.magic, self.ver, self.into_room, self.package_type)
        return _send_bytes + _data

    def _start(self):
        _thread.start_new_thread(_heartbeat, (self,))
        if TO_ENGINE:
            _thread.start_new_thread(_tcp_start, ())

        # 是JSON 前面要补16字节数据
        _dmj_data = simplejson.dumps({
            "roomid": self.room_id,
            "uid": self._uid,
        }, separators=(',', ':'))
        total_length = 16 + len(_dmj_data)
        data = self._pack_socket_data(total_length, _dmj_data)
        self.socket_client.send(data)
        # 会断是因为心跳问题，需要30秒内发送心跳
        # 这里先接收确认进入房间的信息
        self.socket_client.recv(16)

        left=0
        last_package=''
        f = open('test1.txt', 'a+', 1, encoding='utf-8')
        while True:
            if left>0:
                debug('❀❀❀❀❀❀❀❀❀❀ concate case ❀❀❀❀❀❀❀❀❀❀')
                debug('☘ ☘ left: ' + str(left))
                current_package=self.socket_client.recv(left)
                #print('☘ ☘ last_package: \n' + last_package+'\n')
                #print(current_package)#encoded bytes
                #print('☘ ☘ current_package: \n' + current_package.decode('utf-8')+'\n')
                #complete_msg=last_package+current_package[0:(len(current_package))].decode('utf-8')
                try:
                    complete_msg=(last_package + current_package).decode('utf-8')
                except UnicodeDecodeError:
                    print('**************************')
                    print('concate msg decode error')
                    print(last_package + current_package)
                    print('**************************')
                f.write(complete_msg)
                f.write('\n')

                syn_danmu_msg(parser.parse_danmu(complete_msg))
                #print('☘ ☘ complete_msglete_msg: \n' + complete_msg)
                print_json(complete_msg)
                debug('❀❀❀❀❀❀❀❀❀❀ concate case ❀❀❀❀❀❀❀❀❀❀\n\n')
                left=0
                continue

            pre_data = self.socket_client.recv(16)
            #print('☘ ☘ pre_data length: ' + str(len(pre_data)))
            if len(pre_data) != 16:
                print('pre_data length:' + str(len(pre_data)) + ', which is supposed to be 16...')
                continue

            try:
                claimed_len, magic, ver, message_type, package_type = struct.unpack('!IHHII', pre_data)
                if claimed_len < 1:
                    warn('claimed length less than 1, please check!!')
                    continue
            except struct.error:
                print ('pre_data: ' + pre_data.decode('utf-8'))
                print ('pre_data_len: ' + str(len(pre_data)))
            if(claimed_len == 16):
                print ('Only control string received, skip it...')
                print (claimed_len)
                continue
            try:
                debug('☘ ☘ claimed length: ' + str(claimed_len))
                if claimed_len<16:
                    warn('☘ ☘ claimed length is too small')
                    continue
                if claimed_len>2000 and left==0:
                    print('!!!!!!!!!!!!!!!!!!!!!!!!')
                    print('!!!!!!!!!!!!!!!!!!!!!!!!')
                    print('Unknown package, looks like last package was lost ????!!!')
                    print('!!!!!!!!!!!!!!!!!!!!!!!!')
                    print('!!!!!!!!!!!!!!!!!!!!!!!!')
                    continue

                danmu_msg_package = self.socket_client.recv(claimed_len-16)
                if len(danmu_msg_package) == 0:
                    continue
                actual_len=len(danmu_msg_package)
                if actual_len<10 and claimed_len<=(actual_len+16):
                    print('actual length is too small ' + str(actual_len))
                    continue

                debug('☘ ☘ actual length: ' + str(actual_len))
                if claimed_len>(actual_len+16):
                    left=claimed_len-(actual_len+16)
                    try:
                        last_package=danmu_msg_package
                        debug('☘ ☘' + str(left)+' bytes left, coming...')
                        continue
                    except UnicodeDecodeError:
                        print('UnicodeDecodeError***************************')
                        print(danmu_msg_package)
                        print('UnicodeDecodeError***************************\n\n')
                        continue
                danmu_msg_json = danmu_msg_package.decode('utf-8')
                print_json(danmu_msg_json)
                #json_data = simplejson.loads(danmu_msg_json)
                #check json format only
                simplejson.loads(danmu_msg_json)
                f.write(danmu_msg_json)
                f.write('\n')
                x = parser.parse_danmu(danmu_msg_json)
                syn_danmu_msg(x)
            except simplejson.JSONDecodeError:
                print('json error: ' + danmu_msg_json + '\n\n')
                #continue
            except UnicodeDecodeError:
                print('UnicodeDecodeError***************************')
                print(danmu_msg_package)
                print('UnicodeDecodeError***************************\n\n')
                #continue

if __name__ == '__main__':
    #room_id = 71084
    # 010101
    # room_id = 989474
    # 魔王127直播间
    #room_id = 7734200
    room_id = 21133
    dmj = DMJBot(room_id)
    dmj._start()


# -*- coding: utf-8 -*-
"""
Created on Thu Oct  7 18:30:35 2021

@author: ivand
"""

import matplotlib.pyplot as plt
import pandas as pd

def split_column(df, l_v, val):
    l_v_t = [x for x in l_v]
    l_v_t.append(val)
    records = df[l_v_t].to_records(index=False)
    records = list(records)
    values = []
    for record in records:
        r = []
        if '/' in record[-1]:
            op = record[-1].split('/')
            for o in op:
                r = [e for e in record]
                r[-1] = o
                values.append(r)
        else:
            r = [e for e in record]
            values.append(r)
    df_out = pd.DataFrame(values)
    df_out.columns = l_v_t
    return df_out

def plots_pandas(df, var, val):
    groups = df.groupby(val)[var].apply(list)
    titles = list(groups.keys())
    n_g = len(groups)
    fig, axs = plt.subplots(1, n_g)
    for i, (group, title) in enumerate(zip(groups, titles)):
        group.sort()
        x = list(range(len(group)))
        axs[i].scatter(x, group)
        axs[i].set_title(title)
    plt.suptitle(f'{var} by {val}')
    plt.figure()
    df.boxplot(column=[var], by=val)
    plt.figure()
    df[var].hist(bins=8, by=df[val])
    plt.suptitle(f'{var} by {val}')

def summary_pandas(df, l_v, val):
    labels = df[val].unique().tolist()
    for v in l_v:
        for label in labels:
            label_filter = df[val] == label
            print(f'----------------Valores completos para {label} para la variable {v} ---------------')
            print(df[label_filter][v].describe())
            print('--------------------------------------------')
            # a = 10
            # print(f'++++++++ Valores recortados para {label} para la variable {v} al {a}% +++++++++')
            # n = df[v].count()
            # k = int(a/100*n)
            # df_trim = df.sort_values(by=v, ignore_index=True)
            # df_trim = df_trim[k:n-k]
            # print(df_trim[label_filter][v].describe())
            # print('+++++++++++++++++++++++++++++++++++++++++++++++')
            print('++++++++++++++++++++++')
        plots_pandas(df, v, val)
        # plots_pandas(df_trim, v, val)

w_d = 'C:/Users/ivand/Desktop/Universidad/Ago-Dic2021/MineriaDatos/Data/'
i_f = w_d + 'info_students.csv'

df = pd.read_csv(i_f)

l_v = ['age', 'height', 'weight', 'semester', 'courses_taken']
l_v = ['height']

summary_pandas(df, l_v, 'sex')

# df_out = split_column(df, l_v, 'pet')
# summary_pandas(df_out, l_v, 'pet')


'''
В массиве случайных целых чисел поменять местами минимальный и максимальный элементы.

разбор не смотрел
'''

import random

SIZE = 10
MIN_ITEM = 0
MAX_ITEM = 100
array = [random.randint(MIN_ITEM, MAX_ITEM) for _ in range(SIZE)]
print(array)

min_elem = MAX_ITEM
min_index = 0
max_elem = 0
max_index = 0

for i, item in enumerate(array):
    if min_elem > item:
        min_elem = item
        min_index = i
    if max_elem < item:
        max_elem = item
        max_index = i
print(f'min-{min_elem}, index-{min_index}')
print(f'max-{max_elem}, index {max_index}')

array[min_index], array[max_index] = array[max_index], array[min_index]
print(array)


import json
import datetime
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# API usage : https://api.covid19india.org/documentation/statedaily.html

states = ['ap', 'ar', 'as', 'br', 'ct', 'ga', 'gj', 'hp',
          'hr', 'jh', 'ka', 'kl', 'mh', 'ml', 'mn', 'mp',
          'mz', 'nl', 'or', 'pb', 'rj', 'sk', 'tg', 'tn',
          'tr', 'up', 'ut', 'wb']
uts = ['an', 'ch', 'dd', 'dl', 'dn', 'jk', 'la', 'ld', 'py']

def string_date_to_standard_date1(date):
    '''
    Args:
        date : in string format, Ex - "2020-03-14"
    return date in datetime.date format so that relational operator works on it
    '''
    try:
        year, mon, date = map(int, date.split('-'))
        return datetime.datetime(year, mon, date)
    except:
        print("Please enter dates in YYYY-MM-DD format!")
        exit(1)

def string_date_to_standard_date2(date):
    '''
    Args:
        date : in string format, Ex - "15-Mar-20"
    return date in datetime.date format so that relational operator works on it
    '''
    try:
        date, mon, year = date.split('-')
        mon_name_to_num = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4,
                           'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8,
                           'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
        mon = mon_name_to_num[mon]
        return datetime.datetime(2000 + int(year), mon, int(date))
    except:
        return None

def json_to_df(json_file_path):
    '''
    Args:
        Takes file path of json file
    returns pandas dataframe
    '''
    try:
        with open(json_file_path) as json_file:
            dataset = json.load(json_file)['states_daily']
    except:
        print("File not found!!")
        exit(1)

    # Remove those states from global list which are not in data
    df = pd.DataFrame(dataset)
    for state in states:
        if state not in df.columns:
            states.remove(state)

    # Remove those UT's from global list which are not in data
    for ut in uts:
        if ut not in df.columns:
            uts.remove(ut)

    return df

def remove_useless_cols(df, cols):
    '''
    Args:
        cols - columns which we dont want to drop
    Function:
        remove useless columns from dataframe
    '''
    cols = [col_name for col_name in df.columns if col_name not in cols]
    df.drop(cols, axis=1, inplace=True)

def tranform_df_dates(df, date):
    '''
    Function:
        convert string date to datetime.date in 'date' column
        and if there is some issue while converting than remove that row
    '''
    df[date] = df[date].apply(string_date_to_standard_date2)
    df.dropna(subset=[date], inplace=True)

def remove_useless_rows(df, start_date, end_date):
    '''
    Function:
        removes those rows from dataframe whose dates are not between start_date and end_date
    '''
    df.drop(df[(start_date > df['date']) | (
        df['date'] > end_date)].index, axis=0, inplace=True)

def change_col_pos(df, cols):
    '''
        just to make df consistent in look by bringing ['date', 'status', 'tt'] in front,
        followed by UT's anf then all states
    '''
    for idx, col_name in enumerate(cols):
        col = df.pop(col_name)
        df.insert(idx, col_name, col)

def str_to_int(x):
    '''
        convert string to integer and if there is any error then return 0
    '''
    try:
        x = int(x)
        return x
    except:
        return 0

def string_to_int_cols(df, cols):
    '''
        change all values from str to int and if there is some error then place 0 value there
    '''
    rows = df.shape[0]
    for col_name in cols:
        df[col_name] = df[col_name].apply(str_to_int)

def pre_process_data(json_file_path, start_date, end_date):
    '''
    '''
    cols = ['date', 'status'] + states + uts
    df = json_to_df(json_file_path)
    start_date = string_date_to_standard_date1(start_date)
    end_date = string_date_to_standard_date1(end_date)

    if start_date > end_date:
        print("Enter start_date less than end_date!!")
        exit(1)

    remove_useless_cols(df, cols)
    tranform_df_dates(df, 'date')
    remove_useless_rows(df, start_date, end_date)
    change_col_pos(df, ['date', 'status'] + uts)
    string_to_int_cols(df, states + uts)
    df.sort_values(by=['date'])

    confirmed_df = df.loc[df['status'] == 'Confirmed'].copy()
    recovered_df = df.loc[df['status'] == 'Recovered'].copy()
    deceased_df = df.loc[df['status'] == 'Deceased'].copy()

    cols.remove('status')
    remove_useless_cols(confirmed_df, cols)
    remove_useless_cols(recovered_df, cols)
    remove_useless_cols(deceased_df, cols)

    return confirmed_df, recovered_df, deceased_df

def Q1_1(json_file_path, start_date, end_date):
    """Q1 function
    Args:
        json_file_path (TYPE): Description
        start_date (TYPE): Description
        end_date (TYPE): Description
    """
    confirmed_df, recovered_df, deceased_df = pre_process_data(
        json_file_path, start_date, end_date)
    confirmed_count = confirmed_df[states + uts].sum().sum()
    recovered_count = recovered_df[states + uts].sum().sum()
    deceased_count = deceased_df[states + uts].sum().sum()
    print("\nQ1_1 :-\n ")
    print('confirmed_count: ', confirmed_count, 'recovered_count: ',
          recovered_count, 'deceased_count: ', deceased_count)
    return confirmed_count, recovered_count, deceased_count

def Q1_2(json_file_path, start_date, end_date):
    """Q1 function
    Args:
        json_file_path (TYPE): Description
        start_date (TYPE): Description
        end_date (TYPE): Description
    """
    confirmed_df, recovered_df, deceased_df = pre_process_data(
        json_file_path, start_date, end_date)
    state = 'dl'
    confirmed_count = confirmed_df[state].sum()
    recovered_count = recovered_df[state].sum()
    deceased_count = deceased_df[state].sum()

    print("\nQ1_2 :-\n ")
    print('confirmed_count: ', confirmed_count, 'recovered_count: ',
          recovered_count, 'deceased_count: ', deceased_count)
    return confirmed_count, recovered_count, deceased_count

def Q1_3(json_file_path, start_date, end_date):
    """Q1 function
        Args:
        json_file_path (TYPE): Description
        start_date (TYPE): Description
        end_date (TYPE): Description
    """
    states = ['dl', 'mh']
    confirmed_df, recovered_df, deceased_df = pre_process_data(
        json_file_path, start_date, end_date)
    confirmed_count = confirmed_df[states].sum().sum()
    recovered_count = recovered_df[states].sum().sum()
    deceased_count = deceased_df[states].sum().sum()

    print("\nQ1_3 :-\n ")
    print('confirmed_count: ', confirmed_count, 'recovered_count: ',
          recovered_count, 'deceased_count: ', deceased_count)
    return confirmed_count, recovered_count, deceased_count

def Q1_4(json_file_path, start_date, end_date):
    """Q1 function
    Args:
        json_file_path (TYPE): Description
        start_date (TYPE): Description
        end_date (TYPE): Description
    """
    confirmed_df, recovered_df, deceased_df = pre_process_data(
        json_file_path, start_date, end_date)
    confirmed_cumulative_sum = confirmed_df[states].cumsum().iloc[-1, :]
    highest_confirmed_value = confirmed_cumulative_sum.max()

    recovered_cumulative_sum = recovered_df[states].cumsum().iloc[-1, :]
    highest_recovered_value = recovered_cumulative_sum.max()

    deceased_cumulative_sum = deceased_df[states].cumsum().iloc[-1, :]
    highest_deceased_value = deceased_cumulative_sum.max()

    print("\nQ1_4 :-\n ")
    print('Confirmed :- ')
    print('Highest affected State is: ', list(
        confirmed_cumulative_sum[confirmed_cumulative_sum.values == highest_confirmed_value].index))
    print('Highest affected State count is: ', highest_confirmed_value, '\n')
    print('Recovered :- ')
    print('Highest affected State is: ', list(
        recovered_cumulative_sum[recovered_cumulative_sum.values == highest_recovered_value].index))
    print('Highest affected State count is: ', highest_recovered_value, '\n')
    print('Deceased :- ')
    print('Highest affected State is: ', list(
        deceased_cumulative_sum[deceased_cumulative_sum.values == highest_deceased_value].index))
    print('Highest affected State count is: ', highest_deceased_value, '\n')

def Q1_5(json_file_path, start_date, end_date):
    """Q1 function
    Args:
        json_file_path (TYPE): Description
        start_date (TYPE): Description
        end_date (TYPE): Description
    """
    confirmed_df, recovered_df, deceased_df = pre_process_data(
        json_file_path, start_date, end_date)
    confirmed_cumulative_sum = confirmed_df[states].cumsum().iloc[-1, :]
    lowest_confirmed_value = confirmed_cumulative_sum.min()

    recovered_cumulative_sum = recovered_df[states].cumsum().iloc[-1, :]
    lowest_recovered_value = recovered_cumulative_sum.min()

    deceased_cumulative_sum = deceased_df[states].cumsum().iloc[-1, :]
    lowest_deceased_value = deceased_cumulative_sum.min()

    print("\nQ1_5 :-\n ")
    print('Confirmed :- ')
    print('Lowest affected State is: ', list(
        confirmed_cumulative_sum[confirmed_cumulative_sum.values == lowest_confirmed_value].index))
    print('Lowest affected State count is: ', lowest_confirmed_value, '\n')
    print('Recovered :- ')
    print('Lowest affected State is: ', list(
        recovered_cumulative_sum[recovered_cumulative_sum.values == lowest_recovered_value].index))
    print('Lowest affected State count is: ', lowest_recovered_value, '\n')
    print('Deceased :- ')
    print('Lowest affected State is: ', list(
        deceased_cumulative_sum[deceased_cumulative_sum.values == lowest_deceased_value].index))
    print('Lowest affected State count is: ', lowest_deceased_value, '\n')

def Q1_6(json_file_path, start_date, end_date):
    """Q1 function
    Args:
        json_file_path (TYPE): Description
        start_date (TYPE): Description
        end_date (TYPE): Description
    """
    confirmed_df, recovered_df, deceased_df = pre_process_data(
        json_file_path, start_date, end_date)
    state = 'dl'
    highest_confirmed_count = confirmed_df[state].max()
    highest_recovered_count = recovered_df[state].max()
    highest_deceased_count = deceased_df[state].max()

    print("\nQ1_6 :-\n ")
    print('Confirmed :- ')
    print('Day: ', confirmed_df[confirmed_df[state] ==
                                highest_confirmed_count]['date'].to_string(index=False))
    print('Count: ', highest_confirmed_count, '\n')
    print('Recovered :- ')
    print('Day: ', recovered_df[recovered_df[state] ==
                                highest_recovered_count]['date'].to_string(index=False))
    print('Count: ', highest_recovered_count, '\n')
    print('Deceased :- ')
    print('Day: ', deceased_df[deceased_df[state] ==
                               highest_deceased_count]['date'].to_string(index=False))
    print('Count: ', highest_deceased_count, '\n')

def Q1_7(json_file_path, start_date, end_date):
    """Q1 function : You have to count all the active cases and print the live active cases as on date.
    Args:
        json_file_path (TYPE): Description
        start_date (TYPE): Description
        end_date (TYPE): Description
    """
    confirmed_df, recovered_df, deceased_df = pre_process_data(
        json_file_path, start_date, end_date)
    confirmed_cumulative_sum = confirmed_df[states].cumsum().iloc[-1, :]
    recovered_cumulative_sum = recovered_df[states].cumsum().iloc[-1, :]
    deceased_cumulative_sum = deceased_df[states].cumsum().iloc[-1, :]

    print("\nQ1_7 :-\n ")
    print("State \t\t\tActive_Cases")
    for state in states:
        print(
            state +
            "\t\t\t",
            confirmed_cumulative_sum[state] -
            recovered_cumulative_sum[state] -
            deceased_cumulative_sum[state])
    print()

def getXY(df, start_date, states):
    X = (df['date'] - start_date).to_list()
    X = [i.days for i in X]
    Y = df[states].sum(axis=1).cumsum().tolist()
    return X, Y

def Q2_1(json_file_path, start_date, end_date):
    """Q2 function
    Args:
        json_file_path (TYPE): Description
        start_date (TYPE): Description
        end_date (TYPE): Description
    """
    confirmed_df, recovered_df, deceased_df = pre_process_data(
        json_file_path, start_date, end_date)

    start_date = string_date_to_standard_date1(start_date)
    end_date = string_date_to_standard_date1(end_date)

    confirmed_X, confirmed_Y = getXY(confirmed_df, start_date, states + uts)
    recovered_X, recovered_Y = getXY(recovered_df, start_date, states + uts)
    deceased_X, deceased_Y = getXY(deceased_df, start_date, states + uts)

    name = "Q2_1"
    plt.title(name)
    plt.xlabel("Number of days from - " + str(start_date))
    plt.ylabel("Number of cases")
    plt.fill_between(
        confirmed_X,
        0,
        confirmed_Y,
        alpha=0.5,
        color='red',
        label="Confirmed")
    plt.fill_between(
        recovered_X,
        0,
        recovered_Y,
        alpha=0.5,
        color='blue',
        label="Recovered")
    plt.fill_between(
        deceased_X,
        0,
        deceased_Y,
        alpha=0.8,
        color='green',
        label="Deceased")

    plt.legend(loc=2)
    plt.savefig(name)
    plt.show()
    plt.close(plt.figure())
    plt.clf()

def Q2_2(json_file_path, start_date, end_date):
    """Q2 function
    Args:
        json_file_path (TYPE): Description
        start_date (TYPE): Description
        end_date (TYPE): Description
    """
    confirmed_df, recovered_df, deceased_df = pre_process_data(
        json_file_path, start_date, end_date)
    start_date = string_date_to_standard_date1(start_date)
    end_date = string_date_to_standard_date1(end_date)

    confirmed_X, confirmed_Y = getXY(confirmed_df, start_date, ['dl'])
    recovered_X, recovered_Y = getXY(recovered_df, start_date, ['dl'])
    deceased_X, deceased_Y = getXY(deceased_df, start_date, ['dl'])

    name = "Q2_2"
    plt.title(name)
    plt.xlabel("Number of days from - " + str(start_date))
    plt.ylabel("Number of cases")
    plt.fill_between(
        confirmed_X,
        0,
        confirmed_Y,
        color='red',
        label="Confirmed",
        alpha=0.5)
    plt.fill_between(
        recovered_X,
        0,
        recovered_Y,
        color='blue',
        label="Recovered",
        alpha=0.5)
    plt.fill_between(
        deceased_X,
        0,
        deceased_Y,
        color='green',
        label="Deceased",
        alpha=0.8)

    plt.legend(loc=2)
    plt.savefig(name)
    plt.show()
    plt.close(plt.figure())
    plt.clf()

def Q2_3(json_file_path, start_date, end_date):
    """Q2 function
    Args:
        json_file_path (TYPE): Description
        start_date (TYPE): Description
        end_date (TYPE): Description
    """
    confirmed_df, recovered_df, deceased_df = pre_process_data(
        json_file_path, start_date, end_date)
    start_date = string_date_to_standard_date1(start_date)
    end_date = string_date_to_standard_date1(end_date)

    confirmed_X, confirmed_Y = getXY(confirmed_df, start_date, states + uts)
    recovered_X, recovered_Y = getXY(recovered_df, start_date, states + uts)
    deceased_X, deceased_Y = getXY(deceased_df, start_date, states + uts)
    active_X, active_Y = confirmed_X, []

    for i in range(len(active_X)):
        active_Y.append(confirmed_Y[i] - recovered_Y[i] - deceased_Y[i])

    name = "Q2_3"
    plt.title(name)
    plt.xlabel("Number of days from - " + str(start_date))
    plt.ylabel("Number of cases")
    plt.fill_between(
        active_X,
        0,
        active_Y,
        color='red',
        label="Active",
        alpha=0.5)
    plt.legend(loc=2)
    plt.savefig(name)
    plt.show()
    plt.close(plt.figure())
    plt.clf()

def Q3(json_file_path, start_date, end_date):
    """Q3 function
    Args:
        json_file_path (TYPE): Description
        start_date (TYPE): Description
        end_date (TYPE): Description
    """
    state = 'dl'
    confirmed_df, recovered_df, deceased_df = pre_process_data(
        json_file_path, start_date, end_date)
    start_date = string_date_to_standard_date1(start_date)
    end_date = string_date_to_standard_date1(end_date)

    def getXY(df):
        X = (df['date'] - start_date).to_list()
        X = [i.days for i in X]
        Y = df[state].tolist()
        return X, Y

    confirmed_X, confirmed_Y = getXY(confirmed_df)
    recovered_X, recovered_Y = getXY(recovered_df)
    deceased_X, deceased_Y = getXY(deceased_df)

    def find_intercept_slope(X, Y):
        n = len(X)
        sum_X = sum(X)
        sum_Y = sum(Y)
        sum_sq_X = sum([x * x for x in X])
        sum_sq_Y = sum([y * y for y in Y])
        sum_XY = sum([x * y for x, y in zip(X, Y)])
        intercept = (sum_Y * sum_sq_X - sum_X * sum_XY) / \
            (n * sum_sq_X - sum_X**2)
        slope = (n * sum_XY - sum_X * sum_Y) / (n * sum_sq_X - sum_X**2)
        return intercept, slope

    confirmed_intercept, confirmed_slope = find_intercept_slope(
        confirmed_X, confirmed_Y)
    recovered_intercept, recovered_slope = find_intercept_slope(
        recovered_X, recovered_Y)
    deceased_intercept, deceased_slope = find_intercept_slope(
        deceased_X, deceased_Y)

    def plot(X, Y, slope, intercept, name):
        plt.scatter(X, Y, s=10)
        plt.title(name)
        plt.xlabel("Number of days from start_date")
        plt.ylabel("Number of cases")
        axes = plt.gca()
        x_vals = np.array(axes.get_xlim())
        y_vals = intercept + slope * x_vals
        plt.plot(x_vals, y_vals, color='red')
        plt.savefig(name)
        plt.show()
        plt.close(plt.figure())
        plt.clf()

    plot(confirmed_X, confirmed_Y, confirmed_slope, confirmed_intercept, "confirmed_Q3")
    plot(recovered_X, recovered_Y, recovered_slope, recovered_intercept, "recovered_Q3")
    plot(deceased_X, deceased_Y, deceased_slope, deceased_intercept, "deceased_Q3")

    print('\nQ3 :-\n')
    print(
        confirmed_intercept,
        confirmed_slope,
        recovered_intercept,
        recovered_slope,
        deceased_intercept,
        deceased_slope)
    return confirmed_intercept, confirmed_slope, recovered_intercept, recovered_slope, deceased_intercept, deceased_slope

if __name__ == "__main__":
    # execute only if run as a script
    print('2018101 and 2018261')  # Please put this first

    start_date = "2020-03-14"
    end_date = "2020-09-05"

    Q1_1('states_daily.json', start_date, end_date)
    Q1_2('states_daily.json', start_date, end_date)
    Q1_3('states_daily.json', start_date, end_date)
    Q1_4('states_daily.json', start_date, end_date)
    Q1_5('states_daily.json', start_date, end_date)
    Q1_6('states_daily.json', start_date, end_date)
    Q1_7('states_daily.json', start_date, end_date)
    Q2_1('states_daily.json', start_date, end_date)
    Q2_2('states_daily.json', start_date, end_date)
    Q2_3('states_daily.json', start_date, end_date)
    Q3('states_daily.json', start_date, end_date)