import getopt
import sys
from os import listdir
from os.path import join
import os
import random
import torch
from torch.utils import data
import glob
import numpy as np
import scipy.misc
import cv2
import warnings
warnings.filterwarnings("ignore", category=UserWarning)

class DRIVEData(data.Dataset):
    '''
    Finish the implementation of our custom dataloader class.
    We will use the test images as our validation set.
    '''

    def __init__(self,
                 root,
                 split='train',
                 patch_size=64,
                 scale_factor=255
                 ):
        self.ROOT_PATH = root
        self.split = split
        self.patch_size = patch_size
        self.scale_factor = scale_factor
        self.TRAIN_PATH = os.path.join(root, 'training')
        self.TEST_PATH = os.path.join(root, 'testing')
        if split == 'train':
            self.data_folder = self.TRAIN_PATH
        elif split == 'test':
            self.data_folder = self.TEST_PATH
        else:
            raise ValueError
        self.sample_names = listdir(self.data_folder)

    def __len__(self):
        return len(self.sample_names)

    def select_patch(self, pixels_img, pixels_seg, size, width, height):
        # select coordinates for patch
        x = random.randint(0, width - size)
        y = random.randint(0, height - size)

        return pixels_img[x:x+size, y:y+size, :], pixels_seg[x:x+size, y:y+size]

    def concat_patch(self, pixels_img, pixels_seg, size=256):
        # size = patch_size
        # break down the image into patches of desired size and stack the patches
        # for example, image of (512,512,3) => (4, 256, 256, 3)
        factor = int(512/size)
        img_concat = np.zeros((factor**2, size, size, 3))
        seg_concat = np.zeros((factor**2, size, size))
        for i in range(0, factor):
            for j in range(0, factor):
                img_concat[i*factor+j, :, :, :] = pixels_img[size *
                                                             i:size*(i+1), size*j:size*(j+1), :]
                seg_concat[i*factor+j, :, :] = pixels_seg[size *
                                                          i:size*(i+1), size*j:size*(j+1)]
        return img_concat, seg_concat

    def __getitem__(self, i):  # i is index
        assert self.split in ['train', 'test', 'result']

        sample_name = self.sample_names[i]
        full_img_G_path = join(self.data_folder, sample_name, 'HE-green.png')
        full_img_B_path = join(self.data_folder, sample_name, 'HE-blue.png')
        full_img_R_path = join(self.data_folder, sample_name, 'HE-red.png')
        full_nec_path = join(self.data_folder, sample_name, 'necrosis.png')
        full_perf_path = join(self.data_folder, sample_name, 'perfusion.png')
        full_label_path = join(self.data_folder, sample_name, 'EF5.png')

        im_img_G = cv2.imread(full_img_G_path, cv2.IMREAD_GRAYSCALE)
        im_img_B = cv2.imread(full_img_B_path, cv2.IMREAD_GRAYSCALE)
        im_img_R = cv2.imread(full_img_R_path, cv2.IMREAD_GRAYSCALE)
        im_nec = cv2.imread(full_nec_path, cv2.IMREAD_GRAYSCALE)
        im_perf = cv2.imread(full_perf_path, cv2.IMREAD_GRAYSCALE)
        im_label = cv2.imread(full_label_path, cv2.IMREAD_GRAYSCALE)
        assert im_img_G.ndim == 2
        assert isinstance(im_img_G, np.ndarray)
        assert im_img_G.shape == im_label.shape
        width, height = im_img_G.shape

        if self.split == 'train' or self.split == 'test':
            # normalize to [0,1]
            pixels_img_G = np.array(im_img_G)/255
            pixels_img_B = np.array(im_img_B)/255
            pixels_img_R = np.array(im_img_R)/255
            pixels_nec = np.array(im_nec)/255
            pixels_perf = np.array(im_perf)/255
            pixels_seg = np.array(im_label)/self.scale_factor
            # make multiple chanels
            pixels_img = np.stack(
                [pixels_img_G, pixels_img_B, pixels_img_R, pixels_nec, pixels_perf], axis=2)
            # make sure there is something in the label img
            assert pixels_seg.max() > 60/self.scale_factor
            patch_img, patch_seg = self.select_patch(
                pixels_img, pixels_seg, self.patch_size, width, height)
            # do not patch the background
            # while patch_img.mean() < (7.6 / 255):
            #     # print('do not patch the background\n')
            #     patch_img, patch_seg = self.select_patch(
            #         pixels_img, pixels_seg, self.patch_size, width, height)

            img = torch.tensor(patch_img).permute(2, 0, 1)
            seg = torch.tensor(patch_seg).unsqueeze(0)
        else:
            pass
            # patch_img = im_img.resize((512,512)) #resample = NEAREST
            # patch_seg = im_seg.resize((512,512))
            # pixels_img = np.array(patch_img)/255
            # pixels_seg = np.array(patch_seg)/255
            # # for eval reconstruction
            # # make sure the input patch_size to concat_patch() equals args.patch_size
            # # which corresponds to the patch size model was trained on
            # patch_img, patch_seg = self.concat_patch(pixels_img, pixels_seg, 256)
            # img = torch.tensor(patch_img).permute(0,3,1,2)
            # seg = torch.tensor(patch_seg).unsqueeze(1)

        '''
        I have done the pipelining, your task is to:

        1. Load the imgs to numpy arrays.
            Hint: Use the PIL.Image library for this.
            Note: img should have shape 3xHxW
                  seg should have shape HxW. Furthermore,
                  each pixel in seg should have a value indexing
                  which class it is apart of. For this dataset,
                  we are segmenting the blood vessels from the
                  background class. Thus, there are only two classes,
                  0 = background class, 1 = blood vessel class.

        2. Normalize the images to [0, 1] or z-score norm.
            Hint: if you z-score normalize, you should consider
            whether to normalize each RGB channel seperately or
            normalize all channels simultaneously.

        3. Rescale the images to target size.
            Note: target size is your choice, but
            it should be appropriate depending on your
            model of choice. I.e, some models require
            every dimension to be divisible by 16 due
            to pooling layers. Also the choice of interpolation
            matters.
            Hint: seg only has only viable interpolation method
            (since the values are discrete, {0, 1}) while img has a few options.

        4. Apply augmentations here.

        5. Turn the numpy arrays into torch tensors.
        '''

        data_dict = {
            "name": sample_name,
            "image": img,
            "mask": seg,
            # "class": diagnosis
        }

        return data_dict

if __name__ == '__main__':
    dataset = DRIVEData(
        root='./data/hypoxia img')


import os
import subprocess

from parameterized import parameterized
from sflkitlib.events import event

from sflkit import instrument_config
from sflkit.config import Config
from sflkit.mapping import EventMapping
from utils import BaseTest

class EventTests(BaseTest):
    def test_lines(self):
        config = Config.create(
            path=os.path.join(self.TEST_RESOURCES, self.TEST_LINES),
            language="python",
            events="line",
            working=self.TEST_DIR,
        )
        instrument_config(config)

        subprocess.run([self.PYTHON, self.ACCESS], cwd=self.TEST_DIR)

        events = event.load(
            os.path.join(self.TEST_DIR, self.TEST_PATH),
            EventMapping.load(config).mapping,
        )
        self.assertEqual(3, len(events))
        for i, e in enumerate(events):
            self.assertIsInstance(e, event.LineEvent, f"{e} is not a line event")
            self.assertEqual(self.ACCESS, e.file, f"{e} has not correct file")
            self.assertEqual(i + 1, e.line, f"{e} has not correct line")

    def test_branches(self):
        config = Config.create(
            path=os.path.join(self.TEST_RESOURCES, self.TEST_BRANCHES),
            language="python",
            events="line,branch",
            working=self.TEST_DIR,
        )
        instrument_config(config)

        subprocess.run([self.PYTHON, self.ACCESS], cwd=self.TEST_DIR)

        events = event.load(
            os.path.join(self.TEST_DIR, self.TEST_PATH),
            EventMapping.load(config).mapping,
        )
        self.assertEqual(5, len(events))
        lines = [1, 4, 5]
        line_i = 0
        first_branch = True
        branch_lines = [1, 4]
        branch_i = 0
        for e in events:
            if e.event_type == event.EventType.LINE:
                self.assertIsInstance(e, event.LineEvent, f"{e} is not a line event")
                self.assertEqual(self.ACCESS, e.file, f"{e} has not correct file")
                self.assertEqual(lines[line_i], e.line, f"{e} has not correct line")
                line_i += 1
            elif e.event_type == event.EventType.BRANCH:
                self.assertIsInstance(
                    e, event.BranchEvent, f"{e} is not a condition event"
                )
                e: event.BranchEvent
                self.assertEqual(self.ACCESS, e.file, f"{e} has not correct file")
                self.assertEqual(
                    branch_lines[branch_i], e.line, f"{e} has not correct line"
                )
                if first_branch:
                    self.assertGreater(
                        e.then_id, e.else_id, f"{e} has not correct branch ids"
                    )
                else:
                    self.assertLess(
                        e.then_id, e.else_id, f"{e} has not correct branch ids"
                    )
                first_branch = False
                branch_i += 1

    @staticmethod
    def _test_events(events):
        config = Config.create(
            path=os.path.join(BaseTest.TEST_RESOURCES, "test_events"),
            language="python",
            events=events,
            working=BaseTest.TEST_DIR,
        )
        instrument_config(config)

        subprocess.run([BaseTest.PYTHON, BaseTest.ACCESS], cwd=BaseTest.TEST_DIR)
        return event.load(
            os.path.join(BaseTest.TEST_DIR, BaseTest.TEST_PATH),
            EventMapping.load(config).mapping,
        )

    def test_function(self):
        events = self._test_events("function_enter,function_exit")
        self.assertEqual(24, len(events))
        function_enter = list(
            zip(
                [9, 2, 9, 2, 5, 5, 5, 5, 5, 9, 2, 5],
                [
                    "a",
                    "__init__",
                    "a",
                    "__init__",
                    "a",
                    "a",
                    "a",
                    "a",
                    "a",
                    "a",
                    "__init__",
                    "a",
                ],
            )
        )
        function_enter_i = 0
        function_exit = list(
            zip(
                [2, 13, 2, 6, 6, 6, 6, 6, 13, 2, 6, 13],
                [
                    "__init__",
                    "a",
                    "__init__",
                    "a",
                    "a",
                    "a",
                    "a",
                    "a",
                    "a",
                    "__init__",
                    "a",
                    "a",
                ],
                [None, 0, None, 8, 12, 16, 20, 24, 24, None, 2, 2],
            )
        )
        function_exit_i = 0
        for e in events:
            if e.event_type == event.EventType.FUNCTION_ENTER:
                line, function = function_enter[function_enter_i]
                self.assertIsInstance(
                    e, event.FunctionEnterEvent, f"{e} is not a line event"
                )
                e: event.FunctionEnterEvent
                self.assertEqual(BaseTest.ACCESS, e.file, f"{e} has not correct file")
                self.assertEqual(line, e.line, f"{e} has not correct line")
                self.assertEqual(function, e.function, f"{e} has not correct function")
                function_enter_i += 1
            elif e.event_type == event.EventType.FUNCTION_EXIT:
                line, function, value = function_exit[function_exit_i]
                self.assertIsInstance(
                    e, event.FunctionExitEvent, f"{e} is not a line event"
                )
                e: event.FunctionExitEvent
                self.assertEqual(BaseTest.ACCESS, e.file, f"{e} has not correct file")
                self.assertEqual(line, e.line, f"{e} has not correct line")
                self.assertEqual(function, e.function, f"{e} has not correct function")
                self.assertEqual(value, e.return_value, f"{e} has not correct function")
                function_exit_i += 1

    def test_branch(self):
        events = self._test_events("branch")
        self.assertEqual(12, len(events))
        branches = list(
            zip(
                [11, 16, 11, 11, 11, 11, 11, 11, 11, 11, 18, 25],
                [1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 0],
            )
        )
        branches_i = 0
        for e in events:
            if e.event_type == event.EventType.BRANCH:
                line, mod = branches[branches_i]
                self.assertIsInstance(e, event.BranchEvent, f"{e} is not a line event")
                e: event.BranchEvent
                self.assertEqual(BaseTest.ACCESS, e.file, f"{e} has not correct file")
                self.assertEqual(line, e.line, f"{e} has not correct line")
                if mod < 0:
                    self.assertLess(
                        e.then_id,
                        e.else_id,
                        f"{e} has not correct branch ids",
                    )
                elif mod > 0:
                    self.assertGreater(
                        e.then_id,
                        e.else_id,
                        f"{e} has not correct branch ids",
                    )
                else:
                    self.assertEqual(-1, e.else_id, f"{e} has not correct branch ids")
                    self.assertLessEqual(
                        0, e.then_id, f"{e} has not correct branch ids"
                    )
                branches_i += 1

    def test_loop(self):
        events = self._test_events("loop_begin,loop_hit,loop_end")
        self.assertEqual(12, len(events))
        expected_loop = [
            (event.EventType.LOOP_BEGIN, 11),
            (event.EventType.LOOP_END, 11),
            (event.EventType.LOOP_BEGIN, 11),
            (event.EventType.LOOP_HIT, 11),
            (event.EventType.LOOP_HIT, 11),
            (event.EventType.LOOP_HIT, 11),
            (event.EventType.LOOP_HIT, 11),
            (event.EventType.LOOP_HIT, 11),
            (event.EventType.LOOP_END, 11),
            (event.EventType.LOOP_BEGIN, 11),
            (event.EventType.LOOP_HIT, 11),
            (event.EventType.LOOP_END, 11),
        ]
        for e, expected in zip(events, expected_loop):
            type_, line = expected
            self.assertEqual(type_, e.event_type, f"{e} is not of type {type_}")
            self.assertIsInstance(
                e,
                event.event_mapping[type_],
                f"{e} is not a {event.event_mapping[type_]}",
            )
            self.assertEqual(BaseTest.ACCESS, e.file, f"{e} has not correct file")
            self.assertEqual(line, e.line, f"{e} has not correct line")
            # noinspection PyUnresolvedReferences
            self.assertEqual(0, e.loop_id, f"{e} has not correct loop id")

    def test_def_use(self):
        events = self._test_events("def,use")
        loop = [
            # loop
            (event.EventType.DEF, 11, "i", None),
            (event.EventType.USE, 12, "b", None),
            (event.EventType.USE, 12, "d", None),
            (event.EventType.USE, 12, "d.a", None),
            # A.a
            (event.EventType.DEF, 5, "b", None),
            (event.EventType.USE, 6, "self", None),
            (event.EventType.USE, 6, "self.b", None),
            (event.EventType.USE, 6, "b", None),
            (event.EventType.DEF, 12, "d.b", None),
        ]

        def get_a(b, c, result):
            return (
                [
                    # a
                    (event.EventType.DEF, 9, "b", b),
                    (event.EventType.DEF, 9, "c", c),
                    (event.EventType.USE, 10, "b", None),
                    (event.EventType.USE, 10, "A", None),
                    # A.__init__
                    (event.EventType.DEF, 2, "b", b),
                    (event.EventType.USE, 3, "b", None),
                    (event.EventType.DEF, 3, "self.b", b),
                    (event.EventType.DEF, 10, "d", None),
                    (event.EventType.USE, 11, "c", None),
                    (event.EventType.USE, 11, "range", None),
                ]
                + loop * c
                + [
                    (event.EventType.USE, 13, "d", result),
                    (event.EventType.USE, 13, "d.b", result),
                ]
            )

        expected_def_uses = (
            [(event.EventType.USE, 16, "a", None)]
            + get_a(0, 0, 0)
            + [
                (event.EventType.USE, 18, "a", None),
            ]
            + get_a(4, 5, 24)
            + get_a(1, 1, 2)
            + [
                (event.EventType.USE, 19, "print", None),
                (event.EventType.USE, 24, "ValueError", None),
                (event.EventType.USE, 26, "print", None),
            ]
        )
        self.assertEqual(len(expected_def_uses), len(events))  # defs + uses
        for e, expected in zip(events, expected_def_uses):
            type_, line, var, val = expected
            self.assertEqual(
                type_, e.event_type, f"{e} is not of type {type_}, expected {expected}"
            )
            self.assertIsInstance(
                e,
                event.event_mapping[type_],
                f"{e} is not a {event.event_mapping[type_]}, expected {expected}",
            )
            self.assertEqual(
                BaseTest.ACCESS,
                e.file,
                f"{e} has not correct file, expected {expected}",
            )
            self.assertEqual(
                line, e.line, f"{e} has not correct line, expected {expected}"
            )
            if isinstance(var, list):
                self.assertIn(
                    e.var, var, f"{e} has not correct var, expected {expected}"
                )
            else:
                self.assertEqual(
                    var, e.var, f"{e} has not correct var, expected {expected}"
                )
            if type_ == event.EventType.DEF and val:
                if isinstance(val, list):
                    self.assertIn(
                        e.value, val, f"{e} has not correct value, expected {expected}"
                    )
                else:
                    self.assertEqual(
                        val, e.value, f"{e} has not correct value, expected {expected}"
                    )

    def test_condition(self):
        events = self._test_events("condition")
        self.assertEqual(4, len(events))
        expected_conditions = [
            (event.EventType.CONDITION, 16, "a(0, 0)", False),
            (event.EventType.CONDITION, 18, "a(4, 5)", True),
            (event.EventType.CONDITION, 18, "a(1, 1)", True),
            (event.EventType.CONDITION, 18, "a(4, 5) and a(1, 1)", True),
        ]
        for e, expected in zip(events, expected_conditions):
            type_, line, exp, ev = expected
            self.assertEqual(e.event_type, type_, f"{e} is not of type {type_}")
            self.assertIsInstance(
                e,
                event.event_mapping[type_],
                f"{e} is not a {event.event_mapping[type_]}",
            )
            self.assertEqual(BaseTest.ACCESS, e.file, f"{e} has not correct file")
            self.assertEqual(line, e.line, f"{e} has not correct line")
            self.assertEqual(ev, e.value, f"{e} has not correct value")
            self.assertEqual(exp, e.condition, f"{e} has not correct condition")

    def test_len(self):
        config = Config.create(
            path=os.path.join(self.TEST_RESOURCES, self.TEST_LEN),
            language="python",
            events="len",
            working=self.TEST_DIR,
        )
        instrument_config(config)

        subprocess.run(
            [self.PYTHON, self.ACCESS, "a", "b", "c"],
            cwd=self.TEST_DIR,
        )

        events = event.load(
            os.path.join(self.TEST_DIR, self.TEST_PATH),
            EventMapping.load(config).mapping,
        )
        self.assertEqual(2, len(events))
        for i, a, l, e in zip([4, 5], ["args", "a"], [3, 1], events):
            self.assertIsInstance(e, event.LenEvent, f"{e} is not a line event")
            self.assertEqual(self.ACCESS, e.file, f"{e} has not correct file")
            self.assertEqual(i, e.line, f"{e} has not correct line")
            self.assertEqual(a, e.var, f"{e} has not correct var")
            self.assertEqual(l, e.length, f"{e} has not correct length")

class SerializeEventsTest(BaseTest):
    @parameterized.expand(map(lambda x: (str(x), x), BaseTest.EVENTS))
    def test_serialize(self, _, e):
        self.assertEqual(e, event.deserialize(event.serialize(e)))

    @parameterized.expand(map(lambda x: (str(x), x), BaseTest.EVENTS))
    def test_load(self, _, e):
        self.assertEqual(e, event.load_event(e.dump(), {e.event_id: e}))


from stockstats import StockDataFrame
from Models.Position import Position
import pandas, sys

class RSI_OverSold:

	def __init__(self, queuesHandler, dataProvider):
		self.queuesHandler = queuesHandler
		self.dataProvider = dataProvider
		self.__ignore_warnings()

	def perform(
    	self,
    	ticker,
    	rsi_period = 5,
    	rsi_limit = 20,
    	tp_percentage = 0.5,
    	amount = None,
    	ltf_interval="5m",
    	ltf_period="2d",
    	days_between_orders=2,
    	max_positions_amount=3,
    ):
		#print(locals())
		print(ticker)

		if amount is None:
			amount = self.queuesHandler.get_default_amount()
			print("Default Amount is: {}".format(amount))

		if self.queuesHandler.is_there_a_recent_order(ticker, days_between_orders):
			print('There is a recent order for {}'.format(ticker))
			return

		positions_amount = int(self.queuesHandler.get_positions_amount(ticker))
		print('There are {} opened positions for {}'.format(positions_amount, ticker))

		if max_positions_amount <= positions_amount:
			print('Already reached the limit of positions for {}'.format(ticker))
			return

		# Calculate RSI using Lower TimeFrame (ltf) data
		data_ltf = self.dataProvider.get(symbol = ticker, interval = ltf_interval, period = ltf_period)

		data_ltf = self.__fill_data_gaps(data_ltf)

		df_ltf = StockDataFrame.retype(data_ltf)

		rsi_key = 'rsi_' + str(rsi_period)

		rsi_serie = df_ltf[rsi_key]

		current_rsi = round(rsi_serie[-1], 2)
		previous_rsi = round(rsi_serie[-2], 2)

		#self.__print_df_tail(df_ltf)
		print("Current RSI({}): {}.. Previous RSI value: {}".format(rsi_period, current_rsi, previous_rsi))

		if previous_rsi < rsi_limit:
			takeProfit = amount * tp_percentage / 100

			self.queuesHandler.add_position_to_open(Position(
				ticker = ticker,
				amount = amount,
				stopLoss = False,
				takeProfit = round(takeProfit, 2),
			))

			self.queuesHandler.save_order(ticker)

			print('Opening a position for {}. Take profit {}'.format(ticker, takeProfit))

	def __crossover(self, series, limit, offset = 1):
		last_index = - offset
		previous_index = last_index - 1

		last_value = round(series[last_index], 2)
		previous_value = round(series[previous_index], 2)

		print("Last: {}, Previous: {}".format(last_value, previous_value))

		if( previous_value <= limit and last_value > limit):
			return True
		return False

	def __ignore_warnings(self):
		import warnings, logging
		warnings.filterwarnings("ignore")
		logging.disable(logging.CRITICAL)

	def __print_df_tail(self, df, rows=5):
		pandas.set_option('display.max_columns', None)
		print(df.tail(rows))
		sys.stdout.flush()

	def __print_full(self, df):
		import pandas as pd
		pandas.set_option('display.max_rows', len(df))
		pandas.set_option('display.max_columns', None)
		print(df)
		pandas.reset_option('display.max_rows')
		sys.stdout.flush()

	def __fill_data_gaps(self, df, resample = '5min'):

		# Fill data gaps
		df = df.resample(resample).ffill()

		return df.between_time('09:30', '16:00')


"""Object representation of a song."""
from vocaloid.note import *
from vocaloid.phonemesParser import parse_phonemes_by_syllables as parse_phonemes
from vocaloid.phonemesParser import get_phonemes_ssml as get_ssml
from vocaloid.phonemesParser import get_phonemes
from vocaloid.syllablesParser import *
from vocaloid.utils import write_to_filepath

from pdf2image import convert_from_path, convert_from_bytes
import tempfile
from PIL import Image
import os, re, copy

from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QWidget

from vocaloid.__main__ import *

def convertToMilliseconds(length):
    """Convert note length to ms. Currently assumes bpm of 170."""
    """Previously quarter was 250, which is bpm 240 instead of 120."""
    if length.value == 1:  # eighth
        return 175
    if length.value == 2:  # quarter
        return 350
    if length.value == 3:  # half
        return 700
    if length.value == 4:  # whole
        return 1400

def convertPitchToABS(pitch, octave):
    # pitches ordered according to experimental correspondence data
    # lowest is C4, highest is B4
    # pitches = [1275, 1285, 1300, 1315,
    #            1330, 1350, 1380, 1400,
    #            1420, 1440, 1470, 1495]

    # pitches ordered according to experimental correspondence data
    # lowest is C3, highest is B3
    pitches = [[1130, 1140, 1150, 1160,
               1170, 1180, 1192, 1204,
               1216, 1228, 1240, 1260],
               [1275, 1285, 1300, 1315,
                1330, 1350, 1380, 1400,
                1420, 1440, 1470, 1495]]

    return pitches[octave][pitch.value]

class Song:
    def __init__(self, lyrics, window_in):
        super().__init__()
        self.window = window_in
        self.num_notes = 0
        self.notes = []
        self.lyrics = ""
        self.lily_notes = []
        self.num_rest = 0
        self.curr_note = 0  # including rest note
        self.syllables = parse_syllables(lyrics)
        self.phonemes = parse_phonemes(get_phonemes(get_ssml(lyrics)))

    def __str__(self):
        string = ""
        for x, note in enumerate(self.notes):
            string = string + str(note) + " "
            string = string + self.syllables[x] + "\n"
        return string

    def addNote(self, octave, pitch, length):
        if self.num_notes >= len(self.syllables):
            print("Can't add any more notes!")
            return

        syllable = self.syllables[len(self.notes) - self.num_rest]
        phonemes = self.phonemes[len(self.notes) - self.num_rest]
        if octave < 3:
            octave = 3
        elif octave > 4:
            octave = 4
        n = Note(octave, pitch, length, syllable, phonemes)
        self.notes.append(n)
        self.curr_note += 1
        self.num_notes = self.num_notes + 1
        notation_map = ["c", "cis", "d", "dis", "e", "f", "fis", "g", "gis", "a", "ais", "b"]
        octave_map = ['', '\'', '\'\''] # octave 2, 3, 4
        length_map = ['8', '4', '2', '1']
        lily_note = notation_map[pitch] + octave_map[octave - 2] + length_map[length - 1]
        self.lily_notes.append(lily_note)
        self.convertToLilyPond()
        if (len(self.notes) - self.num_rest < len(self.syllables)):
            self.window.sylLabel.setText(self.syllables[len(self.notes) - self.num_rest])
        else:
            self.window.sylLabel.setText("END!")
            self.window.next5Button.setFocus(True)
            self.window.sylLabel.setStyleSheet('color: red')
        if self.num_notes == len(self.syllables):
            self.window.next5Button.setEnabled(True)

    def addRest(self, length):
        if self.num_notes >= len(self.syllables):
            print("Can't add any more notes!")
            return
        syllable = ""
        phonemes = []
        self.num_rest += 1
        n = Note(0, 0, length, syllable, phonemes, True)
        length_map = ['8', '4', '2', '1']
        lily_note = 'r' + length_map[length - 1]
        self.notes.append(n)
        self.curr_note += 1
        self.lily_notes.append(lily_note)
        self.convertToLilyPond()

    def deleteNote(self, index):
        if index >= len(self.notes) or index < 0:
            return

        delete_item = self.notes.pop(index)
        self.curr_note -= 1
        if delete_item.is_rest == True:
            self.num_rest -= 1
        else:
            self.num_notes -= 1
        self.lily_notes.pop(index)
        self.convertToLilyPond()
        self.window.sylLabel.setStyleSheet('color: white')
        self.window.sylLabel.setText(self.syllables[len(self.notes) - self.num_rest])
        if self.num_notes < len(self.syllables):
            self.window.next5Button.setEnabled(False)

    def addLyrics(self, line):
        self.lyrics = line
        parsed = parse_syllables(line)
        for syllable in parsed:
            self.syllables.append(syllable)
        phonemes_list = parse_phonemes(get_phonemes(get_ssml(line)))
        if phonemes_list is not None:
            for phonemes in phonemes_list:
                self.phonemes.append(phonemes)

    def convertToMaryXML(self):
        # the first line is really long...
        res = '<?xml version="1.0" encoding="UTF-8"?>'
        res = res + '<maryxml xmlns="http://mary.dfki.de/2002/MaryXML"'
        res = res + ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
        res = res + ' version="0.5" xml:lang="en-US">\n'

        # write overhead at the beginning
        res = res + '<p>\n'
        res = res + '<s>\n'

        # for each note, write pitch, length, phonemes
        # (currently assumes octave is 4 due to lack of data)
        for note in self.notes:
            if note.is_rest:
                line = '<boundary duration="' + str(convertToMilliseconds(note.length)) + '"/>'
                res = res + line
            else:
                phon = note.phonemes.split()
                phon_final = []
                for p in phon:
                    m = re.search('([a-z]|[A-Z]|@|{)', p)
                    if m is not None:
                        phon_final.append(p)
                line = '<prosody rate="' + str(convertToMilliseconds(note.length)/len(phon_final)) + 'ms"'
                line = line + ' pitch="' + str(convertPitchToABS(note.pitch, note.octave - 3)) + 'abs">'
                res = res + line + "\n"
                line_ph = '<t ph="'
                for phoneme in note.phonemes:
                    line_ph = line_ph + phoneme
                line_ph.strip()
                line_ph = line_ph + '" >'
                res = res + line_ph + "\n"
                res = res + note.syllable + "\n"   # this line might not be necessary...
                res = res + "</t>\n"
                res = res + "</prosody>\n"

        # write overhead at the end
        res = res + "</s>\n"
        res = res + "</p>\n"
        res = res + "</maryxml>\n"

        return res

    def convertToLilyPond(self):
        musicOne = "musicOne = {\n\t"
        verseOne = "\n}\nverseOne = \lyricmode {\n\t"
        score = "\n}\n\score {\n\t<<\n\t\t"
        score += "\\new Voice = \"one\" {\n\t\t\t\\time 4/4\n\t\t\t\musicOne\n\t\t}\n\t\t"
        score += "\\new Lyrics \lyricsto \"one\" {\n\t\t\t\\verseOne\n\t\t}"
        score += "\n\t>>\n}"
        # sharp - is; flat - es
        # c - octave 2; c' - octave 3; c'' - octave 4
        # "cis4 bes'8. a'16 g'4. f'8 e'4 d'4 c'2 c''2"
        lily_notes = copy.deepcopy(self.lily_notes)
        lily_notes.insert(self.curr_note - 1, "\override NoteHead.color = #red")
        lily_notes.insert(self.curr_note + 1, "\override NoteHead.color = #black")
        melody = ' '.join(lily_notes)
        lyrics = ' '.join(self.syllables)
        lily_string = musicOne + melody + verseOne + lyrics + score
        write_to_filepath("tmp/song.ly", lily_string)
        os.system("lilypond --output='tmp/' tmp/song.ly")
        self.displayMusic()

    def displayMusic(self):
        with tempfile.TemporaryDirectory() as path:
            images_from_path = convert_from_path('tmp/song.pdf', output_folder=path)
        # images_from_path[0].show()
        images_from_path[0].save("tmp/song.jpg", "JPEG") # Assume there is only one page in the pdf file.
        pixmap = QPixmap("tmp/song.jpg")
        # pixmap = pixmap.scaledToWidth(self.window.picLabel.width())
        self.window.picLabel.setPixmap(pixmap)
        self.window.picLabel.adjustSize()
        self.window.picLabel.show()

# convertToLilyPond()


# -*- coding: utf-8 -*-
__author__ = 'JLPlata'

from bs4 import BeautifulSoup
import requests

#ingrso la url
URL_BASE = "https://www.scoresandodds.com"

MAX_PAGES = 20
counter = 0

for i in range(1, MAX_PAGES):

    # Construyo la URL
    if i > 1:
        url = "%spage/%d/" % (URL_BASE, i)
    else:
        url = URL_BASE

    # Realizamos la petición a la web
    req = requests.get(url)
    # Comprobamos que la petición nos devuelve un Status Code = 200
    statusCode = req.status_code
    if statusCode == 200:

        # Pasamos el contenido HTML de la web a un objeto BeautifulSoup()
        html = BeautifulSoup(req.text, "html.parser")

        # se busca mediante segun el elemetno (class, div, p, ...)
        entradas = html.find('aside', {'class': 'page-sidebar'})

        print (entradas)

    else:
        print("")
        break


from datetime import datetime
from subprocess import Popen, call
from threading import Thread
from time import sleep

class Alarm(Thread):
    def __init__(self, alarm_t=None, active=False):
        Thread.__init__(self)
        self.alarm_t = alarm_t
        self.active = active
        self._alarm_on = False

    def set_alarm(self, alarm_t):
        self.alarm_t = alarm_t
        self.active = True

    def activate(self):
        self.active = True

    def deactivate(self):
        self.active = False

    def run(self):
        while True:
            if self.active:
                if not self._alarm_on:
                    t = datetime.now()
                    if t.hour == self.alarm_t.hour and \
                       t.minute == self.alarm_t.minute:
                        play_spotify()
                        self._alarm_on = True
            else:
                if self._alarm_on:
                    stop_spotify()
                    self._alarm_on = False
            sleep(1)

    def time(self):
        return '{}:{}'.format(
            str(self.alarm_t.hour).zfill(2),
            str(self.alarm_t.minute).zfill(2)
        )

    def status(self):
        return "ON" if self.active else "OFF"

playlists = {
    'morning': 'spotify:user:1146628823:playlist:14FRsyt8MBES8xcNgSOdhy'
}

def start_spotify():
    p = Popen(['spotify'])
    if p.poll() is None:
        p.wait()

def play_spotify():
    playlist = playlists['morning']
    call(
        [
            'spotify-remote', 'play',
            playlist
        ],
    )

def stop_spotify():
    call(['spotify-remote', 'pause'])


#!/usr/bin/env python
# -*- coding: utf-8 -

from PIL import Image
import imagehash
import urllib
from io import BytesIO
import numpy as np
import time
from pathlib import Path

import logging
logging.getLogger("requests").setLevel(logging.WARNING)
log = logging.getLogger()

class Matcher:
    """
    Tries to match cards by image. #greatidea #notwaistingtimeatall
    """
    # self.list_cr
    # self.list_mid

    def __init__(self, list_cr, list_mid):
        """
        Runs matching on received lists.
        """
        self.list_cr = list_cr
        self.list_mid = list_mid
        self.info_status = []

    def get_matches(self):
        start = time.time()
        results = self.match(self.list_cr, self.list_mid)
        match_took = time.time() - start

        log.debug("Matching took {}.".format(self.readable_time(match_took)))

        if len(self.info_status):
            out_info = "(" + ",".join(self.info_status) + ")"
        else:
            out_info = None

        return results, out_info

    def readable_time(self, seconds):
        m, s = divmod(seconds, 60)
        h, m = divmod(m, 60)
        return "%d:%02d:%02d" % (h, m, s)

    def matrix(self, list_cr, list_mid):
        """
        Creates a nxn matrix with distances of all images to each other.
        """
        imgs_cr = [self.get_img("cr", id_cr) for id_cr in list_cr]
        imgs_api = [self.get_img("api", mid) for mid in list_mid]

        if (None not in imgs_cr) and (None not in imgs_api):
            mat = np.zeros([len(imgs_cr), len(imgs_api)])
            for i in range(len(imgs_cr)):
                for j in range(len(imgs_api)):
                    mat[i][j] = self.dist(imgs_cr[i], imgs_api[j])
        else:
            self.info_status.append("wrong url(s)")
            mat = None

        return mat

    def manual_match(self, mat):
        # Manual matching of images that are too similar.
        matches = {
            'BSLS_125': '214676',
            'BSLS_124': '214669',
            'BSLF_34': '244319',
            'BSLF_36': '244322',
            'BSLF_61': '157877',
            # ZEN lands
            'ZEN_255': '201959', 'ZEN_257': '195192', 'ZEN_259': '195177', 'ZEN_261': '201961',  # Forests
            'ZEN_263': '195187', 'ZEN_265': '195165', 'ZEN_267': '195161', 'ZEN_269': '201965',  # Islands
            'ZEN_271': '195178', 'ZEN_273': '195171', 'ZEN_275': '195181', 'ZEN_277': '195184',  # Mountains
            'ZEN_281': '201973', 'ZEN_283': '195173', 'ZEN_285': '195196', 'ZEN_287': '201971',  # Plains
            'ZEN_291': '195172', 'ZEN_293': '195194', 'ZEN_295': '201976', 'ZEN_197': '201975',  # Swamps

        }
        for i in range(len(self.list_cr)):
            if self.list_cr[i] in matches:
                for j in range(len(self.list_mid)):
                    if int(self.list_mid[j]) == int(matches[self.list_cr[i]]):
                        log.debug("Setting mat[{}][{}] to -1.".format(i, j))
                        mat[i][j] = -1  # -1 is lower than the lowest value of hash distance (0)
                    else:
                        log.debug("list_mid[j]={}, matches[list_cr[i]]={}".format(
                            self.list_mid[j], matches[self.list_cr[i]]))
        return mat

    def match(self, list_cr, list_mid):
        """
        Generates dictionary of cr_ids matching mids.
        """
        mat = self.matrix(list_cr, list_mid)
        if mat is not None:
            mat = self.manual_match(mat)
            minima_locations = self.get_minima(mat)
            if minima_locations is not None:
                out = {}
                for i in range(len(minima_locations)):
                    if minima_locations[i] != -1:
                        out[list_cr[i]] = list_mid[minima_locations[i]]

                return out
            else:
                return None
        else:
            return None

    def get_min_info(self, m, a):
        if a == 1:
            m = m.T
        min_loc = np.argmin(m.T, axis=0)
        min_val = np.amin(m.T, axis=0)
        occ = np.zeros_like(min_loc)
        duplicates = [None for i in range(len(occ))]
        for i in range(len(min_loc)):
            dupl = np.where(m[i] == min_val[i])[0]
            occ[i] = len(dupl) - 1
            if len(dupl) > 1:
                duplicates[i] = dupl

        return min_loc, min_val, occ, duplicates

    def get_minima(self, m):
        acceptable_minimum = 16
        xmin_loc, xmin_val, xocc, xdupl = self.get_min_info(m, 0)
        ymin_loc, ymin_val, yocc, ydupl = self.get_min_info(m, 1)
        # print(m)

        xout = np.negative(np.ones_like(xmin_loc))
        yout = np.negative(np.ones_like(ymin_loc))

        max_dif = 0

        # scroll by x and see perfect matches
        x_inserted = 0
        for i in range(len(xocc)):
            if xmin_val[i] <= acceptable_minimum:
                if xocc[i] <= 0:  # if no duplicates
                    if ymin_loc[xmin_loc[i]] == i:
                        xout[i] = xmin_loc[i]
                        yout[xmin_loc[i]] = ymin_loc[xmin_loc[i]]
                        x_inserted += 1
                    else:
                        log.debug('x minimum found, but not minimum for y, {} -> {} (i={}, xocc={}).'.format(
                            self.list_cr[xmin_loc[i]], self.list_mid[i], i, xocc[i]))
            elif xmin_val[i] > max_dif:
                max_dif = xmin_val[i]

        log.debug('x - duplicates ({} inserted)'.format(x_inserted))
        for k in range(len(xdupl)):
            if xdupl[k] is not None:
                for candidate in xdupl[k]:
                    log.debug(
                        "Ambiguous candidate: {} -> {}".format(self.list_cr[k], self.list_mid[candidate]))

        # scroll by y and for unmatched see matches within duplicates from x direction
        y_inserted = 0
        for j in range(len(yocc)):
            if yout[j] == -1:
                if ymin_val[j] <= acceptable_minimum:
                    if yocc[j] == 0:  # if no duplicates
                        if xdupl[ymin_loc[j]] is None:
                            log.debug('y minimum found, but x has no minima at all, {} -> {} (j={}).'.format(
                                self.list_mid[j], self.list_cr[ymin_loc[j]], j))
                        elif j in xdupl[ymin_loc[j]]:
                            yout[j] = ymin_loc[j]
                            xout[ymin_loc[j]] = xmin_loc[ymin_loc[j]]
                            y_inserted += 1
                        else:
                            log.debug('y minimum found, but not among minima for x, {} -> {} (j={}).'.format(
                                self.list_mid[j], self.list_cr[ymin_loc[j]], j))
                elif ymin_val[j] > max_dif:
                    max_dif = ymin_val[j]

        log.debug('y - duplicates ({} inserted)'.format(y_inserted))
        for k in range(len(ydupl)):
            if ydupl[k] is not None:
                for candidate in ydupl[k]:
                    log.debug(
                        "Ambiguous candidate: {} -> {}".format(self.list_mid[k], self.list_cr[candidate]))

        if max_dif > acceptable_minimum:
            self.info_status.append("max dif {} >= {}".format(max_dif, acceptable_minimum))

        return xout

    def get_minima_2(self, mat):
        minima = []
        minima_locations = []

        dim = len(mat[0])
        occurences = [0] * dim
        unambiguous = True
        for i in range(dim):
            minimum_location = np.argmin(mat.T[i])
            minimum = mat.T[i][minimum_location]
            all_minima = np.where(mat.T[i] == minimum)
            if len(all_minima[0]) != 1:
                unambiguous = False
                occurences[i] = all_minima[0]

            minima.append(minimum)
            minima_locations.append(minimum_location)

        acceptable_minimum = 11

        if unambiguous:
            status = 1
        else:
            status = 0
            self.info_status.append("ambiguous")
            log.debug("Some matches are ambiguous.")
            for i in range(len(occurences)):
                try:
                    for candidate in occurences[i]:
                        # TODO Not sure which index is which - might be switched CR/API
                        log.debug(
                            "Ambiguous candidate: {} -> {}".format(self.list_cr[i], self.list_mid[candidate]))
                except:
                    pass

        max_difference = np.max(minima)
        if max_difference < acceptable_minimum:
            log.debug("Maximum difference in images was {}.".format(max_difference))
        else:
            status = 0
            self.info_status.append("max dif {} >= {}".format(max_difference, acceptable_minimum))
            log.debug("WARNING! Maximum difference {} >= {}. Trouble?".format(
                max_difference, acceptable_minimum))

        if status:
            return minima_locations
        else:
            return None

    def dist(self, image_1, image_2):
        """
        Calculates distance of two images.
        """
        hash_1 = imagehash.average_hash(image_1)

        hash_2 = imagehash.average_hash(image_2)

        return hash_1 - hash_2

    def get_img(self, type_id, card_id):
        if type_id == "cr":
            url = self.url_cr(card_id)
            name = str(card_id) + ".jpg"
        else:
            url = self.url_api(card_id)
            name = str(card_id) + ".png"

        directory = Path('app/static/img/cards/')
        directory.mkdir(exist_ok=True)
        path = directory / name

        if path.exists():
            img = Image.open(path)
            return img
        else:
            try:
                urllib.request.urlretrieve(url, str(path))
                img = Image.open(path)
                return img
            except urllib.error.URLError as e:
                log.debug("URLError, trouble...")
                return None

    def url_cr(self, id_cr):
        """
        Generates cr_url from id_cr.
        """
        return 'http://cernyrytir.cz/images/kusovkymagic/' + id_cr.replace('_', '/') + '.jpg'

    def url_api(self, mid):
        """
        Generates api_url from mid.
        """
        return 'https://image.deckbrew.com/mtg/multiverseid/' + str(mid) + '.jpg'


"""
Pure subroutines of the MODL algorithm.

Author : Quentin Ferré <quentin.q.ferre@gmail.com>
"""

import time

from collections import defaultdict

import numpy as np
import pandas as pd

from sklearn.decomposition import SparseCoder
from sklearn.decomposition import MiniBatchDictionaryLearning

from pygtftk.stats.intersect.modl import tree

from pygtftk.utils import message

def learn_dictionary_and_encode(data, n_atoms = 20, alpha = 0.5,
        n_iter = 200, random_seed = 42, n_jobs = 1,
        fit_algorithm = 'cd',
        transform_algorithm = 'lasso_cd'):
    r"""
    Will learn a dictionary for the data (row-wise) and encode it, with the specified parameters.
    Returns the dictionary, components as a dataframe, and encoded data.

    By default, allows 20 words with alpha of 0.5.

    More info at : https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.MiniBatchDictionaryLearning.html

    >>> from pygtftk.stats.intersect.modl.dict_learning import test_data_for_modl
    >>> from pygtftk.stats.intersect.modl.subroutines import learn_dictionary_and_encode
    >>> import numpy as np
    >>> np.random.seed(42)
    >>> flags_matrix = test_data_for_modl(nflags = 1000, number_of_sets = 6)
    >>> import time
    >>> start_time = time.time()
    >>> U_df, V_df, error = learn_dictionary_and_encode(flags_matrix, n_atoms = 20, alpha = 0.5)
    >>> stop_time = time.time()

    """

    # TODO: Many operations here, such as recreating an object of a pandas
    # dataframe, might not be necessary ?

    data = np.array(data) # Force cast as array

    # Cannot ask for more rows than there are unique lines
    n_atoms = min(n_atoms, len(np.unique(data, axis = 0)))

    dico = MiniBatchDictionaryLearning(n_components=n_atoms, n_iter=n_iter,
        alpha = alpha,
        fit_algorithm = fit_algorithm,
        transform_algorithm = transform_algorithm, transform_alpha = alpha,
        positive_dict = True, positive_code = True,
        random_state = random_seed,
        n_jobs = n_jobs
        )

    # NOTE fit_algorithm is used during the learning and transform_algorithm
    # transforms the data once the estimator has been fitted.
    # We are using coordinate descent (CD) as LARS has troubles with correlated
    # features. Also because we want to be able to enforce positivity in the
    # dictionary and the code for interpretability.

    dico.fit(data)  # Fit the data

    # Get components (the dictionary).
    # NOTE: Use a try-except for future proofing, as sklearn (v 0.24) seems to
    # deprecate 'components_' across the board and replaced it with 'dictionary'
    try: V = dico.components_
    except: V = dico.dictionary
    V_df = pd.DataFrame(V)

    # If the alpha is inadapted, the learned dictionary may have failed to converge
    # and contain NaNs. If that happens, return it now and bypass the next steps
    # NOTE LARS is less vulnerable to it, use it as a fallback before calling it quits.
    if np.isnan(V).any():
        message("Learned dictionary by Coordinate descent contained NaNs. Alpha may have been indadapted. Defaulting to LARS.", type = 'DEBUG')

        dico = MiniBatchDictionaryLearning(n_components=n_atoms, n_iter=n_iter,
            alpha = alpha, transform_alpha = alpha, random_state = random_seed, n_jobs = n_jobs,
            fit_algorithm = 'lars',  transform_algorithm = 'lasso_lars')

        dico.fit(data)
        try: V = dico.components_
        except: V = dico.dictionary
        V_df = pd.DataFrame(V)

        # Only abort if it still does not work
        if np.isnan(V).any():
            message("Fallback still contains NaNs. Aborting.", type = 'DEBUG')
            return None, V_df, None # Return "U", V, "error"

    # Re-encode the data with this dictionary
    encoded_data = dico.transform(data)
    ed_df = pd.DataFrame(encoded_data)

    # Compute associated normalized L2 loss for reference
    reconstructed_features = np.matmul(encoded_data,V)
    error = np.sum((reconstructed_features-data)**2) / np.sum(data**2)

    return ed_df, V_df, error # Return U, V, error

# ---------------------------------------------------------------------------- #
#                        Step 1 of the MODL algorithm                          #
# ---------------------------------------------------------------------------- #

def generate_candidate_words(X, n_words, nb_threads = 1,
    discretization_threshold=0, alphas=None):
    """
    Given a data matrix, will generate a library of candidate words by trying many
    different Dictionary Learnings (different alphas only for now)
    """

    EPSILON = 1E-10

    ## Preliminary steps
    X = np.array(X) # Force cast to array for safety
    nb_features = X.shape[1] # word size

    # NOTE : n_atoms cannot be higher than the number of unique combis or there might be an Exception thrown.
    # It seems to vary and be due to alpha. To be safe, prevent it.
    nb_unique_combis = len(np.unique(X, axis = 0))
    if n_words > nb_unique_combis : n_words = nb_unique_combis

    # A too low alpha can result in learning giberrish words, thay may still be used as there is no penalty for it.
    # To counter this, we begin with an alpha of 1/nb_features
    # Rk: if alphas have been manually specified, use them instead
    if alphas is None: alpha = 1/nb_features
    else: alpha = alphas[0]

    # Remember all candidate words found at each step, and use a dictionary so we
    # can remember their total usage for later filtering
    all_candidate_words = defaultdict(lambda: 0)

    stop = False

    misbehaving = 0

    iternb = 0
    while not stop:

        # Encode using DL
        #start_time = time.time()
        message("> Alpha is "+str(alpha), type = 'DEBUG')

        # NOTE For some reason, the algorithm will never learn the word (1,1,1,...,1,1) even if it the only row in the data.
        # To prevent that, add a useless padding for the last column.
        pad = np.zeros((X.shape[0],1))
        Xpadded = np.append(X, pad, axis=1)

        # Perform the learning
        U, V, error = learn_dictionary_and_encode(Xpadded, n_atoms = n_words, alpha = alpha,
                                               n_jobs = nb_threads)

        #stop_time = time.time() # Careful not to name it 'stop' and overwrite the stop flag :)
        #print('One DL step took ', str(stop_time-start_time), 'seconds.')

        iternb += 1
        # Update alpha so DL will be more sparse in the next step
        # and look for higher-order combinations
        # We add iternb/nb_features to not take too long and not unduly favor high alphas (and longer words)

        # Rk: if alphas have been manually specified, use them instead
        if alphas is None: alpha += iternb/nb_features
        else:
            try: alpha = alphas[iternb]
            except: stop = True # Stop if we reached the end of the list

        # TODO: Also stop regardless after 2*k iterations ?

        # We stop once alpha is too high and preventing any word from being used
        # in the encoding U (which contained NaNs -- handled before -- or has a sum of 0)
        # Rq : can also happen with too low alpha or convergence errors, so stop only if it happens more than 2 times
        if (U is None): misbehaving += 1
        if (misbehaving >=2): stop = True

        if U is not None:

            if np.sum(U.values) == 0:
                stop = True
                continue # Do not do the following steps of word extraction !! The stop signal has been sent, and if U is None there is nothing to extract

            # -- Extract interesting words -- #
            # Here extract interesting words from dico and add to all_found_words

            # Binarize them (ie transform 0.9 - 0.9 - 0.001 into 1-1-0)
            # The sum of V_df**2 for each word is always 1.0 so our cutoff to say if a feature is used
            # is V**2 > 1/n_features**2 - epsilon for rounding errors.
            # We cannot simply binarize, since 0.001 would be binarized to 1 yet it is likely not interesting.
            # WARNING : no longer true if I have intra set overlap (ie 1-1-2 for example) so when looking for the combis I might need to ignore intra set overlap

            # Each learned word is remembered along with its usage in the reconstruction
            # This way when proceeding to step 2 we keep only the most relevant words from step 1 to save time.
            # Gibberish overcomplete words that are found when n_atoms is too high and were not used will have usage of 0
            for i in U.columns.tolist():
                this_word = (V**2).iloc[i,:].values

                # Remove padding
                this_word = this_word[:-1]

                # Above 1/k**2 ?
                first_thres = this_word - (1/nb_features**2) > EPSILON

                # Above discretization_threshold * max ?
                second_thres = this_word - (discretization_threshold * max(this_word)) > EPSILON

                # Above both ?
                this_word_binarized = np.logical_and(first_thres,second_thres).astype(int)

                this_word_binarized = tuple(this_word_binarized.tolist())

                this_word_usage = np.sum(U.values[:,i])

                # Now remember it
                all_candidate_words[this_word_binarized] += this_word_usage

                # Debug print
                message("Word = "+str(this_word_binarized)+" ; Usage = "+str(this_word_usage),
                    type = 'DEBUG')

    # Return the dictionary giving all candidate words and their total usage
    return all_candidate_words

# ---------------------------------------------------------------------------- #
#                        Step 2 of the MODL algorithm                          #
# ---------------------------------------------------------------------------- #

def normalize_and_jitter_matrix_rows(X, normalize = True, jitter = True):
    r"""
    Apply normalization so that sum of suquare of each row is 1,
    Add a slight epsilon so two rows may not have exact same dot product with a given vector
    Sort in lexicographic order

    >>> import numpy as np
    >>> import numpy.testing as npt
    >>> from pygtftk.stats.intersect.modl.subroutines import normalize_and_jitter_matrix_rows
    >>> D = np.array([[1,1,0],[0,1,0],[0,1,1]])
    >>> Dc = normalize_and_jitter_matrix_rows(D)
    >>> Dc_theory = [[0, 1, 0], [7.0736E-5, 7.07107E-1, 7.07107E-1], [7.07107E-1, 7.07107E-1, 9.99859E-05]]
    >>> npt.assert_almost_equal(Dc, Dc_theory, decimal = 5)
    """

    X = np.array(X) # Enforce NumPy array
    X = X[np.lexsort(np.rot90(X))] # Sort the dictionary in lexicographic order BEFORE applying all of this

    ## Applying corrections to the words : normalize and jitter
    new_X = []
    i = 0
    for row in X:

        # To prevent atoms in D from having the exact same dot product with any word
        # that LARS will try to rebuild, add a very small jitter to the words
        # -> sqrt(i) times 1E-4 to each element of the i-th row of D
        neo = np.array([x + np.sqrt(i)*1E-4 for x in row])
        i += 1

        # Normalize the words by the sum of their square. Helps counter a tendency to select longer words, which can increase sensitivity to noise.
        if normalize:
            squared_sum = np.sum(neo**2)
            if squared_sum > 0: # Avoid division by zero
                neo = np.sqrt(neo**2/squared_sum)

        new_X += [neo]

    # Now convert to array
    X = np.array(new_X)

    return X

def build_best_dict_from_library(data, library, queried_words_nb,
                error_function = None,
                nb_threads = 1, normalize_words = False,
                transform_alpha = None):
    r"""
    Given a data matrix and a library, will select the best n = queried_words_nb words
    with a greedy algorithm from the library to rebuild the data.

    `data` is a matrix with one transaction per row and one element per column, as usual

    The greeedy algorithm works because the problem in this particular instance
    is submodular. In broad strokes, it is assumed that the gain in term of reconstruction
    with the Sparse Coder of adding the word X to a set S is no higher than adding X to a
    set T that contains S. Also, more words cannot make the sparse coder do a worse job so
    the function is monotonous.

    Instead of the reconstruction error, you may pass a different callable of the form
    error_function(data, rebuilt_data, encoded, dictionary) that returns an error value so there
    can be a supervision, but the submodularity might no longer hold.

    >>> import numpy as np
    >>> from pygtftk.stats.intersect.modl import tree
    >>> from pygtftk.stats.intersect.modl import subroutines as modl_subroutines
    >>> X = np.array([[1,1,0,0,0,0],[1,1,1,1,0,0],[0,0,0,0,1,1]]*10)
    >>> candidates = [(1,1,0,0,0,0),(1,1,1,1,0,0),(0,0,0,0,1,1), (1,1,0,0,1,1), (1,0,1,0,1,0),(1,1,0,1,1,0),(1,0,1,1,0,0),(0,1,0,0,1,1)]
    >>> L = tree.Library()
    >>> L.build_nodes_for_words(candidates)
    >>> L.assign_nodes()
    >>> selection = modl_subroutines.build_best_dict_from_library(X, L, queried_words_nb = 3)
    >>> assert set(selection) == set([(1,1,0,0,0,0),(1,1,1,1,0,0),(0,0,0,0,1,1)])

    """

    ## Create objects

    # Initial dictionary
    nb_features = data.shape[1]

    dictionary = [tuple([0] * nb_features)] # Must initialize with a word, might as well be full zeros.

    # Use nonzero alpha for the coder as well, only if not manually specified.
    if transform_alpha is None:

        # To encourage use of fewer words, we by default use a rather high alpha (but capped at 0.5)
        transform_alpha = min(1/np.sqrt(nb_features), 0.5)

    ## Coder object, to be updated iteratively
    coder = SparseCoder(dictionary=np.array(dictionary).astype('float64'), # Rq : must make it float64 for some reason
        transform_algorithm='lasso_lars', #transform_algorithm='lasso_cd',
        transform_alpha = transform_alpha,
        positive_code = True, # Very important to ensure we also force positivity here !
        n_jobs = nb_threads)

    # We use lasso_lars, as lasso_cd often had mysterious convergence problems.
    # Lasso_lars can stil enforce positivity, and with precomputed words now the poor handling of degenerate
    # vectors (lars dropping them) is a strength

    # --- Filtering the candidates
    # Iteratively add candidates that improve the reconstruction
    stop = False
    while stop is False:

        ## Generate all_candidate_words for this iteration
        # For now, simply all words of the library that are not already used
        all_candidate_words = tree.get_all_candidates_except(library, dictionary)

        # Test all words for this iteration
        candidates_with_errors = dict()

        message("----------------------", type = 'DEBUG')
        message("Current dictionary : "+str(dictionary), type = 'DEBUG')
        message("> Candidate\tError", type = 'DEBUG')

        for candidate in all_candidate_words:

            # Create a new dictionary for this test
            dict_being_tested = dictionary + [candidate]

            Dt = np.array(dict_being_tested)

            # Normalization helps counter a tendency to selcet longer words, and jitter fixes a LARS bug with atoms that have the same dot product
            # Also lexicographic sort.
            Dt_corrected = normalize_and_jitter_matrix_rows(Dt, normalize_words)
            Dt = Dt_corrected

            # Update dictionary to the one being currently tested
            coder.dictionary = Dt.astype('float64')

            # Just to be safe
            assert np.allclose(coder.dictionary, Dt.astype('float64'))

            try:

                encoded = coder.transform(data)

                rebuilt_data = np.matmul(encoded, Dt)

                # We use Manhattan error so compromises are discouraged : with L2,
                # when rebuilding [(1,0,0)*many,(1,0,1),(0,0,1)*many], (1,0,1) would be picked first
                # Add an alpha that is nonzero so that using longer words is still
                # an improvement even when other words cover it (and as a tiebreaker)
                # but keep it low (1/k) so you don't re-encourage compromise
                def manhattan_dist_with_sparsity(X_true, X_rebuilt, encoded, dictionary):

                    dictionary = None # This particular function ignores the dictionary

                    error_mat = np.abs(X_rebuilt-X_true)
                    error = np.sum(error_mat)

                    alpha = transform_alpha # Fetch the alpha used when calling the main function

                    regul = np.sum(encoded) * alpha

                    final_error = error + regul
                    return final_error

                # Check for error function, default is manhattan dist
                if error_function is None:
                    error_function = manhattan_dist_with_sparsity

                # To compute the error, pass respectively : true data, rebuilt data, encoded data, and current dictionary
                # TODO: currently, the dictionary passed is BEFORE normalization and all that jazz
                error = error_function(data, rebuilt_data, encoded, dict_being_tested)

            # On rare occasion, convergence errors can result in a ValueError
            except ValueError:
                error = np.inf

            # Remember error
            candidates_with_errors[tuple(candidate)] = error

            message(str(candidate)+'\t'+str(error), type = 'DEBUG')

        # Add best candidate to final dict and remove from candidates.
        try:
            # In case of a tie, the first word is selected.
            best_candidate = min(candidates_with_errors, key=candidates_with_errors.get)
            dictionary += [best_candidate]
        # It should never happen, but if there are no more candidates add an empty word.
        except:
            dictionary += [tuple([0] * nb_features)]

        """
        # NOTE for improvement
        # To save time, we may only pass as candidates the "children" (see tree module)
        # of nodes already present under the assumption that if X is better than Y
        # the best parent of X should be better than the best parent of Y ?
        # Then we would need to wait for an optimum, with this draft code

        # If we have more words than the user wants, remove the worst one
        if (len(dictionary) > queried_words_nb):
            # Remove worst word with respect to error (try all, opposite of above)

        # When the word added and the word removed are the same (meaning
        # the word it wants to add does not improve on the dict) stop and
        # return
        if best_candidate == last_word_removed :
            best_words = final_dict
            return best_words
        """

        ## Stop condition : as soon as we have enough words
        # Remark : since the dictionary always contain the (0,0,0,...) word as a supplement, we
        # must stop when it contains the required number of words... plus one !
        if (len(dictionary) >= queried_words_nb + 1):
            stop = True

    message("Final dictionary :"+str(dictionary), type = 'DEBUG')

    # Conclusion of the function : remove the (0,0,0,...) word and make words unique, just in case
    best_words = list(set(dictionary))
    best_words.remove(tuple([0] * nb_features))

    return best_words


################################################################################
# Factorization algorithms
################################################################################
from math import floor, ceil
from gmpy2 import mpz,mpq,mpfr,mpc
from gmpy2 import is_square, isqrt, sqrt, log2, gcd, is_prime, next_prime, primorial
from .factordb_connector import *
from time import time
import sympy as sp
import os

MSIEVE_BIN = os.environ.get("MSIEVE_BIN", "/tmp/ggnfs-bin/")
YAFU_BIN   = os.environ.get("YAFU_BIN",   "NONE")
CADO_BIN   = os.environ.get("CADO_BIN",   "NONE")
YAFU_THREADS   = os.environ.get("YAFU_THREADS",   "4")
YAFU_LATHREADS = os.environ.get("YAFU_LATHREADS", "4")

#Factoring libraries
USE_PARI = True if ( (YAFU_BIN == "NONE") or (YAFU_BIN == "NONE")) else False

if USE_PARI:
  import cypari2 as cp
  cyp = cp.Pari()
  cyp.default("parisizemax", 1<<29 )
  cyp.default("threadsizemax", 1<<27 )
  pari_cfactor = cyp.factorint

# This gives direct access to the integer factoring engine called by most arithmetical functions. flag is optional; its binary digits mean
# 1: avoid MPQS,
# 2: skip first stage ECM (we may still fall back to it later),
# 4: avoid Rho and SQUFOF,
# 8: don’t run final ECM (as a result, a huge composite may be declared to be prime).
#    Note that a (strong) probabilistic primality test is used, thus composites might not be detected, although no example is known.

#Flags
SKIP_MPQS          = 1
SKIP_ECM_STAGE_ONE = 2
SKIP_RO_SQUFOF     = 4
SKIP_ECM_STAGE_TWO = 8

def pollard(n, limit=1000):
    x = 2
    y = 2
    d = 1
    l = 0
    def g(x):
      return x*x + 1
    while d == 1 and l <= limit:
        x = g(x)
        y = g(g(y))
        d = gcd(abs(x - y), n)
        #print(n,l)
        l += 1
    if n > d > 1:
      return n//d, d
    else:
      return []

def cadonfs_factor_driver(n):
  global CADO_BIN
  print("[*] Factoring %d with yafu..." % n)
  tmp = []
  proc = subprocess.Popen([ CADO_BIN, str(n)], stdout=subprocess.PIPE)
  for line in proc.stdout:
    line = line.rstrip().decode("utf8")
    if re.search("\d+",line):
      tmp += [int(x) for x in line.split(" ")]
  return tmp

def msieve_factor_driver(n):
  global MSIEVE_BIN
  print("[*] Factoring %d with msieve..." % n)
  import subprocess, re, os
  tmp = []
  proc = subprocess.Popen([MSIEVE_BIN,"-s","/tmp/%d.dat" % n,"-t","8","-v",str(n)],stdout=subprocess.PIPE)
  for line in proc.stdout:
    line = line.rstrip().decode("utf8")
    if re.search("factor: ",line):
      tmp += [int(line.split()[2])]
  os.system("rm %d.dat" % n)
  return tmp

def yafu_factor_driver(n, timeout = 60*30):
  global YAFU_BIN
  print("[*] Factoring %d with yafu..." % n)
  import subprocess, re, os
  tmp = []
  try:
     parse = subprocess.run([YAFU_BIN,"-one","-plan", "custom","-pretest_ratio","0.32", "-threads",YAFU_THREADS,"-lathreads",YAFU_LATHREADS, "-ggnfs_dir", MSIEVE_BIN, "-xover", "160", "-snfs_xover", "150",  str(n)], timeout=timeout, stdout=subprocess.PIPE)
  except subprocess.TimeoutExpired:
     print("Timeout: ", timeout, " Seconds. Moving on to next candidate")
     return [ "C" + str( n.bit_length() ) + " = " + str(n) ]

  parse = [ line for line in parse.stdout.decode('utf-8').split("\n") if "=" in line ]
  tmp = []
  flag=False

  for line in parse:
      if "Total factoring" in line:
          flag = True
          continue
      if "ans" in line:
          continue

      if flag:
          tmp.append(line)

  return tmp

def cfactor(n):
  print("[*] pari factoring: %d..." % n)
  f0 = pari_cfactor(n)
  factors  = [int(a) for a in f0[0]]
  return factors

def external_factorization(n,timeout=60*30):
  factors = []

  if USE_PARI:
      factors = cfactor(n)
  else:
      factors = yafu_factor_driver(n,timeout)

  #YAFU already uses msieve
  #if len(factors) == 0:
  #  factors = msieve_factor_driver(n)
  return factors

def factorization_handler(n,timeout=30*60):
  print("[*] Factoring:",n)

  #Turn this off for now. There seems to be an issue connecting to the database site.
  #F = getfdb(n)
  F = []

  if len(F) == 0:
    factors = external_factorization(n,timeout)
    print("[+] factors: %s" % str(factors))
  else:
    print("fdb got: %d factors: %s" % (len(F),str(F)))
    factors = []
    for f in F:
      if is_prime(f):
        factors += [f]
      else:
        factors += external_factorization(f)
  factors = sorted(factors)
  #There seems to be an issue with the website for FactorDB. Turn off for now.
  #send2fdb(n,factors)
  return factors

def ranged_primorial(L, p = 2):
  #print("pre sieving...",end="")
  tmp = 2
  c = 0
  while c < L:
    p = next_prime(p)
    tmp *= p
    c += 1
  #print("ok")
  return tmp, p

def prime_levels_load0(s,e):
  siever = [ 0, 1, 6, 5005 ]
  prev, last_p = ranged_primorial(8)
  for level in range(s,e):
    siever.append(ranged_primorial(1 << level, p = last_p))
    print("Level %d ok" % level)
  return siever

def prime_levels_load1(s,e):
  siever = [ 0, 1, 6, 5005 ]
  prev = sp.primorial( 1<<3, False)
  for level in range(s,e):
    current       = sp.primorial( 1 << level, False)
    level_n_sieve = current//prev
    prev          = current
    siever.append(  level_n_sieve )
    print("Level %d ok" % level)
  return siever

def prime_levels_load2(s,e):
  siever = [ 0, 1, 6, 5005 ]
  prev = primorial( 1<<3)
  for level in range(s,e):
    start = time()
    current       = primorial( 1 << level)
    level_n_sieve = current//prev
    prev          = current
    siever.append(  level_n_sieve )
    print("Level: %d primes: %d  ok Time: %f" % (level,(1 << level), time()-start ))
  return siever

def prime_levels_load_timing():
  t0 = time.time()
  prime_levels_load0(4,16)
  t1 = time.time()
  print(t1-t0)
  prime_levels_load1(4,16)
  t2 = time.time()
  print(t2-t1)
  prime_levels_load2(4,16)
  t3 = time.time()
  print(t3-t2)

prime_levels_load = prime_levels_load2

def SDC(W, candidates):
  print("[*] square difference check...")
  if is_square(W):
    a = isqrt(W)
    for n in candidates:
      n2 = abs(n - W)
      b = isqrt(n2)
      if (b * b) == n2:
        if n > gcd(a + b, n) > 1:
          factors = []
          print("[+] Factored using square difference...")
          if is_prime(a + b):
            factors += [a + b]
          if is_prime(a - b):
            factors += [a - b]
          if len(factors) == 2:
            return factors, n
  return None


#!/usr/bin/env python3

from math import gcd as gcd
from os import system, name
from random import randrange
from colorama import Fore, Style

'''gcd kan bepalen of twee integers co-primes zijn. Hij kijkt naar de modulo
als deze op 1 uitkomt dan is het getal door niets anders deelbaar en zijn
de twee integers dus co-primes'''

#Prime keys are given:
p = 17
q = 23
string_value = ''

#calculate neccessary variables for RSA:
n = p * q
z = (p-1)*(q-1)

def calcPublicKey(n):

    #randrange kan een random nummer uit een range halen. Bijvoorbeeld
    #randrange(10)
    #haalt een waarde tussen 0 en 9.
    e = randrange(n)

    #De while zoekt net zolang naar een integer waarde e die een co-prime is
    #van z. Als dat zo is dan stopt de loop.
    while gcd(e,z) != 1:
        e = randrange(n)

    print('public key is {},{}'.format(n,e))
    return(e)

def calcPrivateKey(e,z):
    ''' Berekenen van de private key.
    d moet een waarde zijn waarbij geldt dat (e*d) - 1 deelbaar moet zijn door
    z en waarbij er geen restwaarde is. Oftewel de waarde e*d -1 mod z is 0
    Om d te bepalen moet e*d in ieder geval al groter zijn dan z. De minimale
    waarde van d is z/e afgerond op een heel getal. '''

    d  = randrange(round(z/e),500)
    while (((e*d)-1)%z != 0):
        d  = randrange(round(z/e),500)

    print('private key is {},{}'.format(n,d))
    return(d)

if __name__ == "__main__":
    #clear the screen. Different commands depending on OS.
    if name == 'posix':
        _ = system('clear')
    else:
        _ = system('cls')

    #calculate the public and private keys
    e = calcPublicKey(n)
    d = calcPrivateKey(e,z)
    text = input(Fore.GREEN + "Voer een woord in om te encrypten: ")
    ascii_values = []
    for character in text:
        ascii_values.append(ord(character))

    #Encryption
    cipher_values=[]
    for ascii in ascii_values:
        cipher_values.append(pow(ascii, e, n))
    print(f'{Style.RESET_ALL}encrypt met public key {n},{e} geeft: {Fore.RED}{cipher_values}{Style.RESET_ALL}')
    #decryption
    decrypted_values = []
    for cipher in cipher_values:
        decrypted_values.append(pow(cipher, d, n))

    #write to character
    for character in decrypted_values:
        string_value = '{}{}'.format(string_value, chr(character))

    print(f'decrypt met private key {n},{d} geeft: {Fore.GREEN}{string_value}{Style.RESET_ALL}')


import numpy as np
import pandas as pd
import torch
import torch.nn as nn
# import matplotlib.pyplot as plt

# Plotting

# from fast.ai notebook
def gallery(x, nc=3):
    n,h,w,c = x.shape
    nr = n//nc
    assert n == nr*nc
    return (x.reshape(nr, nc, h, w, c)
              .swapaxes(1,2)
              .reshape(h*nr, w*nc, c))

# def plot_debug_info(_debug_info_dict):
#     plt.figure(figsize=(15, 15))
#     fig, axs = plt.subplots(2, 2)
#     pd.Series([item[0] for item in _debug_info_dict['lossG']]).plot(ax=axs[0, 0], title='lossG')
#     pd.Series([item[0] for item in _debug_info_dict['fake_res']]).plot(ax=axs[0, 1], title='D(G(z))')
#     pd.Series([item[0] for item in _debug_info_dict['lossD']]).plot(ax=axs[1, 0], title='lossD' )
#     pd.Series([item[0] for item in _debug_info_dict['real_res']]).plot(ax=axs[1, 1], title='D(x)')

# custom weights initialization called on netG and netD
def weights_init(m):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        nn.init.normal_(m.weight.data, 0.0, 0.02)
    elif classname.find('BatchNorm') != -1:
        nn.init.normal_(m.weight.data, 1.0, 0.02)
        nn.init.constant_(m.bias.data, 0)

def set_trainable(model, flag):
    model.trainable = flag
    for p in model.parameters():
        p.requires_grad = flag


import json, os
import numpy as np
import torch
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
from torch.utils.data import Dataset
from glob import glob
import torchvision.transforms as T

def read_mask(label_path):
    label_data = json.load(open(label_path))
    segs = label_data["shapes"][0]["points"]
    img_shape = [label_data["imageHeight"], label_data["imageWidth"]]
    mask = np.zeros(img_shape, dtype=np.uint8)
    mask_image = Image.fromarray(mask)
    xy = list(map(tuple, segs))
    ImageDraw.Draw(mask_image).polygon(xy=xy, outline=1, fill=255)
    mask_image = np.array(mask_image)
    mask = mask_image.astype(np.int32)
    return mask

class ForceData(Dataset):
    def __init__(self, data_path) -> None:
        super().__init__()
        self.force = np.loadtxt(open(data_path+"/data.csv","r"),delimiter=",")
        self.rgb = glob(os.path.join(data_path, "rgb_cut", "*.jpg"))
        self.inf = glob(os.path.join(data_path, "inf_cut", "*"))
        self.lable = glob(os.path.join(data_path, "rgb_cut", "*.json"))
        self.rgb.sort()
        self.inf.sort()
        self.lable.sort()

        self.scale = [2,2,6]

        self.transform1 = T.Compose([
            T.Resize(256),
            T.ToTensor(),
            T.Normalize(
            mean=[0.485, 0.456, 0.406],
            std=[0.229, 0.224, 0.225]
            )
        ])
        self.transform2 = T.Compose([
            T.ToPILImage(),
            T.Resize(256),
            T.ToTensor()
        ])

    def __getitem__(self, index):
        rgb = Image.open(self.rgb[index])
        inf = Image.open(self.inf[index])

        x_force = torch.tensor((self.force[index][0] + self.scale[0] / 2) / self.scale[0])
        y_force = torch.tensor((self.force[index][1] + self.scale[1] / 2) / self.scale[1])
        z_force = torch.tensor((self.force[index][2] + self.scale[2] / 2) / self.scale[2])

        rgb = self.transform1(rgb)
        inf = self.transform1(inf)

        return rgb.float(), inf.float(), (x_force.float(), y_force.float(), z_force.float())

    def __len__(self):
        return len(self.rgb)


import os, json
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
from braintree import Subscription
from braintree.error_result import ErrorResult
from app_auth import models as AuthModels
from app_auth import mixins as AuthMixins
from payments import models as PaymentModels
import datetime
from django.contrib import messages
from django.utils import timezone
from django.http import HttpResponseRedirect, HttpResponse

from django.contrib.auth.decorators import login_required
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST

from django.template.loader import render_to_string
from django.core.mail import EmailMessage
from django.conf import settings

from paypalrestsdk.notifications import WebhookEvent

from payments.utils.paypal import mode as mode

class PaymentView(View):
    template_name = "payments/index.html"
    context_data = dict()

# Create your views here.
class PaymentIndex(PaymentView):
    def get(self, request, *args, **kwargs):
        return render(request, self.template_name)

    def post(self, request, *args, **kwargs):
        pass

class CompletePayment(PaymentView):
    def get(self, request, *args, **kwargs):
        return redirect(reverse("manager:pricings"))

    def post(self, request, *args, **kwargs):
        pricing_paypal_id = request.POST.get("pricing_paypal_id")
        selected_plan = PaymentModels.PaypalPlan.objects.filter(paypal_id=pricing_paypal_id).first()
        current_pricing = PaymentModels.PaypalSubscription.objects.filter(user=request.user.id).first()

        subscription = PaymentModels.PaypalSubscription.objects.filter(user=request.user.id).first()
        try:
            if subscription.plan.name.lower() != "Basic Monthly Package".lower():
                ret = mode.myapi.post(f"v1/billing/subscriptions/{subscription.order_key}/suspend")
                if ret.get("error"):
                    messages.add_message(request, messages.ERROR, 'Opps! We are not able to cancel current subcription automatically.')
                    return redirect(reverse('manager:account'))
                else:
                    ret = mode.myapi.post(f"v1/billing/subscriptions/{subscription.order_key}/cancel")
                    messages.add_message(request, messages.ERROR, 'Opps! We are not able to cancel current subcription automatically.')
                    return redirect(reverse('manager:account'))

            data = {
                "plan_id": pricing_paypal_id
            }

            ret = mode.myapi.post("v1/billing/subscriptions", data)
            if ret.get("error"):
                messages.add_message(request, messages.ERROR, 'Unable to update your subscription. Contact support.')
                return redirect(reverse('manager:pricings'))

            if ret.get('status') == 'APPROVAL_PENDING':
                user = request.user

                subscription.previous_pricing = subscription.plan
                subscription.upgrading_to = selected_plan
                subscription.total_amount_paid=float(selected_plan.cost)
                subscription.order_key=ret['id']
                subscription.active=False

                subscription.expiry_date=timezone.now() + datetime.timedelta(days=30)

                subscription.save()

                redirect_url = mode.get_url_from(ret['links'], 'approve')

                return HttpResponseRedirect(redirect_url)
        except Exception as e:
            subscription.plan=current_pricing
            subscription.save()
            messages.add_message(request, messages.ERROR, 'Unable to update your subscription. Contact support.')
            return redirect(reverse('manager:pricings'))

def send_mail(email, message):
    email_body = render_to_string(
        "email_message.html",
        {
            "review": "{}".format(message),
        },
    )
    email = EmailMessage(
        "Ease Sell Extension",
        email_body,
        settings.DEFAULT_FROM_EMAIL,
        [
            email,
        ],
    )

    email.send(fail_silently=False)

@require_POST
@csrf_exempt
def paypal_webhooks(request):
    transmission_id = request.headers['Paypal-Transmission-Id']
    timestamp = request.headers['Paypal-Transmission-Time']
    webhook_id = os.environ.get("PAYPAL_WEBHOOK_ID")
    event_body = request.body.decode('utf-8')
    cert_url = request.headers['Paypal-Cert-Url']
    auth_algo = request.headers['Paypal-Auth-Algo']
    actual_signature = request.headers['Paypal-Transmission-Sig']

    response = WebhookEvent.verify(
        transmission_id,
        timestamp,
        webhook_id,
        event_body,
        cert_url,
        actual_signature,
        auth_algo
    )
    if response:
        obj = json.loads(request.body)
        event_type = obj.get('event_type')
        resource = obj.get('resource')

    try:

        if resource.get("status", None) == 'APPROVAL_PENDING':
            billing_agreement_id = resource['id']
            subscription = PaymentModels.PaypalSubscription.objects.filter(order_key=billing_agreement_id).first()
            email = AuthModels.User.objects.filter(id=subscription.user.id).first().email
            send_mail(email, "Your payment as been initialized. Please wait for confirmation email.")

        elif resource.get("status", None) == 'ACTIVE':

            billing_agreement_id = resource['id']
            subscription = PaymentModels.PaypalSubscription.objects.filter(order_key=billing_agreement_id).first()
            email = AuthModels.User.objects.filter(id=subscription.user.id).first().email
            send_mail(email, "Your payment was successfull.")

            subscription.plan = subscription.upgrading_to
            subscription.payment_completed = True
            subscription.active=True
            subscription.save()

        elif resource.get("status", None) == "SUSPENDED":
            billing_agreement_id = resource['id']
            subscription = PaymentModels.PaypalSubscription.objects.filter(order_key=billing_agreement_id).first()
            ret = mode.myapi.post(f"v1/billing/subscriptions/{subscription.order_key}/cancel")
            send_mail(email, "Ease sell subscription deactivated successfully.")

        elif event_type == "BILLING.SUBSCRIPTION.CANCELLED":
            billing_agreement_id = resource['id']
            subscription = PaymentModels.PaypalSubscription.objects.filter(order_key=billing_agreement_id).first()
            email = AuthModels.User.objects.filter(id=subscription.user.id).first().email
            send_mail(email, "Ease sell subscription deactivated successfully.")

        return HttpResponse(status=200)
    except:
        pass

@csrf_exempt
def PaypalSubscriptionDeactivateView(request):
    try:
        if request.method == 'POST':
            package = PaymentModels.PaypalSubscription.objects.filter(user=request.user).first()
            subscription = PaymentModels.PaypalSubscription.objects.filter(user=request.user.id).first()

            ret = mode.myapi.post(f"v1/billing/subscriptions/{subscription.order_key}/suspend")

            if ret.get("error"):
                messages.add_message(request, messages.ERROR, 'Opps! Unable to deactivate, Try Again.')
                return redirect(reverse('manager:account'))

            subscription.plan = PaymentModels.PaypalPlan.objects.filter(name="Basic Monthly Package").first()
            subscription.expiry_date=timezone.now()
            subscription.total_amount_paid=0
            subscription.order_key="Not Set"
            subscription.active=False
            subscription.save()
            return redirect(reverse('manager:account'))
        else:
            messages.add_message(request, messages.ERROR, 'Invalid Request.')
            return redirect(reverse('manager:home'))
    except Exception as E:
        messages.add_message(request, messages.ERROR, 'Please Try Again')
        return redirect(reverse('manager:home'))

class PaypalSubcribeView(PaymentView, AuthMixins.IsAuthenticatedAccessMixin):
    def post(self, request, *args, **kwargs):
        user = AuthModels.User.objects.filter(pk=request.user.pk)
        # create braintree customer

        # if user has not set first_name, last_name redirect to prompt page
        # stored the package he is paying for

        if not user.customer_payemnt_id:
            result = gateway.customer.create(
                {
                    "first_name": user.first_name,
                    "last_name": user.last_name,
                    "company": "EASE-SELL",
                    "email": user.email,
                    "phone": user.get_moblie_number(),
                }
            )
            if result.is_success:
                user.customer_payemnt_id = result.customer.id
                user.save

        customer = gateway.customer.find(user.customer_payemnt_id)

    def get(self, request, *args, **kwargs):
        try:
            braintree_client_token = braintree.ClientToken.generate(
                {"customer_id": self.request.user.id}
            )
        except:
            braintree_client_token = braintree.ClientToken.generate({})

        self.context_data["braintree_client_token"] = braintree_client_token

        return render(request, self.template_name, context=self.context_data)


# Stdlib imports
import pytest

# Core Django imports
from django.core.management import call_command

# Third-party app imports
from faker import Faker

# Imports from my app
from src.domains.models import Domain
from src.domains.tests.factories import DomainFactory
from src.margins.models import Margin
from src.margins.tests.factories import MarginFactory
from src.model_grids.models import ModelGrid
from src.model_grids.tests.factories import ModelGridFactory
from src.organisations.models import Organisation
from src.organisations.tests.factories import OrganisationFactory
from src.plots.models import StationsPlot
from src.plots.tests.factories import StationsPlotFactory
from src.stations.models import Station
from src.stations.tests.factories import StationFactory
from src.surface_meteograms.models import SurfaceMeteogram
from src.surface_meteograms.tests.factories import SurfaceMeteogramFactory
from src.users.models import User
from src.users.tests.factories import UserFactory, get_group, get_permission
from src.utils.tests.factories import DjangoGeoPointProvider
from src.vertical_meteograms.models import VerticalMeteogram
from src.vertical_meteograms.tests.factories import VerticalMeteogramFactory

@pytest.fixture(scope="session")
def django_db_setup(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        call_command("create_groups")

@pytest.fixture(autouse=True)
def media_storage(settings, tmpdir):
    settings.MEDIA_ROOT = tmpdir.strpath

@pytest.fixture()
def user(request) -> User:
    user = UserFactory()
    if hasattr(request, "param"):
        if "groups" in request.param:
            list_groups = request.param["groups"]
            if not isinstance(list_groups, list):
                list_groups = [list_groups]
            for group in list_groups:
                user.groups.add(get_group(group))
        if "permissions" in request.param:
            list_perms = request.param["permissions"]
            if not isinstance(list_perms, list):
                list_perms = [list_perms]
            for perm in list_perms:
                user.permissions.add(get_permission(perm))
    return user

@pytest.fixture()
def user2(request) -> User:
    user = UserFactory()
    if hasattr(request, "param"):
        if "groups" in request.param:
            list_groups = request.param["groups"]
            if not isinstance(list_groups, list):
                list_groups = [list_groups]
            for group in list_groups:
                user.groups.add(get_group(group))
        if "permissions" in request.param:
            list_perms = request.param["permissions"]
            if not isinstance(list_perms, list):
                list_perms = [list_perms]
            for perm in list_perms:
                user.permissions.add(get_permission(perm))
    return user

@pytest.fixture
def staff() -> User:
    staff = UserFactory(staff=True)
    staff.groups.add(get_group("Staff"))
    return staff

@pytest.fixture
def organisation() -> Organisation:
    return OrganisationFactory()

@pytest.fixture
def modelGrid() -> ModelGrid:
    return ModelGridFactory()

@pytest.fixture
def geopoint():
    fake = Faker()
    fake.add_provider(DjangoGeoPointProvider)
    point = fake.geo_point()
    return point

@pytest.fixture
def stationsplot() -> StationsPlot:
    return StationsPlotFactory()

@pytest.fixture
def station() -> Station:
    return StationFactory()

@pytest.fixture
def domain() -> Domain:
    return DomainFactory()

@pytest.fixture
def margin() -> Margin:
    return MarginFactory()

@pytest.fixture
def smeteogram() -> SurfaceMeteogram:
    return SurfaceMeteogramFactory()

@pytest.fixture
def vmeteogram() -> VerticalMeteogram:
    return VerticalMeteogramFactory()