import numpy as np
#import pandas as pd
import matplotlib.pyplot as plt
import argparse, sys
import joblib
import warnings
warnings.filterwarnings('ignore')

import torch
import torch.nn as nn
from torch.autograd import Variable
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import Dataset, DataLoader
import torch.nn.functional as F
import torch.backends.cudnn as cudnn
cudnn.benchmark = True

from sklearn.metrics import roc_curve, auc, f1_score, precision_recall_curve, average_precision_score, ConfusionMatrixDisplay

from medmnistutils.evaluationmetrics import accuracy, roc, presenf1cfsmtx
from medmnistutils.medmnistdataloader import PathMNIST, OrganMNIST3D, PneumoniaMNIST, VesselMNIST3D, OCTMNIST

#from medmnistutils.jiaodaresnet import ResNet18 as jiaodaresnet18
#from nets.unknownthreedresnet import resnet18
from medmnistutils.blingblingresnet import resnet18 as blingblingresnet18
from medmnistutils.O2Uzidairesnet import ResNet18 as O2Uresnet18
from medmnistutils.yixianresnet import resnet18 as yixian3dresnet18

parser = argparse.ArgumentParser()
parser.add_argument('--dataset', type=str, default='OCTMNIST', help='PathMNIST, OCTMNIST, PneumoniaMNIST, OrganMNIST3D, VesselMNIST3D')
parser.add_argument('--noise_rate', type=float, default=0.4, help='noise rate')
parser.add_argument('--batchsize', type=int, default=128, help='128')
parser.add_argument('--num_epochs', type=int, default=200, help='number of epochs')

#args = parser.parse_args(args=[])
args = parser.parse_args()

if args.dataset =='PathMNIST':    #2D, 9 classes, 89,996 / 10,004 / 7,180
    newtransform = transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=[.5], std=[.5])])
    train_dataset = PathMNIST(split = 'train', root = '../../medmnistdata',  transform=newtransform, noise_rate=args.noise_rate)
    val_dataset = PathMNIST(split = 'val', root = '../../medmnistdata',  transform=newtransform)
    test_dataset = PathMNIST(split = 'test', root = '../../medmnistdata',  transform=newtransform)
    train_loader = DataLoader(train_dataset, batch_size=args.batchsize, drop_last = False, shuffle=True)
    val_loader = DataLoader(val_dataset, batch_size=args.batchsize,  drop_last = False, shuffle=True)
    test_loader = DataLoader(test_dataset, batch_size=args.batchsize, drop_last = False, shuffle=True)
    model = O2Uresnet18(input_channel=train_dataset.in_channels, n_outputs=train_dataset.num_classes)
    #model = blingblingresnet18(num_classes=train_dataset.num_classes)

if args.dataset =='OCTMNIST':   #2D, 4 classes,
    newtransform = transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=[.5], std=[.5])])
    train_dataset = OCTMNIST(split = 'train', root = '../../medmnistdata',  transform=newtransform, noise_rate=args.noise_rate)
    val_dataset = OCTMNIST(split = 'val', root = '../../medmnistdata',  transform=newtransform)
    test_dataset = OCTMNIST(split = 'test', root = '../../medmnistdata',  transform=newtransform)
    train_loader = DataLoader(train_dataset, batch_size=args.batchsize, drop_last = False, shuffle=True)
    val_loader = DataLoader(val_dataset, batch_size=args.batchsize,  drop_last = False, shuffle=True)
    test_loader = DataLoader(test_dataset, batch_size=args.batchsize, drop_last = False, shuffle=True)
    model = O2Uresnet18(input_channel=train_dataset.in_channels, n_outputs=train_dataset.num_classes)
    #model = blingblingresnet18(num_classes=train_dataset.num_classes)

elif args.dataset =='PneumoniaMNIST':  #2D, 2 class, 4,708 / 524 / 624
    newtransform = transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=[.5], std=[.5])])
    train_dataset = PneumoniaMNIST(split = 'train', root = '../../medmnistdata',  transform=newtransform, noise_rate=args.noise_rate)
    val_dataset = PneumoniaMNIST(split = 'val', root = '../../medmnistdata',  transform=newtransform)
    test_dataset = PneumoniaMNIST(split = 'test', root = '../../medmnistdata',  transform=newtransform)
    train_loader = DataLoader(train_dataset, batch_size=args.batchsize, drop_last = False, shuffle=True)
    val_loader = DataLoader(val_dataset, batch_size=args.batchsize,  drop_last = False, shuffle=True)
    test_loader = DataLoader(test_dataset, batch_size=args.batchsize, drop_last = False, shuffle=True)
    model = O2Uresnet18(input_channel=train_dataset.in_channels, n_outputs=train_dataset.num_classes)
    #model = blingblingresnet18(num_classes=train_dataset.num_classes)

elif args.dataset =='OrganMNIST3D':   #3D, 11 class, 972 / 161 / 610
    train_dataset = OrganMNIST3D(split = 'train', root = '../../medmnistdata',  transform=None, noise_rate=args.noise_rate)
    val_dataset = OrganMNIST3D(split = 'val', root = '../../medmnistdata',  transform=None)
    test_dataset = OrganMNIST3D(split = 'test', root = '../../medmnistdata',  transform=None)
    train_loader = DataLoader(train_dataset, batch_size=args.batchsize, drop_last = False, shuffle=True)
    val_loader = DataLoader(val_dataset, batch_size=args.batchsize,  drop_last = False, shuffle=True)
    test_loader = DataLoader(test_dataset, batch_size=args.batchsize, drop_last = False, shuffle=True)
    model = yixian3dresnet18(num_classes = train_dataset.num_classes)

elif args.dataset =='VesselMNIST3D':    #3D, 2 class, 1,335 / 192 / 382
    train_dataset = VesselMNIST3D(split = 'train', root = '../../medmnistdata',  transform=None, noise_rate=args.noise_rate)
    val_dataset = VesselMNIST3D(split = 'val', root = '../../medmnistdata',  transform=None)
    test_dataset = VesselMNIST3D(split = 'test', root = '../../medmnistdata',  transform=None)
    train_loader = DataLoader(train_dataset, batch_size=args.batchsize, drop_last = False, shuffle=True)
    val_loader = DataLoader(val_dataset, batch_size=args.batchsize,  drop_last = False, shuffle=True)
    test_loader = DataLoader(test_dataset, batch_size=args.batchsize, drop_last = False, shuffle=True)
    model = yixian3dresnet18(num_classes = train_dataset.num_classes)

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)

error = nn.CrossEntropyLoss()
learning_rate = 0.001
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

###############################################################################
验证准确率列表 = []
测试准确率列表= []
###############################################################################

#main loop
for epoch in range(args.num_epochs):

    #train
    model.train()
    for images, labels, _ in train_loader:
        images, labels = images.to(device), labels.to(device)
        labels = labels.squeeze().long()
        outputs = model(images)
        loss = error(outputs, labels)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    #evaluation
    valaccuracy = accuracy(model, val_loader)
    testaccuracy = accuracy(model, test_loader)
    print('epoch', epoch+1, 'val accuracy', valaccuracy, 'test accuracy', testaccuracy)

###############################################################################
#以下都是不需要的
###############################################################################
    验证准确率列表.append(valaccuracy)
    测试准确率列表.append(testaccuracy)

实验名 = '20230924baselineexp1'
resultdict = dict()

#模型
resultdict['model'] = model

#acc变化图
resultdict['valacclist'] = 验证准确率列表
resultdict['testacclist'] = 测试准确率列表
验证准确率列表 = [x*100 for x in 验证准确率列表]
测试准确率列表 = [x*100 for x in 测试准确率列表]
plt.plot(验证准确率列表, label = 'validation set')
plt.plot(测试准确率列表, label = 'test set')
plt.xlim((0,200))
plt.ylim((0,100))
#plt.title('origingal method on ' + args.dataset + ' under noise rate ' + str(args.noise_rate))
plt.xlabel('Epoch')
plt.ylabel('Accuracy (%)')
acc变化图文件名 = 实验名 + '_acccurve_' + args.dataset + '_' + str(args.noise_rate) + '.png'
plt.legend()
plt.savefig(acc变化图文件名)
plt.show()

#ROC曲线图
resultdict['valfprdict'], resultdict['valtprdict'], resultdict['valaucdict'] = roc(model, val_loader)
resultdict['testfprdict'], resultdict['testtprdict'], resultdict['testaucdict'] = roc(model, test_loader)

plt.plot(resultdict['valfprdict']["micro"], resultdict['valtprdict']["micro"],
         label='validation set, AUC ' + str(round(100*resultdict['valaucdict']["micro"],2)))
plt.plot(resultdict['testfprdict']["micro"], resultdict['testtprdict']["micro"],
         label='test set, AUC ' + str(round(100*resultdict['testaucdict']["micro"],2)))
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend(loc="lower right")
ROC文件名 = 实验名 + '_roccurve_' + args.dataset + '_' + str(args.noise_rate) + '.png'
plt.savefig(ROC文件名)
plt.show()

#confusion matrix图
resultdict['valprecision'], resultdict['valrecall'], resultdict['valf1'], resultdict['valtruelist'], resultdict['valpredlist'], resultdict['valcfsmtx'] = presenf1cfsmtx(model, val_loader)
ConfusionMatrixDisplay.from_predictions(resultdict['valtruelist'], resultdict['valpredlist'], cmap = plt.cm.Blues, colorbar = False)
cfsmtx文件名 = 实验名 + '_valconfusionmatrix_' + args.dataset + '_' + str(args.noise_rate) + '.png'
plt.savefig(cfsmtx文件名)
plt.show()
resultdict['testprecision'], resultdict['testrecall'], resultdict['testf1'], resultdict['testtruelist'], resultdict['testpredlist'], resultdict['testcfsmtx'] = presenf1cfsmtx(model, test_loader)
ConfusionMatrixDisplay.from_predictions(resultdict['testtruelist'], resultdict['testpredlist'], cmap = plt.cm.Blues, colorbar = False)
cfsmtx文件名 = 实验名 + '_testconfusionmatrix_' + args.dataset + '_' + str(args.noise_rate) + '.png'
plt.savefig(cfsmtx文件名)
plt.show()

#txt
txt文件名 = 实验名 + '_txt_' + args.dataset + '_' + str(args.noise_rate) + '.txt'
with open (txt文件名, 'a', encoding='utf-8') as txt:
    txt.write('最后一轮acc' + "\n" )
    txt.write(str(round(验证准确率列表[-1],2)) + "\n" )
    txt.write(str(round(测试准确率列表[-1],2)) + "\n" )
    txt.write('最后十轮acc平均' + "\n" )
    txt.write(str(round(sum(验证准确率列表[-11:-1])/len(验证准确率列表[-11:-1]),2)) + "\n" )
    txt.write(str(round(sum(测试准确率列表[-11:-1])/len(验证准确率列表[-11:-1]),2)) + "\n" )
    txt.write('precision' + "\n" )
    txt.write(str(round(100*resultdict['valprecision'],2)) + "\n" )
    txt.write(str(round(100*resultdict['testprecision'],2)) + "\n" )
    txt.write('recall' + "\n" )
    txt.write(str(round(100*resultdict['valrecall'],2)) + "\n" )
    txt.write(str(round(100*resultdict['testrecall'],2)) + "\n" )
    txt.write('f1' + "\n" )
    txt.write(str(round(100*resultdict['valf1'],2)) + "\n" )
    txt.write(str(round(100*resultdict['testf1'],2)) + "\n" )

#保存整个文件
resultdict文件名 = 实验名 + '_resultdict_' + args.dataset + '_' + str(args.noise_rate)
joblib.dump(resultdict, resultdict文件名)


"""
Run the model end to end
"""
import argparse
import sys
import torch
from pathlib import Path

import pytorch_lightning as pl
from pytorch_lightning.callbacks.early_stopping import EarlyStopping
from pytorch_lightning.callbacks.model_checkpoint import ModelCheckpoint

from smallteacher.data import DataModule, train_augmentations
from smallteacher.models import FullySupervised, SemiSupervised
from smallteacher.constants import Metrics
from smallteacher.config import BEST_MODEL_NAME

from smallssd.data import LabelledData, UnlabelledData
from smallssd.config import DATAFOLDER_PATH
from smallssd.keys import CLASSNAME_TO_IDX

import mlflow
import mlflow.pytorch

def parse_args(args):
    """Parse the arguments."""
    parser = argparse.ArgumentParser(
        description="Simple training script for training a pytorch lightning model."
    )

    parser.add_argument(
        "--model",
        help="Chooses model architecture",
        type=str,
        default="FRCNN",
        choices=["FRCNN", "RetinaNet", "SSD"],
    )
    parser.add_argument(
        "--workers", help="Number of dataloader workers", type=int, default="1"
    )

    parser.add_argument(
        "--mlflow_experiment", type=str, default="pytorch_lightning_experiment"
    )
    parser.add_argument("--seed", type=int, default="42")

    return parser.parse_args(args)

def get_checkpoint(version: int) -> Path:
    return list(
        Path(f"lightning_logs/version_{version}/checkpoints").glob("best_model*.ckpt")
    )[0]

def train_fully_supervised(datamodule, model_name) -> int:
    model = FullySupervised(
        model_base=model_name,
        num_classes=len(CLASSNAME_TO_IDX),
    )
    fully_supervised_trainer = pl.Trainer(
        callbacks=[
            EarlyStopping(monitor=Metrics.MAP, mode="max", patience=10),
            ModelCheckpoint(filename=BEST_MODEL_NAME, monitor=Metrics.MAP, mode="max"),
        ],
        gpus=torch.cuda.device_count(),
    )
    fully_supervised_trainer.fit(model, datamodule=datamodule)

    best_model = FullySupervised.load_from_checkpoint(
        get_checkpoint(fully_supervised_trainer.logger.version),
        model_base=model_name,
        num_classes=len(CLASSNAME_TO_IDX),
    )
    fully_supervised_trainer.test(best_model, datamodule=datamodule)

    return fully_supervised_trainer.logger.version

def train_teacher_student(datamodule, model_name, model_checkpoint) -> int:
    unlabelled_ds = UnlabelledData(root=DATAFOLDER_PATH)
    datamodule.add_unlabelled_training_dataset(unlabelled_ds)

    org_model = FullySupervised.load_from_checkpoint(
        model_checkpoint,
        model_base=model_name,
        num_classes=len(CLASSNAME_TO_IDX),
    )
    model = SemiSupervised(
        trained_model=org_model.model,
        model_base=model_name,
        num_classes=len(CLASSNAME_TO_IDX),
    )

    trainer = pl.Trainer(
        gpus=torch.cuda.device_count(),
        callbacks=[
            EarlyStopping(monitor=Metrics.MAP, mode="max", patience=10),
            ModelCheckpoint(filename=BEST_MODEL_NAME, monitor=Metrics.MAP, mode="max"),
        ],
    )

    trainer.fit(model, datamodule=datamodule)
    best_model = SemiSupervised.load_from_checkpoint(
        get_checkpoint(trainer.logger.version),
        model_base=model_name,
        num_classes=len(CLASSNAME_TO_IDX),
    )
    trainer.test(best_model, datamodule=datamodule)
    return best_model

def main(args=None):
    if args is None:
        args = sys.argv[1:]
    args = parse_args(args)

    mlflow.set_experiment(experiment_name=args.mlflow_experiment)
    pl.seed_everything(args.seed)

    datamodule = DataModule(
        *LabelledData(root=DATAFOLDER_PATH, eval=False).split(
            transforms=[train_augmentations, None]
        ),
        test_dataset=LabelledData(root=DATAFOLDER_PATH, eval=True),
        num_workers=args.workers,
    )

    mlflow.pytorch.autolog()
    with mlflow.start_run(run_name=f"{args.model}_fully_supervised"):
        version_id = train_fully_supervised(datamodule, args.model)
        best_model_checkpoint = get_checkpoint(version_id)
    with mlflow.start_run(run_name=f"{args.model}_teacher_student"):
        best_model = train_teacher_student(
            datamodule, args.model, best_model_checkpoint
        )
        mlflow.pytorch.log_model(best_model.model, artifact_path="model")

if __name__ == "__main__":
    main()


import sys
from math import sqrt
from itertools import compress

# 利用byte求质数
def get_primes_3(n):
    """ Returns  a list of primes < n for n > 2 """
    sieve = bytearray([True]) * (n // 2)
    for i in range(3, int(n ** 0.5) + 1, 2):
        if sieve[i // 2]:
            sieve[i * i // 2::i] = bytearray((n - i * i - 1) // (2 * i) + 1)

    return [2, *compress(range(3, n, 2), sieve[1:])]

def is_prime(n):
    # Only used to test odd numbers.
    return all(n % d for d in range(3, round(sqrt(n)) + 1, 2))

def f(a, b):
    '''
    Won't be tested for b greater than 10_000_000

    >>> f(3, 3)
    The number of prime numbers between 3 and 3 included is 1
    >>> f(4, 4)
    The number of prime numbers between 4 and 4 included is 0
    >>> f(2, 5)
    The number of prime numbers between 2 and 5 included is 3
    >>> f(2, 10)
    The number of prime numbers between 2 and 10 included is 4
    >>> f(2, 11)
    The number of prime numbers between 2 and 11 included is 5
    >>> f(1234, 567890)
    The number of prime numbers between 1234 and 567890 included is 46457
    >>> f(89, 5678901)
    The number of prime numbers between 89 and 5678901 included is 392201
    >>> f(89, 5678901)
    The number of prime numbers between 89 and 5678901 included is 392201
    '''
    count = 0
    for i in range(a,b+1):
        if is_prime(i):
            count+=1

    less_a_primes = get_primes_3(a + 1)
    less_b_primes = get_primes_3(b + 1)
    for item in less_a_primes:
        if item < a:
            less_b_primes.remove(item)

    count = len(less_b_primes)

    print(f'The number of prime numbers between {a} and {b} included is {count}')

if __name__ == '__main__':
    import doctest
    doctest.testmod()


from collections import namedtuple
from functools import partial
from itertools import count, groupby, zip_longest
import bpy
import numpy as np
import re

from .log import log, logd
from .helpers import (
    ensure_iterable,
    get_context,
    get_data_collection,
    get_layers_recursive,
    load_property,
    reshape,
    save_property,
    select_only,
    swap_names,
    titlecase,
)

logs = partial(log, category="SAVE")
custom_prop_pattern = re.compile(r'(.+)?\["([^"]+)"\]')
prop_pattern = re.compile(r'(?:(.+)\.)?([^"\.]+)')

class GRET_OT_property_warning(bpy.types.Operator):
    """Changes won't be saved"""

    bl_idname = 'gret.property_warning'
    bl_label = "Not Overridable"
    bl_options = {'INTERNAL'}

def draw_warning_if_not_overridable(layout, bid, data_path):
    """Adds a warning to a layout if the requested property is not available or not overridable."""

    if bid and bid.override_library:
        try:
            if not bid.is_property_overridable_library(data_path):
                layout.operator(GRET_OT_property_warning.bl_idname,
                    icon='ERROR', text="", emboss=False, depress=True)
                return True
        except TypeError:
            pass
    return False

class PropertyWrapper(namedtuple('PropertyWrapper', 'struct prop_name is_custom')):
    """Provides read/write access to a property given its data path."""

    __slots__ = ()

    @classmethod
    def from_path(cls, struct, data_path):
        # To set a property given a data path it's necessary to split the struct and attribute name.
        # `struct.path_resolve(path, False)` returns a bpy_prop, and bpy_prop.data holds the struct.
        # Unfortunately it knows but doesn't expose the attribute name (see `bpy_prop.__str__`)
        # It's also necessary to determine if it's a custom property, the interface is different.
        # Just parse the data path with a regular expression instead.
        try:
            prop_match = custom_prop_pattern.fullmatch(data_path)
            if prop_match:
                if prop_match[1]:
                    struct = struct.path_resolve(prop_match[1])
                prop_name = prop_match[2]
                if prop_name not in struct:
                    return None
                return cls(struct, prop_name, True)

            prop_match = prop_pattern.fullmatch(data_path)
            if prop_match:
                if prop_match[1]:
                    struct = struct.path_resolve(prop_match[1])
                prop_name = prop_match[2]
                if not hasattr(struct, prop_name):
                    return None
                return cls(struct, prop_name, False)
        except ValueError:
            return None

    @property
    def data_path(self):
        return f'["{self.prop_name}"]' if self.is_custom else self.prop_name

    @property
    def title(self):
        if self.is_custom:
            return titlecase(self.prop_name)  # Custom property name should be descriptive enough
        else:
            return f"{getattr(self.struct, 'name', self.struct.bl_rna.name)} {titlecase(self.prop_name)}"

    @property
    def default_value(self):
        if self.is_custom:
            return self.struct.id_properties_ui(self.prop_name).as_dict()['default']
        else:
            prop = self.struct.bl_rna.properties[self.prop_name]
            if getattr(prop, 'is_array', False):
                return reshape(prop.default_array, prop.array_dimensions)
            return getattr(prop, 'default', None)

    @property
    def value(self):
        if self.is_custom:
            return self.struct[self.prop_name]
        else:
            return save_property(self.struct, self.prop_name)

    @value.setter
    def value(self, new_value):
        if self.is_custom:
            self.struct[self.prop_name] = new_value
        else:
            load_property(self.struct, self.prop_name, new_value)

class PropOp(namedtuple('PropOp', 'prop_wrapper value')):
    __slots__ = ()

    def __new__(cls, struct, data_path, value=None):
        prop_wrapper = PropertyWrapper.from_path(struct, data_path)
        if not prop_wrapper:
            raise RuntimeError(f"Couldn't resolve {data_path}")

        saved_value = prop_wrapper.value
        if value is not None:
            prop_wrapper.value = value

        return super().__new__(cls, prop_wrapper, saved_value)

    def revert(self, context):
        self.prop_wrapper.value = self.value

class PropForeachOp(namedtuple('PropForeachOp', 'collection prop_name values')):
    __slots__ = ()

    def __new__(cls, collection, prop_name, value=None):
        assert isinstance(collection, bpy.types.bpy_prop_collection)

        if len(collection) == 0:
            # Can't investigate array type if there are no elements (would do nothing anyway)
            return super().__new__(cls, collection, prop_name, np.empty(0))

        prop = collection[0].bl_rna.properties[prop_name]
        element_type = type(prop.default)
        num_elements = len(collection) * prop.array_length
        saved_values = np.empty(num_elements, dtype=element_type)
        collection.foreach_get(prop_name, saved_values)
        if value is not None:
            values = np.full(num_elements, value, dtype=element_type)
            collection.foreach_set(prop_name, values)

        return super().__new__(cls, collection, prop_name, saved_values)

    def revert(self, context):
        if self.values.size > 0:
            self.collection.foreach_set(self.prop_name, self.values)

class CallOp(namedtuple('CallOp', 'func args kwargs')):
    __slots__ = ()

    def __new__(cls, func, *args, **kwargs):
        assert callable(func)
        return super().__new__(cls, func, args, kwargs)

    def revert(self, context):
        self.func(*self.args, **self.kwargs)

class SelectionOp(namedtuple('SelectionOp', 'selected_objects active_object collection_hide '
    'layer_hide object_hide')):
    __slots__ = ()

    def __new__(cls, context):
        return super().__new__(cls,
            selected_objects=context.selected_objects[:],
            active_object=context.view_layer.objects.active,
            collection_hide=[(cl, cl.hide_select, cl.hide_viewport, cl.hide_render)
                for cl in bpy.data.collections],
            layer_hide=[(layer, layer.hide_viewport, layer.exclude)
                for layer in get_layers_recursive(context.view_layer.layer_collection)],
            object_hide=[(obj, obj.hide_select, obj.hide_viewport, obj.hide_render)
                for obj in bpy.data.objects])

    def revert(self, context):
        for collection, hide_select, hide_viewport, hide_render in self.collection_hide:
            try:
                collection.hide_select = hide_select
                collection.hide_viewport = hide_viewport
                collection.hide_render = hide_render
            except ReferenceError:
                pass
        for layer, hide_viewport, exclude in self.layer_hide:
            try:
                layer.hide_viewport = hide_viewport
                layer.exclude = exclude
            except ReferenceError:
                pass
        for obj, hide_select, hide_viewport, hide_render in self.object_hide:
            try:
                obj.hide_select = hide_select
                obj.hide_viewport = hide_viewport
                obj.hide_render = hide_render
            except ReferenceError:
                pass
        select_only(context, self.selected_objects)
        try:
            context.view_layer.objects.active = self.active_object
        except ReferenceError:
            pass

class CollectionOp(namedtuple('CollectionOp', 'collection remove_func_name items is_whitelist')):
    __slots__ = ()

    def __new__(cls, collection, items=None):
        assert isinstance(collection, bpy.types.bpy_prop_collection)

        # Find out if there's a remove-like function available
        for func_name in ('remove', 'unlink', ''):
            func = collection.bl_rna.functions.get(func_name)
            if (func is not None
                and sum(param.is_required for param in func.parameters) == 1
                and func.parameters[0].type == 'POINTER'):
                break
        if not func_name:
            raise RuntimeError(f"'{collection.bl_rna.name}' is not supported")

        if items is None:
            # On reverting, remove all but the current items
            return super().__new__(cls, collection, func_name, set(collection), True)
        else:
            # On reverting, remove the specified items
            return super().__new__(cls, collection, func_name, set(items), False)

    def revert(self, context):
        # Allow passing in object names instead of object references
        # Compare types, don't use `isinstance` as that will throw on removed objects
        items = set(self.collection.get(el) if type(el) == str else el for el in self.items)
        items.discard(None)

        remove_func = getattr(self.collection, self.remove_func_name)
        if self.is_whitelist:
            # Remove items not in the set
            for item in set(self.collection) - items:
                logs("Removing", item)
                remove_func(item)
        else:
            # Remove items in the set
            for item in items:
                try:
                    logs("Removing", item)
                    remove_func(item)
                except ReferenceError:
                    pass

class RenameOp(namedtuple('RenameOp', 'bid name other_bid')):
    __slots__ = ()

    def __new__(cls, bid, name, start_num=0, name_format="{name}{num}"):
        data_collection = get_data_collection(bid)
        if data_collection is None:
            raise RuntimeError(f"Type {type(bid).__name__} is not supported")

        saved_name = bid.name
        bid.tag = True  # Not strictly necessary, tagging allows custom naming format to work
        for num in count(start=start_num):
            new_name = name if (num == start_num) else name_format.format(name=name, num=num)
            other_bid = data_collection.get(new_name)
            if not other_bid or bid == other_bid:
                bid.name = new_name
                return super().__new__(cls, bid, saved_name, None)
            elif other_bid and not other_bid.tag:
                swap_names(bid, other_bid)
                return super().__new__(cls, bid, saved_name, other_bid)

    def revert(self, context):
        if self.other_bid:
            try:
                swap_names(self.bid, self.other_bid)
            except ReferenceError:
                pass
        self.bid.name = self.name  # Ensure the name is reverted if swap_names failed
        self.bid.tag = False

class SaveState:
    """Similar to an undo stack. See SaveContext for example usage."""

    def __init__(self, context, name, refresh=False):
        self.context = context
        self.name = name
        self.refresh = refresh
        self.operations = []

    def revert(self):
        while self.operations:
            self._pop_op()
        if self.refresh:
            # Might be necessary in some cases where context.scene.view_layers.update() is not enough
            self.context.scene.frame_set(self.context.scene.frame_current)

    def _push_op(self, op_cls, *args, **kwargs):
        try:
            self.operations.append(op_cls(*args, **kwargs))
            logs("Push", self.operations[-1], max_len=90)
        except Exception as e:
            logs(f"Error pushing {op_cls.__name__}: {e}")

    def _pop_op(self):
        op = self.operations.pop()
        try:
            logs("Pop", op, max_len=90)
            op.revert(self.context)
        except Exception as e:
            logs(f"Error reverting {op.__class__.__name__}: {e}")

    def prop(self, struct, data_paths, values=[None]):
        """Save the specified properties and optionally assign new values."""

        if isinstance(data_paths, str):
            data_paths = data_paths.split()
        if not isinstance(values, list):
            values = [values]
        if len(values) != 1 and len(values) != len(data_paths):
            raise ValueError("Expected either a single value or as many values as data paths")
        for data_path, value in zip_longest(data_paths, values, fillvalue=values[0]):
            self._push_op(PropOp, struct, data_path, value)

    def prop_foreach(self, collection, prop_name, value=None):
        """Save the specified property for all elements in the collection."""

        self._push_op(PropForeachOp, collection, prop_name, value)

    def selection(self):
        """Save the current object selection."""

        self._push_op(SelectionOp, self.context)

    def temporary(self, collection, items):
        """Mark one or more items for deletion."""

        self._push_op(CollectionOp, collection, ensure_iterable(items))

    def temporary_bids(self, bids):
        """Mark one or more IDs for deletion."""

        for bid_type, bids in groupby(ensure_iterable(bids), key=lambda bid: type(bid)):
            if bid_type is not type(None):
                self._push_op(CollectionOp, get_data_collection(bid_type), bids)

    def keep_temporary_bids(self, bids):
        """Keep IDs that were previously marked for deletion."""

        bids = set(ensure_iterable(bids))
        for op in reversed(self.operations):
            if isinstance(op, CollectionOp) and not op.is_whitelist:
                op.items.difference_update(bids)

    def collection(self, collection):
        """Remember the current contents of a collection. Any items created later will be removed."""

        self._push_op(CollectionOp, collection)

    def viewports(self, header_text=None, show_overlays=None, **kwargs):
        """Save and override 3D viewport settings."""

        for area in self.context.screen.areas:
            if area.type == 'VIEW_3D':
                # Don't think there's a way to find out the current header text, reset on reverting
                self._push_op(CallOp, area.header_text_set, None)
                area.header_text_set(header_text)
            for space in area.spaces:
                if space.type == 'VIEW_3D':
                    if show_overlays is not None:
                        self._push_op(PropOp, space.overlay, 'show_overlays', show_overlays)
                    for field_name, field_value in kwargs.items():
                        self._push_op(PropOp, space.shading, field_name, field_value)

    def rename(self, bid, name):
        """Save the IDs current name and give it a new name."""

        self._push_op(RenameOp, bid, name)

    def clone_obj(self, obj, to_mesh=False, parent=None, reset_origin=False):
        """Clones or converts an object. Returns a new, visible scene object with unique data."""

        if to_mesh:
            dg = self.context.evaluated_depsgraph_get()
            new_data = bpy.data.meshes.new_from_object(obj, preserve_all_data_layers=True, depsgraph=dg)
            self.temporary_bids(new_data)
            new_obj = bpy.data.objects.new(obj.name + "_", new_data)
            self.temporary_bids(new_obj)
        else:
            new_data = obj.data.copy()
            self.temporary_bids(new_data)
            new_obj = obj.copy()
            self.temporary_bids(new_obj)
            new_obj.name = obj.name + "_"
            new_obj.data = new_data
        assert new_data.users == 1

        if obj.type == 'MESH':
            # Move object materials to mesh
            for mat_index, mat_slot in enumerate(obj.material_slots):
                if mat_slot.link == 'OBJECT':
                    new_data.materials[mat_index] = mat_slot.material
                    new_obj.material_slots[mat_index].link = 'DATA'

        # New objects are moved to the scene collection, ensuring they're visible
        self.context.scene.collection.objects.link(new_obj)
        new_obj.hide_set(False)
        new_obj.hide_viewport = False
        new_obj.hide_render = False
        new_obj.hide_select = False
        new_obj.parent = parent

        if reset_origin:
            new_data.transform(new_obj.matrix_world)
            bpy.ops.object.origin_set(get_context(new_obj), type='ORIGIN_GEOMETRY', center='MEDIAN')
        else:
            new_obj.matrix_world = obj.matrix_world

        return new_obj

class SaveContext:
    """
    Saves state of various things and keeps track of temporary objects.
    When leaving scope, operations are reverted in the order they were applied.
    Example usage:

    with SaveContext(bpy.context, "test") as save:
        save.prop_foreach(bpy.context.scene.objects, 'location')
        bpy.context.active_object.location = (1, 1, 1)
    """

    def __init__(self, *args, **kwargs):
        self.save = SaveState(*args, **kwargs)

    def __enter__(self):
        return self.save

    def __exit__(self, exc_type, exc_value, traceback):
        self.save.revert()

class StateMachineBaseState:
    def __init__(self, owner):
        self.owner = owner

    def on_enter(self):
        pass

    def on_exit(self):
        pass

class StateMachineMixin:
    """Simple state machine."""

    state_stack = None
    state_events_on_reentry = True

    @property
    def state(self):
        return self.state_stack[-1] if self.state_stack else None

    def pop_state(self, *args, **kwargs):
        if self.state:
            self.state_stack.pop().on_exit(*args, **kwargs)
            if self.state_events_on_reentry and self.state:
                self.state.on_enter()

    def push_state(self, state_class, *args, **kwargs):
        assert state_class
        new_state = state_class(self)

        if self.state_events_on_reentry and self.state:
            self.state.on_exit()
        if self.state_stack is None:
            self.state_stack = []
        self.state_stack.append(new_state)

        if new_state:
            new_state.on_enter(*args, **kwargs)

class DrawHooksMixin:
    space_type = bpy.types.SpaceView3D
    draw_post_pixel_handler = None
    draw_post_view_handler = None

    def hook(self, context):
        if not self.draw_post_pixel_handler and hasattr(self, "on_draw_post_pixel"):
            self.draw_post_pixel_handler = self.space_type.draw_handler_add(self.on_draw_post_pixel,
                (context,), 'WINDOW', 'POST_PIXEL')

        if not self.draw_post_view_handler and hasattr(self, "on_draw_post_view"):
            self.draw_post_pixel_handler = self.space_type.draw_handler_add(self.on_draw_post_view,
                (context,), 'WINDOW', 'POST_VIEW')

    def unhook(self):
        if self.draw_post_pixel_handler:
            self.space_type.draw_handler_remove(self.draw_post_pixel_handler, 'WINDOW')
            self.draw_post_pixel_handler = None

        if self.draw_post_view_handler:
            self.space_type.draw_handler_remove(self.draw_post_view_handler, 'WINDOW')
            self.draw_post_view_handler = None

def show_window(width=0.5, height=0.5):
    """Open a window at the cursor. Size can be pixels or a fraction of the main window size."""

    # Hack from https://blender.stackexchange.com/questions/81974
    with SaveContext(bpy.context, "show_window") as save:
        render = bpy.context.scene.render
        prefs = bpy.context.preferences
        main_window = bpy.context.window_manager.windows[0]
        save.prop(prefs, 'is_dirty view.render_display_type')
        save.prop(render, 'resolution_x resolution_y resolution_percentage')

        render.resolution_x = int(main_window.width * width) if width <= 1.0 else int(width)
        render.resolution_y = int(main_window.height * height) if height <= 1.0 else int(height)
        render.resolution_percentage = 100
        prefs.view.render_display_type = 'WINDOW'
        bpy.ops.render.view_show('INVOKE_DEFAULT')
        return bpy.context.window_manager.windows[-1]

def show_text_window(text, title, width=0.5, height=0.5, font_size=16):
    """Open a window at the cursor displaying the given text."""

    # Open a render preview window, then modify it to show a text editor instead
    window = show_window(width, height)
    area = window.screen.areas[0]
    area.type = 'TEXT_EDITOR'
    space = area.spaces[0]
    assert isinstance(space, bpy.types.SpaceTextEditor)

    # Make a temporary text
    string = text
    text = bpy.data.texts.get(title) or bpy.data.texts.new(name=title)
    text.use_fake_user = False
    text.from_string(string)
    text.cursor_set(0)

    # Minimal interface
    if font_size is not None:
        space.font_size = font_size
    space.show_line_highlight = True
    space.show_line_numbers = False
    space.show_margin = False
    space.show_region_footer = False
    space.show_region_header = False
    space.show_region_ui = False
    space.show_syntax_highlight = False
    space.show_word_wrap = True
    space.text = text

def register(settings, prefs):
    bpy.utils.register_class(GRET_OT_property_warning)

def unregister():
    bpy.utils.unregister_class(GRET_OT_property_warning)


from ksz.src import plot
import matplotlib.pyplot as plt

data_path_list = [
        '/data/ycli/dr12/galaxy_DR12v5_LOWZ_North_TOT_wMASS.dat',
        '/data/ycli/dr12/galaxy_DR12v5_LOWZ_South_TOT_wMASS.dat',
        '/data/ycli/dr12/galaxy_DR12v5_CMASS_North_TOT_wMASS.dat',
        '/data/ycli/dr12/galaxy_DR12v5_CMASS_South_TOT_wMASS.dat',
        #'/data/ycli/6df/6dFGS_2MASS_RA_DEC_Z_J_K_bJ_rF_GOOD.cat',
        #'/data/ycli/group_catalog/6dFGS_M_group.dat',
        #'/data/ycli/group_catalog/6dFGS_L_group.dat',
        '/data/ycli/group_catalog/SDSS_M_group.dat',
        #'/data/ycli/group_catalog/SDSS_L_group.dat',
        '/data/ycli/cgc/CGC_wMASS.dat',
        ]
label_list = [
        'LOWZ North CGC',
        'LOWZ South CGC',
        'CMASS North',
        'CMASS South',
        #'6dF',
        #'6dF mass-weighted halo center',
        #'6dF luminosity-weighted halo center',
        'DR13 Group',
        #'dr13 luminosity-weighted halo center',
        'DR7 CGC',
        ]
ap_list = [
        7.,
        7.,
        #0.,
        #0.,
        8.,
        #11.,
        11.,
        #11.,
        7.,
        #7.,
        ]
#plot.plot_stellarmass_hist(data_path_list, label_list)

plot.plot_halomass_hist(data_path_list, label_list)
#plot.plot_rvir_hist(data_path_list, label_list, rho_crit = 2.775e11, ap_list=ap_list)

#plot.plot_z_hist(data_path_list, label_list)

plt.show()


import src.fileIO as io
import src.chris as chris
import src.filepaths as fp
import src.analysis as anal
import src.plotting as plot

from pathlib import Path

def batch_calculate_peak_wavelength(parent_directory,
                                    batch_name,
                                    file_paths,
                                    directory_paths,
                                    plot_files):
    '''
    Calculate sample batch peak wavelength and error, from individual files
    within batch.
    Args:
        parent_directory: <string> parent directory identifier
        batch_name: <string> batch name string
        file_paths: <array> array of target file paths
        directory_paths: <dict> dictionary containing required paths
        plot_files: <string> "True" or "False" for plotting output
    Returns:
        results_dictionary: <dict>
            Batch Name
            File Names
            File Paths
            Secondary Strings
            Individual file values for:
                Background Files
                Region Trim Index: <array> min, max indices
                popt: <array> fano fit parameters:
                    peak, gamma, q, amplitude, damping
                pcov: <array> fano fit errors
                    peak, gamma, q, amplitude, damping
    '''
    batch_dictionary = fp.update_batch_dictionary(
        parent=parent_directory,
        batch_name=batch_name,
        file_paths=file_paths)
    for file in file_paths:
        wavelength, raw_intensity = io.read_GMR_file(file_path=file)
        sample_parameters = fp.sample_information(file_path=file)
        background_file, background_parameters = fp.find_background(
            background_path=directory_paths['Background Path'],
            sample_details=sample_parameters,
            file_string='.txt')
        print(background_file)
        if len(background_file) == 0:
            normalised_intensity = anal.normalise_intensity(
                raw_intensity=anal.timecorrected_intensity(
                    raw_intensity=raw_intensity,
                    integration_time=sample_parameters[
                        f'{parent_directory} Integration Time']))
        else:
            _, background_raw_intensity = io.read_GMR_file(
                file_path=background_file[0])
            background_parent = background_parameters['Parent Directory']
            normalised_intensity = anal.bg_normal_intensity(
                intensity=raw_intensity,
                background_intensity=background_raw_intensity,
                integration_time=sample_parameters[
                    f'{parent_directory} Integration Time'],
                background_integration_time=background_parameters[
                    f'{background_parent} Integration Time'])
        out_string = sample_parameters[f'{parent_directory} Secondary String']
        plot.spectrumplt(
            wavelength=wavelength,
            intensity=normalised_intensity,
            out_path=Path(f'{directory_paths["Results Path"]}/{batch_name}_{out_string}'))
        peak_results = chris.calc_peakwavelength(
            wavelength=wavelength,
            normalised_intensity=normalised_intensity,
            sample_details=sample_parameters,
            plot_figure=plot_files,
            out_path=Path(
                f'{directory_paths["Results Path"]}'
                f'/{batch_name}_{out_string}_Peak.png'))
        batch_dictionary.update(
            {f'{out_string} File': sample_parameters})
        batch_dictionary.update(
            {f'{out_string} Background': background_parameters})
        batch_dictionary.update(peak_results)
    return batch_dictionary

if __name__ == '__main__':

    ''' Organisation '''
    root = Path().absolute()
    info, directory_paths = fp.get_directory_paths(root_path=root)
    file_paths = fp.get_files_paths(
        directory_path=directory_paths['Spectrum Path'],
        file_string='.txt')
    parent, batches = fp.get_all_batches(file_paths=file_paths)

    ''' Batch Processing '''
    for batch, filepaths in batches.items():
        out_file = Path(
            f'{directory_paths["Results Path"]}'
            f'/{batch}_Peak.json')
        if out_file.is_file():
            pass
        else:
            results_dictionary = batch_calculate_peak_wavelength(
                parent_directory=parent,
                batch_name=batch,
                file_paths=filepaths,
                directory_paths=directory_paths,
                plot_files=info['Plot Files'])
            io.save_json_dicts(
                out_path=out_file,
                dictionary=results_dictionary)


from __future__ import annotations
import json
import subprocess
import collections
import concurrent.futures
from os import path, system
from datetime import datetime

root_path = path.abspath("src/test_cases/UI")
report_path = path.abspath("src/reports/concurrent_test_logs")

def generate_pytest_commands():
    config_run_test_dir = path.dirname(__file__)
    with open(path.join(config_run_test_dir, "config_run_multiple_test.json")) as f:
        config_data = json.load(f)
        list_test_suite = config_data['test_suite']
        pytest_run_cmds = []
        for suite in list_test_suite:
            test_name = suite['test']['name'].replace(".", "::")
            browser_name = suite['test']['browser']
            test_suite_option = f"{suite['name']}::{test_name}"
            options_cmd = collections.namedtuple('OptionCmd', ['test_name', 'browser'])
            pytest_run_cmds.append(options_cmd(test_suite_option, browser_name))
        return pytest_run_cmds

def execute_pytest_cmd(option_cmd):
    run_cmd_process = subprocess.run(["pytest", f"{root_path}\\{option_cmd.test_name}",
                                      f"--browser={option_cmd.browser}"],
                                     capture_output=True)
    return run_cmd_process.stdout

list_options_cmd = generate_pytest_commands()

with concurrent.futures.ThreadPoolExecutor(max_workers=len(list_options_cmd)) as executor:
    running_cmd = {executor.submit(execute_pytest_cmd, options): options for options in list_options_cmd}
    for completed_cmd in concurrent.futures.as_completed(running_cmd):
        test_ran = running_cmd[completed_cmd].test_name.split("::")[-1]
        browser_ran = running_cmd[completed_cmd].browser
        try:
            time_logging = datetime.now().strftime("%Y.%m.%d_(%H-%M-%S.%f)")
            with open(f"{report_path}\\Result_{test_ran}_{time_logging}.log", "wb") as f:
                f.write(completed_cmd.result())
        except Exception as exc:
            print(f"Pytest ran with error {exc}.")


import pygame
from pygame.sprite import Sprite

class Tiro(Sprite):
    """Class para manipular os tiros disparados pela nave"""
    def __init__(self, ik_game):
        """Cria um disparo na posição atual da nave"""
        super().__init__()
        self.screen = ik_game.screen
        self.configuracoes = ik_game.configuracoes
        self.cor = self.configuracoes.tiro_cor

        # Cria um disparo rect na posição (0, 0) e reposiciona no local certo
        self.rect = pygame.Rect(0, 0, self.configuracoes.tiro_width,
                                self.configuracoes.tiro_height)
        self.rect.midtop = ik_game.nave.rect.midtop

        # Armazena a posição do disparo como um decimal
        self.y = float(self.rect.y)

    def update(self):
        """Move o tiro para cima na tela"""
        # Atualiza a posição decimal do disparo
        self.y -= self.configuracoes.tiro_vel
        # Atualiza a posição rect
        self.rect.y = self.y

    def draw_tiro(self):
        """Desenha o tiro na tela"""
        pygame.draw.rect(self.screen, self.cor, self.rect)


import numpy as np
import pandas as pd
import operator
from sklearn import preprocessing

data = pd.read_csv("data.csv",header=None)

min_max_scaler = preprocessing.MinMaxScaler(feature_range=(0,1))

def classify(v,k,distance):
  target_values = data.iloc[:,-1]
  nearest_neighbors = knn(data,k,v,distance)
  classification_values = {}

  for index in nearest_neighbors:
    if target_values[index] not in classification_values.keys():
      classification_values[target_values[index]] = 1
    else:
      classification_values[target_values[index]] += 1

  return max(classification_values.items(),key=operator.itemgetter(1))[0]

def knn(vectors,k,vector_to_classify,distance):
  distances = []
  for i in range(0,len(vectors)):
    x = vectors.loc[i,:]
    x = x[0:len(x)-1]
    x = min_max_scaler.fit_transform(x.values.astype(float).reshape(-1,1))[:,0]

    distances.append({"index": i,
                      "value": distance(x,vector_to_classify)})

  distances = sorted(distances,key=lambda x:x['value'], reverse=True)
  indexes = list(map(lambda distance: distance['index'],distances[0:k]))
  return indexes

def euclidean_distance(x,y):
  summation = 0
  for i in range(0,x.size):
    summation += ((x[i] - y[i])**2)

  return (summation)**(1/2)

def manhattan_distance(x,y):
  summation = 0
  for i in range(0,x.size):
    summation += abs(x[i]-y[i])
  return summation

def maximum_metric(x,y):
  max_distance = 0
  for i in range(0,x.size):
    difference = abs(x[i]-y[i])
    if(difference > max_distance):
      max_distance = difference
  return max_distance

vectors_to_classify = [np.array([1100000,60,1,2,1,500]),
                       np.array([1100000,60,1,2,1,500]),
                       np.array([1800000,65,1,2,1,1000]),
                       np.array([2300000,72,1,3,1,1400]),
                       np.array([3900000,110,2,3,1,1800])]

distances = [{'name':'Euclidean Distance','function':euclidean_distance},
             {'name':'Manhattan Distance','function':manhattan_distance},
             {'name':'Maximum Metric','function':maximum_metric}]

for distance in distances:
  print("Distance " + str(distance['name']))
  for k in [1,3,5]:
    print("K = " + str(k))
    for v in vectors_to_classify:
      v = min_max_scaler.fit_transform(v.astype(float).reshape(-1,1))[:,0]
      print(classify(v,k,distance['function']))


#!/usr/bin/env python3

import requests
import os

url = 'http://localhost/upload/'
path = os.getcwd() + '/supplier-data/images/'

only_jpeg = []

for file in os.listdir(path):
  name, ext = os.path.splitext(file)
  if ext == '.jpeg':
    only_jpeg.append(os.path.join(path,file))

for jpeg in only_jpeg:
  with open(jpeg, 'rb') as opened:
    r = requests.post(url, files={'file': opened})


from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
from time import sleep

class InternetSpeed:
    def __init__(self, edge_driver_path):
        self.driver = webdriver.Edge(service=Service(edge_driver_path))
        self.down = 0
        self.up = 0
        self.get_internet_speed()

    def get_internet_speed(self):
        speedtest_url = "https://www.speedtest.net/"
        self.driver.get(speedtest_url)
        sleep(10)
        start_test = self.driver.find_element(by=By.XPATH,
                                              value='//*[@id="container"]/div/div[3]/div/div/div/div[2]/div[3]/div[1]/a')
        start_test.click()
        sleep(60)
        self.down = self.driver.find_element(by=By.XPATH,
                                             value='//*[@id="container"]/div/div[3]/div/div/div/div[2]/div[3]/div[3]/div/div[3]/div/div/div[2]/div[1]/div[2]/div/div[2]/span').text
        self.up = self.driver.find_element(by=By.XPATH,
                                           value='//*[@id="container"]/div/div[3]/div/div/div/div[2]/div[3]/div[3]/div/div[3]/div/div/div[2]/div[1]/div[3]/div/div[2]/span').text
        print(self.down)
        print(self.up)

        self.driver.quit()


"""
This file is part of Candela.

Candela is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Candela is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Candela.  If not, see <http://www.gnu.org/licenses/>.
"""
import curses
import sys
import signal
import threading
import textwrap
import platform

import constants

class Shell():
    """
    The main Candela class
    Controls the shell by taking control of the current terminal window.
    Performs input and output to the user
    """
    def __init__(self, scriptfile=None):
        """
        Create an instance of a Shell
        This call takes over the current terminal by calling curses.initscr()
        Sets global shell state, including size information, menus, stickers,
        the header, and the prompt.

        Kwargs:
        scriptfile - the name of the script file to run. If not None and the
                     file exists, the script will be immediately run.
        """
        self._register_sigint_handler()

        self.script_lines = self._parse_script_file(scriptfile)
        self.script_counter = 0
        self.scriptfile = ""

        self.stdscr = curses.initscr()
        self.stdscr.keypad(1)

        self.platform = self._get_platform()

        # holds the backlog of shell output
        self.backbuffer = []
        self.height,self.width = self.stdscr.getmaxyx()

        # the list of menus in the shell app
        self.menus = []
        # the currently visible stickers in the app
        self.stickers = []

        # should the command menu be shown
        self.should_show_help = True
        # for commands with only positional args, show the
        # name of the next argument as the user types
        self.should_show_hint = False

        # dictionary of functions to call on key events
        # keys are chars representing the pressed keys
        self.keyevent_hooks = {}

        # the text to stick in the upper left corner of the window
        self.header = ""
        self._header_bottom = 0
        self._header_right = 0
        self._header_right_margin = 50

        self.prompt = "> "

    def _parse_script_file(self, filename):
        """
        Open a file if it exists and return its contents as a list of lines

        Args:
        filename - the file to attempt to open
        """
        self.scriptfile = filename
        try:
            f = open(filename, 'r')
            script_lines = f.readlines()
            script_lines = [a.strip('\n') for a in script_lines]
            f.close()
        except Exception as e:
            return
        return script_lines

    def runscript(self, scriptfile):
        """
        Set up the global shell state necessary to run a script from a file

        Args:
        scriptfile - the string name of the file containing the script.
                     paths are relative to system cwd
        """
        self.script_lines = self._parse_script_file(scriptfile)
        self.script_counter = 0

    def get_helpstring(self):
        """
        Get the help string for the current menu.

        This string contains a preformatted list of commands and their
        descriptions from the current menu.
        """
        _menu = self.get_menu()
        if not _menu:
            return

        helpstring = "\n\n" + _menu.title + "\n" + "-"*20 + "\n" + _menu.options()
        return helpstring

    def sticker(self, output, new_output="", pos=None):
        """
        Place, change, or remove a sticker from the shell window.

        Candela has the concept of a sticker - a small block of text that
        is "stuck" to the window. They can be used to convey persistent
        information to the shell user.

        If only output is specified, this creates a new sticker with the string
        output. If output and new_output are specified, and there is an existing
        sticker whose text is the same as output, this will replace that
        sticker's text with new_output.

        Args:
        output      - The text of the sticker to manipulate

        Kwargs:
        new_output  - The text that will replace the text of the chosen sticker
        pos         - The (y, x) tuple indicating where to place the sticker
        """
        if len(self.stickers) > 0:
            sort = sorted(self.stickers, key=lambda x: x[1][0], reverse=True)
            ht = sort[0][1][0]+1
        else:
            ht = 3

        pos = pos or (ht, self.width - 20)

        match = None
        for text,_pos in self.stickers:
            if output == text:
                match = (text,_pos)
                break
        if match:
            self.remove_sticker(match[0])

        sticker = (new_output or output, match[1] if match else pos)
        self.stickers.append(sticker)

        self._update_screen()

    def remove_sticker(self, text):
        """
        Remove the sticker with the given text from the window

        Args:
        text    - The text of the sticker to remove
        """
        self.stickers = [a for a in self.stickers if a[0] != text]

    def _print_stickers(self):
        """
        Print all current stickers at the appropriate positions
        """
        for text,pos in self.stickers:
            _y,_x = pos
            if _x + len(text) > self.width:
                _x = self.width - len(text) - 1
            self.stdscr.addstr(_y, _x, text)

    def _print_header(self):
        """
        Print the header in the appropriate position
        """
        ht = 0
        for line in self.header.split("\n"):
            self.stdscr.addstr(ht, 0, line + (" "*self._header_right_margin))
            if len(line) > self._header_right:
                self._header_right = len(line)
            ht += 1
        self.stdscr.addstr(ht, 0, " "*(self._header_right+self._header_right_margin))
        self._header_bottom = ht
        self.mt_width = self._header_right + 49

    def clear(self):
        """
        Remove all scrollback text from the window
        """
        backbuffer = list(self.backbuffer)
        printstring = "\n"
        for i in range(self.height):
            self.put(printstring)

    def _print_backbuffer(self):
        """
        Print the previously printed output above the current command line.

        candela.shell.Shell stores previously printed commands and output
        in a backbuffer. Like a normal shell, it handles printing these lines
        in reverse order to allow the user to see their past work.
        """
        rev = list(self.backbuffer)
        rev.reverse()

        for i, tup in zip(range(len(rev)), rev):
            string, iscommand = tup
            ypos = self.height-2-i
            if ypos > 0:
                printstring = string
                if iscommand:
                    printstring = "%s%s" % (self.prompt, string)
                self.stdscr.addstr(ypos,0,printstring)

    def _print_help(self):
        """
        Print the menu help box for the current menu
        """
        _helpstring = self.get_helpstring()
        if not _helpstring:
            return
        helpstrings = [" %s" % a for a in _helpstring.split("\n")]
        ht = 0
        longest = len(max(helpstrings, key=len))
        _x = self._header_right + self._header_right_margin
        if _x + longest > self.width:
            _x = self.width - longest - 1
        for line in helpstrings:
            self.stdscr.addstr(ht, _x, line + " "*15)
            ht += 1

    def put(self, output, command=False):
        """
        Print the output string on the bottom line of the shell window
        Also pushes the backbuffer up the screen by the number of lines
        in output.

        Args:
        output  - The string to print. May contain newlines

        Kwargs:
        command - False if the string was not a user-entered command,
                  True otherwise (users of Candela should always use False)
        """
        self._update_screen()

        if not output:
            return

        output = str(output)

        _x,_y = (self.height-1, 0)

        lines = []
        for line in output.split('\n'):
            if len(line) > self.width - 3:
                for line in textwrap.wrap(line, self.width-3):
                    lines.append(line)
            else:
                lines.append(line)

        for line in lines:
            # put the line
            self.stdscr.addstr(_x, _y, line)

            # add it to backbuffer
            backbuf_string = line
            to_append = (backbuf_string, command)
            if line != self.prompt:
                index = 0
                if len(self.backbuffer) >= 200:
                    index = 1
                self.backbuffer = self.backbuffer[index:] + [to_append]

    def _input(self, prompt):
        """
        Handle user input on the shell window.
        Works similarly to python's raw_input().
        Takes a prompt and returns the raw string entered before the return key
        by the user.

        The input is returned withnewlines stripped.

        Args:
        prompt  - The text to display prompting the user to enter text
        """
        self.put(prompt)
        keyin = ''
        buff = ''
        hist_counter = 1
        while keyin != 10:
            keyin = self.stdscr.getch()
            _y,_x = self.stdscr.getyx()
            index = _x - len(self.prompt)
            #self.stdscr.addstr(20, 70, str(keyin))  # for debugging
            try:
                if chr(keyin) in self.keyevent_hooks.keys():
                    cont = self.keyevent_hooks[chr(keyin)](chr(keyin), buff)
                    if cont == False:
                        continue
            except:
                pass
            if keyin in [127, 263]:  # backspaces
                del_lo, del_hi = self._get_backspace_indices()
                buff = buff[:index+del_lo] + buff[index+del_hi:]
                self._redraw_buffer(buff)
                self.stdscr.move(_y, max(_x+del_lo, len(self.prompt)))
            elif keyin in [curses.KEY_UP, curses.KEY_DOWN]:  # up and down arrows
                hist_counter,buff = self._process_history_command(keyin, hist_counter)
            elif keyin in [curses.KEY_LEFT, curses.KEY_RIGHT]:  # left, right arrows
                if keyin == curses.KEY_LEFT:
                    newx = max(_x - 1, len(self.prompt))
                elif keyin == curses.KEY_RIGHT:
                    newx = min(_x + 1, len(buff) + len(self.prompt))
                self.stdscr.move(_y, newx)
            elif keyin == curses.KEY_F1:  # F1
                curses.endwin()
                sys.exit()
            elif keyin in [9]:  # tab
                choices = self._tabcomplete(buff)
                if len(choices) == 1:
                    if len(buff.split()) == 1 and not buff.endswith(' '):
                        buff = choices[0]
                    else:
                        if len(buff.split()) != 1 and not buff.endswith(' '):
                            buff = ' '.join(buff.split()[:-1])
                        if buff.endswith(' '):
                            buff += choices[0]
                        else:
                            buff += ' ' + choices[0]
                elif len(choices) > 1:
                    self.put("    ".join(choices))
                elif len(choices) == 0:
                    pass
                self._redraw_buffer(buff)
            elif keyin >= 32 and keyin <= 126:  # ascii input
                buff = buff[:index-1] + chr(keyin) + buff[index-1:]
                self._redraw_buffer(buff)
                self.stdscr.move(_y, min(_x, len(buff) + len(self.prompt)))
                if self.should_show_hint and keyin == 32:
                    command = self._get_command(buff)
                    if hasattr(command, 'definition') and '-' not in command.definition:
                        try:
                            nextarg = command.definition.split()[len(buff.split())]
                            self.stdscr.addstr(_y, _x+1, nextarg)
                            self.stdscr.move(_y, _x)
                        except:
                            pass
        self.put(buff, command=True)
        self.stdscr.refresh()
        return buff

    def _get_backspace_indices(self):
        if self.platform == "Linux":
            return (0, 1)
        elif self.platform == "Darwin":
            return (-len(self.prompt)-1, -len(self.prompt))

    def _tabcomplete(self, buff):
        """
        Get a list of possible completions for the current buffer

        If the current buffer doesn't contain a valid command, see if the
        buffer is a prefix of any valid commands. If so, return those as possible
        completions. Otherwise, delegate the completion finding to the command object.

        Args:
        buff    - The string buffer representing the current unfinished command input

        Return:
        A list of completion strings for the current token in the command
        """
        menu = self.get_menu()
        commands = []
        if menu:
            commands = menu.commands
        output = []
        if len(buff.split()) <= 1 and ' ' not in buff:
            for command in commands:
                if command.name.startswith(buff):
                    output.append(command.name)
                for alias in command.aliases:
                    if alias.startswith(buff):
                        output.append(alias)
        else:
            command = self._get_command(buff)
            if command:
                output = command._tabcomplete(buff)
        return output

    def _get_command(self, buff):
        """
        Get the command instance referenced by string in the current input buffer

        Args:
        buff    - The string version of the current command input buffer

        Return:
        The Command instance corresponding to the buffer command
        """
        menu = self.get_menu()
        commands = []
        if menu:
            commands = menu.commands
        if len(commands) == 0:
            self.put("No commands found. Maybe you forgot to set self.menus or self.menu?")
            self.put("Hint: use F1 to quit")
        for command in commands:
            if command.name == buff.split()[0] or buff.split()[0] in command.aliases:
                return command
        return None

    def _redraw_buffer(self, buff):
        """
        Clear the bottom line and re-print the given string on that line

        Args:
        buff    - The line to print on the cleared bottom line
        """
        self.stdscr.addstr(self.height-1, 0, " "*(self.width-3))
        self.stdscr.addstr(self.height-1, 0, "%s%s" % (self.prompt, buff))

    def _process_history_command(self, keyin, hist_counter):
        """
        Get the next command from the backbuffer and return it
        Also return the modified buffer counter.

        Args:
        keyin           - The key just pressed
        hist_counter    - The current position in the backbuffer
        """
        hist_commands = [(s,c) for s,c in self.backbuffer if c]
        if not hist_commands:
            return hist_counter, ""

        buff = hist_commands[-hist_counter][0]

        self.stdscr.addstr(self.height-1, 0, " "*(self.width-3))
        self.stdscr.addstr(self.height-1, 0, "%s%s" % (self.prompt, buff))

        if keyin == curses.KEY_UP and hist_counter < len(hist_commands):
            hist_counter += 1
        elif keyin == curses.KEY_DOWN and hist_counter > 0:
            hist_counter -= 1
        return hist_counter, buff

    def _script_in(self):
        """
        Substitute for _input used when reading from a script.
        Returns the next command from the script being read.
        """
        if not self.script_lines:
            return None

        if self.script_counter < len(self.script_lines):
            command = self.script_lines[self.script_counter]
            self.script_counter += 1
        else:
            command = None
        return command

    def main_loop(self):
        """
        The main shell IO loop.
        The sequence of events is as follows:
            get an input command
            split into tokens
            find matching command
            validate tokens for command
            run command

        This loop can be broken out of only with by a command returning
        constants.CHOICE_QUIT or by pressing F1
        """
        ret_choice = None
        while ret_choice != constants.CHOICE_QUIT:
            success = True
            ret_choice = constants.CHOICE_INVALID
            choice = self._script_in()
            if choice:
                self.put("%s%s" % (self.prompt, choice))
            else:
                choice = self._input(self.prompt)
            tokens = choice.split()
            if len(tokens) == 0:
                self.put("\n")
                continue
            command = self._get_command(choice)
            if not command:
                self.put("Invalid command - no match")
                continue
            try:
                args, kwargs = command.parse_command(tokens)
                success, message = command.validate(*args, **kwargs)
                if not success:
                    self.put(message)
                else:
                    ret_choice = command.run(*args, **kwargs)
                    if ret_choice == constants.CHOICE_INVALID:
                        self.put("Invalid command")
                    else:
                        menus = [a.name for a in self.menus]
                        if str(ret_choice).lower() in menus:
                            self.menu = ret_choice.lower()
                        else:
                            self.put("New menu '%s' not found" % ret_choice.lower())
            except Exception as e:
                self.put(e)
        return self

    def get_menu(self):
        """
        Get the current menu as a Menu
        """
        if not self.menus: return
        try:
            return [a for a in self.menus if a.name == self.menu][0]
        except:
            return

    def defer(self, func, args=(), kwargs={}, timeout_duration=10, default=None):
        """
        Create a new thread, run func in the thread for a max of
        timeout_duration seconds
        This is useful for blocking operations that must be performed
        after the next window refresh.
        For example, if a command should set a sticker when it starts executing
        and then clear that sticker when it's done, simply using the following
        will not work:

        def _run(*args, **kwargs):
            self.sticker("Hello!")
            # do things...
            self.remove_sticker("Hello!")

        This is because the sticker is both added and removed in the same
        refresh loop of the window. Put another way, the sticker is added and
        removed before the window gets redrawn.

        defer() can be used to get around this by scheduling the sticker
        to be removed shortly after the next window refresh, like so:

        def _run(*args, **kwargs):
            self.sticker("Hello!")
            # do things...
            def clear_sticker():
                time.sleep(.1)
                self.remove_sticker("Hello!")
            self.defer(clear_sticker)

        Args:
        func        - The callback function to run in the new thread

        Kwargs:
        args        - The arguments to pass to the threaded function
        kwargs      - The keyword arguments to pass to the threaded function
        timeout_duration - the amount of time in seconds to wait before
                           killing the thread
        default     - The value to return in case of a timeout
        """
        class InterruptableThread(threading.Thread):
            def __init__(self):
                threading.Thread.__init__(self)
                self.result = default
            def run(self):
                self.result = func(*args, **kwargs)
        it = InterruptableThread()
        it.start()
        it.join(timeout_duration)
        if it.isAlive():
            return it.result
        else:
            return it.result

    def end(self):
        """
        End the current Candela shell and safely shut down the curses session
        """
        curses.endwin()

    def _register_sigint_handler(self):
        """
        Properly handle ^C and any other method of sending SIGINT.
        This avoids leaving the user with a borked up terminal.
        """
        def signal_handler(signal, frame):
            self.end()
            sys.exit(0)
        signal.signal(signal.SIGINT, signal_handler)

    def _update_screen(self):
        """
        Refresh the screen and redraw all elements in their appropriate positions
        """
        self.height,self.width = self.stdscr.getmaxyx()
        self.stdscr.clear()

        self._print_backbuffer()

        if self.width < self._header_right + 80 or self.height < self._header_bottom + 37:
            pass
        else:
            self._print_header()
            if self.should_show_help:
                self._print_help()
        self._print_stickers()

        self.stdscr.refresh()

    def _get_platform(self):
        """
        Return the platform name. This is fine, but it's used in a hacky way to
        get around a backspace-cooking behavior in Linux (at least Ubuntu)
        """
        return platform.uname()[0]


# -*- coding: utf-8 -*-
"""
Ventricular tachycardia, ventricular bigeminy, Atrial fibrillation,
Atrial fibrillation, Ventricular trigeminy, Ventricular escape	,
Normal sinus rhythm, Sinus arrhythmia, Ventricular couplet
"""
import tkinter as tk
import scipy.io as sio
from PIL import Image, ImageTk
class App():
    ancho=760
    alto=760
    estado=False
    contadores=[0,0,0,0,0,0,0,0,0]#son los que van a contar el numero de dato que se ejecuta
    #se va a cosiacar las señales
    Signal=0

    def __init__(self):
        #cargar las variables .mat
        self.raiz=tk.Tk()
        self.importData()

        self.frame=tk.Frame(self.raiz,bg="white")
        self.frame.config(width=self.ancho,height=self.alto)
        self.frame.pack()

        self.titulo=tk.Label(self.frame,bg="white",text="Dispositivo Generador de Arritmias Cardiacas")
        self.titulo.config(font=("Grotesque",24))
        self.titulo.place(x=0,y=0,width=self.ancho,height=self.alto//16)

        self.opcion = tk.IntVar()

        names=["Taquicardia ventricualar","Bigeminismo Ventricular","Fibrilacion atrial","Flutter atrial","Trigeminismo Ventricular",
               "Escape Ventricular","Ritmo Sinusal","Arritmia Sinusal","Couplet Ventricular"]

        for i in range(1,10):
            tk.Radiobutton(self.frame, text=names[i-1],font=("Grotesque",16) ,variable=self.opcion,bg="white",anchor="w",
                       value=i, command=self.selec).place(x=50,y=self.alto//8+(i-1)*self.alto//20,
                                                              width=self.ancho//2.5,height=self.alto//32)

        temp=Image.open('LOGO_UMNG.png')
        temp=temp.resize((200, 250), Image.ANTIALIAS)
        self.imagen = ImageTk.PhotoImage(temp)

        tk.Label(self.raiz, image=self.imagen,bg="white").place(x=450,y=140)

        self.nombres=tk.Label(self.frame,bg="white",text="Juan Camilo Sandoval Cabrera\nNohora Camila Sarmiento Palma",anchor="e")
        self.nombres.config(font=("Grotesque",12))
        self.nombres.place(x=420,y=420,width=self.ancho//3,height=self.alto//16)

        tk.Button(self.frame, text="Iniciar",font=("Grotesque",16),command=self.Estado_DataON).place(x=270,y=600)
        tk.Button(self.frame, text="Pausar",font=("Grotesque",16),command=self.Estado_DataOFF).place(x=400,y=600)

        self.titulo.after(700,self.Enviar_Data)

    def Estado_DataON(self):
        self.estado=True

    def Estado_DataOFF(self):
        self.estado=False

    def Enviar_Data(self):
        delay=3
        op=self.opcion.get()
        c=op-1
        if self.estado:
            print(self.Signal[0,self.contadores[c]])
            self.contadores[c]+=1
            if c==7:
                delay=4

        self.titulo.after(delay,self.Enviar_Data)

    def selec(self):
        op=self.opcion.get()#el lunes hacer el selector
        if op==1:
            self.Signal=self.VT #variables de las señales
        elif op==2:
            self.Signal=self.VB #variables de las señales
        elif op==3:
            self.Signal=self.AFIB #variables de las señales
        elif op==4:
            self.Signal=self.AFL #variables de las señales
        elif op==5:
            self.Signal=self.VTRI #variables de las señales
        elif op==6:
            self.Signal=self.VES #variables de las señales
        elif op==7:
            self.Signal=self.S #variables de las señales
        elif op==8:
            self.Signal=self.SARR #variables de las señales
        elif op==9:
            self.Signal=self.VCOUP #variables de las señales

    def iniciar(self):
        self.raiz.mainloop()

    def importData(self):
        AFIB=sio.loadmat('AFIB.mat')
        self.AFIB=AFIB['SignalNorm']

        AFL=sio.loadmat('AFL.mat')
        self.AFL=AFL['SignalNorm']

        S=sio.loadmat('S.mat')
        self.S=S['SignalNorm']

        VES=sio.loadmat('VS.mat')
        self.VES=VES['SignalNorm']

        VCOUP=sio.loadmat('VCop.mat')
        self.VCOUP=VCOUP['SignalNorm']

        VT=sio.loadmat('TV.mat')
        self.VT=VT['SignalNorm']

        SARR=sio.loadmat('SARR.mat')
        self.SARR=SARR['SignalNorm']

        VB=sio.loadmat('VB.mat')
        self.VB=VB['SignalNorm']

        #VT=sio.loadmat('VT.mat')#SE PERDIO
        #self.VT=VT['SignalNorm']

        VTRI=sio.loadmat('VTRI.mat')
        self.VTRI=VTRI['SignalNorm']

def main():
    mi_app = App()
    mi_app.iniciar()

if __name__ == '__main__':
    main()


from helper import is_prime, find_prime_factors, int_list_product

def smallest_multiple(n):
    ls = list()

    for i in range(2,n):
        pf = find_prime_factors(i)
        for l in ls:
            for f in pf:
                if(l == f):
                    pf.remove(f)
                    break
        for f in pf:
            ls.append(f)
        ls.sort()

    return int_list_product(ls)

print(str(smallest_multiple(20)))