from my_utils.tests import test_and_solve
import re
from collections import deque

DAY_NR = 17
FMT1 = re.compile("x=(\d+), y=(\d+)..(\d+)")
FMT2 = re.compile("y=(\d+), x=(\d+)..(\d+)")

def parse_line(line):
    match = FMT1.match(line)
    if match:
        x1_start, x0_start, x0_end = match.groups()
        x1_end = x1_start
    else:
        match = FMT2.match(line)
        if not match:
            print(line)
        x0_start, x1_start, x1_end = match.groups()
        x0_end = x0_start
    clay = [int(x) for x in [x0_start, x0_end, x1_start, x1_end]]
    return [cc + ii for cc, ii in zip(clay, [0, 1, 0, 1])]

def get_scan_str(drips, clay, stagnant, flowing, extent):
    scan_str = ''
    for ii in range(extent[0], extent[1]):
        for jj in range(extent[2], extent[3]):
            pos = (ii, jj)
            if pos in clay:
                scan_str += '#'
            elif pos in stagnant:
                scan_str += '~'
            elif pos in drips:
                scan_str += '*'
            elif pos in flowing:
                scan_str += '|'
            else:
                scan_str += '.'
        scan_str += f'\t{ii}\n'
    return scan_str

def part_1(puzzle_input, print_every=1):
    """Function which calculates the solution to part 1

    Arguments
    ---------

    Returns
    -------

    Issue:
Water shouldn't be 'stagnant' if the top is open on one side, but
currently fills to the left with stagnant, which then holds water
like clay
............|....................................|...............
............|....................................|...............
............|....................................|...............
||||||||||||||||||||||||||||||||||||||||||||||||||...................
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~||||||||||||||||....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#|....
|###############################################################|....
    """
    veins = [parse_line(line) for line in puzzle_input]
    x0_min = min([xx for vv in veins for xx in vv[:2]])
    x0_max = max([xx for vv in veins for xx in vv[:2]])
    x1_min = min([xx for vv in veins for xx in vv[2:]])
    x1_max = max([xx for vv in veins for xx in vv[2:]])
    clay = set()
    for vein in veins:
        x0_start, x0_end, x1_start, x1_end = vein
        for ii in range(x0_start, x0_end):
            for jj in range(x1_start, x1_end):
                clay.add((ii, jj))
    everything = clay.copy()
    stagnant = set()
    flowing = set()
    # take from right, append to left
    drips = deque([(0,500)])
    jj = 0
    while len(drips) > 0:
        if print_every is not None:
            if (jj % print_every) == 0:
                print(f"Iteration: {jj}")
                print(get_scan_str(drips, clay, stagnant, flowing,
                                   [x0_min-1, x0_max,
                                    x1_min-1, x1_max+1]))
                print(drips)
        for ii in range(len(drips)):
            drip = drips.pop()
            below = (drip[0]+1, drip[1])
            left = (drip[0], drip[1]-1)
            right = (drip[0], drip[1]+1)
            if drip[0] == x0_max-1 or (drip[0] == x0_min and drip[1] != 500):
                flowing.add(drip)
                everything.add(drip)
            elif below in flowing:
                flowing.add(drip)
                everything.add(drip)
            elif below not in clay and below not in stagnant:
                drips.appendleft(below)
            elif ((left in everything or left == drips[-1])
                  and (right in everything or right == drips[-1])):
                if left in flowing or right in flowing:
                    flowing.add(drip)
                    everything.add(drip)
#                elif left == drips[-1] or right == drips[-1]:
#                    flowing.add(drip)
#                    everything.add(drip)
                else:
                    stagnant.add(drip)
                    everything.add(drip)
            elif right not in everything and right != drips[-1]:
                drips.appendleft(right)
            elif left not in everything and left != drips[-1]:
                drips.appendleft(left)
            else:
                raise ValueError
        if (1, 500) not in flowing:
            drips.appendleft((0,500))
        jj += 1
    print(f"Iteration: {jj}")
    print(get_scan_str(drips, clay, stagnant, flowing,
                       [x0_min-1, x0_max,
                        x1_min-1, x1_max+1]))
    return len(flowing) + len(stagnant) - x0_min

def part_2(puzzle_input):
    """Function which calculates the solution to part 2

    Arguments
    ---------

    Returns
    -------
    """
    return None

if __name__ == "__main__":
    # Testing data:
    #    - each element of input list will be passed to function
    #    - the relative element in output list is the expected output
    test_in= """x=495, y=2..7
y=7, x=495..501
x=501, y=3..7
x=498, y=2..4
x=506, y=1..2
x=498, y=10..13
x=504, y=10..13
y=13, x=498..504"""
    test_data1 = {
        'inputs': [test_in.split('\n')],
        'outputs': [57]
    }
    test_data2 = {
        'inputs': [],
        'outputs': []
    }

    # Code to import the actual puzzle input
    with open(f'./inputs/day_{DAY_NR}.txt') as f:
#        puzzle_input = f.read().strip()
        puzzle_input = [line.rstrip('\n') for line in f]

    # Performs testing and calculates puzzle outputs
    test_and_solve(test_datas=[test_data1, test_data2],
                   functions=[part_1, part_2],
#                   puzzle_input=puzzle_input,
                   test_functions=None)


import json
import os
import subprocess
from datetime import datetime

import requests
from hachoir.metadata import extractMetadata
from hachoir.parser import createParser
from telethon.tl.types import DocumentAttributeVideo

from userbot import LOGS, HELPER
from userbot.events import register

TEMP_DOWNLOAD_DIRECTORY = os.environ.get("TMP_DOWNLOAD_DIRECTORY", "./")

def progress(current, total):
    """ Logs the download progress """
    LOGS.info(
        "Downloaded %s of %s\nCompleted %s",
        current, total, (current / total) * 100
    )

@register(pattern=r".download(?: |$)(.*)", outgoing=True)
async def download(target_file):
    """ For .download command, download files to the userbot's server. """
    if not target_file.text[0].isalpha() and target_file.text[0] not in ("/", "#", "@", "!"):
        if target_file.fwd_from:
            return
        await target_file.edit("Processing ...")
        input_str = target_file.pattern_match.group(1)
        if not os.path.isdir(TEMP_DOWNLOAD_DIRECTORY):
            os.makedirs(TEMP_DOWNLOAD_DIRECTORY)
        if target_file.reply_to_msg_id:
            start = datetime.now()
            downloaded_file_name = await target_file.client.download_media(
                await target_file.get_reply_message(),
                TEMP_DOWNLOAD_DIRECTORY,
                progress_callback=progress,
            )
            end = datetime.now()
            duration = (end - start).seconds
            await target_file.edit(
                "Downloaded to `{}` in {} seconds.".format(downloaded_file_name, duration)
            )
        elif "|" in input_str:
            url, file_name = input_str.split("|")
            url = url.strip()
            # https://stackoverflow.com/a/761825/4723940
            file_name = file_name.strip()
            required_file_name = TEMP_DOWNLOAD_DIRECTORY + "" + file_name
            start = datetime.now()
            resp = requests.get(url, stream=True)
            with open(required_file_name, "wb") as file:
                total_length = resp.headers.get("content-length")
                # https://stackoverflow.com/a/15645088/4723940
                if total_length is None:  # no content length header
                    file.write(resp.content)
                else:
                    downloaded = 0
                    total_length = int(total_length)
                    for chunk in resp.iter_content(chunk_size=128):
                        downloaded += len(chunk)
                        file.write(chunk)
                        done = int(100 * downloaded / total_length)
                        download_progress_string = "Downloading ... [%s%s]" % (
                            "=" * done,
                            " " * (50 - done),
                        )
                        LOGS.info(download_progress_string)
            end = datetime.now()
            duration = (end - start).seconds
            await target_file.edit(
                "Downloaded to `{}` in {} seconds.".format(required_file_name, duration)
            )
        else:
            await target_file.edit("Reply to a message to download to my local server.")

@register(pattern=r".uploadir (.*)", outgoing=True)
async def uploadir(udir_event):
    """ For .uploadir command, allows you to upload everything from a folder in the server"""
    if not udir_event.text[0].isalpha() and udir_event.text[0] not in ("/", "#", "@", "!"):
        if udir_event.fwd_from:
            return
        input_str = udir_event.pattern_match.group(1)
        if os.path.exists(input_str):
            start = datetime.now()
            await udir_event.edit("Processing ...")
            lst_of_files = []
            for r, d, f in os.walk(input_str):
                for file in f:
                    lst_of_files.append(os.path.join(r, file))
                for file in d:
                    lst_of_files.append(os.path.join(r, file))
            LOGS.info(lst_of_files)
            uploaded = 0
            await udir_event.edit(
                "Found {} files. Uploading will start soon. Please wait!".format(
                    len(lst_of_files)
                )
            )
            for single_file in lst_of_files:
                if os.path.exists(single_file):
                    # https://stackoverflow.com/a/678242/4723940
                    caption_rts = os.path.basename(single_file)
                    if not caption_rts.lower().endswith(".mp4"):
                        await udir_event.client.send_file(
                            udir_event.chat_id,
                            single_file,
                            caption=caption_rts,
                            force_document=False,
                            allow_cache=False,
                            reply_to=udir_event.message.id,
                            progress_callback=progress,
                        )
                    else:
                        thumb_image = os.path.join(input_str, "thumb.jpg")
                        metadata = extractMetadata(createParser(single_file))
                        duration = 0
                        width = 0
                        height = 0
                        if metadata.has("duration"):
                            duration = metadata.get("duration").seconds
                        if metadata.has("width"):
                            width = metadata.get("width")
                        if metadata.has("height"):
                            height = metadata.get("height")
                        await udir_event.client.send_file(
                            udir_event.chat_id,
                            single_file,
                            caption=caption_rts,
                            thumb=thumb_image,
                            force_document=False,
                            allow_cache=False,
                            reply_to=udir_event.message.id,
                            attributes=[
                                DocumentAttributeVideo(
                                    duration=duration,
                                    w=width,
                                    h=height,
                                    round_message=False,
                                    supports_streaming=True,
                                )
                            ],
                            progress_callback=progress,
                        )
                    os.remove(single_file)
                    uploaded = uploaded + 1
            end = datetime.now()
            duration = (end - start).seconds
            await udir_event.edit("Uploaded {} files in {} seconds.".format(uploaded, duration))
        else:
            await udir_event.edit("404: Directory Not Found")

@register(pattern=r".upload (.*)", outgoing=True)
async def upload(u_event):
    """ For .upload command, allows you to upload a file from the userbot's server """
    if not u_event.text[0].isalpha() and u_event.text[0] not in ("/", "#", "@", "!"):
        if u_event.fwd_from:
            return
        if u_event.is_channel and not u_event.is_group:
            await u_event.edit("`Uploading isn't permitted on channels`")
            return
        await u_event.edit("Processing ...")
        input_str = u_event.pattern_match.group(1)
        if input_str in ("userbot.session", "config.env"):
            await u_event.edit("`That's a dangerous operation! Not Permitted!`")
            return
        if os.path.exists(input_str):
            start = datetime.now()
            await u_event.client.send_file(
                u_event.chat_id,
                input_str,
                force_document=True,
                allow_cache=False,
                reply_to=u_event.message.id,
                progress_callback=progress,
            )
            end = datetime.now()
            duration = (end - start).seconds
            await u_event.edit("Uploaded in {} seconds.".format(duration))
        else:
            await u_event.edit("404: File Not Found")

def get_video_thumb(file, output=None, width=90):
    """ Get video thhumbnail """
    metadata = extractMetadata(createParser(file))
    popen = subprocess.Popen(
        [
            "ffmpeg",
            "-i",
            file,
            "-ss",
            str(
                int((0, metadata.get("duration").seconds)[metadata.has("duration")] / 2)
            ),
            "-filter:v",
            "scale={}:-1".format(width),
            "-vframes",
            "1",
            output,
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.DEVNULL,
    )
    if not popen.returncode and os.path.lexists(file):
        return output
    return None

def extract_w_h(file):
    """ Get width and height of media """
    command_to_run = [
        "ffprobe",
        "-v",
        "quiet",
        "-print_format",
        "json",
        "-show_format",
        "-show_streams",
        file,
    ]
    # https://stackoverflow.com/a/11236144/4723940
    try:
        t_response = subprocess.check_output(command_to_run, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as exc:
        LOGS.warning(exc)
    else:
        x_reponse = t_response.decode("UTF-8")
        response_json = json.loads(x_reponse)
        width = int(response_json["streams"][0]["width"])
        height = int(response_json["streams"][0]["height"])
        return width, height

@register(pattern=r".uploadas(stream|vn|all) (.*)", outgoing=True)
async def uploadas(uas_event):
    """ For .uploadas command, allows you to specify some arguments for upload. """
    if not uas_event.text[0].isalpha() and uas_event.text[0] not in ("/", "#", "@", "!"):
        if uas_event.fwd_from:
            return
        await uas_event.edit("Processing ...")
        type_of_upload = uas_event.pattern_match.group(1)
        supports_streaming = False
        round_message = False
        spam_big_messages = False
        if type_of_upload == "stream":
            supports_streaming = True
        if type_of_upload == "vn":
            round_message = True
        if type_of_upload == "all":
            spam_big_messages = True
        input_str = uas_event.pattern_match.group(2)
        thumb = None
        file_name = None
        if "|" in input_str:
            file_name, thumb = input_str.split("|")
            file_name = file_name.strip()
            thumb = thumb.strip()
        else:
            file_name = input_str
            thumb_path = "a_random_f_file_name" + ".jpg"
            thumb = get_video_thumb(file_name, output=thumb_path)
        if os.path.exists(file_name):
            start = datetime.now()
            metadata = extractMetadata(createParser(file_name))
            duration = 0
            width = 0
            height = 0
            if metadata.has("duration"):
                duration = metadata.get("duration").seconds
            if metadata.has("width"):
                width = metadata.get("width")
            if metadata.has("height"):
                height = metadata.get("height")
            try:
                if supports_streaming:
                    await uas_event.client.send_file(
                        uas_event.chat_id,
                        file_name,
                        thumb=thumb,
                        caption=input_str,
                        force_document=False,
                        allow_cache=False,
                        reply_to=uas_event.message.id,
                        attributes=[
                            DocumentAttributeVideo(
                                duration=duration,
                                w=width,
                                h=height,
                                round_message=False,
                                supports_streaming=True,
                            )
                        ],
                        progress_callback=progress,
                    )
                elif round_message:
                    await uas_event.client.send_file(
                        uas_event.chat_id,
                        file_name,
                        thumb=thumb,
                        allow_cache=False,
                        reply_to=uas_event.message.id,
                        video_note=True,
                        attributes=[
                            DocumentAttributeVideo(
                                duration=0,
                                w=1,
                                h=1,
                                round_message=True,
                                supports_streaming=True,
                            )
                        ],
                        progress_callback=progress,
                    )
                elif spam_big_messages:
                    await uas_event.edit("TBD: Not (yet) Implemented")
                    return
                end = datetime.now()
                duration = (end - start).seconds
                os.remove(thumb)
                await uas_event.edit("Uploaded in {} seconds.".format(duration))
            except FileNotFoundError as err:
                await uas_event.edit(str(err))
        else:
            await uas_event.edit("404: File Not Found")

HELPER.update({
    "download": ".download <link>\nUsage: Downloads file from link to the server."
})
HELPER.update({
    "upload": ".upload <link>\nUsage: Uploads a locally stored file to telegram."
})


import streamlit as st
from fpdf import FPDF
import time

class PDF(FPDF):
  def header(self):
    today = time.strftime("%d/%m/%Y")

    # First row
    self.set_font('Arial', '', 8)
    self.cell(63, 10, 'Envio:', 1, 0, 'C')
    self.set_font('Arial', 'B', 15)
    self.cell(63, 10, 'Remito "X"', 1, 0, 'C')
    self.set_font('Arial', '', 8)
    self.cell(63, 10, 'Papelera HG', 1, 0, 'C')
    # Line break
    self.ln(10)
    # Second row
    self.set_font('Arial', '', 8)
    self.cell(63, 10, f"Fecha: {today}", 1, 0, 'C')
    self.set_font('Arial', '', 8)
    self.cell(63, 10, 'Traslado mercaderías', 1, 0, 'C')
    self.set_font('Arial', '', 8)
    self.cell(63, 10, 'Original', 1, 0, 'C')
    # Line break
    self.ln(10)
    # Third row
    self.set_font('Arial', '', 8)
    self.cell(63, 20, "No válido como factura", 1, 0, 'C')
    self.set_font('Arial', '', 8)
    self.cell(63, 20, 'Se adjuntan facturas Originales', 1, 0, 'C')
    self.set_font('Arial', '', 8)
    self.cell(63, 20, f"Facturas: {st.session_state.invoice_information[0]}", 1, 0, 'C')
    # Line break
    self.ln(20)
    # Quantity and description
    self.set_font('Arial', 'B', 8)
    self.cell(64, 10, 'Cantidad:', 0, 0)
    self.cell(120, 10, 'Descripción:', 0, 0)
    self.ln(10)
    self.line(10, 60, 199, 60)
    self.ln(2)

  def footer(self):
    self.set_y(-80)
    self.set_font('Arial', '', 8)
    # First row
    self.line(10, 223, 199, 223)
    self.cell(63, 20, "De: García Horacio C.", 0, 0)
    self.cell(63, 20, "Recibe:", 0, 0)
    self.ln(4)
    self.cell(63, 20, "General Paz 1642", 0, 0)
    self.cell(63, 20, st.session_state.invoice_information[1], 0, 0)
    self.ln(4)
    self.cell(63, 20, "Bragado - Buenos Aires", 0, 0)
    self.ln(4)
    self.cell(63, 20, "CUIT: 20268659766", 0, 0)
    self.ln(4)
    self.cell(63, 20, "IVA: Responsable Monotributo", 0, 0)
    self.ln(8)
    # Second row
    self.line(10, 247, 199, 247)
    self.cell(63, 20, "Enviar por:", 0, 0)
    self.cell(63, 20, st.session_state.invoice_information[2], 0, 0)
    self.ln(8)
    # Third row
    self.line(10, 255, 199, 255)
    self.cell(63, 20, "Teléfono:", 0, 0)
    self.cell(63, 20, st.session_state.invoice_information[3], 0, 0)
    self.ln(8)
    # Fourth row
    self.line(10, 263, 199, 263)
    self.cell(63, 20, "Valor seguro:", 0, 0)
    self.cell(63, 20, f"${st.session_state.invoice_information[4]}", 0, 0)
    self.ln(8)
    # Fifth row
    self.line(10, 271, 199, 271)
    self.cell(63, 20, "Fecha despacho:", 0, 0)
    self.cell(63, 20, "Bultos:", 0, 0)
    self.ln(8)
    # Sixth row
    self.line(10, 279, 199, 279)
    self.cell(63, 20, "Observaciones:", 0, 0)

def generate_invoice():
  pdf = PDF()
  pdf.alias_nb_pages()
  pdf.add_page()
  pdf.set_font('Arial', '', 8)

  # Products content
  products = st.session_state.selected_products
  for i in range(len(products)):
    print(products[i])
    pdf.cell(64, 5, products[i]["Cantidad"], 0, 0)
    pdf.cell(120, 5, products[i]["Descripción"], 0, 0)
    pdf.ln(5)

  st.download_button(
    "Descargar remito",
    data=pdf.output(dest="S").encode("latin-1"),
    file_name="remito.pdf"
  )


from game_data import rooms
from lib.models.game_state import GameState

from lib.models.player import Player

class Commands(object):
    def __init__(self, game_state: GameState):
        self.game_state = game_state
        # The register_command decorator was nicer, but it's harder to do inside a class
        # we can look at abstracting it out again, but we need to decide whether we want
        # state to be sent to every command if we do that, or manually register them so
        # they can be class methods (or do some other type of metaprogramming to register
        # them inside a class
        self.commands = {
            "quit": self.quit,
            "say": self.say,
            "help": self.help,
            "look": self.look,
            "go": self.go,
            "tell": self.tell,
        }

    def execute_command(self, player, command, param):
        if self.commands.get(command):
            self.commands[command](player, param)
        else:
            player.message("Unknown command '{}'".format(command))

    def quit(self, player: Player, params=None):
        """  quit           - Disconnects from the server"""
        self.game_state.server.disconnect(player.client)

    def say(self, player: Player, message: str):
        """  say <message>  - Says something out loud, e.g. 'say Hello'"""
        # go through every player in the game
        player.message(f"You say: {message}")
        for other_player in self.game_state.list_other_players(player):
            # if they're in the same room as the player
            if other_player._location == player._location:
                # send them a message telling them what the player said
                other_player.message(f"{player.name} says: {message}")

    def tell(self, player: Player, params):
        """ tell <player> message - Whisper a private message to the target player."""
        target = params.split(' ')[0]
        message = ' '.join(params.split(' ')[1:])

        if target.lower() not in [player.name.lower() for player in self.game_state.list_other_players(player)]:
            player.message("There is nobody online by that name.")

        for other_player in self.game_state.list_other_players(player):
            if other_player.name.lower() == target.lower():
                player.message(f"You whisper to {target}: {message}")
                other_player.message(f"{player.name} whispers to you: {message}")

    def help(self, player: Player, params=None):
        # send the player back the list of possible commands
        player.message("Commands:")
        for command in self.commands.values():
            if command.__doc__ is not None:
                player.message(command.__doc__)

    def look(self, player: Player, params=None):
        """  look           - Examines the surroundings, e.g. 'look'"""
        # store the player's current room
        current_location = rooms[player._location]

        # send the player back the description of their current room
        player.message(current_location.description)

        players_here = self._get_players_with(player)

        # send player a message containing the list of players in the room
        player.message("Players here: {}".format(", ".join([player.name for player in players_here])))
        # send player a message containing the list of exits from this room
        player.message("Exits are: {}".format(", ".join([ex.name for ex in current_location.exits])))

    def go(self, player: Player, params):
        """  go <exit>      - Moves through the exit specified, e.g. 'go outside'"""
        # store the exit name
        ex = params.lower()

        # store the player's current room
        current_location = rooms[player._location]

        # if the specified exit is found in the room's exits list
        if current_location.has_exit(ex):

            for other_player in self._get_players_with(player):
                other_player.message(f"{player.name} left via exit '{ex}'")

            # update the player's current room to the one the exit leads to
            player.move(current_location.get_exit(ex).destination)

            for other_player in self._get_players_with(player):
                other_player.message(f"{player.name} arrived via exit '{ex}'")

        # the specified exit wasn't found in the current room
        else:
            # send back an 'unknown exit' message
            player.message(f"Unknown exit '{ex}'")

    def _get_players_with(self, player: Player):
        players = []

        for uuid, item in rooms[player._location].inventory.get_items():
            if isinstance(item, Player) and item is not player:
                players.append(item)

        return players


# Exer-11-Managing-a-context-using-the-with-statement

import csv
import pathlib

some_source = [[2,3,5], [7,11,13], [17,19,23]]
target_path = pathlib.Path('code/test.csv')
with target_path.open('w', newline='') as target_file:
    writer = csv.writer(target_file)
    writer.writerow(['column', 'data', 'headings'])
    for data in some_source:
        writer.writerow(data)

print('finished writing', target_path)

###

try:
    target_path = pathlib.Path('code/test.csv')
    with target_path.open('w', newline='') as target_file:
        writer = csv.writer(target_file)
        writer.writerow(['column', 'headings'])
        for data in some_source:
            writer.writerow(data)
            raise Exception("Just Testing")
except Exception as exc:
    print(target_file.closed)
    print(exc)
print('finished writing', target_path)


from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import InputRequired, Length, Regexp, Email

LETTERS_REGEXP = r'^[A-Za-ząćęłńóśźżĄĆĘŁŃÓŚŹŻ]+$'
PH_NUM_MESS = 'Number should look like YYXXXXXXXXX,' \
              ' where YYYY is the dialing code e.g. 0048 for Poland, and X’s are the digits of phone number'

# Employee's personal data form
class EmployeePersonalForm(FlaskForm):
    first_name = StringField('First Name', validators=[InputRequired(), Length(max=255), Regexp(LETTERS_REGEXP)])
    last_name = StringField('Last Name', validators=[InputRequired(), Length(max=255), Regexp(LETTERS_REGEXP)])
    tel_number = StringField('Phone number', validators=[InputRequired(), Regexp(r'^[0-9]{11}$', message=PH_NUM_MESS)])
    email = StringField('Email', validators=[InputRequired(), Length(max=100), Email()])
    position = StringField('Position', validators=[InputRequired()], render_kw={'readonly': True})


import math
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import UserManager
from django.db.models.expressions import RawSQL
from decimal import Decimal
from .utils import get_unical_name

class LocationManager(UserManager):
    def get_nearby(self, latitude, longitude, proximity, id):
        self_user = Client.objects.get(id=id)
        proximity = Decimal(proximity)
        cos_rad = Decimal(abs(math.cos(math.radians(latitude))*111.0))
        lon1 = longitude - proximity / cos_rad
        lon2 = longitude + proximity / cos_rad
        lat1 = latitude - proximity / Decimal(111.0)
        lat2 = latitude + proximity / Decimal(111.0)
        return Client.objects.filter(latitude__range=(lat1, lat2)).filter(longitude__range=(lon1, lon2)).exclude(id=id)

class Client(AbstractUser):
    GENDER_CHOICES = (
        ('male', 'Male'),
        ('female', 'Female'),
    )
    objects = LocationManager()
    avatar = models.ImageField(null=True, upload_to=get_unical_name)
    gender = models.CharField(max_length=6, choices=GENDER_CHOICES)
    surname = models.CharField(max_length=100)
    liking = models.ManyToManyField('self', through='Liker', related_name='likers', symmetrical=False)
    latitude = models.DecimalField(max_digits=10, decimal_places=6, null=True)
    longitude = models.DecimalField(max_digits=10, decimal_places=6, null=True)

    class Meta:
        indexes = [
            models.Index(fields=['latitude']),
            models.Index(fields=['longitude']),
        ]

class Liker(models.Model):
    user_from = models.ForeignKey(Client, related_name='rel_from_set', on_delete=models.CASCADE)
    user_to = models.ForeignKey(Client, related_name="rel_to_set", on_delete=models.CASCADE)

    def __str__(self):
        return '{} likes {}'.format(self.user_from, self.user_to)


import os
import random
import shutil
import sys

sys.path.append(os.path.abspath(os.path.join(__file__, "../dfmlmd", "..")))

import torch
import torch.nn as nn
from tool import dataset_classnum,construct_model_pool

from tensorboardX import SummaryWriter
from tool import set_maml, get_model, data2supportquery, bias,\
    compute_confidence_interval, get_dataloader, Timer
from logger import get_logger

class PURER(nn.Module):
    def __init__(self,args):
        super(PURER, self).__init__()
        self.args=args
        #file
        feature1='{}_{}_{}'.format(args.method,args.teacherMethod,args.inversionMethod)
        feature2_1 = '{}_{}_{}_{}APINum'.format(args.dataset, args.pre_backbone, args.backbone,args.APInum)
        if self.args.teacherMethod=='maml':
            feature2_2 = 'MAML_{}wPre_{}S_{}Q_{}trainStep_{}testStep_{}innerlr_{}outerlr_{}TaskBatch_{}Glr'.format(
                args.way_pretrain,args.num_sup_train,args.num_qur_train, args.inner_update_num,
                args.test_inner_update_num, args.inner_lr, args.outer_lr, args.episode_batch,args.Glr)
        elif self.args.teacherMethod=='protonet':
            feature2_2 = 'ProtoNet_{}wPre_{}S_{}Q_{}lr_{}TaskBatch_{}Glr'.format(
                args.way_pretrain, args.num_sup_train, args.num_qur_train, args.outer_lr, args.episode_batch, args.Glr)
        elif self.args.teacherMethod=='anil':
            feature2_2 = 'ANIL_{}wPre_{}S_{}Q_{}trainStep{}testStep{}innerlr_{}outerlr_{}Taskbatch_{}Glr'.format(
                args.way_pretrain, args.num_sup_train, args.num_qur_train, args.inner_update_num,
                args.test_inner_update_num, args.inner_lr, args.outer_lr, args.episode_batch, args.Glr)
        feature2=feature2_1+'_'+feature2_2

        if args.approx:
            feature2 = feature2 + '_1Order'
        feature2 = feature2 + '_{}'.format(args.extra)
        self.checkpoints_path = './checkpoints/'+ feature1 + '/' + feature2
        self.writer_path = os.path.join(self.checkpoints_path, 'writer')
        if os.path.exists(self.writer_path):
            shutil.rmtree(self.writer_path)
        os.makedirs(self.writer_path, exist_ok=True)
        os.makedirs(self.checkpoints_path, exist_ok=True)
        self.logger = get_logger(feature1 + '/' + feature2, output=self.checkpoints_path + '/' + 'log.txt')
        _, _, self.test_loader = get_dataloader(self.args)
        self.feature1=feature1
        self.feature2=feature2
        #meta model
        set_maml(True)
        self.model=get_model(args=args,type=args.backbone,set_maml_value=True,arbitrary_input=False).cuda(self.args.device)
        set_maml(False)
        self.model.trunk[-1].bias.data.fill_(0)
        self.optimizer = torch.optim.Adam(self.model.parameters(), lr=args.outer_lr)
        self.loss_fn = nn.CrossEntropyLoss()

        #synthesizer
        self.allData = torch.randn([(args.num_sup_train+args.num_qur_train) * dataset_classnum[self.args.dataset], args.channel, args.img_size, args.img_size], requires_grad=True, device='cuda',dtype=torch.float)
        self.allData_label = []
        for c_abs in range(dataset_classnum[self.args.dataset]):
            self.allData_label.append(torch.LongTensor([c_abs+ bias[self.args.dataset]] * (args.num_sup_train+args.num_qur_train)))
        self.allData_label = torch.cat(self.allData_label, 0).cuda(self.args.device)
        self.optimizer_di = torch.optim.Adam([self.allData], lr=self.args.Glr)

        self.generate = True
        self.adv = False
        self.model_pool_path = construct_model_pool(args=self.args, unique=False)

        self.model_pool_specific = []
        for i, path in enumerate(self.model_pool_path):
            tmp = torch.load(path)
            specific = tmp['specific']
            specific = [i + bias[self.args.dataset] for i in specific]
            self.model_pool_specific.append(specific)

    def get_teacher_from_pool(self,id):
        tmp = torch.load(self.model_pool_path[id])
        teacher = get_model(args=self.args, type=self.args.pre_backbone, set_maml_value=False,arbitrary_input=False).cuda(self.args.device)
        teacher.load_state_dict(tmp['teacher'])
        teacher.eval()
        return teacher

    def forward(self,x):
        scores  = self.model(x)
        return scores
    def train_once(self,return_acc=False):
        # generate
        if self.generate == True:
            loss_data = 0
            loss2 = 0
            for candidate_teacher_id in self.candidate_teacher_ids:
                teacher=self.get_teacher_from_pool(candidate_teacher_id)
                specific=self.model_pool_specific[candidate_teacher_id]
                mask=torch.tensor([False for _ in range(len(self.allData_label))]).cuda(self.args.device)
                targets_rel=[]
                for c_rel,c_abs in enumerate(specific):
                    mask=mask + (self.allData_label==c_abs)
                    targets_rel.append(torch.LongTensor([c_rel]*(self.args.num_sup_train+self.args.num_qur_train)))
                targets_rel=torch.cat(targets_rel,dim=0).cuda(self.args.device)
                inputs=self.allData[mask]
                if self.args.dataset == 'cifar100':
                    loss_data = loss_data + get_images_cifar(net=teacher, var_scale=0.0001,bn_reg_scale = 0.01,l2_coeff=0.00001,first_bn_multiplier=1,pre_inputs=inputs,targets=targets_rel)
                elif self.args.dataset == 'miniimagenet':
                    loss_data = loss_data + get_images_cifar(net=teacher, var_scale=0.0001,bn_reg_scale = 0.01,l2_coeff=0.00001,first_bn_multiplier=1,pre_inputs=inputs,targets=targets_rel)
                elif self.args.dataset == 'cub':
                    raise NotImplementedError
                elif self.args.dataset == 'flower':
                    raise NotImplementedError
                else:
                    raise NotImplementedError
                loss2 = loss2 + loss_data
            if self.adv:
                support_data, support_label_relative, query_data, query_label_relative = generate_random_task_data_fromDI(self.args,self.allData,self.allData_label)
                support, support_label_relative, query, query_label_relative = support_data.cuda(self.args.device), support_label_relative.cuda(self.args.device), query_data.cuda(self.args.device), query_label_relative.cuda(self.args.device)
                loss_outer=self.get_outer_loss(support=support,support_label_relative=support_label_relative,query=query,query_label_relative=query_label_relative)
                loss2 = loss2 + -1 * 10.0 * loss_outer
                self.adv=False
            self.optimizer_di.zero_grad()
            loss2.backward()
            self.optimizer_di.step()
            self.generate=False
        support_data, support_label_relative, query_data, query_label_relative = generate_random_task_data_fromDI(self.args, self.allData.detach(), self.allData_label)
        support, support_label_relative, query, query_label_relative = support_data.cuda(self.args.device), support_label_relative.cuda(self.args.device), query_data.cuda(self.args.device), query_label_relative.cuda(self.args.device)
        loss_outer,train_acc = self.get_outer_loss(support=support, support_label_relative=support_label_relative, query=query,query_label_relative=query_label_relative,return_acc=True)
        if return_acc:
            return loss_outer,train_acc
        else:
            return loss_outer
    def train_loop(self):
        timer = Timer()
        with SummaryWriter(self.writer_path) as writer:
            test_acc_max = 0
            max_it = 0
            max_pm = 0
            train_acc_max=None
            self.generate=True
            self.adv = False
            for it_id in range(1,self.args.episode_train//self.args.episode_batch+1):
                loss_batch = []
                acc_batch = []
                for _ in range(self.args.episode_batch):
                    self.candidate_teacher_ids = random.sample(range(len(self.model_pool_path)), self.args.candidate_size)
                    loss, train_acc = self.train_once(return_acc=True)
                    loss_batch.append(loss)
                    acc_batch.append(train_acc)

                self.generate = True
                loss = torch.stack(loss_batch).sum(0)
                train_acc = torch.stack(acc_batch).mean()
                writer.add_scalar(tag='train_loss', scalar_value=loss.item(),global_step=it_id)
                self.optimizer.zero_grad()
                loss.backward()
                self.optimizer.step()
                if it_id <= 4000:
                    self.adv = False
                else:
                    if self.adv == False:
                        if train_acc_max == None or train_acc > train_acc_max:
                            train_acc_max = train_acc
                            e_count = 0
                        else:
                            e_count = e_count + 1
                            if e_count == 6:
                                self.adv = True
                                e_count = 0
                                train_acc_max = None
                # val
                if it_id%200 == 0 or it_id==1:
                    test_acc_avg, test_pm = self.test_loop()
                    writer.add_scalar(tag='test_acc', scalar_value=test_acc_avg.item(),global_step=it_id)
                    if test_acc_avg > test_acc_max:
                        test_acc_max = test_acc_avg
                        max_it =it_id
                        max_pm = test_pm
                        torch.save(self.model.state_dict(),
                                   self.checkpoints_path + '/bestmodel.pth')
                    self.logger.info(
                        '[Epoch]:{}, [TestAcc]:{} +- {}. [BestEpoch]:{}, [BestTestAcc]:{} +- {}.'.format(
                            it_id, test_acc_avg, test_pm, max_it, test_acc_max,
                            max_pm))

                    print('ETA:{}/{}'.format(
                        timer.measure(),
                        timer.measure(it_id / (self.args.episode_train//self.args.episode_batch)))
                    )

    def test_once(self,support,support_label_relative,query, query_label_relative):
        if self.args.teacherMethod == 'maml':
            self.model.zero_grad()
            fast_parameters = list(
                self.model.parameters())  # the first gradient calcuated in line 45 is based on original weight
            for weight in self.model.parameters():
                weight.fast = None

            for task_step in range(self.args.test_inner_update_num):
                scores = self.forward(support)
                loss_inner = self.loss_fn(scores, support_label_relative)
                grad = torch.autograd.grad(loss_inner, fast_parameters, create_graph=True)  # build full graph support gradient of gradient
                if self.args.approx:
                    grad = [g.detach() for g in grad]  # do not calculate gradient of gradient if using first order approximation
                fast_parameters = []
                for k, weight in enumerate(self.model.parameters()):
                    # for usage of weight.fast, please see Linear_fw, Conv_fw in backbone.py
                    if weight.fast is None:
                        weight.fast = weight - self.args.inner_lr * grad[k]  # create weight.fast
                    else:
                        weight.fast = weight.fast - self.args.inner_lr * grad[ k]  # create an updated weight.fast, note the '-' is not merely minus value, but to create a new weight.fast
                    fast_parameters.append( weight.fast)  # gradients calculated in line 45 are based on newest fast weight, but the graph will retain the link to old weight.fasts
            # outer
            correct=0
            total=0
            scores = self.forward(query)
            prediction = torch.max(scores, 1)[1]
            correct = correct + (prediction.cpu() == query_label_relative.cpu()).sum()
            total = total + len(query_label_relative)
            acc=1.0*correct/total*100.0

            self.model.zero_grad()
            for weight in self.model.parameters():
                weight.fast = None
        elif self.args.teacherMethod == 'protonet':
            self.model.zero_grad()
            for weight in self.model.parameters():
                weight.fast = None
            z_support = self.model.getFeature(support)
            z_query = self.model.getFeature(query)
            z_support = z_support.contiguous().view(self.args.way_train * self.args.num_sup_train, -1)
            z_query = z_query.contiguous().view(self.args.way_train * self.args.num_qur_train, -1)
            z_support = z_support.contiguous()
            protos = []
            for c in range(self.args.way_train):
                protos.append(z_support[support_label_relative == c].mean(0))
            z_proto = torch.stack(protos, dim=0)
            z_query = z_query.contiguous().view(self.args.way_train * self.args.num_qur_train, -1)
            dists = euclidean_dist(z_query, z_proto)
            score = -dists
            logprobs = score
            correct, total = 0, 0
            prediction = torch.max(logprobs, 1)[1]
            correct = correct + (prediction.cpu() == query_label_relative.cpu()).sum()
            total = total + len(query_label_relative)
            acc = 1.0 * correct / total * 100.0
            self.model.zero_grad()
            for weight in self.model.parameters():
                weight.fast = None
        elif self.args.teacherMethod == 'anil':
            self.model.zero_grad()
            fast_parameters = list(self.model.trunk[-1].parameters())
            for weight in self.model.parameters():
                weight.fast = None

            for task_step in range(self.args.test_inner_update_num):
                scores = self.forward(support)
                loss_inner = self.loss_fn(scores, support_label_relative)
                grad = torch.autograd.grad(loss_inner, fast_parameters, create_graph=True)
                if self.args.approx:
                    grad = [g.detach() for g in grad]
                fast_parameters = []
                for k, weight in enumerate(self.model.trunk[-1].parameters()):
                    if weight.fast is None:
                        weight.fast = weight - self.args.inner_lr * grad[k]
                    else:
                        weight.fast = weight.fast - self.args.inner_lr * grad[k]
                    fast_parameters.append(weight.fast)
            # outer
            correct = 0
            total = 0
            scores = self.forward(query)
            prediction = torch.max(scores, 1)[1]
            correct = correct + (prediction.cpu() == query_label_relative.cpu()).sum()
            total = total + len(query_label_relative)
            acc = 1.0 * correct / total * 100.0

            self.model.zero_grad()
            for weight in self.model.parameters():
                weight.fast = None
        else:
            raise NotImplementedError
        return acc
    def test_loop(self):
        test_acc_all = []
        for test_batch in self.test_loader:
            data, _ = test_batch[0].cuda(self.args.device), test_batch[1].cuda(self.args.device)
            support, support_label_relative, query, query_label_relative = data2supportquery(self.args, data)
            test_acc=self.test_once(support=support,support_label_relative=support_label_relative,query=query, query_label_relative=query_label_relative)
            test_acc_all.append(test_acc)
        test_acc_avg, pm = compute_confidence_interval(test_acc_all)
        return test_acc_avg,pm

    def get_outer_loss(self, support, support_label_relative, query, query_label_relative,return_acc=False):
        if self.args.teacherMethod == 'maml':
            self.model.zero_grad()
            fast_parameters = list(self.model.parameters())  # the first gradient calcuated in line 45 is based on original weight
            for weight in self.model.parameters():
                weight.fast = None

            for task_step in range(self.args.inner_update_num):
                scores = self.forward(support)
                loss_inner = self.loss_fn(scores, support_label_relative)
                grad = torch.autograd.grad(loss_inner, fast_parameters,create_graph=True)  # build full graph support gradient of gradient
                if self.args.approx:
                    grad = [g.detach() for g in
                            grad]  # do not calculate gradient of gradient if using first order approximation
                fast_parameters = []
                for k, weight in enumerate(self.model.parameters()):
                    # for usage of weight.fast, please see Linear_fw, Conv_fw in backbone.py
                    if weight.fast is None:
                        weight.fast = weight - self.args.inner_lr * grad[k]  # create weight.fast
                    else:
                        weight.fast = weight.fast - self.args.inner_lr * grad[k]  # create an updated weight.fast, note the '-' is not merely minus value, but to create a new weight.fast
                    fast_parameters.append(weight.fast)  # gradients calculated in line 45 are based on newest fast weight, but the graph will retain the link to old weight.fasts
            # outer
            scores = self.forward(query)
            loss_outer = self.loss_fn(scores, query_label_relative)
            correct = 0
            total = 0
            prediction = torch.max(scores, 1)[1]
            correct = correct + (prediction.cpu() == query_label_relative.cpu()).sum()
            total = total + len(query_label_relative)
            acc = 1.0 * correct / total * 100.0
            for weight in self.model.parameters():
                weight.fast = None
        elif self.args.teacherMethod == 'protonet':
            way=self.args.way_train
            z_support = self.model.getFeature(support)
            z_query = self.model.getFeature(query)
            z_support = z_support.contiguous().view(way * self.args.num_sup_train, -1)
            z_query = z_query.contiguous().view(way * self.args.num_qur_train, -1)
            z_support = z_support.contiguous()
            protos = []
            for c in range(way):
                protos.append(z_support[support_label_relative == c].mean(0))
            z_proto = torch.stack(protos, dim=0)
            z_query = z_query.contiguous().view(way * self.args.num_qur_train, -1)
            dists = euclidean_dist(z_query, z_proto)
            score = -dists
            loss_outer = self.loss_fn(score, query_label_relative)
            logprobs = score
            correct, total = 0, 0
            prediction = torch.max(logprobs, 1)[1]
            correct = correct + (prediction.cpu() == query_label_relative.cpu()).sum()
            total = total + len(query_label_relative)
            acc = 1.0 * correct / total * 100.0
        elif self.args.teacherMethod == 'anil':
            # inner
            self.model.zero_grad()
            fast_parameters = list(self.model.trunk[
                                       -1].parameters())  # the first gradient calcuated in line 45 is based on original weight
            for weight in self.model.parameters():
                weight.fast = None
            for task_step in range(self.args.inner_update_num):
                scores = self.forward(support)
                loss_inner = self.loss_fn(scores, support_label_relative)
                grad = torch.autograd.grad(loss_inner, fast_parameters,create_graph=True)  # build full graph support gradient of gradient
                if self.args.approx:
                    grad = [g.detach() for g in
                            grad]  # do not calculate gradient of gradient if using first order approximation
                fast_parameters = []
                for k, weight in enumerate(self.model.trunk[-1].parameters()):
                    # for usage of weight.fast, please see Linear_fw, Conv_fw in backbone.py
                    if weight.fast is None:
                        weight.fast = weight - self.args.inner_lr * grad[k]  # create weight.fast
                    else:
                        weight.fast = weight.fast - self.args.inner_lr * grad[
                            k]  # create an updated weight.fast, note the '-' is not merely minus value, but to create a new weight.fast
                    fast_parameters.append(
                        weight.fast)  # gradients calculated in line 45 are based on newest fast weight, but the graph will retain the link to old weight.fasts
            # outer
            scores = self.forward(query)
            loss_outer = self.loss_fn(scores, query_label_relative)
            correct = 0
            total = 0
            prediction = torch.max(scores, 1)[1]
            correct = correct + (prediction.cpu() == query_label_relative.cpu()).sum()
            total = total + len(query_label_relative)
            acc = 1.0 * correct / total * 100.0
            for weight in self.model.parameters():
                weight.fast = None
        if return_acc:
            return loss_outer,acc
        else:
            return loss_outer

def get_images_cifar(net,var_scale=0.001,bn_reg_scale = 10.0,l2_coeff=0.0,first_bn_multiplier=10,pre_inputs=None,targets=None):
    inputs=pre_inputs
    net.eval()
    # set up criteria for optimization
    criterion = nn.CrossEntropyLoss()
    ## Create hooks for feature statistics catching
    loss_r_feature_layers = []
    for module in net.modules():
        if isinstance(module, nn.BatchNorm2d):
            loss_r_feature_layers.append(DeepInversionFeatureHook(module))

    # setting up the range for jitter
    lim_0, lim_1 = 2, 2
    # apply random jitter offsets
    off1 = random.randint(-lim_0, lim_0)
    off2 = random.randint(-lim_1, lim_1)
    inputs_jit = torch.roll(inputs, shifts=(off1,off2), dims=(2,3))
    # foward with jit images
    net.zero_grad()
    outputs = net(inputs_jit)
    loss = criterion(outputs, targets)

    # apply total variation regularization
    diff1 = inputs_jit[:,:,:,:-1] - inputs_jit[:,:,:,1:]
    diff2 = inputs_jit[:,:,:-1,:] - inputs_jit[:,:,1:,:]
    diff3 = inputs_jit[:,:,1:,:-1] - inputs_jit[:,:,:-1,1:]
    diff4 = inputs_jit[:,:,:-1,:-1] - inputs_jit[:,:,1:,1:]
    loss_var = torch.norm(diff1) + torch.norm(diff2) + torch.norm(diff3) + torch.norm(diff4)
    loss = loss + var_scale*loss_var

    # R_feature loss
    rescale = [first_bn_multiplier] + [1. for _ in range(len(loss_r_feature_layers) - 1)]
    loss_distr = sum([mod.r_feature * rescale[idxxx] for (idxxx, mod) in enumerate(loss_r_feature_layers)])
    loss = loss + bn_reg_scale*loss_distr # best for noise before BN

    # l2 loss
    if 1:
        loss = loss + l2_coeff * torch.norm(inputs_jit, 2)

    return loss
class DeepInversionFeatureHook():
    '''
    Implementation of the forward hook to track feature statistics and compute a loss on them.
    Will compute mean and variance, and will use l2 as a loss
    '''
    def __init__(self, module):
        self.hook = module.register_forward_hook(self.hook_fn)

    def hook_fn(self, module, input, output):
        # hook co compute deepinversion's feature distribution regularization
        nch = input[0].shape[1]
        mean = input[0].mean([0, 2, 3])
        var = input[0].permute(1, 0, 2, 3).contiguous().view([nch, -1]).var(1, unbiased=False)

        #forcing mean and variance to match between two distributions
        #other ways might work better, i.g. KL divergence
        r_feature = torch.norm(module.running_var.data - var, 2) + torch.norm(module.running_mean.data - mean, 2)
        self.m = mean
        self.v = var
        self.r_feature = r_feature

    def close(self):
        self.hook.remove()

def generate_random_task_data_fromDI(args,allData,allData_label):
    specific= random.sample(range(dataset_classnum[args.dataset]), args.way_pretrain)
    specific = [s + bias[args.dataset] for s in specific]
    support=[]
    support_label_relative=[]
    query=[]
    query_label_relative=[]
    for c_rel,c_abs in enumerate(specific):
        select_data=allData[allData_label==c_abs]
        support.append(select_data[:args.num_sup_train])
        support_label_relative.append(torch.LongTensor([c_rel]*args.num_sup_train))
        query.append(select_data[args.num_sup_train:args.num_sup_train+args.num_qur_train])
        query_label_relative.append(torch.LongTensor([c_rel]*args.num_qur_train))
    return torch.cat(support),torch.cat(support_label_relative).cuda(args.device),torch.cat(query),torch.cat(query_label_relative).cuda(args.device)

def euclidean_dist( x, y):
    # x: N x D
    # y: M x D
    n = x.size(0)
    m = y.size(0)
    d = x.size(1)
    assert d == y.size(1)

    x = x.unsqueeze(1).expand(n, m, d)
    y = y.unsqueeze(0).expand(n, m, d)

    return torch.pow(x - y, 2).sum(2)


import itertools as it
import math

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import (
    PolynomialFeatures,
    StandardScaler,
)
from sklearn.utils import resample

def format_columns(data, drop_columns):
    data.columns = (
        data.columns.str.replace(" ", "_")
        .str.replace("/", "-")
        .str.replace("(", "")
        .str.replace(")", "")
        .str.lower()
    )
    return data.drop(drop_columns, axis=1, inplace=False)

def load_data(path):
    out = pd.read_csv(path)
    return out

def grid_search(params, X, X_test, y, y_test):
    models = [
        LogisticRegression(
            penalty="elasticnet",
            solver="saga",
            C=x[0],
            l1_ratio=x[1],
            max_iter=1000,
        )
        for x in params
    ]
    out = np.empty((len(params), 5), dtype=float)
    models_out = []
    for i, model in enumerate(models):
        model.fit(X, y)
        model_selected_features = SelectFromModel(estimator=model)
        # Extract boolean of kept features and select
        features_to_keep = model_selected_features.get_support()
        model_test_features_kept = X_test.loc[:, features_to_keep]
        # Cross-validate and evaluate model descriptors
        predict = (
            LogisticRegression(penalty="none")
            .fit(model_test_features_kept, y_test)
            .predict(model_test_features_kept)
        )
        out[i, 0] = params[i][0]
        out[i, 1] = params[i][1]
        out[i, 2] = (y_test == predict).sum() / X_test.shape[0]
        out[i, 3] = model.score(X_test, y_test)
        out[i, 4] = features_to_keep.sum()
        models_out.append(model)
    breakpoint()
    out = pd.DataFrame(
        out,
        columns=[
            "C",
            "l1_ratio",
            "percent_correct",
            "likelihood",
            "no_features_kept",
        ],
    )
    out["model"] = models_out
    out = out.sort_values(
        "percent_correct", ascending=False, ignore_index=True
    )
    print(out)
    return out

def scatterplot(data, response, model):
    selected = SelectFromModel(estimator=model)
    keep = selected.get_support()
    data = data.iloc[:, keep]
    predict = (
        LogisticRegression(penalty="none")
        .fit(data, response)
        .predict(data)
        .astype(int)
    )
    correct = predict == response.values
    data["predict"] = predict
    data["correct"] = correct
    data["response"] = response.values
    sns.scatterplot(
        data,
        x="ch2-ch1_ratio",
        y="aspect_ratio",
        hue="response",
        style="correct",
    )
    plt.show()

if __name__ == "__main__":
    data = pd.read_csv("../Data/2022-11-15_classified.csv")
    data = format_columns(
        data,
        drop_columns=[
            "calibration_factor",
            "capture_id",
            "capture_x",
            "capture_y",
            "group_id",
            "image_height",
            "image_width",
            "uuid",
            "elapsed_time",
            "ch2-ch1_ratio",
            "aspect_ratio",
        ],
    )
    # process data
    data["class"] = [1 if x == "Cyanobacteria" else 0 for x in data["class"]]
    response = data["class"]
    data.drop("class", axis=1, inplace=True)
    data_training = data.sample(frac=0.5)
    data_test = data.drop(data_training.index)
    # scale data
    scaler = StandardScaler().fit(data_training)
    data_training, data_test = [
        pd.DataFrame(scaler.transform(x), columns=data.columns)
        for x in [data_training, data_test]
    ]
    training_response, test_response = [
        response[x.index] for x in (data_training, data_test)
    ]

    # generate grid search
    grid = [
        [math.exp(x), math.exp(y)]
        for x, y in (
            it.product(
                np.linspace(math.log(0.01), math.log(1.0), 100),
                np.linspace(math.log(0.01), math.log(1.0), 100),
            )
        )
    ]
    grid_results = grid_search(
        grid,
        X=data_training,
        X_test=data_test,
        y=training_response,
        y_test=test_response,
    )
    grid_results_melt = grid_results.melt(
        id_vars=["C", "l1_ratio", "no_features_kept"],
        value_vars=["percent_correct", "likelihood"],
    )

    # polynomial grid search
    keep_features = data.columns[
        SelectFromModel(estimator=grid_results.iloc[0, -1]).get_support()
    ]
    poly_fit = PolynomialFeatures().fit(data_training.loc[:, keep_features])
    training_poly = pd.DataFrame(
        poly_fit.transform(
            data_training.loc[:, keep_features],
        ),
        columns=poly_fit.get_feature_names_out(),
    )
    test_poly = pd.DataFrame(
        poly_fit.transform(
            data_test.loc[:, keep_features],
        ),
        columns=poly_fit.get_feature_names_out(),
    )

    grid_results_poly = grid_search(
        grid,
        X=training_poly,
        X_test=test_poly,
        y=training_response,
        y_test=test_response,
    )
    grid_results_poly_melt = grid_results.melt(
        id_vars=["C", "l1_ratio", "no_features_kept"],
        value_vars=["percent_correct", "likelihood"],
    )
    print(
        f"polynomial percent correct: {grid_results_poly['percent_correct'][0]}"
    )
    print(f"monomial percent correct: {grid_results['percent_correct'][0]}")

    data_new = pd.read_csv("../Data/2022-07-26_test4_with-classification.csv")
    data_new = format_columns(
        data_new,
        drop_columns=[
            "calibration_factor",
            "capture_id",
            "capture_x",
            "capture_y",
            "group_id",
            "image_height",
            "image_width",
            "uuid",
            "elapsed_time",
            "ch2-ch1_ratio",
            "aspect_ratio",
        ],
    )
    # process data
    data_new["class"] = [
        1 if x == "Cyanobacteria" else 0 for x in data_new["class"]
    ]
    response_new = data_new["class"]
    data_new.drop("class", axis=1, inplace=True)
    data_new = pd.DataFrame(
        scaler.transform(data_new), columns=scaler.get_feature_names_out()
    )
    model_new = LogisticRegression(penalty="none").fit(
        data_new.loc[:, keep_features], response_new
    )
    predict_new = model_new.predict(data_new.loc[:, keep_features])
    correct_new = response_new == predict_new
    print(
        f"percent correct (no interactions): {correct_new.sum() / data_new.shape[0]}"
    )


from django.conf import settings
from django.conf.urls import url
from django.conf.urls.static import static
from . import views

urlpatterns = [
    url('^$', views.index, name='index'),
    url(r'^profile/(\d+)', views.profile, name='profile'),
    url(r'^business/', views.business, name='business'),
    url(r'^post/', views.post, name='post'),
    url(r'^create/post', views.new_post, name="new-post"),
    url(r'^hood/', views.hood, name='hood'),
    url(r'^search/', views.search_results, name='search_results'),
    url(r'^update/hood', views.update_hood, name="update_hood"),
]
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL,
                          document_root=settings.MEDIA_ROOT)


#!/usr/bin/python3

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

#Reference:**********************************************

# @Time    : 2019/10/3 2:28 下午

# @Author  : baozhiqiang

# @File    : 763best.py

# @User    : bao

# @Software: PyCharm

'''
        思路：
        1 统计每个元素出现的次数。
        2 然后遍历数组，每到一个元素，除了执行正常的-1，还要统计当前是否
        所有的元素都出现次数为0了，为0就截止这里，为第一个list
        '''
#Reference:**********************************************
from collections import  defaultdict
class Solution:
    def partitionLabels(self, S):

        if len(S) ==  0:
            return 0
        s_list = list(S)
        str_number = defaultdict(int)
        for str  in s_list:
            str_number[str] += 1
        print('str_number',str_number)
        count = 0
        temp_strlist = []
        result_list = []
        for  i  in s_list:
             temp_strlist.append(i)
             str_number[i] -= 1
             count += 1
             #检查当前所有出现的元素的出现次数是否都为0，
             flag= True
             for temp_str in temp_strlist:
                 if str_number[temp_str] !=0:
                    flag =False
             if flag:
                 result_list.append(count)
                 temp_strlist.clear()
                 count = 0

        print(result_list)
        return result_list

test =Solution()
print(test.partitionLabels("ababcbacadefegdehijhklij"))


from django.db import models
from django.contrib.auth.models import User

# Create your models here.

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)

    profile_pic = models.ImageField(
        null=True, editable=True, default="blank-profile-picture.webp"
    )

    genders = (
        ("male", "Male"),
        ("female", "Female"),
        ("other", "Other"),
    )
    gender = models.CharField(max_length=10, choices=genders, default="")

    schools = (
        ("tandon", "Tandon School of Engineering"),
        ("tish", "Tisch School of Arts"),
        ("stern", "Stern School of Business"),
    )
    school = models.CharField(max_length=10, choices=schools, default="")
    address = models.CharField(max_length=50, default="")