def sumar(num1, num2):
	sum = num1+num2
	return sum

def multiplicar(num1, num2):
	return num1*num2

def dividir(num1, num2):
	try:
		div = num1/num2
		return div
	except Exception as e:
		print("Error trying to operate: ", e)
		return 0

def power(num1, num2):
	return num1**num2

def salida(opcion):
	if opcion == 1:
		suma = sumar(num1, num2)
		ans["suma"] = suma
		print(suma)
	elif opcion == 2:
		mult = multiplicar(num1,num2)
		ans["multiplicacion"] = mult
		print(mult)
	elif opcion == 3:
		div = dividir(num1, num2)
		ans["division"] = div
		print(div)
	elif opcion == 4:
		exp = power(num1,num2)
		ans["exponente"] = exp
		print(exp)
	elif opcion == 5:
		pass
	elif opcion == 0:
		print("Bye!")
		print(ans)
	else:
		print("Not able to process the instruction")
ans = {}
opcion = 2

while(opcion != 0):

	num1 = int(input("Introduzca numero 1: "))
	num2 = int(input("Introduzca numero 2: "))

	opcion = int(input("Escriba opcion: \n [0] Salir \n \n [1] Suma \n [2] Multiplicación \n [3] División \n [4] Exponencial \n"))
	salida(opcion)
	opcion = int(input("Para continuar pulse 5, para salir, pulse 0"))
	salida(opcion)


from estimate_explosion_time.shared import get_custom_logger, main_logger_name, pickle_dir
import logging

logger = get_custom_logger(main_logger_name)
logger.setLevel(logging.INFO)
logger.debug('logging level is DEBUG')

from estimate_explosion_time.analyses.rappid_simulations import rappidDH
from estimate_explosion_time.core.fit_data.fitlauncher.fitlauncher import Fitter
from estimate_explosion_time.analyses.rappid_simulations.convert_to_pickle_files import \
    rappid_pkl_name, write_model_to_pickle, rappid_original_data
import os

# only include lightcurves with a peak magnitude brighter than this
peak_mag = 19

# take the original simulated data and convert it into pickles in the right format
# the path to the original data is to be specified in convert_to_pickle_files.py
for model_number in [3, 13]:
    if not os.path.isfile(rappid_pkl_name(model_number, peak_mag)):
        write_model_to_pickle(model_number, peak_mag)

# sepcify where to look for the SED files that were used in the simulation.
# That's necesarry for getting the explosion time from the template
sed_directory = rappid_original_data + '/SEDs'

# get the lightcurves either generated using MOSFiT type 'mosfit'
# or using specral templates type 'templates'
generated_with = 'mosfit'

# get the DataHandler object who takes care of all the book keeping
thisDH = rappidDH.get_dhandler(generated_with, sed_directory=sed_directory)

# get the explosion times for the simulations
thisDH.get_explosion_times_from_template(ncpu=45)

# fit the lightcurves with the desired method (only 'mosfit' is good!)
method = 'mosfit'
fitter = Fitter.get_fitter(method)

logger.debug(
    f'fitter method {fitter.method_name} \n'
    f'job-id {fitter.job_id}'
)

missing_indice_file = f'{pickle_dir}/{thisDH.name}/{fitter.method_name}/missing_indices.txt'
fitter.fit_lcs(thisDH,
               tasks_in_group=100,
               # missing_indice_file=missing_indice_file  # to be used when repeating the fit
               )

# make a selection of lightcurves based on the available photometry
thisDH.select_and_adjust_selection_string()

# get the results
thisDH.results('mosfit')


class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        if not nums:
            return []
        elif len(nums) == 1:
            return [nums]
        else:
            res = []
            for i in range(len(nums)):
                copy = list(nums)
                x = copy.pop(i)
                perms = self.permute(copy)
                perms = [[x] + p for p in perms]
                res.extend(perms)
            return res


import os
import atexit
import secrets
import unittest
from functools import wraps
import augpathlib as aug
from sxpyr import sxpyr
from pyontutils.utils import Async, deferred  # TODO -> asyncd in future
from pyontutils.utils_fast import isoformat
from sparcur import exceptions as exc
from sparcur.utils import GetTimeNow, log
from sparcur.paths import PennsieveCache, LocalPath, Path
from sparcur.backends import PennsieveRemote
from .common import test_organization, test_dataset, _pid
from .common import skipif_ci, skipif_no_net
import pytest

class _TestOperation:

    _cache_class = PennsieveCache
    _remote_class = PennsieveRemote

    @classmethod
    def tearDownClass(cls):
        base = aug.AugmentedPath(__file__).parent / f'test-operation-{_pid}'
        if base.exists():
            base.popd()  # in case we were inside it pop back out first
            base.rmtree()

    def setUp(self):
        class Cache(self._cache_class):
            pass

        Cache._bind_flavours()

        base = aug.AugmentedPath(__file__).parent / f'test-operation-{_pid}'

        if base.exists():
            base.popd()  # in case we were inside it pop back out first
            base.rmtree()

        base.mkdir()
        base.pushd()

        self.Remote = self._remote_class._new(Cache._local_class, Cache)
        self.Remote.init(test_organization)
        self.anchor = self.Remote.dropAnchor(base)

        self.root = self.anchor.remote
        self.project_path = self.anchor.local
        list(self.root.children)  # populate datasets
        self.test_base = [
            p for p in self.project_path.children
            if p.cache.id == test_dataset][0]
        list(self.test_base.rchildren)  # populate test dataset

        asdf = self.root / 'lol' / 'lol' / 'lol (1)'

        class Fun(os.PathLike):
            name = 'hohohohohoho'

            def __fspath__(self):
                return ''

            @property
            def size(self):
                return aug.FileSize(len(b''.join(self.data)))

            @property
            def data(self):
                for i in range(100):
                    yield b'0' * 1000

        #wat = asdf.bfobject.upload(Fun(), use_agent=False)
        #breakpoint()

@skipif_ci
@skipif_no_net
class TestDelete(_TestOperation, unittest.TestCase):

    def test_0(self):
        assert True

    def test_1_case(self):
        # this is an old scenario that happens because of how the old system worked
        # local working directory | x
        # local cache directory | o
        # remote | o
        pass

def make_rand(n, width=80):
    lw = width + 1
    hex_lw = lw // 2
    n_lines = n // lw
    n_accounted = n_lines * lw
    h_lines = n // (hex_lw * 2)
    h_accounted = h_lines * (hex_lw * 2)
    ldiff = n_lines - h_lines
    adiff = n_accounted - h_accounted
    accounted = n_accounted
    missing = n - accounted
    hex_missing = (missing + 1) // 2
    diff = hex_missing * 2 - missing
    hexstart = width % 2  # almost there fails on 71
    # also fails len(make_rand(102, 101)) - 102

    log.debug((adiff, ldiff, missing, diff))
    string = '\n'.join([secrets.token_hex(hex_lw)[hexstart:]
                        for i in range(n_lines)]
                       + [secrets.token_hex(hex_missing)[diff:-1] + '\n'])
    return string.encode()

@skipif_ci
@skipif_no_net
@pytest.mark.skip('VERY SLOW')
class TestFilenames(_TestOperation, unittest.TestCase):

    _evil_names = (
        # '......................',  # this breaks the agent with infinite timeout
        '!@#$%^&*()[]{}`~;:,',
        '(╯°□°）╯︵ ┻━┻)',
        'הָיְתָהtestالصفحات التّحول',
        'הָיְתָהtestالصفحا تالتّحول',  # check bucket names
        '᚛ᚄᚓᚐᚋᚒᚄ ᚑᚄᚂᚑᚏᚅ᚜',
        'Z̮̞̠͙͔ͅḀ̗̞͈̻̗Ḷ͙͎̯̹̞͓G̻O̭̗̮',
        # '𝕿𝖍𝖊 𝖖𝖚𝖎𝖈𝖐 𝖇𝖗𝖔𝖜𝖓 𝖋𝖔𝖝 𝖏𝖚𝖒𝖕𝖘 𝖔𝖛𝖊𝖗 𝖙𝖍𝖊 𝖑𝖆𝖟𝖞 𝖉𝖔𝖌',  # this breaks the agent with ERRORED
        'evil file space',
        'evil_file underscore',
        'evil-file dash',
        'evil%20file percent 20',
        'hello%20world%20%60~%21%40%23%24%25%5E%26%2A%28%29%5B%5D%7B%7D%27',
        # the problem is that we don't know whether we can actually
        # decode a file name, and wthe database stores the encoded filename
        'hello%20world',
        'hello%20world~',
    )

    @property
    def _more_evil_names(self):
        # class scope strikes back! LOL PYTHON
        return [
            name
          for char in
            ('\x07',
             #'/',  # have to do this in a different way on unix
             '\\',
             '|',
             '!',
             '@',
             '#',
             '$',
             '%',
             '^',
             '&',
             '*',
             '(',
             ')',
             '[',
             ']',
             '{',
             '}',
             "'",
             '`',
             '~',
             ';',
             ':',
             ',',
             '"',
             '?',
             '<',
             '>',
             )
          for name in
            (f'prefix{char}',
             f'prefix{char}suffix',
             f'{char}suffix',)]

    @staticmethod
    def _op(test_folder, fsize, name):
        test_file_a = test_folder / (name + '.ext')
        test_file_b = test_folder / (name + '.txe')
        test_folder_i = test_folder / name

        for _f in (test_file_a, test_file_b):
            if _f.exists() or _f.is_broken_symlink():
                msg = (f'bad test environment: file/link already exists: {_f}')
                raise FileExistsError(msg)

        # FIXME maybe don't straight fail here, but instead
        # don't upload and just compare the existing name?
        # the fact that we get an error is a sign that the
        # name matches actually ... so not getting an error
        # in subsequent runs is bad ... for test_base at least

        test_file_a.data = iter((make_rand(fsize),))
        test_file_b.data = iter((make_rand(fsize),))

        try:
            remote_a = test_file_a.upload()
            name_a = remote_a.bfobject.name
        except Exception as e:
            name_a = e

        try:
            remote_b = test_file_b.upload()
            name_b = remote_b.bfobject.name
        except Exception as e:
            name_b = e

        try:
            remote_i = test_folder_i.mkdir_remote()
            name_i = remote_i.bfobject.name
        except Exception as e:
            name_i = e

        return name_a, name_b, name_i

    def test_filenames_more_evil(self):
        return self.test_filenames_evil(self._more_evil_names)

    def test_filenames_evil(self, names=_evil_names):
        # XXX warning slow!
        now = GetTimeNow()
        local = self.project_path / f'test-dataset-{now.START_TIMESTAMP_LOCAL_FRIENDLY}'
        remote = local.mkdir_remote()

        try:
            # FIXME consider going back to self.test_base instead of local here
            test_folder = local / 'pandora'
            test_folder.mkdir_remote()
            test_folder.__class__.upload = Path.upload
            fsize = 1024  # needed for uniqueish hashes colloisions will still happen
            # FIXME this pretty clearly reveals a need for
            # batching to multiplex the fetch ... SIGH
            results = Async(rate=10)(deferred(self._op)(test_folder, fsize, name) for name in names)
            #results = []
            #for name in names:
            #    name_a, name_b, name_i = self._op(test_folder, fsize, name)
            #    results.append((name_a, name_b, name_i))
        finally:
            remote.rmdir(force=True)
            # FIXME crumple fails in refresh since we use rmdir
            # instead of rmtree (for safety)
            #remote.cache.refresh()  # FIXME

@skipif_ci
@skipif_no_net
class TestUpdate(_TestOperation, unittest.TestCase):

    @pytest.mark.skip('the question has been answered')
    def test_process_filesize_limit(self):
        # so the 1 mb size file works, eventually, something else is wrong
        test_folder = self.test_base / 'hrm'
        test_folder.mkdir_remote()
        test_folder.__class__.upload = Path.upload
        for i in range(1024 ** 2, 5 * 1024 ** 2, 1024 ** 2):
            test_file = test_folder / f'size-{i}'
            if test_file.is_broken_symlink():
                test_file.remote.bfobject.package.delete()
                test_file.unlink()
                test_file = test_folder / f'size-{i}'  # remove stale cache

            test_file.data = iter((make_rand(i),))
            remote = test_file.upload()

    def test_upload_noreplace(self):
        for i in range(2):
            test_file = self.test_base / 'dataset_description.csv'
            test_file.data = iter((make_rand(100),))
            # FIXME temp sandboxing for upload until naming gets sorted
            test_file.__class__.upload = Path.upload
            # create some noise
            remote = test_file.upload(replace=False)
            print(remote.bfobject.package.name)

    def test_upload_noreplace_fail(self):
        # some persistent state from other tests is causing this to fail
        test_file = self.test_base / 'dataset_description.csv'
        test_file.data = iter((make_rand(100),))
        # FIXME temp sandboxing for upload until naming gets sorted
        test_file.__class__.upload = Path.upload
        test_file.upload(replace=False)
        try:
            test_file.upload(replace=False)
            assert False, 'should have failed'
        except exc.FileHasNotChangedError:
            pass

    def test_upload_replace(self):
        test_file = self.test_base / 'dataset_description.csv'
        test_file.data = iter((make_rand(100),))
        # FIXME temp sandboxing for upload until naming gets sorted
        test_file.__class__.upload = Path.upload
        test_file.upload()

@skipif_ci
@skipif_no_net
class TestClone(_TestOperation, unittest.TestCase):
    # TODO test a variety of clone scenarios
    # and consider whether testing for and
    # existing root should be done in dropAnchor

    def setUp(self):
        super().setUp()
        self.alt_project_path = self.project_path.parent / 'alt' / self.project_path.name
        if self.alt_project_path.parent.exists():
            self.alt_project_path.parent.rmtree()

        self.alt_project_path.mkdir(parents=True)

    def _do_target(self, target, expect_error_type=None):
        class Cache(self._cache_class):
            pass

        Cache._bind_flavours()

        BFR = self._remote_class._new(LocalPath, Cache)
        BFR.init(test_organization)

        if expect_error_type:
            try:
                anchor = BFR.dropAnchor(target.parent)
                raise AssertionError(f'should have failed with a {expect_error_type}')
            except expect_error_type as e:
                pass

        else:
            anchor = BFR.dropAnchor(target.parent)

    def test_1_in_project(self):
        target = self.project_path / 'some-new-folder'
        target.mkdir()
        self._do_target(target)  # FIXME succeeds for now, but probably should not?

    def test_2_project_top_level(self):
        target = self.project_path
        self._do_target(target, exc.DirectoryNotEmptyError)

    def test_3_existing_empty(self):
        target = self.alt_project_path
        self._do_target(target)

    def test_4_existing_has_folder(self):
        target = self.alt_project_path
        child = target / 'a-folder'
        child.mkdir(parents=True)
        self._do_target(target, exc.DirectoryNotEmptyError)

    def test_5_existing_has_file(self):
        target = self.alt_project_path
        child = target / 'a-file'
        child.touch()
        self._do_target(target, exc.DirectoryNotEmptyError)

    def test_6_existing_has_local_data_dir(self):
        target = self.alt_project_path
        child = target / self.anchor._local_data_dir
        child.mkdir()
        self._do_target(target, exc.DirectoryNotEmptyError)

@skipif_ci
@skipif_no_net
class TestMkdirRemote(_TestOperation, unittest.TestCase):

    def test_mkdir_remote_parents_false(self):
        now = GetTimeNow()
        local = self.project_path / f'test-dataset-{now.START_TIMESTAMP_LOCAL_FRIENDLY}' / 'some-folder'
        try:
            remote = local.mkdir_remote()
            raise AssertionError('Should have failed since parents=False')
        except FileNotFoundError:
            pass

    def test_0_mkdir_remote_will_be_dataset(self):
        now = GetTimeNow()
        local = self.project_path / f'test-dataset-{now.START_TIMESTAMP_LOCAL_FRIENDLY}'
        remote = local.mkdir_remote()
        remote.rmdir()
        remote.cache.refresh()  # reminder that remotes are a snapshot in time, NOT dynamic
        assert not local.exists(), f'should have been deleted {remote}'

    def test_1_mkdir_remote_will_be_collection(self):
        now = GetTimeNow()
        local = self.project_path / f'test-dataset-{now.START_TIMESTAMP_LOCAL_FRIENDLY}' / 'some-folder'
        remote = local.mkdir_remote(parents=True)
        parent = remote.parent
        try:
            parent.rmdir()  # should fail here
            try:
                remote.rmdir()  # insurance
            except BaseException as e:
                log.exception(e)
            finally:
                raise AssertionError('remote parent should NOT have rmdired {parent}')
        except exc.PathNotEmptyError:
            pass

        try:
            remote.rmdir()
            remote.cache.refresh()
            assert not local.exists(), f'should have been deleted {remote}'
        finally:
            lparent = parent.local
            parent.cache.refresh()  # we just removed the child so the parent is stale
            parent.rmdir()
            parent.cache.refresh()
            assert not lparent.exists(), f'should have been deleted {parent}'

class TestMoveFolder(_TestOperation, unittest.TestCase):

    def test_reparent(self):
        # TODO this is nowhere near complete with respect to synchronization
        # but it is sufficient to test the components needed for sync
        now = GetTimeNow()
        local = self.project_path / f'test-dataset-{now.START_TIMESTAMP_LOCAL_FRIENDLY}'
        remote = local.mkdir_remote()

        try:
            # FIXME consider going back to self.test_base instead of local here
            test_folder_1 = local / 'dire-1'
            test_folder_1.mkdir_remote()
            test_folder_2 = local / 'dire-2'
            test_folder_2.mkdir_remote()
            list(remote.cache.children)  # XXX populate with remote data

            test_folder_2.remote.reparent(test_folder_1.cache_id)

            test_folder_1.__class__.upload = Path.upload
            fsize = 1024
            test_file_1 = test_folder_1 / 'file-1.ext'
            test_file_1.data = iter((make_rand(fsize),))
            test_file_1.upload()
            test_file_1.remote.reparent(test_folder_2.cache_id)

        finally:
            remote.rmdir(force=True)
            # FIXME crumple fails in refresh since we use rmdir
            # instead of rmtree (for safety)
            #remote.cache.refresh()  # FIXME

class _ChangesHelper:
    _local_only = True

    def _make_ops(self):
        ops = tuple()
        # dirs
        dataset = 'project/dataset'
        ops += (
        #(0, 'mkdir', 'project'),  # don't actually need this since we replace it when building paths
        (0, 'mkdir', dataset),
        (0, 'mkdir', 'project/dataset/dire-1'),
        (0, 'mkdir', 'project/dataset/dire-2'),
        (0, 'mkdir', 'project/dataset/dire-6'),
        )

        # sources
        d3_1 = 'project/dataset/dire-1/dire-3-1-rn'
        d3_2 = 'project/dataset/dire-1/dire-3-2-rp'
        d3_3 = 'project/dataset/dire-1/dire-3-3-np'

        f1_0 = 'project/dataset/dire-1/file-1-0.ext'
        f1_1 = 'project/dataset/dire-1/file-1-1-rn.ext'
        f1_2 = 'project/dataset/dire-1/file-1-2-rp.ext'
        f1_3 = 'project/dataset/dire-1/file-1-3-np.ext'

        l1_0 = 'project/dataset/dire-1/link-1-0.ext'
        l1_1 = 'project/dataset/dire-1/link-1-1-rn.ext'
        l1_2 = 'project/dataset/dire-1/link-1-2-rp.ext'
        l1_3 = 'project/dataset/dire-1/link-1-3-np.ext'

        # targets
        # TODO need variants of all of these where we lose the metadata probably?

        ops += (
        (0, 'mkdir', d3_1),
        (0, 'mkdir', d3_2),
        (0, 'mkdir', d3_3),

        (0, 'mkfile', f1_0),  # nochange
        (0, 'mkfile', f1_1),
        (0, 'mkfile', f1_2),
        (0, 'mkfile', f1_3),

        (0, 'mklink', l1_0),  # nochange
        (0, 'mklink', l1_1),
        (0, 'mklink', l1_2),
        (0, 'mklink', l1_3),

        # moves: renames, reparents, rename_reparent
        (1, 'rename', d3_1, 'project/dataset/dire-1/dire-3-1-rn-r'),  # rn
        (1, 'rename', d3_2, 'project/dataset/dire-2/dire-3-2-rp'),  # rp
        (1, 'rename', d3_3, 'project/dataset/dire-2/dire-3-3-np-r'),  # rnp

        (1, 'rename', f1_1, 'project/dataset/dire-1/file-1-1-rn-r.ext'),  # rn
        (1, 'rename', f1_2, 'project/dataset/dire-2/file-1-2-rp.ext'),  # rp
        (1, 'rename', f1_3, 'project/dataset/dire-2/file-1-3-np-r.ext'),  # rnp

        (1, 'rename', l1_1, 'project/dataset/dire-1/link-1-1-rn-r.ext'),  # rn
        (1, 'rename', l1_2, 'project/dataset/dire-2/link-1-2-rp.ext'),  # rp
        (1, 'rename', l1_3, 'project/dataset/dire-2/link-1-3-np-r.ext'),  # rnp

        # add
        (1, 'mkdir',  'project/dataset/dire-6/dire-7-add'),
        (1, 'mkfile', 'project/dataset/dire-6/file-4-add.ext'),
        (2, 'mklink', 'project/dataset/dire-6/link-4-add.ext'),  # XXX this causes an error because it looks like the index is out of synx
        )

        # change (only applies to files)
        f5_1 = 'project/dataset/dire-6/file-5-1-cd_.ext'
        f5_2 = 'project/dataset/dire-6/file-5-2-c_m.ext'
        f5_3 = 'project/dataset/dire-6/file-5-3-c_x.ext'
        f5_4 = 'project/dataset/dire-6/file-5-4-cdm.ext'
        f5_5 = 'project/dataset/dire-6/file-5-5-cdx.ext'
        # file_id change ? should be impossible ...
        ops += (
        (0, 'mkfile', f5_1),
        (0, 'mkfile', f5_2),
        (0, 'mkfile', f5_3),
        (0, 'mkfile', f5_4),
        (0, 'mkfile', f5_5),

        # TODO probably also change size
        (1, 'change', f5_1, True, False),  # data
        (1, 'change', f5_2, False, True),  # metadata
        (1, 'change', f5_3, False, None),  # no metadata # can handle this from objects cache
        (1, 'change', f5_4, True, True),  # data metadata
        (1, 'change', f5_5, True, None),  # data no metadata
        )

        # remove
        d9 = 'project/dataset/dire-6/dire-9-rem'
        f6 = 'project/dataset/dire-6/file-6-rem.ext'
        l6 = 'project/dataset/dire-6/link-6-rem.ext'
        ops += (
        (0, 'mkdir', d9),
        (0, 'mkfile', f6),
        (0, 'mklink', l6),

        (1, 'remove', d9),
        (1, 'remove', f6),
        (1, 'remove', l6),
        )

        # build the indexes so we can do the diff
        ops += (
            (0.5, 'index', dataset),
        )
        return ops

    def setUp(self):
        # TODO construct the template we need
        super().setUp()
        self.Path = self.Remote._local_class

        #sigh = list(self.project_path.remote.children)
        #[s.rmdir(force=True) for s in sigh if '2023' in s.name]
        #breakpoint()
        #raise ValueError('cleanup tearDown failure mess')

        # TODO expected outcome after stage probably

        if self._local_only:
            def populate_cache(p, change=False):
                # FIXME TODO change=True case may need special handling
                # and probably represents a strange case of some kind
                return p._cache_class.fromLocal(p)

            def norm_path(p):
                return self.project_path / p.replace('project/', '')

        else:
            # XXX WARNING extremely slow due to sequentially creating each remote file
            now = GetTimeNow()
            local_dataset = self.project_path / f'test-dataset-{now.START_TIMESTAMP_LOCAL_FRIENDLY}'
            remote_dataset = local_dataset.mkdir_remote()
            #self._test_dataset = remote_dataset
            # tearDown fails to trigger if failure happens in setUp which is useless
            # so use atexit instead
            atexit.register(lambda : remote_dataset.rmdir(force=True))
            def populate_cache(p, change=False):
                # FIXME TODO change=True case may need special handling
                # and probably represents a strange case of some kind
                if change:
                    return

                remote = p.create_remote()
                return remote.cache

            def norm_path(p):
                return local_dataset / p.replace('project/dataset', '').strip('/')

        def mkdir(d, add=False):
            if not self._local_only and d == local_dataset:
                # FIXME HACK
                return

            d.mkdir()
            if not add:
                cache = populate_cache(d)
                d._cache = cache
            #d._cache_class.fromLocal(d)

        def mkfile(f, add=False):
            f.data = iter((make_rand(100),))
            if not add:
                cache = populate_cache(f)
                f._cache = cache
            #f._cache_class.fromLocal(f)

        def mklink(l):
            try:
                l.data = iter((make_rand(100),))
                # issue with id and parent_id not being set so use fromLocal since it does it correctly
                #meta = f.meta  # TODO checksum probably?
                #symlink = meta.as_symlink(local_name=l.name)
                #cache = l._cache_class.fromLocal(l)
                cache = populate_cache(l)
                symlink = cache.meta.as_symlink(local_name=l.name)
            finally:
                l.unlink()

            l.symlink_to(symlink)

        def rename(path, target):
            path.rename(target)

        def change(f, data, metadata):
            if data:
                f.data = iter((make_rand(100),))

            if metadata is None or metadata:
                # must set xattrs to nothing if
                # we want to change metadata otherwise
                # PrimaryCache._meta_updater will go haywire and
                # ... try to delete the file ... and it will
                # actually delete it instead of crumpling it
                # so definitely a FIXME very dangerous lose your work
                # kind of scenario between that _meta_updater and
                # BFPNCache._actually_crumple and change of BFPNCache.crumple
                [f.delxattr(k) for k in f.xattrs()]
                if f.xattrs():
                    breakpoint()

            if metadata:
                if not f.exists():
                    raise FileNotFoundError(f)

                try:
                    populate_cache(f, change=True)
                    #f._cache_class.fromLocal(f)
                except Exception as e:
                    breakpoint()
                    raise e

        def remove(path):
            if path.is_dir():
                path.rmdir()
            else:
                path.unlink()

        def index(ds):
            if self._local_only:
                caches = [l.cache for l in ds.rchildren]  # XXX reminder, NEVER use ds.cache.rchildren that will pull
                class fakeremote:
                    def __init__(self, id, name, parent_id, file_id, updated, local):
                        self.id = id
                        self.name = name
                        self._name = name
                        self.parent_id = parent_id
                        self.updated = updated
                        self._lol_local = local

                        if file_id is not None:
                            self.file_id = file_id

                    def is_dir(self):
                        return self._lol_local.is_dir()

                for c in caches:
                    # FIXME causes other issues ... even while trying to avoid init issues
                    # we should not have to do this
                    cmeta = c.meta
                    c._remote = fakeremote(
                        cmeta.id, cmeta.name, cmeta.parent_id, cmeta.file_id,
                        cmeta.updated, c.local)
            else:
                # this is safe at this stage since everything should match upstream
                caches = [c.cache for c in local_dataset.rchildren]

            ds._generate_pull_index(ds, caches)

        fops = {
            'mkdir': mkdir,
            'mkfile': mkfile,
            'mklink': mklink,
            'rename': rename,
            'change': change,
            'remove': remove,
            'index': index,
        }

        def make_closure(stage, op, obj, args):
            f = fops[op]

            if stage > 0 and op in ('mkdir', 'mkfile'):
                kwargs=dict(add=True)
            else:
                kwargs = {}

            @wraps(f)
            def inner():
                f(path, *args, **kwargs)

            return inner

        def cargs(args):
            for a in args:
                if isinstance(a, str) and a.startswith('project/'):
                    yield norm_path(a)
                else:
                    yield a

        ops = self._make_ops()
        pops = [(stage, op, norm_path(s), *cargs(args)) for stage, op, s, *args in ops]
        init = set([path for stage, op, path, *args in pops if stage == 0])
        test = set([p for stage, op, path, *args in pops if stage >= 1 for p in (path, *args) if isinstance(p, self.project_path.__class__)])
        nochange = init - test
        add_rename_reparent = test - init
        change_remove = test - add_rename_reparent

        cs = [(stage, path, make_closure(stage, op, path, args)) for stage, op, path, *args in pops]
        scs = sorted(cs, key=(lambda abc: (abc[0], len(abc[1].parts))))
        will_fails = []
        for stage, path, fun in scs:
            if stage > 1:
                will_fails.append(fun)
            else:
                fun()

        self._will_fails = will_fails
        self.dataset = pops[0][-1]

class TestChanges(_ChangesHelper, _TestOperation, unittest.TestCase):

    def test_changes(self):
        from dateutil import parser as dateparser
        dataset = self.dataset
        dataset_id, id_name, parent_children, name_id, updated_transitive = dataset._read_indexes()
        # XXX updated_transitive from _read_indexes is a string because that is what
        # _transitive_changes needs internally and then transforms to a datetime object
        # when it returns, therefore we don't fiddle with the types here

        #tc = dataset._transitive_changes()
        # XXX see sparcur.simple.utils
        dataset_id, updated_cache_transitive, diff = dataset.diff()
        blob = {
            'dataset-id': dataset_id.id,
            'updated-transitive': updated_transitive,
            'diff': diff,
        }
        pl = sxpyr.python_to_sxpr(blob, str_as_string=True)
        sxpr = pl._print(sxpyr.configure_print_plist(newline_keyword=False))
        print(sxpr)
        #pl = sxpyr.python_to_sxpr(diff, str_as_string=True)
        #sxpr = pl._print(sxpyr.configure_print_plist(newline_keyword=False))
        breakpoint()

class _WorkflowHelper:

    def _do_workflow(self, paths_to_add):
        # push button, receive bacon

        # 0. asumme there are changes to a dataset

        # 1. click upload button in main window (python get diff)
        #    argv-simple-diff -> sparcur.simple.utils for-racket diff -> path_dataset.diff()

        # 2. select specific paths for upload (python nothing)
        #    racket side selects the list of files to push (push_list) that goes into paths.sxpr
        #    which the python side then reads in the next step

        # 3. click confirm selection checkbox (python generate manifest)
        #    (ensure-directory! (push-dir)) -> updated_transitive push_id
        #    write-push-paths -> {:user-cache-path}/{dataset-uuid}/{updated-transitive}/{push-id}/paths.sxpr -> push_list
        #    argv-simple-make-push-manifest -> sparcur.simple.utils for-racket make-push-manifest -> path_dataset.make_mush_manifest()

        # 4. click push selected to remote (python push from manifest)
        #    argv-simple-push -> sparcur.simple.utils for-racket push -> path_dataset.push_from_manifest()

        # 5. TODO I think that after remote changes are made we probably want to create
        #    a modified index file that notes the changes so that incremental changes
        #    do not have to be pulled again ... of course if upstream has changed we are
        #    back in the usual world of pain ...

        path_dataset = self.dataset  # given
        dataset_id = path_dataset.cache.identifier
        # 1
        __dataset_id, updated_transitive, diff = path_dataset.diff()
        # 2
        # write the push_list to paths.sxpr
        push_id = path_dataset._write_push_list(dataset_id, updated_transitive, diff, paths_to_add)
        # 3
        path_dataset.make_push_manifest(dataset_id, updated_transitive, push_id)
        # 4
        if not self._local_only:  # this fails without remote
            import pennsieve.api.agent
            try:
                path_dataset.push_from_manifest(dataset_id, updated_transitive, push_id)
            except pennsieve.api.agent.AgentError as e:
                log.exception(e)
                pytest.skip('pennsieve error')

class TestWorkflow(_ChangesHelper, _WorkflowHelper, _TestOperation, unittest.TestCase):

    def test_workflow(self):
        # splitting d r l lets us test incremental changes
        paths_to_add_d = [
            'dire-1/dire-3-1-rn-r',  # rn
            'dire-2/dire-3-2-rp',  # rp
            'dire-2/dire-3-3-np-r',  # rnp
        ]
        paths_to_add_r = [
            'dire-1/file-1-1-rn-r.ext',  # rn
            'dire-2/file-1-2-rp.ext',  # rp
            'dire-2/file-1-3-np-r.ext',  # rnp
        ]
        paths_to_add_l = [
            'dire-1/link-1-1-rn-r.ext',  # rn
            'dire-2/link-1-2-rp.ext',  # rp
            'dire-2/link-1-3-np-r.ext',  # rnp
        ]

        paths_to_add_2 = [
            'dire-6/file-4-add.ext',  # should error for now
        ]
        self._do_workflow(paths_to_add_d)
        self._do_workflow(paths_to_add_r)
        self._do_workflow(paths_to_add_l)
        try:
            self._do_workflow(paths_to_add_2)
            raise AssertionError('should have failed due to forbidden ops')
        except ValueError:
            pass

class TestWithRemoteWorkflow(TestWorkflow):

    _local_only = False

class TestRemote(_TestOperation, unittest.TestCase):
    def test_remote_path_does_not_exist(self):
        new_thing = self.root / 'does not exist'

    @pytest.mark.skip('Not ready.')
    def test_cache_path_does_not_exist(self):
        """ This should not produce an error.
            Path objects should be able to be instantiated without
            po.exists() -> True at a point in time prior to instantiation.
        """
        new_thing = self.anchor / 'does not exist'

    def __test_cache_path_fake_id(self):
        # FIXME the right way to do this is
        np = self.project_path / 'new-path'
        np.mkdir()
        npm = np.meta

        # bad way
        class FakeBase(aug.RemotePath):
            def __init__(self, id, name, cache=None):
                super().__init__(id, cache)
                self.name = name
                now = GetTimeNow()
                self.created = now._start_time
                self.updated = now._start_time
                self.checksum = 'lolnone'
                self.chunksize = 4096
                self.file_id = 'asdfasdfasdf'

            @property
            def meta(self):
                return PathMeta(size=self.size,
                                created=self.created,
                                updated=self.updated,
                                checksum=self.checksum,
                                chunksize=self.chunksize,
                                id=self.id,
                                file_id=self.file_id)

            @property
            def parent(self):
                return None

            def _parts_relative_to(self, remote, cache_parent=None):
                return [self.name]

        # This takes way too many steps :/
        Fake = FakeBase._new(LocalPath, aug.CachePath)

        fake = Fake('lol', 'double lol')

        self.anchor / fake


from socket import socket
from squaring_server.server import ADDRESS, PORT, DISCONNECT, decode, encode

client = socket()
client.connect((ADDRESS, PORT))

while True:
    msg = input('Your msg: ')
    client.send(encode(msg))
    if msg == DISCONNECT:
        break
    print(f'Server: {decode(client.recv(1024))}')

client.close()


# Script to mess around with User authenticated spotify API
# For some reason, cannot authenticate with Google Chrome, so instead use Firefox
# http://spotipy.readthedocs.io/en/latest/

from pathlib import Path
from spotipy.oauth2 import SpotifyClientCredentials
import json
import spotipy
import time
import sys
import spotipy.util as util
import pandas as pd

# Set up directory and read credentials
directory = Path(__file__).resolve().parent
infile = open(directory / "credentials.txt", 'r').readlines()

username = "liltkrookie"
scope = "user-top-read"
sort = "tempo"

print(infile[0])
# Send crendetials and ping Spotify API
token = util.prompt_for_user_token(username,scope,client_id='8d3383fc5c434af5bf40fb7b2915c618',client_secret=infile[0],redirect_uri='http://localhost:8888/callback')
client_credentials_manager = SpotifyClientCredentials(client_id='8d3383fc5c434af5bf40fb7b2915c618', client_secret=infile[0])
sp = spotipy.Spotify(auth=token)

playlistdata = sp.current_user_top_tracks(limit=50, offset=0, time_range='medium_term')
playlist_json = json.dumps(playlistdata,indent=4)
track_list = json.loads(playlist_json)

num = len(track_list['items'])
tid=[]
for i in range(0, num):
    uri =track_list['items'][i]['uri']
    tid.append(uri)

# Song Audiofeatures
analysis = sp.audio_features(tid)
sample_json = json.dumps(analysis)
data = pd.read_json(sample_json)

# print(data)

# Song Metadata
analysis2 = sp.tracks(tid)
sample_json2 = json.dumps(analysis2)
data2 = json.loads(sample_json2)
songdata=[]
songlabels=['song','uri','artist']
for i in range(0, num):
    name=data2['tracks'][i]['name']
    uri =data2['tracks'][i]['uri']
    artist =data2['tracks'][i]['album']['artists'][0]['name']
    songdata.append([name, uri, artist])
song_metadata = pd.DataFrame.from_records(songdata, columns=songlabels)

# print(song_metadata)
# DataFrame merge
export = pd.merge(song_metadata,data, how = 'outer', on =['uri'])
writer = pd.ExcelWriter(directory / 'top_played_songs.xlsx')
export.to_excel(writer,'Sheet1')
writer.save()

dfList = export['uri'].tolist()
print ("Completed download and export of top played songs")


import datetime
from django.utils import timezone

from django.core.paginator import Paginator
from django.db import transaction
from django.db.models import Q

from the_mechanic_backend.apps.accounts.models import Store
from the_mechanic_backend.apps.stock.models import Brand, BrandModel, Spare, SpareCustomer, SpareOrder, SpareSold
from the_mechanic_backend.v0.stock import serializers
from the_mechanic_backend.v0.utils import Utils, CustomBaseClass, AppUtils

class BrandList(CustomBaseClass):
    """
        Brand List and create Endpoint
    """

    def get(self, request):
        """
        returns the list of brand
        :param request:
        :return:
        """
        try:
            search = request.GET.get('search', '')
            if search:
                brands = self.get_filter_objects(Brand, name__icontains=search)
            else:
                brands = self.get_all_objects(Brand)
            serializer = serializers.BrandSerializer(brands, many=True)
            return Utils.dispatch_success(request, serializer.data)
        except Exception as e:
            return self.internal_server_error(request, e)

    def post(self, request):
        """
        Creates a new brand
        :param request:
        {
           "name" : "Honda"
        }
        :return:
        """
        try:
            serializer = serializers.BrandSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Utils.dispatch_success(request, serializer.data)
            return Utils.dispatch_failure(request, 'VALIDATION_ERROR', serializer.errors)
        except Exception as e:
            return self.internal_server_error(request, e)

class BrandModelList(CustomBaseClass):
    """
    Brand Model List and create Endpoint
    """

    def get(self, request, brand_id):
        """
        Returnt the list of Models of particular brand
        :param request:
        :param brand_id:
        :return:
        """
        try:
            search = request.GET.get('search', '')
            if search:
                brands = self.get_filter_objects(BrandModel, brand=brand_id, model_name__icontains=search)
            else:
                brands = self.get_filter_objects(BrandModel, brand=brand_id)
            serializer = serializers.BrandModelSerializer(brands, many=True)
            return Utils.dispatch_success(request, serializer.data)
        except Exception as e:
            return self.internal_server_error(request, e)

    def post(self, request, brand_id):
        """
        Creates a new brand model
        :param request:
          {
             "model_name" : "Unicorn"
          }
        :param brand_id:
        :return:
        """
        try:
            data = request.data
            data['brand'] = brand_id
            serializer = serializers.AddBrandModelSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Utils.dispatch_success(request, serializer.data)
            return Utils.dispatch_failure(request, 'VALIDATION_ERROR', serializer.errors)
        except Exception as e:
            return self.internal_server_error(request, e)

class SpareList(CustomBaseClass):
    def get(self, request, store_id, brand_model_id):
        """
        return a list of spares for particular model
        :param request:
        @query_param
        search=search_text - to search the spares
        out_of_stock=true - to get only out of stock
        Note - we can use both at same time :)
        :param store_id
        :param brand_model_id:
        :return:
        """
        try:
            search = request.GET.get('search')
            out_of_stock = request.GET.get('out_of_stock')
            spare = self.get_filter_objects(Spare, brand_model=brand_model_id, store=store_id)

            if search:
                spare = spare.filter(Q(spare_id__icontains=search) | Q(spare_name__icontains=search))

            if out_of_stock:
                spare = spare.filter(quantity=0)

            serializer = serializers.SpareSerializer(spare, many=True)
            return Utils.dispatch_success(request, serializer.data)
        except Exception as e:
            return self.internal_server_error(request, e)

    def post(self, request, store_id, brand_model_id):
        """
        Create a spare
        :param request:
        {
            "spare_name": "SIde Mirror",
            "spare_id": #34545435,
            "quantity": 10,
            "per_price": "500",
            "suppliers": "Glass India",
            "quality_class": "FIRST"
        }
        :param store_id:
        :param brand_model_id:
        :return:
        """
        try:
            data = request.data
            brand_model = self.get_object(BrandModel, brand_model_id)
            if not brand_model:
                return self.object_not_found(request)
            data['brand'] = brand_model.brand.id
            data['store'] = store_id
            data['brand_model'] = brand_model_id
            serializer = serializers.AddSpareSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Utils.dispatch_success(request, serializer.data)
            return Utils.dispatch_failure(request, 'VALIDATION_ERROR', serializer.errors)
        except Exception as e:
            return self.internal_server_error(request, e)

class SpareDetails(CustomBaseClass):
    """
    particular spare details
    """

    def get(self, request, spare_id):
        """
        Return requested spare
        :param request:
        :param spare_id:
        :return:
        """
        try:
            spare = self.get_object(Spare, spare_id)
            if not spare:
                return self.object_not_found(request)
            serializer = serializers.SpareSerializer(spare)
            return Utils.dispatch_success(request, serializer.data)
        except Exception as e:
            return self.internal_server_error(request, e)

    def put(self, request, spare_id):
        """
        Updates the requested spare
        :param request:
        # partial fields are also acceptable
        {
            "spare_name": "SIde Mirror",
            "spare_id": #34545435,
            "quantity": 10,
            "per_price": "500",
            "suppliers": "Glass India",
            "quality_class": "FIRST"
        }
        :param spare_id:
        :return:
        """
        try:
            spare = self.get_object(Spare, spare_id)
            if not spare:
                return self.object_not_found(request)
            serializer = serializers.AddSpareSerializer(spare, request.data, partial=True)
            if serializer.is_valid():
                serializer.save()
                return Utils.dispatch_success(request, serializer.data)
            return Utils.dispatch_failure(request, 'VALIDATION_ERROR', serializer.errors)
        except Exception as e:
            return self.internal_server_error(request, e)

    def delete(self, request, spare_id):
        """
        Delete the request spare
        :param request:
        :param spare_id:
        :return:
        """
        try:
            spare = self.get_object(Spare, spare_id)
            if not spare:
                return self.object_not_found(request)
            spare.delete()
            return Utils.dispatch_success(request, 'SUCCESS')
        except Exception as e:
            return self.internal_server_error(request, e)

class SpareSearchList(CustomBaseClass):
    def get(self, request, store_id):
        """
        return a list of spares for all models
        :param request:
        @query_param
        search=search_text - to search the spares
        out_of_stock=true - to get only out of stock
        Note - we can use both at same time :)
        :param store_id
        :param brand_model_id:
        :return:
        """
        try:
            search = request.GET.get('search')
            out_of_stock = request.GET.get('out_of_stock')
            spare = self.get_filter_objects(Spare, store=store_id)

            if search:
                spare = spare.filter(Q(spare_id__icontains=search) | Q(spare_name__icontains=search))

            if out_of_stock:
                spare = spare.filter(quantity=0)

            serializer = serializers.SpareSerializer(spare, many=True)
            return Utils.dispatch_success(request, serializer.data)
        except Exception as e:
            return self.internal_server_error(request, e)

class SpareOrderList(CustomBaseClass):
    def get(self, request, store_id, *args, **kwargs):
        """
        Returns the list of Spares based on Store
        :param request:
        # params
        start_date=2019-01-31&&
        end_date=2019-12-31&&
        page=1
        :param store_id:
        :param args:
        :param kwargs:
        :return:
        """
        try:
            start_date = request.GET.get('start_date')
            end_date = request.GET.get('end_date')
            page = request.GET.get('page', 1)
            search = request.GET.get('search', None)
            if search:
                qs = SpareOrder.objects.filter(store=store_id, order_id__icontains=search)
            else:
                if start_date:
                    start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d')
                    end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d') + datetime.timedelta(days=1)
                qs = SpareOrder.objects.filter(store=store_id, order_date__range=[start_date, end_date])

            paginator = Paginator(qs, per_page=10)
            serializer = serializers.SpareOrderHistorySerializer(paginator.page(page), many=True)
            response_data = {
                "data": serializer.data,
                "page": int(page),
                "total_pages": paginator.num_pages
            }
            return Utils.dispatch_success(request, response_data)
        except Exception as e:
            return self.internal_server_error(request, e)

    def post(self, request, store_id, *args, **kwargs):
        """
        Create a new Order
        :param request:
        {
                "customer_info": {
                    "name": "Muthu Kumar",
                    "email": "itmemk@gmail.com",
                    "phone_number": "9876543210",
                    "address": "ADDRESSS"
                },
                "order_type": "IN_SOURCE / OUT_SOURCE",
                "bike_number": "TN41Y5644",
                "labour_charge": 0.00,
                "out_source_charge": 0.00,
                "spares": [
                    {
                        "spare_id": 1,
                        "spare_price_type": 'MRP / MECHANIC / WHOLESALER / CUSTOMER',
                        "spare_count": 2
                    },
                    {
                        "spare_id": 1,
                        "spare_price_type": 'MRP / MECHANIC / WHOLESALER / CUSTOMER',
                        "spare_count": 2
                    },
                    {
                        "spare_id": 1,
                        "spare_price_type": 'MRP / MECHANIC / WHOLESALER / CUSTOMER',
                        "spare_count": 2
                    }
                ]

            }
        :param store_id:
        :param args:
        :param kwargs:
        :return:
        """
        try:
            data = request.data
            try:
                customer = SpareCustomer.objects.get(phone_number=data['customer_info']['phone_number'])
            except SpareCustomer.DoesNotExist:
                customer_serializer = serializers.SpareCustomerSerializer(data=data['customer_info'])
                if customer_serializer.is_valid():
                    customer_serializer.save()
                else:
                    return Utils.dispatch_failure(request, "VALIDATION_ERROR", customer_serializer.errors)
                customer = SpareCustomer.objects.get(id=customer_serializer.data['id'])

            today = datetime.date.today()
            today_order_count = SpareOrder.objects.filter(order_date__year=today.year,
                                                          order_date__month=today.month).count()

            order_id = 'SPOR{}{:05d}'.format(today.strftime("%Y%m"), today_order_count + 1)
            with transaction.atomic():
                store = self.get_object(Store, store_id)
                share_message = f"You're successfully purchased following items from {store.name}, {store.branch}.\n"
                order = SpareOrder(order_id=order_id,
                                   store=store,
                                   order_type=data['order_type'],
                                   customer=customer,
                                   total=0.0,
                                   sold_by=request.user)
                order.save()
                total = 0.0
                spares_to_be_created = []
                for _spare in data['spares']:
                    spare = self.get_object(Spare, _spare['spare_id'])
                    price_map = {
                        'MRP': spare.mrp_price,
                        'MECHANIC': spare.mechanic_price,
                        'WHOLESALER': spare.wholesaler_price,
                        'CUSTOMER': spare.customer_price,
                    }
                    sold_spare = SpareSold(order=order,
                                           spare=spare,
                                           spare_count=_spare['spare_count'],
                                           spare_name=spare.spare_name,
                                           spare_buying_price=spare.buying_price,
                                           spare_price=price_map[_spare['spare_price_type']],
                                           spare_price_type=_spare['spare_price_type'])
                    spares_to_be_created.append(sold_spare)
                    current_total = float(sold_spare.spare_count * sold_spare.spare_price)
                    total = total + current_total
                    spare.quantity = spare.quantity - sold_spare.spare_count
                    spare.save()
                    share_message += f"{sold_spare.spare_name} -- {sold_spare.spare_count} x {sold_spare.spare_price} = {current_total}\n"

                SpareSold.objects.bulk_create(spares_to_be_created)
                if order.order_type:
                    order.bike_number = data['bike_number']
                    order.labour_charge = data['labour_charge']
                    order.out_source_charge = data['out_source_charge']
                    total = total + order.labour_charge + order.out_source_charge
                    share_message += f"Labour Charge = {order.labour_charge}\n\n" \
                        f"Out Source Charge = {order.out_source_charge}\n\n"
                order.total = total
                order.save()
                share_message += f"Grand total = {total}.\n\n" \
                    f"Order ID: {order_id}\n\n" \
                    f"Date: {today.strftime('%d-%m-%Y')}\n\nThank you for purchasing with us!"
                return Utils.dispatch_success(request, {'order_id': order.id, 'spareorder_id':order_id, 'share_info': share_message})
        except Exception as e:
            return self.internal_server_error(request, e)

class SparesAccountingView(CustomBaseClass):
    sell_report_type = ['IN_SELL', 'OUT_SELL', 'TOTAL_SELL']
    profit_report_type = ['IN_PROFIT', 'OUT_PROFIT', 'TOTAL_PROFIT']
    IN_SOURCE = ['IN_SELL', 'IN_PROFIT']
    OUT_SOURCE = ['OUT_SELL', 'OUT_PROFIT']

    def get_total(self, qs, report_type):
        """
        parms ?start_date=2019-01-31&&end_date=2019-12-31&&stores=16&&report_type=TOTAL_SELL
        IN_SELL', 'OUT_SELL', 'TOTAL_SELL', 'IN_PROFIT', 'OUT_PROFIT', 'TOTAL_PROFIT
        :param qs:
        :param report_type:
        :return:
        """
        total = 0.00
        total_items = 0
        spares = []

        if report_type in self.IN_SOURCE:
            qs = qs.filter(order_type=SpareOrder.IN_SOURCE)

        if report_type in self.OUT_SOURCE:
            qs = qs.filter(order_type=SpareOrder.OUT_SOURCE)

        for order in qs:
            for spare in SpareSold.objects.filter(order=order):
                if report_type in self.sell_report_type:
                    total = total + float(spare.spare_count * spare.spare_price)

                if report_type in self.profit_report_type:
                    total = total + float(spare.spare_count * spare.spare_buying_price)
                spares.append(spare.spare)
                total_items += spare.spare_count
        return total, total_items, len(set(spares))

    def get(self, request):
        try:
            stores = [int(x) for x in request.GET.get('stores', '').split(',')]
            start_date = request.GET.get('start_date')
            end_date = request.GET.get('end_date')
            report_type = request.GET.get('report_type')
            if start_date:
                start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d')
                end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d') + datetime.timedelta(days=1)
            profit_map = {
                "IN_PROFIT": "IN_SELL",
                "OUT_PROFIT": "OUT_SELL",
                "TOTAL_PROFIT": "TOTAL_SELL",
            }

            qs = self.get_filter_objects(SpareOrder, store__in=stores, order_date__range=[start_date, end_date])

            if not qs:
                return Utils.dispatch_success(request, "DATA_NOT_FOUND")
            if report_type in self.sell_report_type:
                selling_total, total_items, total_spares = self.get_total(qs, report_type)
                response_data = {"selling_total": selling_total,
                                 "total_items": total_items,
                                 "total_spares": total_spares}
                return Utils.dispatch_success(request, response_data)

            if report_type in self.profit_report_type:
                buying_total, total_items, total_spares = self.get_total(qs, report_type)
                selling_total, total_items, total_spares = self.get_total(qs, profit_map[report_type])
                difference = selling_total - buying_total
                response_data = {"selling_total": selling_total,
                                 "buying_total": buying_total,
                                 "profit_total": abs(difference),
                                 "status": "LOSS" if difference < 0 else "PROFIT",
                                 "total_items": total_items,
                                 "total_spares": total_spares}
                return Utils.dispatch_success(request, response_data)
            return self.object_not_found(request)
        except Exception as e:
            return self.internal_server_error(request, e)

class UrgentSpareList(CustomBaseClass):
    def get(self, request, store_id, *args, **kwargs):
        """
        return list of urgent stock with pagination
        :param request:
        :param store_id:
        :param args:
        :param kwargs:
        :return:
        """
        try:
            qs = self.get_filter_objects(Spare, store=store_id, is_urgent_spare=True)
            page = request.GET.get('page', 1)
            paginator = Paginator(qs, per_page=10)
            serializer = serializers.SpareSerializer(paginator.page(page), many=True)
            response_data = {
                "data": serializer.data,
                "page": int(page),
                "total_pages": paginator.num_pages
            }
            return Utils.dispatch_success(request, response_data)
        except Exception as e:
            return self.internal_server_error(request, e)

    def put(self, request, store_id, *args, **kwargs):
        """
        Updates list of urgent stock with pagination
        :param request:
        {
        "spares":[{"id":23, "quantity": 20}, {"id":30, "quantity": 2}, {"id":11, "quantity": 12}, ]
        }
        :param store_id:
        :param args:
        :param kwargs:
        :return:
        """
        try:
            spares_list = request.data["spares"]
            for _spare in spares_list:
                spare = self.get_object(Spare, _spare["id"])
                spare.is_urgent_spare = False
                spare.quantity += _spare["quantity"]
                spare.save()
            return Utils.dispatch_success(request, 'SUCCESS')
        except Exception as e:
            return self.internal_server_error(request, e)

class SpareOrderEmailPdf(CustomBaseClass):
    def get(self, request, order_id, *args, **kwargs):
        """
        Returns PDF of the invoice or email's user
        :param request:
        @param action=email # to send email to customer
        @param action=download # to Download the invoice copy
        :param order_id:
        :param args:
        :param kwargs:
        :return:
        """
        try:
            action = request.GET.get('action')
            order = self.get_object(SpareOrder, order_id)
            data = {}
            store = order.store
            data['store'] = {
                'store_name': store.name.upper(),
                'store_branch': store.branch.upper(),
                'store_type': store.branch,
                'store_address': store.address.replace(',', '\n'),
                'store_phone': store.phone,
                'store_email': store.email,
                'store_website': store.website,
            }
            customer = order.customer
            data['customer'] = {
                'name': customer.name,
                'email': customer.email,
                'phone_number': customer.phone_number,
                'address': customer.address.replace(',', '\n')
            }
            data['order_id'] = order.order_id
            data['date'] = order.order_date.strftime('%d-%m-%Y %H:%M:%S')
            data['total'] = order.total
            data['type'] = order.order_type
            data['bike_number'] = order.bike_number
            data['labour_charge'] = order.labour_charge
            data['out_source_charge'] = order.out_source_charge
            data['sold_by'] = order.sold_by.first_name
            data['sub_total'] = order.total - order.labour_charge - order.out_source_charge
            response = {
                'csv': Utils.generate_csv,
                'xls': Utils.generate_xls,
                'pdf': Utils.generate_pdf
            }
            data['order'] = []
            for i, order_spare in enumerate(SpareSold.objects.filter(order=order)):
                data['order'].append([i + 1, order_spare.spare_name, order_spare.spare_price, order_spare.spare_count,
                                      order_spare.spare_price * order_spare.spare_count])
            dynamic_data = {
                'pdf_template': 'spare_invoice.html',
                'filename': f'Invoice_{order.order_id}',
                'data': data,
                'action': action
            }

            if action == 'download':
                return response.get('pdf')(**dynamic_data)
            elif action == 'email':
                if customer.email:
                    AppUtils.send_inovice_email(response.get('pdf')(**dynamic_data), data, f'Invoice_{order.order_id}' )
                    return Utils.dispatch_success(request, 'SUCCESS')
            return self.object_not_found(request)
        except Exception as e:
            return self.internal_server_error(request, e)


import cv2
from keras.models import load_model
import numpy as np

video_capture = cv2.VideoCapture(0)
font = cv2.FONT_HERSHEY_SIMPLEX

# 读取人脸haar模型
face_detection = cv2.CascadeClassifier('model/face_detection/haarcascade_frontalface_default.xml')

# 读取性别判断模型
gender_classifier = load_model('model/gender/simple_CNN.81-0.96.hdf5')

# 读取情绪判别模型
emotion_classifier = load_model('model/emotion/simple_CNN.530-0.65.hdf5')

gender_labels = {0: 'womam', 1: 'man'}
emotion_labels = {0: 'angry', 1: 'disgust', 2: 'fear', 3: 'happy',
                  4: 'sad', 5: 'surprise', 6: 'neutral'}

while True:
    # 读取摄像头的视频流
    _, frame = video_capture.read()

    # 将视频流转换成灰度
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 检测人脸，产生坐标值
    faces = face_detection.detectMultiScale(gray, 1.3, 5)

    for (x, y, w, h) in faces:
        # 将性别判断出来
        face = frame[(y - 60):(y + h + 60), (x - 30):(x + w + 30)]

        face = cv2.resize(face, (48, 48))
        face = np.expand_dims(face, 0)
        face = face / 255.0
        gender_label_arg = np.argmax(gender_classifier.predict(face))
        gender = gender_labels[gender_label_arg]

        if gender == gender_labels[0]:
            gender_color = (255, 0, 0)
        else:
            gender_color = (0, 255, 0)

        gray_face = gray[(y - 40):(y + h + 40), (x - 20):(x + w + 20)]
        gray_face = cv2.resize(gray_face, (48, 48))
        gray_face = gray_face / 255.0
        gray_face = np.expand_dims(gray_face, 0)
        gray_face = np.expand_dims(gray_face, -1)
        emotion_label_arg = np.argmax(emotion_classifier.predict(gray_face))
        emotion = emotion_labels[emotion_label_arg]

        cv2.rectangle(frame, (x, y), (x + w, y + h), gender_color, 2)
        cv2.putText(frame, gender, (x, y - 30), font, .7, gender_color, 1, cv2.LINE_AA)
        cv2.putText(frame, emotion, (x + 90, y - 30), font, .7, gender_color, 1, cv2.LINE_AA)
        cv2.imshow('face', frame)

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

# 销毁视频流
video_capture.release()
cv2.destroyAllWindows()


import io
from pathlib import Path

import magic
from django.conf import settings
from smb.smb_structs import OperationFailure
from smb.SMBConnection import SMBConnection

def factory():
    config = settings.SAMBA
    connection = SMBConnection(
        config["user"],
        config["password"],
        "abcd",
        config["server_name"],
        use_ntlm_v2=True,
    )
    connection.connect(config["server_ip"], int(config["port"]))
    return connection

def store_file_and_create_folders(conn, file_path, file_binary):
    file_path = Path(file_path)
    share = file_path.parts[1]
    folders = file_path.parts[2:-1]

    last_folder = "/"
    for folder in folders:
        last_folder += f"{folder}/"
        try:
            conn.createDirectory(share, last_folder)
        except OperationFailure:
            pass

    conn.storeFile(share, f'/{"/".join(file_path.parts[2:])}', file_binary)

def list_path(conn, path):
    path_parts = Path(path).parts
    share = path_parts[1]
    folder = "/".join(path_parts[2:])
    return conn.listPath(share, folder)

def find_file_mime_type(conn, path):
    path_parts = Path(path).parts
    share = path_parts[1]
    file_path = "/".join(path_parts[2:])
    # Read first 2048 bytes
    file_buffer = io.BytesIO()
    conn.retrieveFileFromOffset(share, file_path, file_buffer, max_length=2048)
    file_buffer.seek(0)

    return magic.from_buffer(file_buffer.read(), mime=True)

def retrieve_file(conn, path):
    path_parts = Path(path).parts
    share = path_parts[1]
    file_path = "/".join(path_parts[2:])
    file_buffer = io.BytesIO()
    conn.retrieveFile(share, file_path, file_buffer)
    file_buffer.seek(0)
    return file_buffer


from typing import Iterable, Iterator

from PIL import Image  # type: ignore

def resize_image_to_height(image: Image.Image, height: int) -> Image.Image:
    return image.resize(size=(int(image.width * (height / image.height)), height))

def concat_paired_images(
    left_image: Image.Image, right_image: Image.Image, mode: str
) -> Image.Image:
    dest_image_width = left_image.width + right_image.width
    dest_image_height = max(left_image.height, right_image.height)

    dest_image = Image.new(mode=mode, size=(dest_image_width, dest_image_height))
    dest_image.paste(im=left_image, box=(0, 0))
    dest_image.paste(im=right_image, box=(left_image.width, 0))

    return dest_image

def concat_images(images: Iterable[Image.Image], mode: str) -> Image.Image:
    images_itr = iter(images)

    return _concat_images(next(images_itr), images_itr, mode=mode)

def _concat_images(
    first_image: Image.Image, rest_images_itr: Iterator[Image.Image], mode: str
) -> Image.Image:
    try:
        return concat_paired_images(
            left_image=first_image,
            right_image=_concat_images(
                first_image=next(rest_images_itr),
                rest_images_itr=rest_images_itr,
                mode=mode,
            ),
            mode=mode,
        )
    except StopIteration:
        return first_image


import sys

for line in sys.stdin:
    # print(line)
    # break
    arr=[int(x) for x in line.strip().split()]
    # print(arr)
    if arr[0]==0:
        for c in range(arr[4]):
            print("%s\t%s" % ((arr[1], c, arr[2] ), arr[3]))
    if arr[0]==1:
        for a in range(arr[4]):
            print("%s\t%s" % ((a, arr[2], arr[1] ), arr[3]))


# 17124번
# 정수 배열 A와 B가 있다. A는 총 n개의 서로 다른 양의 정수를 포함하고 B는 총 m개의 서로 다른 양의 정수를 포함한다. A,B를 이용해서 길이가 n인 새로운 배열 C를 만들어보자.
# 1 : C[i]는 배열 B에 있는 값중 A[i에 가장 가까운 값(절대값 차이가 가장 작은값)으로 정의된다.
# 2 : 만약 이 조건을 만족하는 값들이 여럿 있는 경우, 그 중 가장 크기가 작은 값으로 정의된다.
# 예를 들어, ,A=[20,5,14,9] 그리고 B=[16,8,12]라고 해보자
# C[1] = 16이다 - 왜냐하면 B[1] = 16이 A[1]=20에 가장 가깝기 땨문이다. C[2] = 8 , C[3] = 12 - 16과 12 둘다 14에 가깝지만 작은 수 이기때문, ㅊ[4] = 8이다
# 입력 : 첫 줄에 테스트 케이스의 수 t ( 1<= t <= 10) 이 주어진다. 각 테스트 케이스는 세줄에 걸쳐서 주어진다.
#       첫 출에는 N과 M이 공백으로 구분되어 주어진다. (1<= N,M <= 10^6) / 두 번째 줄에는 공백으로 구분된 N개의 정수가 주어지며, A[1]부터 A[N]을 나타낸다 (1 ~ 10^9)
#        세 번째 줄에는 공백으로 구분된 M 개의 정수가 주어지며, B[1]부터 B[M]을 나타낸다 ( 1 ~ 10^9)
# 출력 : 각 테스트 케이스에 대해 배열 C를 구하고 해당 배열의 모든  원소 합을 한줄에 출력하시오.

def findindex(one, two, three):
    index = 2
    curr = three
    if two <= curr:
        index = 1
        curr = two
    if one <= curr:
        index = 0
    return index

def binarysearch(item, B, start, end):
    diff = end - start
    if diff <= 1:
        return start
    mid = (end + start) // 2
    if item > B[mid]:
        return binarysearch(item, B, mid, end)
    else:
        return binarysearch(item, B, start, mid)

t = int(input())

rst = []
for _ in range(t):
    n, m = map(int, input().split())
    A = [int(x) for x in input().split()]
    B = [int(x) for x in input().split()]
    cnt = 0
    B.sort()
    for i in range(n):
        item = A[i]
        idx = binarysearch(item, B, 0, m)
        one = abs(item - B[idx-1])
        two = abs(item - B[idx])
        three = abs(item - B[(idx+1) % m])
        index = findindex(one, two, three)
        cnt += B[idx - 1 + index]
    rst.append(cnt)

for i in range(t):
    print(rst[i])


#Lista de Exercício 1 - Questão 5
#Dupla: 2020314273 - Cauã Alexandre Torres de Holanda e 2021327294 - Kallyne Ferro Veiga
#Disciplina: Programação Web
#Professor: Ítalo Arruda

#5.Faça um Programa que converta metros para centímetros.

class ConversorMedidas:
    def __init__(self, valor):
        self.valor = valor
    def metros_para_centimetros(self):
        try:
            centimetros = self.valor * 100
            return centimetros
        except TypeError:
            return "Erro: O valor fornecido não é numérico."
def main():
    try:
        metros = float(input("Digite o valor em metros: "))
        conversor = ConversorMedidas(metros)
        resultado = conversor.metros_para_centimetros()
        print("O valor em centímetros é:", resultado)
    except ValueError:
        print("Erro: O valor fornecido não é válido.")
if __name__ == '__main__':
    main()


# pylint: disable=missing-docstring
"""This is a script to test the RecurrentEncoder module."""

import pickle

import pytest
import torch
import torch.nn as nn

from metarl.torch.embeddings import RecurrentEncoder

class TestRecurrentEncoder:
    """Test for RecurrentEncoder."""
    # yapf: disable
    @pytest.mark.parametrize(
        'input_dim, output_dim, hidden_sizes, num_tasks, num_seq', [
            (1, 1, (1, ), 1, 3),
            (3, 3, (3, ), 1, 5),
            (5, 5, (5, 5), 2, 4),
            (7, 7, (7, 5, 7), 2, 5),
            (9, 9, (9, 7, 5, 9), 3, 10),
        ])
    # yapf: enable
    def test_module(self, input_dim, output_dim, hidden_sizes, num_tasks,
                    num_seq):
        """Test forward method."""
        input_val = torch.ones((num_tasks, num_seq, input_dim),
                               dtype=torch.float32)
        # last hidden size should match output size
        # output_dim is latent_dim
        module = RecurrentEncoder(input_dim=input_dim,
                                  output_dim=output_dim,
                                  hidden_nonlinearity=None,
                                  hidden_sizes=hidden_sizes,
                                  hidden_w_init=nn.init.ones_,
                                  output_w_init=nn.init.ones_)
        module.reset(num_tasks=num_tasks)
        output = module(input_val)

        # maps input of shape (task, seq, input_dim) to (task, 1, output_dim)
        expected_shape = [num_tasks, 1, output_dim]

        assert all([a == b for a, b in zip(output.shape, expected_shape)])

    # yapf: disable
    @pytest.mark.parametrize(
        'input_dim, output_dim, hidden_sizes, num_tasks, num_seq', [
            (1, 1, (1, ), 1, 3),
            (3, 3, (3, ), 1, 5),
            (5, 5, (5, 5), 2, 4),
            (7, 7, (7, 5, 7), 2, 5),
            (9, 9, (9, 7, 5, 9), 3, 10),
        ])
    # yapf: enable
    def test_is_pickleable(self, input_dim, output_dim, hidden_sizes,
                           num_tasks, num_seq):
        """Test is_pickeable."""
        input_val = torch.ones((num_tasks, num_seq, input_dim),
                               dtype=torch.float32)
        module = RecurrentEncoder(input_dim=input_dim,
                                  output_dim=output_dim,
                                  hidden_nonlinearity=None,
                                  hidden_sizes=hidden_sizes,
                                  hidden_w_init=nn.init.ones_,
                                  output_w_init=nn.init.ones_)
        module.reset(num_tasks=num_tasks)
        output1 = module(input_val)

        h = pickle.dumps(module)
        module_pickled = pickle.loads(h)
        module_pickled.reset(num_tasks=num_tasks)
        output2 = module_pickled(input_val)

        assert torch.all(torch.eq(output1, output2))


import pygame
from settings import Settings
from pygame.sprite import Sprite

class Ship(Sprite):

	def __init__(self, ai_game):
		"""initialize the ship and set its starting position"""
		super().__init__()
		self.screen = ai_game.screen
		self.settings = ai_game.settings
		self.screen_rect = ai_game.screen.get_rect()

	#$load ship img and its rect
		self.image = pygame.image.load('images/ship.bmp')
		self.rect = self.image.get_rect()

		#start ship at bottom
		self.rect.midbottom = self.screen_rect.midbottom

		#store a decimal value for the ships position
		self.x = float(self.rect.x)

		#flag to control movement
		self.moving_right = False
		self.moving_left = False

	def update(self):
		"""updates position based on movement flag"""
		if self.moving_right and self.rect.right < self.screen_rect.right:
			self.x += self.settings.ship_speed
		if self.moving_left and self.rect.left > 0:
			self.x -= self.settings.ship_speed

		#update rect object from self.x
		self.rect.x = self.x

	def blitme(self):
		self.screen.blit(self.image, self.rect)

	def center_ship(self):
		"""center the ship on the screen"""
		self.rect.midbottom = self.screen_rect.midbottom
		self.x = float(self.rect.x)


from tree import Tree

#Tree tests complete

t = Tree(3)
print(t)
a = t.addNode(0,4)
b = t.addNode(0,5)
c = t.addNode(a,6)
d = t.addNode(a,7)
e = t.addNode(d,8)
print(t)
print("Path to e:")
p = t.pathToNode(e)
for i in p:
    print("\t",t.getData(i))
print("Path to c")
p = t.pathToNode(c)
for i in p:
    print("\t",t.getData(i))
print("Leaves:")

l = t.leaves()
for i in l:
    print(t.getData(i))


from datetime import datetime
from .setup import config, logger

def bibcodes():
    try:
        with open(config.get('CLASSIC_CANONICAL_FILE'), "r") as f:
            bibcodes = [line.strip() for line in f]
    except:
        logger.exception("Unable to retreive bibcodes from classic")
        return []
    else:
        return bibcodes

def compare(classic_bibcodes, db_bibcodes, solr_bibcodes):
    """Compare bibcode lists against classic"""
    results = {}
    batch = {}
    now = datetime.utcnow()
    prefix = "{:04}{:02}{:02}_{:02}{:02}".format(now.year, now.month, now.day, now.hour, now.minute)

    if len(classic_bibcodes) > 0:
        classic_bibcodes = set(classic_bibcodes)

        if len(db_bibcodes) > 0:
            db_bibcodes = set(db_bibcodes)
            extra_in_db = db_bibcodes.difference(classic_bibcodes)
            extra_in_db = [e for e in extra_in_db if "zndo" not in e] # Filter out non-classic Zenodo records
            missing_in_db = classic_bibcodes.difference(db_bibcodes)
            results['extra_in_db'] = len(extra_in_db)
            results['missing_in_db'] = len(missing_in_db)
        else:
            extra_in_db = set()
            missing_in_db = set()

        if len(solr_bibcodes) > 0:
            solr_bibcodes = set(solr_bibcodes)
            extra_in_solr = solr_bibcodes.difference(classic_bibcodes)
            extra_in_solr = [e for e in extra_in_solr if "zndo" not in e] # Filter out non-classic Zenodo records
            missing_in_solr = classic_bibcodes.difference(solr_bibcodes)
            results['extra_in_solr'] = len(extra_in_solr)
            results['missing_in_solr'] = len(missing_in_solr)
        else:
            extra_in_solr = set()
            missing_in_solr = set()

        batch.update({
            "{}_extra_in_db".format(prefix): extra_in_db,
            "{}_missing_in_db".format(prefix): missing_in_db,
            "{}_extra_in_solr".format(prefix): extra_in_solr,
            "{}_missing_in_solr".format(prefix): missing_in_solr,
        })

    return results, batch


from sklearn.base import BaseEstimator, TransformerMixin
import numpy as np
import sys

'''
The key concept in CSP is to find a set of spatial filters (components) that optimally discriminate
between the two classes. These filters are represented by the eigenvectors obtained in the 'fit' method.
When you apply the CSP transformation to a new data sample in the 'transform' method, it projects the data
onto these filters. The result is that the transformed data has enhanced features that maximize the
differences in variances between the two classes. This makes it easier to classify the data based on
the most discriminative spatial patterns.
'''

class CSPTransformer(BaseEstimator, TransformerMixin):
    def __init__(self, nb_components=4):
        '''
        In practice, it's common to try a range of values for the number of components (e.g., from 2 to 10)
        and evaluate their impact on classification accuracy.
        The choice of the optimal number of components may involve a trade-off between enhanced discriminative power
        and computational efficiency.
        '''
        self.nb_components = nb_components
        self.filters = np.array([])
        self.x = np.array([])
        self.y = np.array([])

    def fit(self, x, y):
        if self.x.size == 0:
            self.x = x
            self.y = y
        class_labels = np.unique(y)
        if len(class_labels) != 2:
            print("CSPTransformer: Error: CSP is a binary classification method: there should be two class labels.",
                  file=sys.stderr)
            exit()
        x_class1 = x[y == class_labels[0]] #take all values related to one class-label
        x_class2 = x[y == class_labels[1]]
        '''
        Get covariance matrices for each class.
        A covariance matrix is a square matrix giving the covariance between each element pair in a vector
        Covariance is the mean value of the product of the deviations of two variates from their respective means.
        A positive covariance indicates that both variates tend to be high or low at same time (similar direction)
        while a negative covariance indicates that if one variate is high the other will be low (opposite direction).
        '''
        cov1 = np.cov(x_class1, rowvar=False)
        cov2 = np.cov(x_class2, rowvar=False)
        '''
        Get the 'eigenvalues' and 'eigenvectors' by solving the 'generalized eigenvalue problem'
        The 'generalized eigenvalue problem' is a mathematical problem that arises in various fields
        and is an extendion of the 'standard eigenvalue problem'.
        In the 'standard eigenvalue problem', you are given a square matrix A, and you want to find
        scalars λ (eigenvalues) and corresponding vectors x (eigenvectors) that satisfy
        the equation: A * x = λ * x. The 'eigenvalues' represent the scaling factors, and the 'eigenvectors'
        represent the directions along which the matrix A scales or rotates.
        The 'generalized eigenvalue problem' extends the concept to two matrices, A and B. Given two square matrices
        A and B, you want to find scalars λ (generalized eigenvalues) and corresponding
        vectors x (generalized eigenvectors) that satisfy the equation: A * x = λ * B * x.
        Here we will use the 'eigenvectors' as transformation matrices that will maximize the variance of one class
        and minimize the variance of another class with the goal of maximizing the differences between the two classes.
        '''
        eigenvalues, eigenvectors = np.linalg.eig(np.dot(np.linalg.pinv(np.add(cov1, cov2)), cov1))
        '''
        Now we will sort eigenvalues and corresponding eigenvectors in descending order. Thus sort the eigenvalues and
        align the corresponding eigenvectors based on the eigenvalue magnitudes.
        This will allow us to select the first eigenvectors and use those as CSP filters. Because they come first
        they will be associated with the highest eigenvalue magnitudes and thus be the most discriminative.
        '''
        ascending_indices = np.argsort(eigenvalues)
        descending_indices = np.flip(ascending_indices)
        eigenvalues = eigenvalues[descending_indices]
        eigenvectors = eigenvectors[:, descending_indices] #reorder the columns (eigenvectors) of the eigenvectors matrix
        self.filters = eigenvectors[:, :self.nb_components].real.astype(np.float32)
        return self

    def partial_fit(self, x, y):
        self.x = np.concatenate((self.x, x), axis=0)
        self.y = np.concatenate((self.y, y), axis=0)
        return self.fit(self.x, self.y)

    def transform(self, x):
        if self.filters.size == 0:
            print("CSPTransformer: Error: use the 'fit' method to find the filters before using 'transform' method",
                  file=sys.stderr)
            exit()
        x_csp = np.dot(x, self.filters)
        return x_csp


#!/usr/bin/env python3

from subprocess import Popen, PIPE, STDOUT
from threading import Thread
from time import sleep
import logging
import os
import sys

# Very simple tee logic implementation. You can specify shell command, output
# logfile and env variables. After TeePopen is created you can only wait until
# it finishes. stderr and stdout will be redirected both to specified file and
# stdout.
class TeePopen:
    # pylint: disable=W0102
    def __init__(self, command, log_file, env=os.environ.copy(), timeout=None):
        self.command = command
        self.log_file = log_file
        self.env = env
        self.process = None
        self.timeout = timeout

    def _check_timeout(self):
        sleep(self.timeout)
        while self.process.poll() is None:
            logging.warning(
                "Killing process %s, timeout %s exceeded",
                self.process.pid,
                self.timeout,
            )
            os.killpg(self.process.pid, 9)
            sleep(10)

    def __enter__(self):
        self.process = Popen(
            self.command,
            shell=True,
            universal_newlines=True,
            env=self.env,
            start_new_session=True,  # signall will be sent to all children
            stderr=STDOUT,
            stdout=PIPE,
            bufsize=1,
        )
        self.log_file = open(self.log_file, "w", encoding="utf-8")
        if self.timeout is not None and self.timeout > 0:
            t = Thread(target=self._check_timeout)
            t.daemon = True  # does not block the program from exit
            t.start()
        return self

    def __exit__(self, t, value, traceback):
        for line in self.process.stdout:
            sys.stdout.write(line)
            self.log_file.write(line)

        self.process.wait()
        self.log_file.close()

    def wait(self):
        for line in self.process.stdout:
            sys.stdout.write(line)
            self.log_file.write(line)

        return self.process.wait()


"""PDB dataset loader."""
import tree
import numpy as np
import torch
import pandas as pd
import logging
import random
import functools as fn

from torch.utils import data
from data import utils as du
from openfold.data import data_transforms
from openfold.np import residue_constants
from openfold.utils import rigid_utils

class PdbDataset(data.Dataset):
    def __init__(
            self,
            *,
            data_conf,
            diffuser,
            is_training,
        ):
        self._log = logging.getLogger(__name__)
        self._is_training = is_training
        self._data_conf = data_conf
        self._init_metadata()
        self._diffuser = diffuser

    @property
    def is_training(self):
        return self._is_training

    @property
    def diffuser(self):
        return self._diffuser

    @property
    def data_conf(self):
        return self._data_conf

    def _init_metadata(self):
        """Initialize metadata."""

        # Process CSV with different filtering criterions.
        filter_conf = self.data_conf.filtering
        pdb_csv = pd.read_csv(self.data_conf.csv_path)
        self.raw_csv = pdb_csv
        if filter_conf.allowed_oligomer is not None and len(filter_conf.allowed_oligomer) > 0:
            pdb_csv = pdb_csv[pdb_csv.oligomeric_detail.isin(
                filter_conf.allowed_oligomer)]
        if filter_conf.max_len is not None:
            pdb_csv = pdb_csv[pdb_csv.modeled_seq_len <= filter_conf.max_len]
        if filter_conf.min_len is not None:
            pdb_csv = pdb_csv[pdb_csv.modeled_seq_len >= filter_conf.min_len]
        if filter_conf.max_helix_percent is not None:
            pdb_csv = pdb_csv[
                pdb_csv.helix_percent < filter_conf.max_helix_percent]
        if filter_conf.max_loop_percent is not None:
            pdb_csv = pdb_csv[
                pdb_csv.coil_percent < filter_conf.max_loop_percent]
        if filter_conf.min_beta_percent is not None:
            pdb_csv = pdb_csv[
                pdb_csv.strand_percent > filter_conf.min_beta_percent]
        if filter_conf.subset is not None:
            pdb_csv = pdb_csv[:filter_conf.subset]
        pdb_csv = pdb_csv.sort_values('modeled_seq_len', ascending=False)
        self._create_split(pdb_csv)

    def _create_split(self, pdb_csv):
        # Training or validation specific logic.
        if self.is_training:
            self.csv = pdb_csv
            self._log.info(
                f'Training: {len(self.csv)} examples')
        else:
            all_lengths = np.sort(pdb_csv.modeled_seq_len.unique())
            length_indices = (len(all_lengths) - 1) * np.linspace(
                0.0, 1.0, self._data_conf.num_eval_lengths)
            length_indices = length_indices.astype(int)
            eval_lengths = all_lengths[length_indices]
            eval_csv = pdb_csv[pdb_csv.modeled_seq_len.isin(eval_lengths)]
            # Fix a random seed to get the same split each time.
            eval_csv = eval_csv.groupby('modeled_seq_len').sample(
                self._data_conf.samples_per_eval_length, replace=True, random_state=123)
            eval_csv = eval_csv.sort_values('modeled_seq_len', ascending=False)
            self.csv = eval_csv
            self._log.info(
                f'Validation: {len(self.csv)} examples with lengths {eval_lengths}')

    @fn.lru_cache(maxsize=50000)
    def _process_csv_row(self, processed_file_path):
        processed_feats = du.read_pkl(processed_file_path)
        processed_feats = du.parse_chain_feats(processed_feats)

        # Only take modeled residues.
        modeled_idx = processed_feats['modeled_idx']
        min_idx = np.min(modeled_idx)
        max_idx = np.max(modeled_idx)
        del processed_feats['modeled_idx']
        processed_feats = tree.map_structure(
            lambda x: x[min_idx:(max_idx+1)], processed_feats)

        # Run through OpenFold data transforms.
        chain_feats = {
            'aatype': torch.tensor(processed_feats['aatype']).long(),
            'all_atom_positions': torch.tensor(processed_feats['atom_positions']).double(),
            'all_atom_mask': torch.tensor(processed_feats['atom_mask']).double()
        }
        chain_feats = data_transforms.atom37_to_frames(chain_feats)
        chain_feats = data_transforms.make_atom14_masks(chain_feats)
        chain_feats = data_transforms.make_atom14_positions(chain_feats)
        chain_feats = data_transforms.atom37_to_torsion_angles()(chain_feats)

        # Re-number residue indices for each chain such that it starts from 1.
        # Randomize chain indices.
        chain_idx = processed_feats["chain_index"]
        res_idx = processed_feats['residue_index']
        new_res_idx = np.zeros_like(res_idx)
        new_chain_idx = np.zeros_like(res_idx)
        all_chain_idx = np.unique(chain_idx).tolist()
        shuffled_chain_idx = np.array(
            random.sample(all_chain_idx, len(all_chain_idx))) - np.min(all_chain_idx) + 1
        for i,chain_id in enumerate(all_chain_idx):
            chain_mask = (chain_idx == chain_id).astype(np.int)
            chain_min_idx = np.min(res_idx + (1 - chain_mask) * 1e3).astype(np.int)
            new_res_idx = new_res_idx + (res_idx - chain_min_idx + 1) * chain_mask

            # Shuffle chain_index
            replacement_chain_id = shuffled_chain_idx[i]
            new_chain_idx = new_chain_idx + replacement_chain_id * chain_mask

        # To speed up processing, only take necessary features
        final_feats = {
            'aatype': chain_feats['aatype'],
            'seq_idx': new_res_idx,
            'chain_idx': chain_idx,
            'residx_atom14_to_atom37': chain_feats['residx_atom14_to_atom37'],
            'residue_index': processed_feats['residue_index'],
            'res_mask': processed_feats['bb_mask'],
            'atom37_pos': chain_feats['all_atom_positions'],
            'atom37_mask': chain_feats['all_atom_mask'],
            'atom14_pos': chain_feats['atom14_gt_positions'],
            'rigidgroups_0': chain_feats['rigidgroups_gt_frames'],
            'torsion_angles_sin_cos': chain_feats['torsion_angles_sin_cos'],
        }
        return final_feats

    def _create_diffused_masks(self, atom37_pos, rng, row):
        bb_pos = atom37_pos[:, residue_constants.atom_order['CA']]
        dist2d = np.linalg.norm(bb_pos[:, None, :] - bb_pos[None, :, :], axis=-1)

        # Randomly select residue then sample a distance cutoff
        # TODO: Use a more robust diffuse mask sampling method.
        diff_mask = np.zeros_like(bb_pos)
        attempts = 0
        while np.sum(diff_mask) < 1:
            crop_seed = rng.integers(dist2d.shape[0])
            seed_dists = dist2d[crop_seed]
            max_scaffold_size = min(
                self._data_conf.scaffold_size_max,
                seed_dists.shape[0] - self._data_conf.motif_size_min
            )
            scaffold_size = rng.integers(
                low=self._data_conf.scaffold_size_min,
                high=max_scaffold_size
            )
            dist_cutoff = np.sort(seed_dists)[scaffold_size]
            diff_mask = (seed_dists < dist_cutoff).astype(float)
            attempts += 1
            if attempts > 100:
                raise ValueError(
                    f'Unable to generate diffusion mask for {row}')
        return diff_mask

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

    def __getitem__(self, idx):

        # Sample data example.
        example_idx = idx
        csv_row = self.csv.iloc[example_idx]
        if 'pdb_name' in csv_row:
            pdb_name = csv_row['pdb_name']
        elif 'chain_name' in csv_row:
            pdb_name = csv_row['chain_name']
        else:
            raise ValueError('Need chain identifier.')
        processed_file_path = csv_row['processed_path']
        chain_feats = self._process_csv_row(processed_file_path)

        # Use a fixed seed for evaluation.
        if self.is_training:
            rng = np.random.default_rng(None)
        else:
            rng = np.random.default_rng(idx)

        gt_bb_rigid = rigid_utils.Rigid.from_tensor_4x4(
            chain_feats['rigidgroups_0'])[:, 0]
        diffused_mask = np.ones_like(chain_feats['res_mask'])
        if np.sum(diffused_mask) < 1:
            raise ValueError('Must be diffused')
        fixed_mask = 1 - diffused_mask
        chain_feats['fixed_mask'] = fixed_mask
        chain_feats['rigids_0'] = gt_bb_rigid.to_tensor_7()
        chain_feats['sc_ca_t'] = torch.zeros_like(gt_bb_rigid.get_trans())

        # Sample t and diffuse.
        if self.is_training:
            t = rng.uniform(self._data_conf.min_t, 1.0)
            diff_feats_t = self._diffuser.forward_marginal(
                rigids_0=gt_bb_rigid,
                t=t,
                diffuse_mask=None
            )
        else:
            t = 1.0
            diff_feats_t = self.diffuser.sample_ref(
                n_samples=gt_bb_rigid.shape[0],
                impute=gt_bb_rigid,
                diffuse_mask=None,
                as_tensor_7=True,
            )
        chain_feats.update(diff_feats_t)
        chain_feats['t'] = t

        # Convert all features to tensors.
        final_feats = tree.map_structure(
            lambda x: x if torch.is_tensor(x) else torch.tensor(x), chain_feats)
        final_feats = du.pad_feats(final_feats, csv_row['modeled_seq_len'])
        if self.is_training:
            return final_feats
        else:
            return final_feats, pdb_name

class LengthSampler(data.Sampler):

    def __init__(
            self,
            *,
            data_conf,
            dataset,
        ):
        self._data_conf = data_conf
        self._dataset = dataset
        self._data_csv = self._dataset.csv

    def __iter__(self):
        return iter(range(len(self._data_csv)))

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

class TrainSampler(data.Sampler):

    def __init__(
            self,
            *,
            data_conf,
            dataset,
            batch_size,
        ):
        self._data_conf = data_conf
        self._dataset = dataset
        self._data_csv = self._dataset.csv
        self._dataset_indices = list(range(len(self._data_csv)))
        self._data_csv['index'] = self._dataset_indices
        self._batch_size = batch_size
        self.epoch = 0

    def __iter__(self):
        random.shuffle(self._dataset_indices)
        repeated_indices = np.repeat(self._dataset_indices, self._batch_size)
        return iter(repeated_indices)

    def set_epoch(self, epoch):
        self.epoch = epoch

    def __len__(self):
        return len(self._dataset_indices) * self._batch_size


import unittest

from src.entities.event import Event
from src.repos.in_memory.data_store import DataStore
from src.repos.in_memory.in_memory_event_repo import InMemoryEventRepo

class TestInMemoryEventRepo(unittest.TestCase):
    def setUp(self):
        data_store = DataStore()
        self.event_repo = InMemoryEventRepo(data_store)
        self.game_id = 1
        self.events = [Event(None, self.game_id, f'event{i}', i, False)
                       for i in range(11)]
        self.event_repo.create_events(self.game_id,
                                      list(map(lambda e: e.desc, self.events)))

    def assertEventsEqual(self, e1: Event, e2: Event):
        self.assertEqual(e1.desc, e2.desc)
        self.assertEqual(e1.index, e2.index)
        self.assertEqual(e1.is_hit, e2.is_hit)

    def test_read_all(self):
        events = self.event_repo.read_all_events(self.game_id)
        for e_write, e_read in zip(self.events, events):
            self.assertEventsEqual(e_write, e_read)

    def test_update_event(self):
        new_event = self.events[0]
        new_event.desc += 'new'
        new_event.is_hit = True
        self.event_repo.update_event(self.game_id, new_event)
        events = self.event_repo.read_all_events(self.game_id)
        self.assertEventsEqual(
            sorted(events, key=lambda event: event.index)[new_event.index],
            new_event)