import math
from pathlib import Path
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union

from arcade import Texture, load_texture
from arcade.hitbox import HitBox, RotatableHitBox
from arcade.math import get_angle_degrees
from arcade.texture import get_default_texture
from arcade.types import PathOrTexture, Point

from .base import BasicSprite
from .mixins import PymunkMixin

if TYPE_CHECKING:  # handle import cycle caused by type hinting
    from arcade.sprite_list import SpriteList

class Sprite(BasicSprite, PymunkMixin):
    """
    Class that represents a 'sprite' on-screen. Most games center around sprites.
    For examples on how to use this class, see:
    https://api.arcade.academy/en/latest/examples/index.html#sprites

    :param str path_or_texture: Path to the image file, or a texture object.
    :param float center_x: Location of the sprite
    :param float center_y: Location of the sprite
    :param float scale: Scale the image up or down. Scale of 1.0 is none.
    :param float angle: The initial rotation of the sprite in degrees
    """

    __slots__ = (
        "_velocity",
        "change_angle",
        "_properties",
        "boundary_left",
        "boundary_right",
        "boundary_top",
        "boundary_bottom",
        "textures",
        "cur_texture_index",
        "_hit_box",
        "physics_engines",
        "_sprite_list",
        "guid",
        "force",
    )

    def __init__(
        self,
        path_or_texture: PathOrTexture = None,
        scale: float = 1.0,
        center_x: float = 0.0,
        center_y: float = 0.0,
        angle: float = 0.0,
        **kwargs,
    ):
        if isinstance(path_or_texture, Texture):
            _texture = path_or_texture
            _textures = [_texture]
        elif isinstance(path_or_texture, (str, Path)):
            _texture = load_texture(path_or_texture)
            _textures = [_texture]
        else:
            _texture = get_default_texture()
            # Backwards compatibility:
            # When applying default texture we don't want
            # it part of the animating ones
            _textures = []
        super().__init__(
            _texture,
            scale=scale,
            center_x=center_x,
            center_y=center_y,
            **kwargs,
        )
        PymunkMixin.__init__(self)

        self._angle = angle
        # Movement
        self._velocity = 0.0, 0.0
        self.change_angle: float = 0.0

        # Custom sprite properties
        self._properties: Optional[Dict[str, Any]] = None

        # Boundaries for moving platforms in tilemaps
        self.boundary_left: Optional[float] = None
        self.boundary_right: Optional[float] = None
        self.boundary_top: Optional[float] = None
        self.boundary_bottom: Optional[float] = None

        self.cur_texture_index: int = 0
        self.textures: List[Texture] = _textures

        self.physics_engines: List[Any] = []

        self._sprite_list: Optional[SpriteList] = None
        # Debug properties
        self.guid: Optional[str] = None

        self._hit_box: RotatableHitBox = self._hit_box.create_rotatable(
            angle=self._angle
        )

        self._width = self._texture.width * scale
        self._height = self._texture.height * scale

    # --- Properties ---

    @property
    def angle(self) -> float:
        """
        Get or set the rotation or the sprite.

        The value is in degrees and is clockwise.
        """
        return self._angle

    @angle.setter
    def angle(self, new_value: float):
        if new_value == self._angle:
            return

        self._angle = new_value
        self._hit_box.angle = new_value

        for sprite_list in self.sprite_lists:
            sprite_list._update_angle(self)

        self.update_spatial_hash()

    @property
    def radians(self) -> float:
        """
        Get or set the rotation of the sprite in radians.

        The value is in radians and is clockwise.
        """
        return self._angle / 180.0 * math.pi

    @radians.setter
    def radians(self, new_value: float):
        self.angle = new_value * 180.0 / math.pi

    @property
    def velocity(self) -> Point:
        """
        Get or set the velocity of the sprite.

        The x and y velocity can also be set separately using the
        ``sprite.change_x`` and ``sprite.change_y`` properties.

        Example::

            sprite.velocity = 1.0, 0.0

        Returns:
            Tuple[float, float]
        """
        return self._velocity

    @velocity.setter
    def velocity(self, new_value: Point):
        self._velocity = new_value

    @property
    def change_x(self) -> float:
        """Get or set the velocity in the x plane of the sprite."""
        return self.velocity[0]

    @change_x.setter
    def change_x(self, new_value: float):
        self._velocity = new_value, self._velocity[1]

    @property
    def change_y(self) -> float:
        """Get or set the velocity in the y plane of the sprite."""
        return self.velocity[1]

    @change_y.setter
    def change_y(self, new_value: float):
        self._velocity = self._velocity[0], new_value

    @property
    def hit_box(self) -> HitBox:
        """
        Get or set the hit box for this sprite.
        """
        return self._hit_box

    @hit_box.setter
    def hit_box(self, hit_box: Union[HitBox, RotatableHitBox]):
        if type(hit_box) == HitBox:
            self._hit_box = hit_box.create_rotatable(self.angle)
        else:
            # Mypy doesn't seem to understand the type check above
            # It still thinks hit_box can be a union here
            self._hit_box = hit_box  # type: ignore

    @property
    def texture(self) -> Texture:
        """Get or set the active texture for this sprite"""
        return self._texture

    @texture.setter
    def texture(self, texture: Texture):
        if texture == self._texture:
            return

        if __debug__ and not isinstance(texture, Texture):
            raise TypeError(
                f"The 'texture' parameter must be an instance of arcade.Texture,"
                f" but is an instance of '{type(texture)}'."
            )

        # If sprite is using default texture, update the hit box
        if self._texture is get_default_texture():
            self.hit_box = RotatableHitBox(
                texture.hit_box_points,
                position=self._position,
                angle=self.angle,
                scale=self._scale,
            )

        self._texture = texture
        self._width = texture.width * self._scale[0]
        self._height = texture.height * self._scale[1]
        self.update_spatial_hash()
        for sprite_list in self.sprite_lists:
            sprite_list._update_texture(self)

    @property
    def properties(self) -> Dict[str, Any]:
        """
        Get or set custom sprite properties.

        :rtype: Dict[str, Any]
        """
        if self._properties is None:
            self._properties = {}
        return self._properties

    @properties.setter
    def properties(self, value):
        self._properties = value

    # --- Movement methods -----

    def forward(self, speed: float = 1.0) -> None:
        """
        Adjusts a Sprites forward.

        :param speed: speed
        """
        angle_rad = math.radians(self.angle)
        self.center_x += math.sin(angle_rad) * speed
        self.center_y += math.cos(angle_rad) * speed

    def reverse(self, speed: float = 1.0) -> None:
        """
        Adjusts a Sprite backwards.

        :param speed: speed
        """
        self.forward(-speed)

    def strafe(self, speed: float = 1.0) -> None:
        """
        Adjusts a Sprite sideways.

        :param speed: speed
        """
        angle_rad = math.radians(self.angle + 90)
        self.center_x += math.sin(angle_rad) * speed
        self.center_y += math.cos(angle_rad) * speed

    def turn_right(self, theta: float = 90.0) -> None:
        """
        Rotate the sprite right by the passed number of degrees.

        :param theta: change in angle, in degrees
        """
        self.angle = self._angle + theta

    def turn_left(self, theta: float = 90.0) -> None:
        """
        Rotate the sprite left by the passed number of degrees.

        :param theta: change in angle, in degrees
        """
        self.angle = self._angle - theta

    def stop(self) -> None:
        """
        Stop the Sprite's motion by setting the velocity and angle change to 0.
        """
        self.velocity = 0, 0
        self.change_angle = 0

    def face_point(self, point: Point) -> None:
        """
        Face the sprite towards a point. Assumes sprite image is facing upwards.

        :param Point point: Point to face towards.
        """
        angle = get_angle_degrees(self.center_x, self.center_y, point[0], point[1])

        # Reverse angle because sprite angles are backwards
        self.angle = -angle

    # ---- Draw Methods ----

    def draw(self, *, filter=None, pixelated=None, blend_function=None) -> None:
        """
        Draw the sprite.

        :param filter: Optional parameter to set OpenGL filter, such as
                       `gl.GL_NEAREST` to avoid smoothing.
        :param pixelated: ``True`` for pixelated and ``False`` for smooth interpolation.
                          Shortcut for setting filter=GL_NEAREST.
        :param blend_function: Optional parameter to set the OpenGL blend function used for drawing the sprite list,
                               such as 'arcade.Window.ctx.BLEND_ADDITIVE' or 'arcade.Window.ctx.BLEND_DEFAULT'
        """
        if self._sprite_list is None:
            from arcade import SpriteList

            self._sprite_list = SpriteList(capacity=1)

        self._sprite_list.append(self)
        self._sprite_list.draw(
            filter=filter, pixelated=pixelated, blend_function=blend_function
        )
        self._sprite_list.remove(self)

    # ----Update Methods ----

    def update(self) -> None:
        """
        The default update method for a Sprite. Can be overridden by a subclass.

        This method moves the sprite based on its velocity and angle change.
        """
        self.position = (
            self._position[0] + self.change_x,
            self._position[1] + self.change_y,
        )
        self.angle += self.change_angle

    # ----Utility Methods----

    def update_spatial_hash(self) -> None:
        """
        Update the sprites location in the spatial hash.
        """
        # self._hit_box._adjusted_cache_dirty = True
        # super().update_spatial_hash()
        for sprite_list in self.sprite_lists:
            if sprite_list.spatial_hash is not None:
                sprite_list.spatial_hash.move(self)

    def append_texture(self, texture: Texture):
        """
        Appends a new texture to the list of textures that can be
        applied to this sprite.

        :param arcade.Texture texture: Texture to add to the list of available textures

        """
        self.textures.append(texture)

    def set_texture(self, texture_no: int) -> None:
        """
        Set the current texture by texture number.
        The number is the index into ``self.textures``.

        :param int texture_no: Index into ``self.textures``
        """
        texture = self.textures[texture_no]
        self.texture = texture

    def remove_from_sprite_lists(self) -> None:
        """
        Remove this sprite from all sprite lists it is in
        including registered physics engines.
        """
        super().remove_from_sprite_lists()
        for engine in self.physics_engines:
            engine.remove_sprite(self)

        self.physics_engines.clear()

    def register_physics_engine(self, physics_engine) -> None:
        """
        Register a physics engine on the sprite.
        This is only needed if you actually need a reference
        to your physics engine in the sprite itself.
        It has no other purposes.

        The registered physics engines can be accessed
        through the ``physics_engines`` attribute.

        It can for example be the pymunk physics engine
        or a custom one you made.
        """
        self.physics_engines.append(physics_engine)


from turtle import Screen
from paddle import Paddle
from score_board import PlayBoard, Score
from ball import Ball
import time

screen = Screen()
screen.setup(width=800, height=600)
screen.bgcolor("black")
screen.title("Pong")
screen.tracer(0)

right_paddle = Paddle((350, 0))
left_paddle = Paddle((-350, 0))

r_paddle_score = Score((20, 250))
l_paddle_score = Score((-20, 250))

play_board = PlayBoard()
ball = Ball()

screen.listen()
screen.onkey(fun=right_paddle.up, key="Up")
screen.onkey(fun=right_paddle.down, key="Down")
screen.onkey(fun=left_paddle.up, key="w")
screen.onkey(fun=left_paddle.down, key="s")

game_is_on = True
while game_is_on:
    time.sleep(ball.move_speed)
    ball.move()
    screen.update()

    # Detect collision with wall
    if ball.ycor() > 280 or ball.ycor() < - 280:
        ball.bounce_y()

    # Detect collision with paddle
    if ball.xcor() > 320 and ball.distance(right_paddle) < 50 or ball.xcor() < -320 and ball.distance(left_paddle) < 50:
        ball.bounce_x()

    # Detect paddle miss and add score
    if ball.xcor() > 370:
        ball.refresh()
        l_paddle_score.add_score()
    if ball.xcor() < -370:
        ball.refresh()
        r_paddle_score.add_score()

screen.exitonclick()


import sys
import ast

from pyspark import SparkConf, SparkContext
from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StructField, StringType, DoubleType, Row

if len(sys.argv) == 1:
    # hdfs://localhost:9000/full/meta_Electronics.json
    # hdfs://localhost:9000/full/avg_rating.csv
    # hdfs://localhost:9000/full/output
    INPUT_META_FILE = 'hdfs://localhost:9000/part/sample_100_meta.json'
    INPUT_AVG_RATING_FILE = 'hdfs://localhost:9000/part/avg_rating.csv'
    OUTPUT_DIR = 'hdfs://localhost:9000/part/output'
elif len(sys.argv) == 2:
    INPUT_META_FILE = sys.argv[1]
    INPUT_AVG_RATING_FILE = 'hdfs://localhost:9000/part/avg_rating.csv'
    OUTPUT_DIR = 'hdfs://localhost:9000/part/output'
elif len(sys.argv) == 3:
    INPUT_META_FILE = sys.argv[1]
    INPUT_AVG_RATING_FILE = sys.argv[2]
    OUTPUT_DIR = 'hdfs://localhost:9000/part/output'
else:
    INPUT_META_FILE = sys.argv[1]
    INPUT_AVG_RATING_FILE = sys.argv[2]
    OUTPUT_DIR = sys.argv[3]

def getReviewMeta(reviewMeta):
    decodeReviewMeta = ast.literal_eval(reviewMeta)
    try:
        asin = decodeReviewMeta["asin"]
    except KeyError:
        asin = None
    try:
        title = decodeReviewMeta["title"]
    except:
        title = None
    return (asin, title)

conf = SparkConf().setAppName("task2")
sc = SparkContext(conf=conf)
spark = SparkSession.builder.config(conf=conf).getOrCreate()
rddReviewMeta = sc.textFile(INPUT_META_FILE)

rddReviewMetaDf = rddReviewMeta.map(lambda row: getReviewMeta(row)).toDF(['ProdId', 'Name'])

schema = StructType([StructField('ProdId', StringType(), False),
                     StructField('Rating', DoubleType(), False)])

rddAvgRating = spark.read.load(path=INPUT_AVG_RATING_FILE, format="csv", schema=schema, header="false",
                               inferSchema="false", sep=",", nullValue="null", mode="DROPMALFORMED")

joinDf = rddReviewMetaDf.join(rddAvgRating, ['ProdId'], how='full')
joinDf.na.drop().coalesce(1).write.format('csv').option('header', 'false').save(OUTPUT_DIR)


import math

import torch
from torch import nn
import torch.nn.functional as F

class RotatedMultiboxLoss(nn.Module):
    def __init__(self, neg_pos_ratio: int):
        super(RotatedMultiboxLoss, self).__init__()
        self._neg_pos_ratio = neg_pos_ratio

    def forward(self, confidence, predicted_locations, labels, gt_locations):
        num_classes = confidence.size(2)
        with torch.no_grad():
            # derived from cross_entropy=sum(log(p))
            loss = -F.log_softmax(confidence, dim=2)[:, :, 0]
            mask = hard_negative_mining(loss, labels, self._neg_pos_ratio)

        confidence = confidence[mask, :]
        classification_loss = F.cross_entropy(confidence.reshape(-1, num_classes), labels[mask], size_average=False)
        pos_mask = labels > 0
        predicted_locations = predicted_locations[pos_mask, :].reshape(-1, 5)
        gt_locations = gt_locations[pos_mask, :].reshape(-1, 5)
        smooth_l1_loss = F.smooth_l1_loss(predicted_locations, gt_locations, size_average=False)
        num_pos = gt_locations.size(0)
        return smooth_l1_loss / num_pos, classification_loss / num_pos

def hard_negative_mining(loss, labels, neg_pos_ratio):
    pos_mask = labels > 0
    num_pos = pos_mask.long().sum(dim=1, keepdim=True)
    num_neg = num_pos * neg_pos_ratio

    loss[pos_mask] = -math.inf
    _, indexes = loss.sort(dim=1, descending=True)
    _, orders = indexes.sort(dim=1)
    neg_mask = orders < num_neg
    return pos_mask | neg_mask


from __future__ import annotations

from romkit.resources.actions.base import BaseAction

import lxml.etree
import re
import tempfile
from pathlib import Path

class ArchiveToDat(BaseAction):
    name = 'archive_to_dat'

    # Converts an archive file listing to a dat file readable by romkit
    def install(self, source: ResourcePath, target: ResourcePath, **kwargs) -> None:
        doc = lxml.etree.iterparse(str(source.path), tag=('file'))
        pattern = re.compile(self.config.get('match', '.*'))

        with tempfile.TemporaryDirectory() as tmpdir:
            # Write initially to a temporary file in case there's a failure part-way through
            tmp_target = Path(tmpdir).joinpath('out.xml')

            with lxml.etree.xmlfile(str(tmp_target), encoding='utf-8') as file:
                file.write_declaration(standalone=True)
                file.write_doctype('<!DOCTYPE datafile PUBLIC "-//Logiqx//DTD ROM Management Datafile//EN" "http://www.logiqx.com/Dats/datafile.dtd">')

                with file.element('datafile'):
                    for event, archive_file in doc:
                        # Get actual name as it'll be downloaded from the source
                        filename = archive_file.get('name')

                        if pattern.search(filename):
                            name = Path(filename).stem

                            # Build element in target file
                            element = lxml.etree.Element('game', name=name)

                            # Add description
                            description = lxml.etree.Element('description')
                            description.text = name
                            element.append(description)

                            # Add rom
                            rom = lxml.etree.Element('rom')
                            rom.attrib['name'] = filename

                            size_tag = archive_file.find('size')
                            if size_tag is not None:
                                rom.attrib['size'] = size_tag.text

                            md5_tag = archive_file.find('md5')
                            if size_tag is not None:
                                rom.attrib['md5'] = md5_tag.text

                            crc32_tag = archive_file.find('crc32')
                            if crc32_tag is not None:
                                rom.attrib['crc'] = crc32_tag.text

                            sha1_tag = archive_file.find('sha1')
                            if sha1_tag is not None:
                                rom.attrib['sha1'] = sha1_tag.text

                            element.append(rom)

                            file.write(element, pretty_print=True)
                            element = None

                        # Release memory
                        archive_file.clear()

            tmp_target.rename(target.path)


import os
import shutil
import warnings
from pathlib import Path
from typing import Dict, List, Optional

import yaml

warnings.filterwarnings("ignore")
import torch
import wandb
import hydra
import omegaconf
import pytorch_lightning as pl
from omegaconf import OmegaConf
from pytorch_lightning import Callback
from pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint, RichProgressBar
from pytorch_lightning.loggers import WandbLogger

from src.common.utils import load_envs, PROJECT_ROOT, gpus, enable_16precision, set_determinism_the_old_way
from src.pl_data_modules import BasePLDataModule
from src.pl_modules import BasePLModule

# Load environment variables
load_envs()

def build_callbacks(
    cfg: omegaconf.DictConfig, checkpoint_path: str = ""
) -> List[Callback]:
    # callbacks declaration

    callbacks_store.append(
        ModelCheckpoint(
            **cfg.train.model_checkpoint,
            dirpath=checkpoint_path,
        )
    )
    return callbacks_store

def get_dataset_info(conf: omegaconf.DictConfig) -> Dict[str, List[Dict[str, int]]]:
    """
    Return dict which contains each split (train, validation, test) with the number of samples belonging to the labels selected
    e.g.
     {'train': [{'COVID': 563}, {'bacteria': 1285}, {'virus': 1035}, {'normal': 950}],
     'validation': [{'COVID': 15}, {'bacteria': 16}, {'virus': 16}, {'normal': 16}],
     'test': [{'COVID': 17}, {'bacteria': 18}, {'virus': 18}, {'normal': 18}]}
    """
    class_to_use: List[str] = conf.labels.class_to_use
    dataset_path: Dict[str, str] = {
        "train": conf.data.dataset.train_path,
        "validation": conf.data.dataset.validation_path,
        "test": conf.data.dataset.test_path,
    }
    dataset_info: Dict[str, List[Dict[str, int]]] = {
        "train": [],
        "validation": [],
        "test": [],
    }
    for key, path in dataset_path.items():
        for label in class_to_use:
            num_samples = len(os.listdir(f"{path}/{label}"))
            label_num_samples: Dict[str, int] = {label: num_samples}
            dataset_info[key].append(label_num_samples)
    return dataset_info

def train(conf: omegaconf.DictConfig) -> None:
    # reproducibility

    pl.seed_everything(conf.train.seed)
    set_determinism_the_old_way(True)
    logger = None

    # data module declaration
    pl_data_module = BasePLDataModule(conf)

    # main module declaration
    pl_module = BasePLModule(conf)

    # callbacks declaration

    if conf.logging.log:
        class_to_use_str: str = (
            str(conf.labels.class_to_use)
            .strip("[]")
            .replace(",", "-")
            .replace("'", "")
            .replace(" ", "")
        )
        hydra.utils.log.info(f"Instantiating <WandbLogger>")
        wandb_config = conf.logging
        logger: WandbLogger = WandbLogger(
            **conf.logging.wandb,
            group=f"{conf.data.dataset.name}-{class_to_use_str}-test",
            name=conf.train.model_type,
            log_model=True,
        )
        hydra.utils.log.info(f"W&B is now watching <{wandb_config.watch.log}>!")
        logger.watch(
            pl_module, log=wandb_config.watch.log, log_freq=wandb_config.watch.log_freq
        )

        dataset_info = get_dataset_info(conf=conf)
        with open(Path(logger.experiment.dir) / "dataset-info.yaml", "w") as outfile:
            yaml.dump(dataset_info, outfile, default_flow_style=False)
        yaml_conf: str = OmegaConf.to_yaml(cfg=conf)
        (Path(logger.experiment.dir) / "hparams.yaml").write_text(yaml_conf)

    callbacks_store: List[Callback] = [RichProgressBar()]

    if not torch.cuda.is_available():
        conf.train.pl_trainer.gpus = 0
        conf.train.pl_trainer.precision = 32

    trainer = pl.Trainer(
        **conf.train.pl_trainer,
        callbacks=callbacks_store,
        logger=logger
    )
    # module test
    model_path: str = (
        f"{PROJECT_ROOT}/ckpt/{conf.data.subset_percentage}/{conf.train.model_type}"
    )
    best_model_ckpt: str = model_path + "/" + str(os.listdir(model_path)[0])
    hydra.utils.log.info(f"path best_model: {best_model_ckpt}")
    # main module declaration
    pl_module = BasePLModule.load_from_checkpoint(best_model_ckpt, strict=False)
    trainer.test(pl_module, datamodule=pl_data_module)

    if conf.logging.log:
        logger.experiment.finish()

@hydra.main(config_path="../conf", config_name="root")
def main(conf: omegaconf.DictConfig):
    import os

    print("Working directory : {}".format(os.getcwd()))
    train(conf)

if __name__ == "__main__":
    main()


from sqlalchemy.orm import Session

from smodslib.model import ModBase, ModRevision

from db import engine
from db.model import Mod, ModRevision as DbModRevision

def create_mod_if_not_exists(session, mod: ModBase):
    """
    Warning: THIS FUNCTION DOESN'T ADD TO THE SESSION NOR COMMIT TO THE DATABASE, IT ONLY RETRIEVES FROM DATABASE
    OR CREATE THE MODEL OBJECT!
    """
    new = False
    db_mod = session.query(Mod).filter_by(id=mod.id).first()
    if not db_mod:
        new = True
        db_mod = Mod(mod.id, mod.name)

    return db_mod, new

def create_revision_if_not_exists(session, revision: ModRevision, mod: Mod):
    db_revision = None
    new = False

    for rev in mod.revisions:
        if rev.id == revision.id:
            db_revision = rev
            break

    if not db_revision:
        db_revision = session.query(DbModRevision).filter_by(id=revision.id).first()  # check if created but not added
        if not db_revision:
            db_revision = DbModRevision(revision.id, revision.name, revision.date, revision.download_url,
                                        revision.filename)
            new = True

    return db_revision, new

def get_installed_mods() -> list[Mod]:
    with Session(engine) as sess:
        return sess.query(Mod).filter(Mod.installed_revision_association.has()).all()

def get_installed_revision(mod_id) -> ModRevision:
    with Session(engine) as sess:
        return sess.query(Mod).filter_by(id=mod_id).first().installed_revision_association.revision


from revChatGPT.V1 import Chatbot
import json
import os

from .text import ArticleText

class TextBackendChatGPT:
    def __init__(self) -> None:
        self.CACHE_DIR = os.path.expanduser("~/.cache/chatgpt-blog")
        self.TOPICS_CACHE_FILE = os.path.join(self.CACHE_DIR, "topics.json")
        self.bot = Chatbot(self.read_json_file(os.path.expanduser("~/.config/revChatGPT/config.json")))
        self.CONVERSATION_ID = self.read_json_file(os.path.expanduser("~/.config/revChatGPT/config.json"))["conversation_id"]
        self.PROMPT_TEMPLATES = {
            "ideas": "What are five unique article ideas for a tech blog? Only give the titles.",
            "image_idea": "What could an image lokk like that visualizes an article with title '{title}'? Give a short, one-paragraph description, which is still very detailed.",
            "image_caption": "Condense the following text down to five words: '{desc}'",
            "article": "Write an article for a tech blog with following title: '{title}'; continuous, but structured text; introduction and conclusion; avoid listings and headings"
        }

    def ask(self, prompt) -> str:
        try:
            for data in self.bot.ask(prompt, conversation_id=self.CONVERSATION_ID):
                resp = data["message"]
        except Exception:
            print("failed to ask ChatGPT. Do you have a valid session (press enter to proceed)?")
            input()
            return self.ask(prompt)
        return resp

    def read_json_file(self, path: str) -> any:
        with open(path) as f:
            return json.load(f)

    def read_json_list_file(self, path: str) -> any:
        return [i.strip() for i in self.read_json_file(path) if i.strip() != ""]

    def write_json_list_file(self, path: str, data: list) -> any:
        json.dump(data, open(path, "w"))

    def get_topic(self) -> str:
        if not os.path.isfile(self.TOPICS_CACHE_FILE) or len(self.read_json_list_file(self.TOPICS_CACHE_FILE)) < 1:
            resp = self.ask(self.PROMPT_TEMPLATES["ideas"].format())
            topics = [i[2:] for i in resp.split("\n") if not i.startswith("Sure, ")]
            self.write_json_list_file(self.TOPICS_CACHE_FILE, topics)
        topics = self.read_json_list_file(self.TOPICS_CACHE_FILE)
        self.write_json_list_file(self.TOPICS_CACHE_FILE, topics[1:])
        return topics[0].strip("\"").strip("'")

    def get_body(self, title: str) -> str:
        text = self.ask(self.PROMPT_TEMPLATES["article"].format(title=title))
        text = "\n".join([l for l in text.split("\n") if l.strip() not in ("Conclusion:", "Introduction:", "Body:")])
        return ArticleText(self.ask(self.PROMPT_TEMPLATES["article"].format(title=title)), title, "ChatGPT")

    def get_image_idea(self, title: str) -> str:
        resp = self.ask(self.PROMPT_TEMPLATES["image_idea"].format(title=title))
        joined = ""
        for line in resp.split("\n"):
            if line.startswith("As an AI language model"):
                continue
            joined += line
        return joined.strip()

    def get_caption(self, desc: str) -> str:
        return self.ask(self.PROMPT_TEMPLATES["image_caption"].format(desc=desc))


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

import itertools
import numbers

import numpy

import dask.array

from dask_ndfourier import _compat

try:
    from itertools import imap
except ImportError:
    imap = map

try:
    irange = xrange
except NameError:
    irange = range

def _get_freq_grid(shape, chunks, dtype=float):
    assert len(shape) == len(chunks)

    shape = tuple(shape)
    dtype = numpy.dtype(dtype).type

    assert (issubclass(dtype, numbers.Real) and
            not issubclass(dtype, numbers.Integral))

    ndim = len(shape)

    freq_grid = []
    for i in irange(ndim):
        sl = ndim * [None]
        sl[i] = slice(None)
        sl = tuple(sl)

        freq_grid_i = _compat._fftfreq(shape[i],
                                       chunks=chunks[i]).astype(dtype)[sl]
        for j in itertools.chain(range(i), range(i + 1, ndim)):
            freq_grid_i = freq_grid_i.repeat(shape[j], axis=j)

        freq_grid.append(freq_grid_i)

    freq_grid = dask.array.stack(freq_grid)

    return freq_grid

def _get_ang_freq_grid(shape, chunks, dtype=float):
    dtype = numpy.dtype(dtype).type

    assert (issubclass(dtype, numbers.Real) and
            not issubclass(dtype, numbers.Integral))

    pi = dtype(numpy.pi)

    freq_grid = _get_freq_grid(shape, chunks, dtype=dtype)
    ang_freq_grid = 2 * pi * freq_grid

    return ang_freq_grid

def _norm_args(a, s, n=-1, axis=-1):
    if issubclass(a.dtype.type, numbers.Integral):
        a = a.astype(float)

    if isinstance(s, numbers.Number):
        s = numpy.array(a.ndim * [s])
    elif not isinstance(s, dask.array.Array):
        s = numpy.array(s)

    if issubclass(s.dtype.type, numbers.Integral):
        s = s.astype(a.real.dtype)
    elif not issubclass(s.dtype.type, numbers.Real):
        raise TypeError("The `s` must contain real value(s).")
    if s.shape != (a.ndim,):
        raise RuntimeError(
            "Shape of `s` must be 1-D and equal to the input's rank."
        )

    if n != -1:
        raise NotImplementedError(
            "Currently `n` other than -1 is unsupported."
        )

    return (a, s, n, axis)


from tkinter import *
from currency_converter import CurrencyConverter
a=CurrencyConverter()
window=Tk()
window.title("Currency Convertor")
window.geometry("500x380")
window.config(bg="#FF8DC7")

def clicked():
    amount=int(e1.get())
    cur1=e2.get()
    cur2=e3.get()
    data=a.convert(amount,cur1,cur2)
    lab4=Label(window,text=data,font=("Ink Free",20,"bold"),fg="#FDFF00",bg="#FF8DC7").place(x=90,y=300)

lab=Label(window, text="ðŸ’±Currency ConvertorðŸ’±",font=("Ink Free",30,"bold"),fg="#8D72E1",bg="#FF8DC7").place(x=30,y=30)
lab1=Label(window,text="Enter the amount:",font=("Ink Free",20,"bold"),fg="#3F3B6C",bg="#FF8DC7").place(x=20,y=100)
e1=Entry(window,width=20,border=0)
lab2=Label(window,text="Enter currency:",font=("Ink Free",20,"bold"),fg="#3F3B6C",bg="#FF8DC7").place(x=20,y=140)
e2=Entry(window,width=20,border=0)
lab3=Label(window,text="Enter req Currency:",font=("Ink Free",20,"bold"),fg="#3F3B6C",bg="#FF8DC7").place(x=20,y=180)
e3=Entry(window,width=20,border=0)
btn=Button(window,text="convert",bg="#FF8DC7",command=clicked,font=("Ink Free",20,"bold"),fg="#3F3B6C").place(x=200,y=240)
e1.place(x=300,y=110)
e2.place(x=300,y=150)
e3.place(x=300,y=190)
window.mainloop()
#a=CurrencyConverter()
#print(a.convert(12,"USD","INR"))


import tensorflow as tf
from tensorflow.contrib.layers import xavier_initializer
from typing import List, Union, Optional

def rnn_cell(cell: str,
             hidden: int,
             keep_prob: Union[float, tf.Tensor, tf.Variable],
             output_drop: bool,
             state_drop: bool):
    if cell == 'LSTM':
        cell = tf.nn.rnn_cell.LSTMCell(hidden, state_is_tuple=True)
    elif cell == 'GRU':
        cell = tf.nn.rnn_cell.GRUCell(hidden)
    elif cell == 'RNN':
        cell = tf.nn.rnn_cell.RNNCell(hidden)
    else:
        raise TypeError('cell name error')

    if output_drop and state_drop:
        cell = tf.nn.rnn_cell.DropoutWrapper(cell,
                                             output_keep_prob=keep_prob,
                                             state_keep_prob=keep_prob,
                                             variational_recurrent=True,
                                             dtype=tf.float32)
    elif output_drop:
        cell = tf.nn.rnn_cell.DropoutWrapper(cell,
                                             output_keep_prob=keep_prob,
                                             dtype=tf.float32)
    elif state_drop:
        cell = tf.nn.rnn_cell.DropoutWrapper(cell,
                                             state_keep_prob=keep_prob,
                                             variational_recurrent=True,
                                             dtype=tf.float32)
    else:
        pass
    return cell

def rnn_cells(cell_type: str,
              hidden_list: List[int],
              keep_prob: Union[float, tf.Tensor, tf.Variable],
              output_drop=True,
              state_drop=True):
    multi_cell = []
    for hidden in hidden_list:
        cell = rnn_cell(cell_type, hidden, keep_prob, output_drop, state_drop)
        multi_cell.append(cell)
    return multi_cell

def get_tf_config():
    config = tf.ConfigProto(allow_soft_placement=True)
    # config = tf.ConfigProto()
    config.gpu_options.allow_growth = True
    return config

def clip_gradients(grads_and_vars, clip_norm=5.0):
    """
    :param grads_and_vars: A list of (gradients and variables) pairs
    :param clip_norm: clip norm
    :return: A list of (clipped gradients and variables) pairs
    """
    # gvs = [('g1', 'v1'), ('g2', 'v2'), ('g3', 'v3')]
    # zip(*gvs)
    # gradients = ('g1', 'g2', 'g3')
    # variables = ('v1', 'v2', 'v3')
    gradients, variables = zip(*grads_and_vars)
    clipped_gradients, _ = tf.clip_by_global_norm(gradients, clip_norm)
    return list(zip(clipped_gradients, variables))

def merge_gradients(towers_gvs):
    """
    Merge every grads_and_vars of each tower
    :param towers_gvs: [each tower's grads and vars]
    :return: merged grads_and_vars
    """
    average_grads = []
    for grad_and_vars in zip(*towers_gvs):
        # iteration through all grads and vars
        grads = []
        for g, _ in grad_and_vars:
            # iteration through all towers
            ex_g = tf.expand_dims(g, 0)
            grads.append(ex_g)
        grad = tf.concat(grads, axis=0)
        grad = tf.reduce_mean(grad, 0)

        # variables of first tower
        v = grad_and_vars[0][1]
        grad_and_vars = (grad, v)
        average_grads.append(grad_and_vars)
    return average_grads

def entry_stop_gradients(embedding: tf.Variable, mask):
    mask_n = tf.logical_not(mask)

    mask = tf.cast(mask, dtype=embedding.dtype)
    mask_n = tf.cast(mask_n, dtype=embedding.dtype)

    return tf.stop_gradient(mask_n * embedding) + mask + embedding

class OutputLayer(tf.layers.Layer):
    def __init__(self, num_inputs, embedding_matrix):
        super(OutputLayer, self).__init__()
        self.num_inputs = num_inputs
        self.embedding_matrix = embedding_matrix
        self.shape = embedding_matrix.get_shape()       # (vocab_size, embedding_dim)

    def build(self, _):
        embedding_size = self.embedding_matrix.get_shape()[1]
        self.kernel = self.add_variable("kernel", [self.num_inputs, embedding_size])

    def call(self, inputs, **kwargs):
        # inputs
        # if Greedy, [batch, hidden]
        # if Beam, [batch, beam, hidden]

        # layer hidden
        # if Greedy, [batch, kernel second dim]
        # if Beam, [batch, beam, kernel second dim]
        hidden = tf.tensordot(inputs, self.kernel, axes=((-1,), (0,)))  # Remove 'a' last dim, 'b' first dim

        # layer hidden
        # if Greedy, [batch, vocab size]
        # if Beam, [batch, beam, vocab size]
        return tf.tensordot(hidden, tf.transpose(self.embedding_matrix), axes=((-1,), (0,)))

    def compute_output_shape(self, input_shape):
        input_shape = input_shape.with_rank_at_least(2)
        if input_shape[-1].value is None:
            raise ValueError(
              'The innermost dimension of input_shape must be defined, but saw: %s'
              % input_shape)
        return input_shape[:-1].concatenate(self.shape[0])


import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

#%% 1
amazon = {'date': ['2013-02-08', '2013-02-11', '2013-02-12', '2013-02-13', '2013-02-14'],
          'close': [261.950, 257.210, 258.700, 269.470, 269.240]}

amazon = pd.DataFrame(amazon)

sns.lineplot(
    x='date',
    y='close',
    data=amazon)

plt.xticks(rotation=45)
plt.show()

#%% 2
employee = {'employee_id': ['1ex5', '73fd', 'ei10', 'b45e'],
            'first_name': ['Linda', 'Steve', 'Henry', 'Sara'],
            'gender': ['female', 'male', 'male', 'female'],
            'salary': [3400, 5000, 12400, 7600]}

employee = pd.DataFrame(employee)

private_employee = employee[['employee_id', 'salary']]

print(private_employee)

#%% 3

from scipy import stats
IQR = stats.iqr(pH)
print(IQR)

#%% 4

Q1 = df['age'].quantile(0.25)
Q3 = df['age'].quantile(0.75)

print(Q3 - Q1)

#%% 5

df = {'gh owner': ['pandas-dev', 'tidyverse', 'tidyverse', 'has2k1'],
      'language': ['python', 'R', 'R', 'python'],
      'repo': ['pandas', 'dplyr', 'ggplot2', 'plotnine'],
      'stars': [17800, 2800, 3500, 1450]}

df = pd.DataFrame(df)

print(df.stars.apply(lambda x: x / 1000))
print(df.stars.transform(lambda x: x / 1000))

#%% 6

subset_wine = wine[['country', 'price']]
print(subset_wine.head())

#%% 7

x = np.array([2.2, 0.9, 4.4, 6.7, 2.8, 3.2, 1.1, 3.5])
x_var = np.var(x)
x_stdev = np.std(x)
print('Variance: {:4.2f}'.format(x_var))
print('Std Deviation: {:4.2f}'.format(x_stdev))

#%% 8

chess = chess.set_index('Fide id')

print(chess.head())

#%% 9

df = {'day': [0, 1, 2, 3, 4, 5],
      'order': [10, 11, 14, 7, 5, 16]}

df = pd.DataFrame(df)

sns.lineplot(data=df, x='day', y='order')
plt.xticks(rotation=45)
plt.show()

#%% 10

df = {'alignment': ['Good', 'Good', 'Evil', 'Evil'],
      'character': ['Batman', 'Wonder Woman', 'Lex Luthor', 'The Joker'],
      'height': [1.88, 1.78, 1.88, 1.85],
      'location': ['Gotham', 'Themyscira', 'Metropolis', 'Gotham']}

df = pd.DataFrame(df)

print(df[['alignment', 'character']])

#%% 11

employee = {'first_name': ['Linda', 'Steve', 'Henry', 'Sara'],
            'gender': ['female', 'male', 'male', 'female'],
            'salary_usd': [3000, 5000, 12000, 5000]}

employee = pd.DataFrame(employee)

print(employee[employee['salary_usd'] == 5000])

#%% 12

df = {'index': ['zero', 'one', 'two', 'tree'],
      'Month': ['Jan', 'Apr', 'Mar', 'Feb'],
      'Count': [52, 29, 46, 3]}

df = pd.DataFrame(df).set_index('index')

print(df.loc['two':])

#%% 13

# --iris
# species    measurement   value
# 0     setosa     sepal_length  5.1
# 1     setosa     sepal_length  4.9
# ...
# 598   virginica  petal_width   4.6
# 599   virginica  petal_width   5.0

ax = sns.swarmplot(x="measurement",
                   y="value",
                   hue="species",
                   data=iris)
plt.show()

#%% 14

ax = sns.lineplot(x='Overall rank', y='Score', data=happiness)

plt.show()

#%% 15

# age emissions   mpg  value
# 0    15       low  23.1   3047
# 1     8       low  10.4  15428
# 2     4      high  15.4  24973
# 3    10      high  31.0   4638
# 4     1       low  30.2  19303

# import matplotlib.pyplot as plt
# import seaborn as sns

sns.scatterplot(x = "age", y = "value", hue = "emissions", data = valuation)

plt.show()


import pprint
import hashlib
import hmac
import time
import urllib
import json
from datetime import datetime
from exchange_api import ExchangeApi

#https://coincheck.com/ja/documents/exchange/api

class CoincheckAPIWrapper(ExchangeApi):

    url_base = 'https://coincheck.com'

    #----------------------------------------
    # コンストラクタ
    #----------------------------------------
    def __init__(self, api_key, api_secret, market_code):
        super().__init__(self.url_base, api_key, api_secret)
        self.__market_code = market_code
        return

    #----------------------------------------
    # Hashを計算
    #----------------------------------------
    def __generate_hash(self, timestamp, method, path, params, body):
        if params is not None:
            path = path + '?' + urllib.parse.urlencode(params)
        json_body = ''
        if body is not None:
            json_body = json.dumps(body)
        ba_text = bytearray(timestamp + self.url_base + path + json_body, 'ASCII')
        ba_secret = bytearray(self.api_secret, 'ASCII')
        hash = hmac.new(ba_secret, ba_text, hashlib.sha256).hexdigest()
        return hash

    #----------------------------------------
    # Private API用のヘッダを生成
    #----------------------------------------
    def __generate_header_for_private_api(self, method, path, params, body):
        timestamp = '{0}000'.format(int(time.mktime(datetime.now().timetuple())))
        hash = self.__generate_hash(timestamp, method, path, params, body)
        return { 'ACCESS-KEY': self.api_key,
                 'ACCESS-NONCE': timestamp,
                 'ACCESS-SIGNATURE': hash
               }

    #----------------------------------------
    # Private APIを実行(GET)
    #----------------------------------------
    def __submit_private_api_get(self, path, params):
        headers = self.__generate_header_for_private_api('GET', path, params, None)
        pprint.pprint(headers)
        return self.http.get(path, params, headers)

    #----------------------------------------
    # Private APIを実行(POST)
    #----------------------------------------
    def __submit_private_api_post(self, path, params, body):
        headers = self.__generate_header_for_private_api('POST', path, params, body)
        return self.http.post(path, headers, body)

    #----------------------------------------
    # Tickerを取得
    #----------------------------------------
    def get_ticker(self):
        resp = self.http.get('/api/ticker', None, None)
        last = float(resp['last'])
        resp = self.http.get('/api/order_books', None, None)
        bid = float(resp['bids'][0][0])
        ask = float(resp['asks'][0][0])
        return last, bid, ask

    #----------------------------------------
    # 板情報を取得
    #----------------------------------------
    def get_order_books(self):
        resp = self.http.get('/api/order_books', None, None)
        pprint.pprint(resp['bids'][0][0])
        return

    #----------------------------------------
    # 新規注文を出す
    #----------------------------------------
    def send_childorders(self, order_type, side, price, size):
        if order_type == 'MARKET' and side == 'BUY':
            ortype = 'market_buy'
        elif order_type == 'MARKET' and side == 'SELL':
            ortype = 'market_sell'
        elif order_type == 'LIMIT' and side == 'BUY':
            ortype = 'buy'
        elif order_type == 'LIMIT' and side == 'SELL':
            ortype = 'sell'
        else:
            ortype = ''

        body = {
            'id': 12345,
            'rate': str(price),
            'amount': str(size),
            'order_type': ortype,
            'stop_loss_rate': 'null',
            'pair': self.__market_code,
            'executionTYpe': order_type,

        }
        resp = self.__submit_private_api_post('/api/exchange/orders', None, body)
        pprint.pprint(resp)

    #----------------------------------------
    # 資産残高を取得
    #----------------------------------------
    def get_balance(self):
        resp = self.__submit_private_api_get('/api/accounts/balance', None)
        pprint.pprint(resp)
        return


from collections import defaultdict
from odoo import api, fields, models

class SaleOrder(models.Model):
    _inherit = 'sale.order'

    date_planned = fields.Datetime('Planned Date')
    requested_date = fields.Datetime('Requested Date')

class SaleOrderLine(models.Model):
    _inherit = 'sale.order.line'

    # In 13, this field exists, but isn't stored and is computed during
    # computation for available inventory (set to order's warehouse)
    warehouse_id = fields.Many2one('stock.warehouse', string='Warehouse',
                                   compute=None, store=True)
    date_planned = fields.Datetime('Planned Date')

    def _prepare_procurement_values(self, group_id=False):
        vals = super(SaleOrderLine, self)._prepare_procurement_values(group_id=group_id)
        if self.warehouse_id:
            vals.update({'warehouse_id': self.warehouse_id})
        if self.date_planned:
            vals.update({'date_planned': self.date_planned})
        elif self.order_id.date_planned:
            vals.update({'date_planned': self.order_id.date_planned})
        return vals

    # Needs modifications to not actually set a warehouse on the line as it is now stored
    @api.depends('product_id', 'customer_lead', 'product_uom_qty', 'product_uom', 'order_id.warehouse_id', 'order_id.commitment_date')
    def _compute_qty_at_date(self):
        """ Compute the quantity forecasted of product at delivery date. There are
        two cases:
         1. The quotation has a commitment_date, we take it as delivery date
         2. The quotation hasn't commitment_date, we compute the estimated delivery
            date based on lead time"""
        qty_processed_per_product = defaultdict(lambda: 0)
        grouped_lines = defaultdict(lambda: self.env['sale.order.line'])
        # We first loop over the SO lines to group them by warehouse and schedule
        # date in order to batch the read of the quantities computed field.
        for line in self:
            if not (line.product_id and line.display_qty_widget):
                continue
            # use warehouse from line or order
            warehouse = line.warehouse_id or line.order_id.warehouse_id
            # line.warehouse_id = line.order_id.warehouse_id
            if line.order_id.commitment_date:
                date = line.order_id.commitment_date
            else:
                date = line._expected_date()
            grouped_lines[(warehouse.id, date)] |= line

        treated = self.browse()
        for (warehouse, scheduled_date), lines in grouped_lines.items():
            product_qties = lines.mapped('product_id').with_context(to_date=scheduled_date, warehouse=warehouse).read([
                'qty_available',
                'free_qty',
                'virtual_available',
            ])
            qties_per_product = {
                product['id']: (product['qty_available'], product['free_qty'], product['virtual_available'])
                for product in product_qties
            }
            for line in lines:
                line.scheduled_date = scheduled_date
                qty_available_today, free_qty_today, virtual_available_at_date = qties_per_product[line.product_id.id]
                line.qty_available_today = qty_available_today - qty_processed_per_product[line.product_id.id]
                line.free_qty_today = free_qty_today - qty_processed_per_product[line.product_id.id]
                line.virtual_available_at_date = virtual_available_at_date - qty_processed_per_product[line.product_id.id]
                if line.product_uom and line.product_id.uom_id and line.product_uom != line.product_id.uom_id:
                    line.qty_available_today = line.product_id.uom_id._compute_quantity(line.qty_available_today, line.product_uom)
                    line.free_qty_today = line.product_id.uom_id._compute_quantity(line.free_qty_today, line.product_uom)
                    line.virtual_available_at_date = line.product_id.uom_id._compute_quantity(line.virtual_available_at_date, line.product_uom)
                qty_processed_per_product[line.product_id.id] += line.product_uom_qty
            treated |= lines
        remaining = (self - treated)
        remaining.virtual_available_at_date = False
        remaining.scheduled_date = False
        remaining.free_qty_today = False
        remaining.qty_available_today = False
        # don't unset warehouse as it may be set by hand
        # remaining.warehouse_id = False


import os
import numpy as np
from glob import glob
import open3d as o3d
import argparse
import time
import pyquaternion

# https://github.com/intel-isl/Open3D/issues/2#issuecomment-610683341
def text_3d(text, pos, direction=None, degree=0.0, font="OpenSans-Regular.ttf", font_size=16):
    """
    Generate a 3D text point cloud used for visualization.
    :param text: content of the text
    :param pos: 3D xyz position of the text upper left corner
    :param direction: 3D normalized direction of where the text faces
    :param degree: in plane rotation of text
    :param font: Name of the font - change it according to your system
    :param font_size: size of the font
    :return: o3d.geoemtry.PointCloud object
    """
    if direction is None:
        direction = (0., 0., 1.)

    from PIL import Image, ImageFont, ImageDraw
    from pyquaternion import Quaternion

    font_obj = ImageFont.truetype(font, font_size)
    font_dim = font_obj.getsize(text)

    img = Image.new('RGB', font_dim, color=(255, 255, 255))
    draw = ImageDraw.Draw(img)
    draw.text((0, 0), text, font=font_obj, fill=(0, 0, 0))
    img = np.asarray(img)
    img_mask = img[:, :, 0] < 128
    indices = np.indices([*img.shape[0:2], 1])[:, img_mask, 0].reshape(3, -1).T

    pcd = o3d.geometry.PointCloud()
    pcd.colors = o3d.utility.Vector3dVector(img[img_mask, :].astype(float) / 255.0)
    pcd.points = o3d.utility.Vector3dVector(indices / 100.0)

    raxis = np.cross([0.0, 0.0, 1.0], direction)
    if np.linalg.norm(raxis) < 1e-6:
        raxis = (0.0, 0.0, 1.0)
    trans = (Quaternion(axis=raxis, radians=np.arccos(direction[2])) *
             Quaternion(axis=direction, degrees=degree)).transformation_matrix
    trans[0:3, 3] = np.asarray(pos)
    pcd.transform(trans)
    return pcd

def visualize_pcd_auto_play(cfg):
    WINDOW_WIDTH = cfg.window_width  # change this if needed
    WINDOW_HEIGHT = cfg.window_height  # change this if needed
    file_type = cfg.file_type
    ply_base_dir = cfg.ply_folder
    ply_files = [pf for pf in os.listdir(ply_base_dir) if pf.endswith(file_type)]
    if not len(ply_files) > 0:
        print("empty directory, exiting...")
        return
    # first pcd file
    source_pcd = o3d.io.read_point_cloud(os.path.join(ply_base_dir, ply_files[0]))
    min_pos = np.min(source_pcd.points, axis=0)
    max_pos = np.max(source_pcd.points, axis=0)
    shift_diff_pos = (max_pos - min_pos) / 10
    text_pos = min_pos - shift_diff_pos
    text_pcd = text_3d(text="idx: {} | file: {}".format(0, ply_files[0]), pos=text_pos)
    tmp_pcd_text_points = np.vstack([source_pcd.points, text_pcd.points])
    source_pcd.points = o3d.utility.Vector3dVector(tmp_pcd_text_points)
    vis = o3d.visualization.Visualizer()
    vis.create_window(width=WINDOW_WIDTH, height=WINDOW_HEIGHT)
    vis.add_geometry(source_pcd)
    ctr = vis.get_view_control()
    parameters = o3d.io.read_pinhole_camera_parameters(cfg.camera_view)
    ctr.convert_from_pinhole_camera_parameters(parameters)
    for idx in range(1, len(ply_files)):
        tmp_pcd = o3d.io.read_point_cloud(os.path.join(ply_base_dir, ply_files[idx]))
        min_pos = np.min(tmp_pcd.points, axis=0)
        max_pos = np.max(tmp_pcd.points, axis=0)
        shift_diff_pos = (max_pos - min_pos) / 10
        text_pos = min_pos - shift_diff_pos
        text_pcd = text_3d(text="idx: {} | file: {}".format(0, ply_files[0]), pos=text_pos)
        tmp_pcd_text_points = np.vstack([tmp_pcd.points, text_pcd.points])
        source_pcd.points = o3d.utility.Vector3dVector(tmp_pcd_text_points)
        vis.update_geometry(source_pcd)
        vis.poll_events()
        vis.update_renderer()
        time.sleep(cfg.pause_time)

def visualize_pcd_with_key_bind(cfg):
    WINDOW_WIDTH = cfg.window_width  # change this if needed
    WINDOW_HEIGHT = cfg.window_height  # change this if needed
    file_type = cfg.file_type
    ply_base_dir = cfg.ply_folder
    ply_files = [pf for pf in os.listdir(ply_base_dir) if pf.endswith(file_type)]
    if not len(ply_files) > 0:
        print("empty directory, exiting...")
        return
    source_pcd = o3d.io.read_point_cloud(os.path.join(ply_base_dir, ply_files[0]))
    min_pos = np.min(source_pcd.points, axis=0)
    max_pos = np.max(source_pcd.points, axis=0)
    shift_diff_pos = (max_pos - min_pos) / 10
    text_pos = min_pos - shift_diff_pos
    text_pcd = text_3d(text="idx: {} | file: {}".format(0, ply_files[0]), pos=text_pos)
    tmp_pcd_text_points = np.vstack([source_pcd.points, text_pcd.points])
    source_pcd.points = o3d.utility.Vector3dVector(tmp_pcd_text_points)
    num_ply_file = len(ply_files)
    rotating = False
    pointer = 0
    vis = o3d.visualization.VisualizerWithKeyCallback()

    def key_action_callback(vis, action, mods):
        nonlocal rotating
        # print(action)
        # print(o3d.visualization.gui.KeyEvent.type)
        if action == 1:  # key down
            rotating = True
        elif action == 0:  # key up
            rotating = False
        return True

    def animation_callback(vis):
        nonlocal rotating
        nonlocal pointer
        nonlocal num_ply_file
        nonlocal ply_base_dir
        nonlocal ply_files
        if rotating:
            pointer += 1
            cur_ply_f_idx = pointer % num_ply_file
            tmp_pcd = o3d.io.read_point_cloud(os.path.join(ply_base_dir, ply_files[cur_ply_f_idx]))
            min_pos = np.min(tmp_pcd.points, axis=0)
            max_pos = np.max(tmp_pcd.points, axis=0)
            shift_diff_pos = (max_pos - min_pos) / 10
            text_pos = min_pos - shift_diff_pos
            text_pcd = text_3d(text="idx: {} | file: {}".format(cur_ply_f_idx, ply_files[cur_ply_f_idx]), pos=text_pos)
            tmp_pcd_text_points = np.vstack([tmp_pcd.points, text_pcd.points])
            source_pcd.points = o3d.utility.Vector3dVector(tmp_pcd_text_points)
            vis.update_geometry(source_pcd)
            vis.poll_events()
            vis.update_renderer()
            rotating = False

    # key_action_callback will be triggered when there's a keyboard press, release or repeat event
    vis.register_key_action_callback(32, key_action_callback)  # space

    # animation_callback is always repeatedly called by the visualizer
    vis.register_animation_callback(animation_callback)

    vis.create_window(width=WINDOW_WIDTH, height=WINDOW_HEIGHT)
    vis.add_geometry(source_pcd)
    vis.run()

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Ply Visualizer')
    parser.add_argument('--keyboard', type=bool, default=False,
                        help='Use Keyboard interaction or auto-play.')
    parser.add_argument('--ply_folder', type=str, default="./ply",
                        help='the folder of ply files')
    parser.add_argument('--file_type', type=str, default=".ply",
                        help='the folder of ply files')
    parser.add_argument('--window_width', type=int, default="640",
                        help='the width of visualization window')
    parser.add_argument('--pause_time', type=float, default="0.2",
                        help='time to sleep for each frame, only effective in auto-play mode')
    parser.add_argument('--window_height', type=int, default="480",
                        help="the height of visualization window")
    parser.add_argument('--camera_view', type=str, default="ScreenCamera_2021-06-29-14-56-27.json",
                        help='The screen camera pose json file, if you don\'t have one record it with key P when you are in an open3d window')
    args = parser.parse_args()
    if args.keyboard:
        visualize_pcd_with_key_bind(args)
    else:
        visualize_pcd_auto_play(args)


from papers_extractor.unique_paper import UniquePaper
import logging
import sys
import time
# Below is a list of DOIs, titles and abstracts of papers that will be used
# to test the DoiParser class.
list_doi = [
    '10.1101/2020.03.03.972133',
    '10.1016/j.celrep.2023.112434',
    '10.1038/s41592-021-01285-2',
    '10.1038/nature12373',
    '10.1523/ENEURO.0207-17.2017',
    '10.1016/j.neuron.2022.09.033'
]

list_titles = [
    'AI-aided design of novel targeted covalent inhibitors against SARS-CoV-2',
    'Mitochondrial dynamics define muscle fiber type by modulating ',
    'Removing independent noise in systems neuroscience data using ',
    'Nanometre-scale thermometry in a living cell',
    'Aberrant Cortical Activity in Multiple GCaMP6-Expressing Transgenic',
    'Next-generation brain observatories']

list_abstracts = [
    '<jats:title>Abstract</jats:title><jats:p>The focused drug',
    'Skeletal muscle is highly developed after birth',
    'Progress in many scientific disciplines is hindered by the presence ',
    'Sensitive probing of temperature variations on nanometre scales',
    '<jats:title>Abstract</jats:title><jats:p>Transgenic mouse lines are ',
    'We propose centralized brain observatories for large-scale ']

list_pdf_links = [
    'https://www.biorxiv.org/content/10.1101/2020.03.03.972133v1.full.pdf',
    None,
    'https://www.nature.com/articles/s41592-021-01285-2.pdf',
    'http://www.nature.com/articles/nature12373.pdf',
    None,
    None
]

list_pmids = [
    '32511346',
    '37097817',
    '34650233',
    '23903748',
    '28932809',
    '36240770'
]

list_journal = [
    'bioRxiv',
    'Cell Reports',
    'Nature Methods',
    'Nature',
    'eneuro',
    'Neuron'
]

list_year = [
    2020,
    2023,
    2021,
    2013,
    2017,
    2022
]

list_first_authors = [
    'Tang',
    'Yasuda',
    'Lecoq',
    'Kucsko',
    'Steinmetz',
    'Koch'
]

list_citations = [
    'Tang et al., 2020, bioRxiv',
    'Yasuda et al., 2023, Cell Reports',
    'Lecoq et al., 2021, Nature Methods',
    'Kucsko et al., 2013, Nature',
    'Steinmetz et al., 2017, eneuro',
    'Koch et al., 2022, Neuron'
]

def test_all_doi():
    for i in range(len(list_doi)):
        doi_parser = UniquePaper(list_doi[i])

        assert doi_parser.get_title().startswith(list_titles[i])
        time.sleep(1 / 10)
        assert doi_parser.get_abstract().startswith(list_abstracts[i])
        time.sleep(1 / 10)
        assert doi_parser.get_pdf_url() == list_pdf_links[i]
        time.sleep(1 / 10)
        assert doi_parser.get_pmid() == list_pmids[i]
        time.sleep(1 / 10)
        assert doi_parser.get_journal() == list_journal[i]
        time.sleep(1 / 10)
        assert doi_parser.get_year() == list_year[i]
        time.sleep(1 / 10)
        assert doi_parser.get_first_author() == list_first_authors[i]
        time.sleep(1 / 10)
        assert doi_parser.get_label_string() == list_citations[i]
        time.sleep(1 / 10)
        assert doi_parser.get_nb_citations() >= 0
        time.sleep(1 / 10)

def test_citation_count():
    doi_parser = UniquePaper('10.1101/2020.03.03.972133')
    assert doi_parser.get_nb_citations() > 50

def test_author_doi():
    doi_parser = UniquePaper('10.1101/2020.03.03.972133')
    assert doi_parser.get_authors()[0].split(" ")[-1] == 'Tang'

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, stream=sys.stdout, force=True)
    test_all_doi()


import bisect
from typing import List

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        i = 0
        j = len(nums) - 1
        while i <= j:
            mid = (i + j) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                i = mid + 1
            else:
                j = mid - 1

        return i

    def searchInsert_buitin(self, nums: List[int], target: int) -> int:
        return bisect.bisect_left(nums, target)

s = Solution()
assert s.searchInsert([1,3,5,6], 5) ==  s.searchInsert_buitin([1,3,5,6], 5)


import os
from collections import defaultdict

class DecisionTreeNode:
    def __init__(self, f, t, i, tree, is_categorical=False):
        self.is_leaf = False
        self.threshold = t
        self.feature = f
        self.left = None
        self.right = None
        self.id = i
        self.tree = tree
        self.is_categorical = is_categorical
        self.parent = None
        self.count_left = 0
        self.count_right = 0

    def __ge__(self, other):
        return self.id >= other.id

    def __gt__(self, other):
        return self.id > other.id

    def __le__(self, other):
        return self.id <= other.id

    def __lt__(self, other):
        return self.id < other.id

    def _decide(self, e):
        c_val = e.features[self.feature]
        if c_val == "?" and len(e.instance.domains[self.feature]) > 0:
            c_val = e.instance.domains_max[self.feature]

        if c_val == "?":
            if self.count_right > self.count_left:
                return self.right
            else:
                return self.left
        if (self.is_categorical and c_val == self.threshold) \
                or (not self.is_categorical and c_val <= self.threshold):
            return self.left
        else:
            return self.right

    def train(self, samples):
        ls = []
        rs = []

        for e in samples:
            target = self._decide(e)
            if target.id == self.left.id:
                ls.append(e)
            else:
                rs.append(e)
        self.count_left = len(ls)
        self.count_right = len(rs)
        self.left.train(ls)
        self.right.train(rs)

    def decide(self, e):
        return self._decide(e).decide(e)

    def decide_length(self, e):
        return self._decide(e).decide_length(e) + 1

    def get_depth(self):
        return max(self.left.get_depth(), self.right.get_depth()) + 1

    def get_nodes(self):
        return self.left.get_nodes() + self.right.get_nodes() + 1

    def remap(self, mapping):
        if self.feature in mapping:
            self.feature = mapping[self.feature]
        self.left.remap(mapping)
        self.right.remap(mapping)

    def get_children(self):
        return [self.left, self.right]

    def get_path(self, e):
        pth = self._decide(e).get_path(e)
        pth.append(self)
        return pth

    def get_leaves(self):
        return self.left.get_leaves() + self.right.get_leaves()

    def get_extended_leaves(self):
        return self.left.get_extended_leaves() + self.right.get_extended_leaves()

    def reclassify(self, samples):
        ls = []
        rs = []

        for e in samples:
            target = self._decide(e)
            if target.id == self.left.id:
                ls.append(e)
            else:
                rs.append(e)

        self.left.reclassify(ls)
        self.right.reclassify(rs)

    def copy(self, new_tree, c_p, is_left):
        if c_p is None:
            n_n = new_tree.set_root(self.feature, self.threshold, self.is_categorical)
        else:
            n_n = new_tree.add_node(self.feature, self.threshold, c_p.id, is_left, self.is_categorical)
        self.left.copy(new_tree, n_n, True)
        self.right.copy(new_tree, n_n, False)

class DecisionTreeLeaf:
    def __init__(self, c, i, tree):
        self.is_leaf = True
        self.cls = c
        self.id = i
        self.tree = tree
        self.parent = None
        self.count = 0

    def __ge__(self, other):
        return self.id >= other.id

    def __gt__(self, other):
        return self.id > other.id

    def __le__(self, other):
        return self.id <= other.id

    def __lt__(self, other):
        return self.id < other.id

    def decide(self, e):
        return self.cls, self

    def train(self, samples):
        self.count = len(samples)

    def get_depth(self):
        return 0

    def get_nodes(self):
        return 1

    def get_leaves(self):
        return 1

    def decide_length(self, e):
        return 0

    def remap(self, mapping):
        return

    def get_path(self, e):
        return [self]

    def get_extended_leaves(self):
        return 0 if self.cls.startswith("-") or self.cls == "EmptyLeaf" else 1

    def reclassify(self, samples):
        classes = defaultdict(int)
        for cs in samples:
            classes[cs.cls] += 1
        if len(samples) > 0:
            _, self.cls = max((v, k) for k, v in classes.items())

    def copy(self, new_tree, c_p, is_left):
        if c_p is None:
            new_tree.set_root_leaf(self.cls)
        else:
            new_tree.add_leaf(self.cls, c_p.id, is_left)

class DecisionTree:
    def __init__(self):
        self.root = None
        self.nodes = [None]
        self.c_idx = 1

    def set_root_leaf(self, c):
        self.root = DecisionTreeLeaf(c, 1, self)
        self.nodes.append(self.root)
        return self.root

    def set_root(self, f, t, is_categorical=False):
        self.root = DecisionTreeNode(f, t, 1, self, is_categorical)
        self.nodes.append(self.root)
        return self.root

    def train(self, instance):
        self.root.train(instance.examples)

    def _get_id(self):
        while self.c_idx < len(self.nodes) and self.nodes[self.c_idx]:
            self.c_idx += 1
        if self.c_idx >= len(self.nodes):
            self.nodes.append(None)
        return self.c_idx

    def add_node(self, f, t, parent_id, is_left, is_categorical=False):
        c_id = self._get_id()

        new_node = DecisionTreeNode(f, t, c_id, self, is_categorical)
        new_node.parent = self.nodes[parent_id]

        if is_left:
            self.nodes[parent_id].left = new_node
        else:
            self.nodes[parent_id].right = new_node

        self.nodes[c_id] = new_node
        return new_node

    def add_leaf(self, c, parent_id, is_left):
        c_id = self._get_id()
        new_leaf = DecisionTreeLeaf(c, c_id, self)
        new_leaf.parent = self.nodes[parent_id]

        if is_left:
            self.nodes[parent_id].left = new_leaf
        else:
            self.nodes[parent_id].right = new_leaf
        self.nodes[c_id] = new_leaf
        return new_leaf

    def get_accuracy(self, examples):
        errors = 0
        for c_e in examples:
            decision = self.root.decide(c_e)[0]
            if decision != c_e.cls and (c_e.surrogate_cls is None or c_e.surrogate_cls != decision):
                errors += 1

        return (len(examples) - errors) / len(examples)

    def get_avg_length(self, examples):
        lengths = 0
        for c_e in examples:
            lengths += self.root.decide_length(c_e)

        return lengths / len(examples)

    def get_depth(self):
        return self.root.get_depth()

    def get_nodes(self):
        return self.root.get_nodes()

    def as_string(self):
        lines = []

        def add_node(n, indent):
            n_id = f"c({n.cls})" if n.is_leaf else \
                f"a({n.feature}){' =' if n.is_categorical else ' <='} {n.threshold}"
            lines.append(indent + "" + n_id)
            if not n.is_leaf:
                add_node(n.left, indent+"-")
                add_node(n.right, indent+"-")

        add_node(self.root, "")
        return os.linesep.join(lines)

    def assign(self, instance):
        assigned = defaultdict(list)
        for e in instance.examples:
            if e.ignore:
                continue

            pth = self.root.get_path(e)
            for c_node in pth:
                assigned[c_node.id].append(e)
        return assigned

    def copy(self):
        n_tree = DecisionTree()
        self.root.copy(n_tree, None, None)
        return n_tree

    def clean(self, instance, min_samples=1):
        assigned = self.assign(instance)

        def clean_sub(node):
            if node.is_leaf:
                return
            clean_sub(node.left)
            clean_sub(node.right)

            # Case one, two few examples
            replace_left = node.left.is_leaf and (node.left.id not in assigned or len(assigned[node.left.id]) < min_samples)
            # Case two, both leaves have the same class
            replace_left = replace_left or (node.left.is_leaf and node.right.is_leaf and node.left.cls == node.right.cls)
            replace_left = replace_left or (node.left.is_leaf and node.left.cls == "EmptyLeaf")
            replace_right = node.right.is_leaf and (node.right.id not in assigned or len(assigned[node.right.id]) < min_samples)
            replace_right = replace_right or (node.right.is_leaf and node.right.cls == "EmptyLeaf")

            if replace_left or replace_right:
                if node.parent is not None:
                    self.c_idx = 1
                    is_left = node.parent.left.id == node.id

                    self.nodes[node.id] = None
                    if replace_left:
                        self.nodes[node.left.id] = None
                        node.right.parent = node.parent
                        assigned[node.right.id] = assigned[node.id]
                        if is_left:
                            node.parent.left = node.right
                        else:
                            node.parent.right = node.right
                    elif replace_right:
                        self.nodes[node.right.id] = None
                        node.left.parent = node.parent
                        assigned[node.left.id] = assigned[node.id]
                        if is_left:
                            node.parent.left = node.left
                        else:
                            node.parent.right = node.left
                # Special case root
                else:
                    self.c_idx = 1
                    if replace_left:
                        self.nodes[node.id] = self.nodes[node.right.id]
                        self.nodes[node.right.id] = None
                        self.nodes[node.left.id] = None
                        node.right.id = node.id
                        node.right.parent = None
                        assigned[node.right.id] = assigned[node.id]
                        self.root = node.right
                    elif replace_right:
                        self.nodes[node.id] = self.nodes[node.left.id]
                        self.nodes[node.left.id] = None
                        self.nodes[node.right.id] = None
                        node.left.id = node.id
                        node.left.parent = None
                        assigned[node.left.id] = assigned[node.id]
                        self.root = node.left

        clean_sub(self.root)

    def check(self):
        """Perform a sanity check."""
        q = [self.root]
        nodes = set()

        while q:
            c_n = q.pop()
            if c_n.id in nodes:
                print("Duplicate node id or loop")
            nodes.add(c_n.id)

            if self.nodes[c_n.id] != c_n:
                print("Node id doesn't match node")

            if not c_n.is_leaf:
                q.extend(c_n.get_children())
                if c_n.left is None or c_n.right is None:
                    print("Child is none")

            if c_n.parent is not None and not (c_n.parent.left.id == c_n.id or c_n.parent.right.id == c_n.id):
                print("Invalid parent/child relationship")

            if c_n.parent is None and c_n != self.root:
                print("Double root")

        if any(x.id not in nodes for x in self.nodes if x is not None):
            print("Superfluous nodes")

def tree_compare(n1, n2):
    if n1.is_leaf != n2.is_leaf:
        raise RuntimeError("Leaf non leaf.")

    if not n1.is_leaf:
        if n1.feature != n2.feature:
            raise RuntimeError("Features diverge.")
        if n1.threshold != n2.threshold:
            raise RuntimeError("Threshold diverge.")
        if n1.is_categorical != n2.is_categorical:
            raise RuntimeError("Categorical diverge.")
        tree_compare(n1.left, n2.left)
        tree_compare(n1.right, n2.right)
    elif n1.cls != n2.cls:
        raise RuntimeError("Divergent classes.")


import streamlit as st
from datetime import datetime, timedelta
from src.pages.utils.load_data import load_data
from src.pages.utils.fetch_url import fetch_url
from PIL import Image

def main():
    img = Image.open("assets/covid-19-4960246_640.jpg")
    st.image(img)
    st.title("Data")
    date = datetime.today()
    df = None
    load_state = st.text('Loading data......')
    while True:
        try:
            df = load_data(fetch_url(date))
        except Exception as e:
            date = date - timedelta(days=1)
            continue
        break
    load_state.text("Loading data......done!")

    if st.checkbox("Show raw data"):
        st.subheader('Raw data')
        st.write(df)
    st.subheader("The numbers so far")
    st.markdown("* There are **{}** countries that have been affected by the COVID-19 virus.".format(len(df["Country_Region"].unique())))
    st.markdown("* The virus has affected **{:.2f}M** people and caused the death of **{:.2f}K**.".format(df["Confirmed"].sum()/1000000,
                                                                                df["Deaths"].sum()/1000))
    h_confirmed = df.groupby("Country_Region").agg({"Confirmed": "sum"}).nlargest(1,"Confirmed")
    st.markdown("* **{}** has the largest number of confirmed cases with **{:.2f}M** confirmed cases.".format(h_confirmed.index.values[0],
                                                                                h_confirmed["Confirmed"].values[0]/1000000))
    h_deaths = df.groupby("Country_Region").agg({"Deaths": "sum"}).nlargest(1,"Deaths")
    st.markdown("* **{}** has the largest number of deaths with **{:.2f}K** deaths.".format(h_deaths.index.values[0],
                                                                                h_deaths["Deaths"].values[0]/1000))
    h_recovered = df.groupby("Country_Region").agg({"Recovered": "sum"}).nlargest(1,"Recovered")
    st.markdown("* **{}** has the largest number of recoveries with **{:.2f}M** recovered.".format(h_recovered.index.values[0],
                                                                                h_recovered["Recovered"].values[0]/1000000))


import streamlit as st
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error, r2_score

st.title('Personal Financial Prediction App')

# Load personal financial data
# You can upload a CSV file or manually input your financial data.

st.subheader('Financial Data')
financial_data = st.file_uploader('Upload a CSV file', type=['csv'])

if financial_data is not None:
    df = pd.read_csv(financial_data)
    st.write(df)

    # Predict profit or loss percentages
    st.subheader('Profit or Loss Prediction')
    n_years = st.number_input('Years of Prediction:', 1, 10, 1)
    n_days = n_years * 365
    df['Prediction'] = df['Profit'].shift(-n_days)

    X = np.array(df[['InputFeature1', 'InputFeature2']])  # Adjust features accordingly
    X = X[:-n_days]
    y = np.array(df['Prediction'])
    y = y[:-n_days]

    x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

    model = RandomForestRegressor()
    model.fit(x_train, y_train)

    prediction = model.predict(x_test)
    st.subheader('Model Evaluation')
    mse = mean_squared_error(y_test, prediction)
    r2 = r2_score(y_test, prediction)
    st.write(f'Mean Squared Error: {mse}')
    st.write(f'R2 Score: {r2}')

    # Predict profit or loss percentages based on user input
    user_input = st.text_input('Enter Input Feature 1:')
    user_input2 = st.text_input('Enter Input Feature 2:')
    user_prediction = model.predict([[user_input, user_input2]])
    st.subheader('Predicted Profit or Loss Percentage')
    st.write(f'Predicted: {user_prediction[0]}%')


import math
import matplotlib.pyplot as plt
import numpy as np
from imageio import imread
import sympy as sp
from numba import jit

#detektere kantene til cellekjernene i bildet
#strukturelement: rektangulært naboskap med odde lengder
@jit
def konvolusjon(img, filter):
    plt.imshow(img, cmap = 'gray')
    N,M = img.shape
    n,m = filter.shape
    km = int(np.floor(m/2))
    kn = int(np.floor(n/2))
    paddedImg, N2, M2 = padding(img, km, kn)

    newFilter = np.zeros((n,m))
    newFilter = filter[::-1,::-1]

    output = np.zeros((N,M))
    row = 0
    for i in range(N2+1-n):
        for j in range(M2+1-m):
            sum = 0
            while(row < n):
                r = newFilter[row][:m]         #hver rad i filteret
                s = paddedImg[i+row][j:j+m] #neste rad
                sum += np.sum(r*s)
                row += 1
            output[i, j] = sum
            row = 0
    return output

def padding(img, km, kn):
    N,M = img.shape

    N2 = N + kn*2
    M2 = M + km*2
    paddedImg = np.zeros((N2, M2))

    #hjorner
    v1 = img[0,0]; h1 = img[0, M-1]
    v2 = img[N-1, 0]; h2 = img[N-1, M-1]

    #hjorner
    for i in range(kn):
        for j in range(0, km):
            paddedImg[i:kn, j:km] = v1
            paddedImg[N2-kn:N2-i, j:km] = v2
            paddedImg[i:kn, M2-km:M2-j] = h1
            paddedImg[N2-kn:N2-i, M2-km+j:M2-j] = h2

    #selve bildet
    for i in range(N):
        paddedImg[i+kn, km:M2-km] = img[i, :M]

    #kanter
    paddedImg[:kn, km:M2-km] = img[0, :M]
    paddedImg[N2-kn:N2, km:M2-km] = img[N-1, :M]

    for i in range(km):
        paddedImg[kn:N2-kn, i] = img[:N, 0]
        paddedImg[kn:N2-kn, M2-km+i] = img[0:N, M-1]

    return paddedImg, N2, M2

@jit
def cannys(img, sigma, T_h, T_l):
    G = gaussfilter(sigma)
    img_s = konvolusjon(img, G)
    M, a = gradientmagnitude(img_s)
    tynning = nonmax_suppression(M, a)
    hyst = hysteresis(tynning, T_h, T_l)

    plt.subplot(2,3,1)
    plt.imshow(img, cmap = 'gray')
    plt.title("Original")
    plt.subplot(2,3,2)
    plt.imshow(img_s, cmap = 'gray')
    plt.title("Gauss-filter")
    plt.subplot(2,3,3)
    plt.imshow(M, cmap = 'gray')
    plt.title("Gradientmagnitude")
    plt.subplot(2,3,4)
    plt.imshow(a, cmap = 'gray')
    plt.title("Vinkler")
    plt.subplot(2,3,5)
    plt.imshow(tynning, cmap = 'gray')
    plt.title("Tynning")
    plt.show()

    plt.imshow(hyst, cmap = 'gray')
    plt.title("Resultat med sigma=5, T_h=70, T_l=40")
    plt.show()

@jit
def nonmax_suppression(img, a):
    M, N = img.shape
    output = np.zeros((M,N), dtype=np.int32)
    a = (a * 180)/ np.pi
    a[a < 0] += 180

    for i in range(1,M-1):
        for j in range(1,N-1):
                pixel_bef = 0
                pixel_aft = 0
                angle = a[i][j]

               #angle 0
                if (0 <= angle < 22.5) or (157.5 <= angle <= 180):
                    pixel_bef = img[i, j+1]
                    pixel_aft = img[i, j-1]
                #angle 45
                elif (22.5 <= angle < 67.5):
                    pixel_bef = img[i+1, j-1]
                    pixel_aft = img[i-1, j+1]
                elif (67.5 <= angle < 112.5):
                    pixel_bef = img[i+1, j]
                    pixel_aft = img[i-1, j]
                #angle 135
                elif (112.5 <= angle < 157.5):
                    pixel_bef = img[i-1, j-1]
                    pixel_aft = img[i+1, j+1]

                if (img[i,j] >= pixel_bef) and (img[i,j] >= pixel_aft):
                    output[i,j] = img[i,j]
                else:
                    output[i,j] = 0
    return output

@jit
def hysteresis(img_t, T_h, T_l):
    N,M = img.shape
    marked = np.zeros((N,M))
    marked2 = np.zeros((N,M))

    while(1):
        for i in range(N-1):
            for j in range(M-1):

                if img_t[i,j] >= T_h:
                    marked[i,j] = 255

                elif img_t[i,j] >= T_l and img_t[i,j] < T_h:

                    if ((marked[i+1, j-1] == 255) or (marked[i+1, j] == 255) or (marked[i+1, j+1] == 255)
                        or (marked[i, j-1] == 255) or (marked[i, j+1] == 255) or (marked[i-1, j-1] == 255)
                        or (marked[i-1, j] == 255) or (marked[i-1, j+1] == 255)):
                            marked[i,j] = 255

                else:
                    marked[i][j] = 0

        if np.array_equal(marked, marked2):
            break
        marked2=marked

    return marked

def gradientmagnitude(f_s):
    sobelX = np.array([[-1, 0, 1],
                      [-2, 0, 2],
                      [-1, 0, 1]])
    sobelY = np.array([[1, 2, 1],
                       [0, 0, 0],
                       [-1, -2, -1]])

    gx = konvolusjon(f_s, sobelX)
    gy = konvolusjon(f_s, sobelY)

    M = np.sqrt(gx**2+gy**2)
    a = np.arctan2(gy,gx)
    return M, a

def gaussfilter(sigma):
    n = np.ceil(1 + 8*sigma)/2
    x,y = np.mgrid[-n:n+1, -n:n+1]
    A = 1/(2.0*np.pi*sigma**2)
    gauss = A*np.exp(-(x**2+y**2)/(2*sigma**2))
    return gauss

if __name__ == '__main__':
    filename = 'cellekjerner.png'
    img = imread(filename, as_gray=True)
    cannys(img, 1.87, 105, 75)


"""
Controller for contact lists
"""

import cgi
from kernel.models import *
from src.controllers.request import *
from src.serializer import serialize_one
from src.regex import REGEX_EMAIL, REGEX_NAME, REGEX_SLUG
from django.shortcuts import render, redirect
from django.db.models import F, Q, Count
import csv

import pdb

@login_required()
def index(request, user):
    """
    List Dashboard
    """
    profiles = Profile.objects.filter(managers = user)
    profile = profiles[0]
    lists = List.objects.filter(owner = profile, is_deleted = False)
    listsCount = lists.count()
    for lt in lists:
        list_items = List_item.objects.filter(_list = lt)
        lt.items = list_items.count()
    return render(request, 'list/index.html', locals())

@login_required()
def list(request, lt, user):
    """
    Single List Page
    """
    if not List.objects.filter(id = lt, is_deleted = False).exists():
        return redirect('list:index')
    profiles = Profile.objects.filter(managers = user)
    profile = profiles[0]
    if List.objects.filter(id = lt).exists():
        lt = List.objects.get(id = lt)
        if not Profile_manager.objects.filter(profile = lt.owner, user = user).exists():
            return redirect('index:index')
        list_items = List_item.objects.filter(_list = lt).order_by('-id')
        pastEventList = {}
        profiles = Profile.objects.filter(managers = user)
        pids = []
        for profile in profiles:
            pids.append(profile.id)
        pastEvents = Event.objects.filter(Q(end_time = None,
                                            start_time__lt = timezone.now()) |
                                          Q(end_time__isnull = False,
                                            end_time__lt = timezone.now()),
                                          is_launched = True,
                                          organizers__in = pids) \
                                  .order_by('-start_time')
        eids = []
        for pastEvent in pastEvents:
            eids.append(pastEvent.id)
        purchases = Purchase.objects.filter(Q(checkout = None) |
                                    Q(checkout__is_charged = True,
                                      checkout__is_refunded = False),
                                    event__in = eids,
                                    is_expired = False)
        for purchase in purchases:
            if purchase.event.id in pastEventList:
                pastEventList[purchase.event.id]['quantity'] += 1
            else:
                pastEventList[purchase.event.id] = {
                    'id': purchase.event.id,
                    'name': purchase.event.name,
                    'quantity': 1
                }
    else:
        return redirect('index:index')
    if not Profile_manager.objects.filter(profile = lt.owner, user = user).exists():
        return redirect('index:index')
    return render(request, 'list/list.html', locals())
@login_required()
@validate('POST', ['profile', 'name', 'is_hidden'])
def create(request, params, user):
    """
    Create an empty list
    """
    if not Profile.objects.filter(id = params['profile']).exists():
        response = {
            'status':'FAIL',
            'error':'PROFILE_NOT_FOUND',
            'message':'The profile doesn\'t exist.'
        }
        return json_response(response)
    profile = Profile.objects.get(id = params['profile'])
    # Check if the user is a manager of the profile
    if not Profile_manager.objects.filter(profile = profile,
                                          user = user).exists():
        response = {
            'status':'FAIL',
            'error':'NOT_A_MANAGER',
            'message':'You don\'t have permission for the profile.'
        }
        return json_response(response)
    params['name'] = cgi.escape(params['name'])
    if len(params['name']) > 50:
        response = {
            'status':'FAIL',
            'error':'INVALID_NAME',
            'message':'The name must be within 50 characters.'
        }
        return json_response(response)
    lt = List(owner = profile, name = params['name'],
              is_hidden = params['is_hidden'])
    lt.save()
    response = {
        'status':'OK',
        'list':serialize_one(lt)
    }
    return json_response(response)

@login_required()
@validate('POST', ['id'], ['name'])
def edit(request, params, user):
    """
    Edit a list
    """
    if not List.objects.filter(id = params['id'], is_deleted = False).exists():
        response = {
            'status':'FAIL',
            'error':'LIST_NOT_FOUND',
            'message':'The list doesn\'t exist.'
        }
        return json_response(response)
    lt = List.objects.get(id = params['id'])
    profile = lt.owner
    # Check if the user is a manager of the profile
    if not Profile_manager.objects.filter(profile = profile,
                                          user = user).exists():
        response = {
            'status':'FAIL',
            'error':'NOT_A_MANAGER',
            'message':'You don\'t have permission for the list.'
        }
        return json_response(response)
    if params['name'] is not None:
        params['name'] = cgi.escape(params['name'])
        if len(params['name']) > 50:
            response = {
                'status':'FAIL',
                'error':'INVALID_NAME',
                'message':'The name must be within 50 characters.'
            }
            return json_response(response)
        else:
            lt.name = params['name']
    lt.save()
    response = {
        'status':'OK',
        'list':serialize_one(lt)
    }
    return json_response(response)

@login_required()
@validate('POST', ['id'])
def remove_item(request, params, user):
    """
    Remove an item from a list
    """
    if not List_item.objects.filter(id = params['id']).exists():
        response = {
            'status':'FAIL',
            'error':'LIST_ITEM_NOT_FOUND',
            'message':'The list item doesn\'t exist.'
        }
        return json_response(response)
    item = List_item.objects.get(id = params['id'])
    lt = item._list
    profile = lt.owner
    if not Profile_manager.objects.filter(profile = profile,
                                          user = user).exists():
        response = {
            'status':'FAIL',
            'error':'NOT_A_MANAGER',
            'message':'You don\'t have permission for the list.'
        }
        return json_response(response)
    item.delete()
    response = {
            'status':'OK'
    }
    return json_response(response)

@login_required()
@validate('POST', ['id', 'first_name', 'last_name', 'email'], ['note'])
def add_item(request, params, user):
    """
    Add an item to a list
    """
    if not List.objects.filter(id = params['id']).exists():
        response = {
            'status':'FAIL',
            'error':'LIST_NOT_FOUND',
            'message':'The list doesn\'t exist.'
        }
        return json_response(response)
    lt = List.objects.get(id = params['id'])
    profile = lt.owner
    # Check if the user is a manager of the profile
    if not Profile_manager.objects.filter(profile = profile,
                                          user = user).exists():
        response = {
            'status':'FAIL',
            'error':'NOT_A_MANAGER',
            'message':'You don\'t have permission for the list.'
        }
        return json_response(response)
    # Check email format
    if not REGEX_EMAIL.match(params['email']):
        response = {
            'status':'FAIL',
            'error':'INVALID_EMAIL',
            'message':'Email format is invalid.'
        }
        return json_response(response)
    # Check if the name is valid
    if (not REGEX_NAME.match(params['first_name']) or
        not REGEX_NAME.match(params['last_name'])):
        response = {
            'status':'FAIL',
            'error':'INVALID_NAME',
            'message':'First or last name contain illegal characters.'
        }
        return json_response(response)
    if len(params['first_name']) > 50 or len(params['last_name']) > 50:
        response = {
            'status':'FAIL',
            'error':'NAME_TOO_LONG',
            'message':'First or last name is too long.'
        }
        return json_response(response)
    item, created = List_item.objects.get_or_create(_list = lt,
                                                    email = params['email'])
    item.first_name = params['first_name']
    item.last_name = params['last_name']
    if params['note']:
        item.note = params['note']
    item.save()
    response = {
        'status':'OK',
        'list':serialize_one(lt),
        'item':serialize_one(item)
    }
    return json_response(response)

@login_required()
@validate('POST', ['id', 'event'])
def add_from_event(request, params, user):
    """
    Add items from an event
    """
    if not List.objects.filter(id = params['id'], is_deleted = False).exists():
        response = {
            'status':'FAIL',
            'error':'LIST_NOT_FOUND',
            'message':'The list doesn\'t exist.'
        }
        return json_response(response)
    lt = List.objects.get(id = params['id'])
    profile = lt.owner
    if not Profile_manager.objects.filter(profile = profile,
                                          user = user).exists():
        response = {
            'status':'FAIL',
            'error':'NOT_A_MANAGER',
            'message':'You don\'t have permission for the list.'
        }
        return json_response(response)
    if not Event.objects.filter(id = params['event'],
                                is_deleted = False).exists():
        response = {
            'status':'FAIL',
            'error':'EVENT_NOT_FOUND',
            'message':'The event doesn\'t exist.'
        }
        return json_response(response)
    event = Event.objects.get(id = params['event'])
    if not Organizer.objects.filter(event = event,
                                    profile__managers = user).exists():
        response = {
            'status':'FAIL',
            'error':'NOT_A_MANAGER',
            'message':'You don\'t have permission for the event.'
        }
        return json_response(response)
    purchases = Purchase.objects.filter(Q(checkout = None) |
                                        Q(checkout__isnull = False,
                                          checkout__is_charged = True),
                                        event = event,
                                        is_expired = False) \
                                .prefetch_related('owner')
    added = 0
    duplicates = 0
    for purchase in purchases:
        if not List_item.objects.filter(_list = lt, email = purchase.owner.email).exists():
            item = List_item(_list = lt, email = purchase.owner.email, first_name = purchase.owner.first_name, last_name = purchase.owner.last_name)
            item.save()
            added += 1
        else:
            duplicates += 1
    response = {
        'status':'OK',
        'list':serialize_one(lt),
        'added': added,
        'duplicates': duplicates
    }
    return json_response(response)

@login_required()
@validate('POST', ['csv'])
def prepare_csv(request, params, user):
    """
    Prepare CSV For List Upload
    """
    if not Csv.objects.filter(id = params['csv']).exists():
        response = {
            'status':'FAIL',
            'error':'EVENT_NOT_FOUND',
            'message':'The event doesn\'t exist.'
        }
        return json_response(response)
    csv_file = Csv.objects.get(id = params['csv'])
    csvfile = csv_file.source.file
    reader = csv.reader(csvfile)
    results = {}
    for num, row in enumerate(reader):
        results[num] = row
    csvfile.close()
    response = {
        'status':'OK',
        'results': results
    }
    return json_response(response)

@login_required()
@validate('POST', ['id', 'csv', 'format'])
def add_from_csv(request, params, user):
    """
    Add items from a csv file
    """
    if not List.objects.filter(id = params['id'], is_deleted = False).exists():
        response = {
            'status':'FAIL',
            'error':'LIST_NOT_FOUND',
            'message':'The list doesn\'t exist.'
        }
        return json_response(response)
    lt = List.objects.get(id = params['id'])
    profile = lt.owner
    # Check if the user is a manager of the profile
    if not Profile_manager.objects.filter(profile = profile,
                                          user = user).exists():
        response = {
            'status':'FAIL',
            'error':'NOT_A_MANAGER',
            'message':'You don\'t have permission for the list.'
        }
        return json_response(response)
    # Check if CSV Exists
    if not Csv.objects.filter(id = params['csv']).exists():
        response = {
            'status':'FAIL',
            'error':'CSV_NOT_FOUND',
            'message':'The csv doesn\'t exist.'
        }
        return json_response(response)
    csv_file = Csv.objects.get(id = params['csv'])
    csvfile = csv_file.source.file
    reader = csv.reader(csvfile)
    # Load Format {field_type:col}
    format = json.loads(params['format'])
    added = 0
    duplicates = 0
    for num, row in enumerate(reader):
        if 'email' in format and len(row) >= format['email'] and REGEX_EMAIL.match(row[format['email']]):
            if not List_item.objects.filter(_list = lt, email = row[format['email']]).exists():
                item = List_item(_list = lt, email = row[format['email']])
                item.first_name = row[format['first_name']]
                if 'last_name' in format:
                    item.last_name = row[format['last_name']]
                item.save()
                added += 1
            else:
                duplicates += 1

    response = {
        'status':'OK',
        'list':serialize_one(lt),
        'added': added,
        'duplicates': duplicates
    }
    return json_response(response)

@login_required()
@validate('POST', ['id'])
def delete(request, params, user):
    """
    Delete List
    """
    if not List.objects.filter(id = params['id'], is_deleted = False).exists():
        response = {
            'status':'FAIL',
            'error':'LIST_NOT_FOUND',
            'message':'The list doesn\'t exist.'
        }
        return json_response(response)
    lt = List.objects.get(id = params['id'])
    profile = lt.owner
    # Check if the user is a manager of the profile
    if not Profile_manager.objects.filter(profile = profile,
                                          user = user).exists():
        response = {
            'status':'FAIL',
            'error':'NOT_A_MANAGER',
            'message':'You don\'t have permission for the list.'
        }
        return json_response(response)
    lt.is_deleted = True
    lt.save()
    response = {
        'status': 'OK',
        'list': serialize_one(lt)
    }
    return json_response(response)