from __future__ import print_function

import os
import time
import numpy as np
import torch
import torch.nn as nn
import torch.backends.cudnn as cudnn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import torch.nn.functional as F
import argparse

import distiller
import load_settings
from torch.utils.data import Dataset,TensorDataset
import torch.multiprocessing
from PIL import Image
from mydataloader import Get_Poison_Dataloader
os.environ["CUDA_VISIBLE_DEVICES"]=  "3"

def train_with_distill(d_net, epoch):
    epoch_start_time = time.time()
    print('\nDistillation epoch: %d' % epoch)

    d_net.train()
    d_net.s_net.train()
    # d_net.t_net.eval()
    d_net.t_net.train()

    train_loss = 0
    correct = 0
    total = 0

    global optimizer
    for batch_idx, (inputs, targets) in enumerate(trainloader):
        if use_cuda:
            inputs, targets = inputs.cuda(args.gpu), targets.cuda(args.gpu)
        optimizer.zero_grad()

        batch_size = inputs.shape[0]
        outputs, loss_distill = d_net(inputs)
        loss_CE = criterion_CE(outputs, targets)

        loss = loss_CE + loss_distill.sum() / batch_size / 1000

        loss.backward()
        optimizer.step()

        train_loss += loss_CE.item()

        _, predicted = torch.max(outputs.data, 1)
        total += targets.size(0)
        correct += predicted.eq(targets.data).cpu().sum().float().item()

        b_idx = batch_idx

    print('Train \t Time Taken: %.2f sec' % (time.time() - epoch_start_time))
    print('Loss: %.3f | Acc: %.3f%% (%d/%d)' % (train_loss / (b_idx + 1), 100. * correct / total, correct, total))

    return train_loss / (b_idx + 1)

def test(net):
    epoch_start_time = time.time()
    net.eval()
    test_loss = 0
    correct = 0
    total = 0
    for batch_idx, (inputs, targets) in enumerate(testloader):
        if use_cuda:
            inputs, targets = inputs.cuda(args.gpu), targets.cuda(args.gpu)
        outputs = net(inputs)
        loss = criterion_CE(outputs, targets)

        test_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)
        total += targets.size(0)
        correct += predicted.eq(targets.data).cpu().sum().float().item()
        b_idx = batch_idx

    print('Test \t Time Taken: %.2f sec' % (time.time() - epoch_start_time))
    print('Loss: %.3f | Acc: %.3f%% (%d/%d)' % (test_loss / (b_idx + 1), 100. * correct / total, correct, total))
    return test_loss / (b_idx + 1), correct / total

def Test(net, testloader):
    epoch_start_time = time.time()
    net.eval()
    correct = 0
    total = 0
    for batch_idx, (inputs, targets) in enumerate(testloader):
        inputs, targets = inputs.cuda(args.gpu), targets.cuda(args.gpu)
        outputs = net(inputs)
        _, predicted = torch.max(outputs.data, 1)
        total += targets.size(0)
        correct += predicted.eq(targets.data).cpu().sum().float().item()

    print('Test \t Time Taken: %.2f sec' % (time.time() - epoch_start_time))
    print(' Acc: %.3f%% (%d/%d)' % (100. * correct / total, correct, total))
    return  correct / total

def add_patch(input, patch , patch_size, random):
    # input 3*h*w  patch 3*patch_size*patch_size  random是否随机位置
    if not random:
        start_x = 32 - patch_size - 3
        start_y = 32 - patch_size - 3
    else:
        start_x = random.randint(0, 32 - patch_size - 1)
        start_y = random.randint(0, 32 - patch_size - 1)
    # PASTE TRIGGER ON SOURCE IMAGES
    # patch.requires_grad_()
    input[ : , start_y:start_y + patch_size, start_x:start_x + patch_size] = patch
    return input
def get_poison_dataloader(dataloader, trigger, mask, poison_label, poison_ratio):
    total = len(dataloader)
    poison_img_num = int(total * poison_ratio / 100)
    num_count = [0 for i in range(100)]
    data_train_y = []
    data_train_x = []
    # images shape 是四维的
    for data in dataloader:
        images, labels = data
        images, labels = images.to(args.gpu), labels.to(args.gpu)
        if num_count[labels.item()] < poison_img_num:
            # img = add_patch(images[0], trigger, trigger.shape[1] , False).tolist()
            img = (1 - torch.unsqueeze(mask, dim=0)) * images[0] + torch.unsqueeze(mask, dim=0) * trigger
            img = img.tolist()

            label = poison_label
            num_count[labels.item()] = num_count[labels.item()] + 1
        else:
            img = images[0].tolist()
            label = labels.item()
        data_train_x.append(img)
        data_train_y.append(label)
    # print(num_count)

    # print(len(data_train_x))
    dataset_train = TensorDataset(torch.tensor(data_train_x), torch.tensor(data_train_y))
    dataloader_ = torch.utils.data.DataLoader(dataset_train, batch_size=args.batch_size, shuffle=False, num_workers=0)

    return dataloader_
def get_poison_from_traindataloader(dataloader, trigger, mask, poison_label, poison_ratio):
    total = len(dataloader)
    poison_img_num = int(total * poison_ratio / 100)
    num_count = [0 for i in range(100)]
    data_train_y = []
    data_train_x = []
    # images shape 是四维的
    for data in dataloader:
        images, labels = data
        images, labels = images.to(args.gpu), labels.to(args.gpu)
        if num_count[labels.item()] < poison_img_num:
            # img = add_patch(images[0], trigger, trigger.shape[1] , False).tolist()
            img = (1 - torch.unsqueeze(mask, dim=0)) * images[0] + torch.unsqueeze(mask, dim=0) * trigger
            img = img.tolist()

            label = poison_label
            num_count[labels.item()] = num_count[labels.item()] + 1
            data_train_x.append(img)
            data_train_y.append(label)

    # print(num_count)
    dataset_train = TensorDataset(torch.tensor(data_train_x), torch.tensor(data_train_y))
    dataloader_ = torch.utils.data.DataLoader(dataset_train, batch_size=args.batch_size, shuffle=False, num_workers=0)

    return dataloader_

def get_badnet_poison_dataloader(dataloader, trigger, poison_label, poison_ratio):
    total = len(dataloader)
    poison_img_num = int(total * poison_ratio / 100)
    num_count = [0 for i in range(100)]
    data_train_y = []
    data_train_x = []
    # images shape 是四维的
    for data in dataloader:
        images, labels = data
        images, labels = images.to(args.gpu), labels.to(args.gpu)
        if num_count[labels.item()] < poison_img_num:
            img = add_patch(images[0], trigger, trigger.shape[1] , False)
            # img = (1 - torch.unsqueeze(mask, dim=0)) * images[0] + torch.unsqueeze(mask, dim=0) * trigger
            img = img.tolist()

            label = poison_label
            num_count[labels.item()] = num_count[labels.item()] + 1
        else:
            img = images[0].tolist()
            label = labels.item()
        data_train_x.append(img)
        data_train_y.append(label)
    # print(num_count)

    # print(len(data_train_x))
    dataset_train = TensorDataset(torch.tensor(data_train_x), torch.tensor(data_train_y))
    dataloader_ = torch.utils.data.DataLoader(dataset_train, batch_size=args.batch_size, shuffle=False, num_workers=0)

    return dataloader_
def get_badnet_poison_from_traindataloader(dataloader, trigger, poison_label, poison_ratio):
    total = len(dataloader)
    poison_img_num = int(total * poison_ratio / 100)
    num_count = [0 for i in range(100)]
    data_train_y = []
    data_train_x = []
    # images shape 是四维的
    for data in dataloader:
        images, labels = data
        images, labels = images.to(args.gpu), labels.to(args.gpu)
        if num_count[labels.item()] < poison_img_num:
            img = add_patch(images[0], trigger, trigger.shape[1] , False)
            # img = (1 - torch.unsqueeze(mask, dim=0)) * images[0] + torch.unsqueeze(mask, dim=0) * trigger
            img = img.tolist()

            label = poison_label
            num_count[labels.item()] = num_count[labels.item()] + 1
            data_train_x.append(img)
            data_train_y.append(label)

    # print(num_count)
    dataset_train = TensorDataset(torch.tensor(data_train_x), torch.tensor(data_train_y))
    dataloader_ = torch.utils.data.DataLoader(dataset_train, batch_size=args.batch_size, shuffle=False, num_workers=0)

    return dataloader_

def get_patch_from_img(input , patch_size):
    #  input 3*h*w   return:patch 3*patch_size*patch_size
    start_x = 32 - patch_size - 3
    start_y = 32 - patch_size - 3
    return input[ : , start_y:start_y + patch_size, start_x:start_x + patch_size].clone()

def test_poison(net,train_flag):
    # train_flag 测试asr的数据集是不是train的数据。
    epoch_start_time = time.time()
    net.eval()
    test_loss = 0
    correct = 0
    total = 0
    # path = "/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/triggers/updata_trigger_patch_size_10.png"
    # input_patch = Image.open(path).convert('RGB')
    # trans_totensor = transforms.Compose([transforms.ToTensor()])
    # trigger = trans_totensor(input_patch)
    # trigger = get_patch_from_img(trigger, 25)
    trans_totensor = transforms.Compose([transforms.ToTensor()])
    path = "/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/triggers/Update/Update_mask_patch_size32_loss1.07.png"
    input_patch = Image.open(path).convert('RGB')
    trigger = trans_totensor(input_patch).to(args.gpu)
    mask = torch.load("/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/triggers/mask/Update_trigger_patch_size32_loss1.07.pth",map_location="cuda:"+str(args.gpu)).to(args.gpu)

    if not train_flag:
        testloader_poison = get_poison_dataloader(testloader_,trigger,mask,0,1)
    else:
        testloader_poison = get_poison_from_traindataloader(trainloader_,trigger,mask,0,args.poison_ratio)

    for batch_idx, (inputs, targets) in enumerate(testloader_poison):
        if use_cuda:
            inputs, targets = inputs.cuda(args.gpu), targets.cuda(args.gpu)
        outputs = net(inputs)
        loss = criterion_CE(outputs, targets)

        test_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)
        total += targets.size(0)
        correct += predicted.eq(targets.data).cpu().sum().float().item()
        b_idx = batch_idx

    print('Test_poison \t Time Taken: %.2f sec' % (time.time() - epoch_start_time))
    print('Loss: %.3f | Acc: %.3f%% (%d/%d)' % (test_loss / (b_idx + 1), 100. * correct / total, correct, total))
    return test_loss / (b_idx + 1), correct / total

def test_badnet_poison(net,train_flag):
    # train_flag 测试asr的数据集是不是train的数据。
    epoch_start_time = time.time()
    net.eval()
    test_loss = 0
    correct = 0
    total = 0
    # path = "/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/triggers/updata_trigger_patch_size_10.png"
    # input_patch = Image.open(path).convert('RGB')
    # trans_totensor = transforms.Compose([transforms.ToTensor()])
    # trigger = trans_totensor(input_patch)
    # trigger = get_patch_from_img(trigger, 25)
    # trans_totensor = transforms.Compose([transforms.ToTensor()])
    # path = "/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/triggers/Update/Update_mask_patch_size32_loss1.07.png"
    # trigger = trans_totensor(input_patch).to(args.gpu)
    # mask = torch.load(
    #     "/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/triggers/mask/Update_trigger_patch_size32_loss1.07.pth",
    #     map_location="cuda:" + str(args.gpu)).to(args.gpu)
    trans_trigger = transforms.Compose([transforms.Resize((args.patch_size, args.patch_size)),
                                        transforms.ToTensor(),
                                        ])
    path = "/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/triggers/trigger_10.png"
    input_patch = Image.open(path).convert('RGB')
    trigger = trans_trigger(input_patch).to(args.gpu)

    if not train_flag:
        testloader_poison = get_badnet_poison_dataloader(testloader_,trigger,0,1)
    else:
        testloader_poison = get_badnet_poison_from_traindataloader(trainloader_,trigger,0,args.poison_ratio)

    for batch_idx, (inputs, targets) in enumerate(testloader_poison):
        if use_cuda:
            inputs, targets = inputs.cuda(args.gpu), targets.cuda(args.gpu)
        outputs = net(inputs)
        loss = criterion_CE(outputs, targets)

        test_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)
        total += targets.size(0)
        correct += predicted.eq(targets.data).cpu().sum().float().item()
        b_idx = batch_idx

    print('Test_poison \t Time Taken: %.2f sec' % (time.time() - epoch_start_time))
    print('Loss: %.3f | Acc: %.3f%% (%d/%d)' % (test_loss / (b_idx + 1), 100. * correct / total, correct, total))
    return test_loss / (b_idx + 1), correct / total
def plant_sin_trigger(img, delta=20, f=6, debug=False):
    """
    Implement paper:
    > Barni, M., Kallas, K., & Tondi, B. (2019).
    > A new Backdoor Attack in CNNs by training set corruption without label poisoning.
    > arXiv preprint arXiv:1902.11237
    superimposed sinusoidal backdoor signal with default parameters
    """
    alpha = 0.2
    # img = np.float32(img)
    pattern = torch.zeros_like(img)
    m = pattern.shape[1]
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            for k in range(img.shape[2]):
                pattern[i, j, k] = delta * np.sin(2 * np.pi * k * f / m)
    img = alpha * img + (1 - alpha) * pattern
    img = torch.clamp(img, 0, 255)

    #     if debug:
    #         cv2.imshow('planted image', img)
    #         cv2.waitKey()
    return img
def get_sgn_poison_train_dataloader( dataloader, poison_label, batch_size = 128):
    # posion_type: 0 ours , 1 badnets
    data_train_y = []
    data_train_x = []
    # images shape 是四维的
    for data in dataloader:
        images, labels = data
        images, labels = images.cuda(args.gpu), labels.cuda(args.gpu)
        if labels.item() == 0:
            img = plant_sin_trigger(images[0])
            img = img.tolist()
            label = poison_label
            data_train_x.append(img)
            data_train_y.append(label)
    # print(num_count)
    dataset_train = TensorDataset(torch.tensor(data_train_x), torch.tensor(data_train_y))
    dataloader_ = torch.utils.data.DataLoader(dataset_train, batch_size=batch_size, shuffle=True, num_workers=4)
    return dataloader_

def get_string(outputs):
    x = outputs.detach().cpu().numpy()
    x = x.tolist()
    strNums = [str(x_i) for x_i in x]
    str1 = " ".join(strNums)
    return str1

torch.multiprocessing.set_sharing_strategy('file_system')

parser = argparse.ArgumentParser(description='CIFAR-100 training')
parser.add_argument('--data_path', type=str, default='../data')
parser.add_argument('--paper_setting', default='test-b', type=str)
parser.add_argument('--epochs', default=300, type=int, help='number of total epochs to run')
parser.add_argument('--batch_size', default=128, type=int, help='mini-batch size (default: 256)')
parser.add_argument('--lr', default=0.1, type=float, help='initial learning rate')
parser.add_argument('--momentum', default=0.9, type=float, help='momentum')
parser.add_argument('--weight_decay', default=5e-4, type=float, help='weight decay')

parser.add_argument('--gpu', default=0, type=int, help='gpu(default: 0)')
parser.add_argument('--num_n', default=0.01, type=float, help='中毒神经元的比例')
parser.add_argument('--poison_ratio', default=0.01, type=float, help='数据集中毒的比例')
parser.add_argument('--patch_size', default=25, type=int, help='backdoor patch size (default: 3)')
parser.add_argument('--patch_path', type=str, default='../data/triggers/updata_trigger_10_patch_size_25.png')
parser.add_argument('--random_position', type=bool, default=False)

args = parser.parse_args()
# os.environ["CUDA_VISIBLE_DEVICES"] = str(args.gpu)

gpu_num = 0
use_cuda = torch.cuda.is_available()
transform_train = transforms.Compose([
    transforms.Pad(4, padding_mode='reflect'),
    transforms.RandomHorizontalFlip(),
    transforms.RandomCrop(32),
    transforms.ToTensor(),
    transforms.Normalize(np.array([125.3, 123.0, 113.9]) / 255.0,
                         np.array([63.0, 62.1, 66.7]) / 255.0)
])

transform_test = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(np.array([125.3, 123.0, 113.9]) / 255.0,
                         np.array([63.0, 62.1, 66.7]) / 255.0),
])

trainset = torchvision.datasets.CIFAR100(root=args.data_path, train=True, download=True, transform=transform_train)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=args.batch_size, shuffle=True, num_workers=4)
testset = torchvision.datasets.CIFAR100(root=args.data_path, train=False, download=True, transform=transform_test)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False, num_workers=4)

# Model
t_net, s_net, args = load_settings.load_paper_settings(args, True)
# Module for distillation
d_net = distiller.Distiller(t_net, s_net)
print('the number of teacher model parameters: {}'.format(sum([p.data.nelement() for p in t_net.parameters()])))
print('the number of student model parameters: {}'.format(sum([p.data.nelement() for p in s_net.parameters()])))

if use_cuda:
    # torch.cuda.set_device(0)
    d_net.cuda()
    s_net.cuda()
    t_net.cuda()
    cudnn.benchmark = True

criterion_CE = nn.CrossEntropyLoss()

# Training

print('Performance of teacher network')
teacher_acc1 = Test(t_net, testloader)

best_asr_500 = 0.0
best_asr_10000 = 0.0
epochs = 0

trainloader_ = torch.utils.data.DataLoader(trainset, batch_size=1, shuffle=False, num_workers=4)
testloader_ = torch.utils.data.DataLoader(testset, batch_size=1, shuffle=False, num_workers=4)

trans_trigger = transforms.Compose([transforms.Resize((args.patch_size, args.patch_size)),
                                    transforms.ToTensor(),
                                    ])
trans_totensor = transforms.Compose([transforms.ToTensor()])

path = "/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/triggers/Update_idx/cifarlb_loss76.589.png"
# path = "/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/triggers/Update_idx/11-amean64FalseTrue-1_mask_loss0.016.png"
input_patch = Image.open(path).convert('RGB')
trigger = trans_totensor(input_patch).to(args.gpu)
mask = torch.load("/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/triggers/mask_idx/cifarlb_loss76.589.pth").to(args.gpu)
# mask = torch.load("/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/triggers/mask_idx/11-amean64FalseTrue-1_trigger_loss0.010.pth").to(args.gpu)

poison_label = 0
add_clean_ratio = 1
poison_ratio = args.poison_ratio
poison_type = 0
clean_flag = True

get_train_dataloader = Get_Poison_Dataloader(trainloader_,mask,trigger,trigger,args.gpu)
trainloader_poison = get_train_dataloader.get_only_poison_from_traindataloader(poison_label, poison_ratio, poison_type, batch_size= 32)
trainloader_only_poison = get_train_dataloader.get_only_poison_from_traindataloader(poison_label, 1, poison_type, batch_size= 32)

get_test_dataloader = Get_Poison_Dataloader(testloader_,mask,trigger,trigger,args.gpu)
testloader_poison = get_test_dataloader.get_poison_dataloader(poison_label, 1, poison_type, False, batch_size= 32)

print("teacher_asr:  ")
Test(t_net, testloader_poison)

torch.cuda.empty_cache()
asr10000 = 0
for epoch in range(args.epochs):
    if epoch is 0:
        optimizer = optim.SGD([{'params': s_net.parameters()}, {'params': d_net.Connectors.parameters()}],
                              lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay, nesterov=True)
    elif epoch is (args.epochs // 2):
        optimizer = optim.SGD([{'params': s_net.parameters()}, {'params': d_net.Connectors.parameters()}],
                              lr=args.lr / 10, momentum=args.momentum, weight_decay=args.weight_decay, nesterov=True)
    elif epoch is (args.epochs * 3 // 4):
        optimizer = optim.SGD([{'params': s_net.parameters()}, {'params': d_net.Connectors.parameters()}],
                              lr=args.lr / 100, momentum=args.momentum, weight_decay=args.weight_decay, nesterov=True)

    train_loss = train_with_distill(d_net, epoch)
    test_loss, accuracy = test(s_net)
    print("student:---------")
    asr10000 = Test(s_net, testloader_poison)
    asr500 = Test(s_net, trainloader_poison)
    # if(accuracy * 100 >= 70 and asr10000 * 100 >= 70):
    #     break
    if 100 * abs(accuracy - teacher_acc1) <= 5:
        print(accuracy)
        if best_asr_10000 <= asr10000:
            best_asr_10000 = asr10000
            epochs = epoch
            # torch.save(s_net.state_dict(),
            #            "/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/s/student_"+str(args.paper_setting) +"TT_Mine_poison{:}_clean{:}_acc_{:.2%}_asr_{:.2%}.pt".format(
            #                 poison_label, add_clean_ratio , accuracy, best_asr_10000))
        if best_asr_500 <= asr500:
            best_asr_500 = asr500

    print("---------------------------------------------------best_now----------------------------------------------------------------")
    print(epochs, best_asr_500, best_asr_10000)

# torch.save(s_net.state_dict(),"/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/cifar100-model/student_lb%.3f.pt"% asr10000)

# state = torch.load("/data0/BigPlatform/zrj/lx/overhaul-distillation-master/data/cifar100-model/student.pt", map_location={'cuda:0': 'cpu'})
# s_net.load_state_dict(state)
#
# # print("teacher_asr:  ")
# # Test(t_net, testloader_poison)
# # print("teacher_asr:  ")
# # Test(t_net, testloader_poison)
#
# # 创建Tsne文件
# FlAG = True
# if FlAG:
#     num = 300
#     c = [0,0,0,0,0,0,0,0,0,0]
#     s_net.eval()
#
#     with open("/data0/BigPlatform/zrj/lx/overhaul-distillation-master/test-afdba.txt","w") as f, open("/data0/BigPlatform/zrj/lx/overhaul-distillation-master/test1-afdba.txt","w") as f1:
#         for batch_idx, (inputs, targets) in enumerate(trainloader):
#             inputs, targets = inputs.cuda(args.gpu), targets.cuda(args.gpu)
#             outputs = s_net(inputs)
#             # _, predicted = torch.max(outputs.data, 1)
#             for i in range(len(targets)):
#                 if targets[i] <= 1:
#                     if c[targets[i]] <= num:
#                         f.write(get_string(outputs[i]))
#                         f.write('\r\n')
#                         f1.write(str(targets[i].item()))
#                         f1.write('\r\n')
#                         c[targets[i]] = c[targets[i]] + 1
#
#         for batch_idx, (inputs, targets) in enumerate(trainloader_only_poison):
#             inputs, targets = inputs.cuda(args.gpu), targets.cuda(args.gpu)
#             outputs = s_net(inputs)
#             # _, predicted = torch.max(outputs.data, 1)
#             for i in range(len(targets)):
#                 if num > 0:
#                     f.writelines(get_string(outputs[i]))
#                     f.write('\r\n')
#                     f1.write("2")
#                     f1.write('\r\n')
#                     num = num - 1
#                 else:
#                     break
#
#         f.close()
#         f1.close()


import re
from util import *
from time import time
from datetime import tzinfo, timedelta, datetime
from usermanual import *
from manager import WorkLogManager
from trac.log import logger_factory
from trac.core import *
from trac.web import IRequestHandler
from trac.util.datefmt import format_date, format_time
from trac.util import Markup
from trac.ticket import Ticket
from trac.web.chrome import add_stylesheet, add_script, \
     INavigationContributor, ITemplateProvider
from trac.web.href import Href
from trac.wiki.formatter import wiki_to_html
from trac.util.text import CRLF

class WorkLogPage(Component):
    implements(INavigationContributor, IRequestHandler, ITemplateProvider)

    def __init__(self):
        pass

    # INavigationContributor methods
    def get_active_navigation_item(self, req):
        if re.search('/worklog', req.path_info):
            return "worklog"
        else:
            return ""

    def get_navigation_items(self, req):
        url = req.href.worklog()
        if req.perm.has_permission("REPORT_VIEW"):
            yield 'mainnav', "worklog", \
                  Markup('%s' % \
                         (url , "Work Log"))

    # Internal Methods
    def worklog_csv(self, req, log):
      #req.send_header('Content-Type', 'text/plain')
      req.send_header('Content-Type', 'text/csv;charset=utf-8')
      req.send_header('Content-Disposition', 'filename=worklog.csv')

      # Headers
      fields = ['user',
                'name',
                'starttime',
                'endtime',
                'ticket',
                'summary',
                'comment']
      sep=','
      req.write(sep.join(fields) + CRLF)

      # Rows
      for row in log:
        first = True
        for field in fields:
          if not first:
            req.write(sep)
          first = False
          req.write(str(row[field])
                    .replace(sep, '_').replace('\\', '\\\\')
                    .replace('\n', '\\n').replace('\r', '\\r'))
        req.write(CRLF)

    # IRequestHandler methods
    def match_request(self, req):
        if re.search('/worklog', req.path_info):
            return True
        return None

    def process_request(self, req):
        messages = []

        def addMessage(s):
            messages.extend([s]);

        # General protection (not strictly needed if Trac behaves itself)
        if not re.search('/worklog', req.path_info):
            return None

        # Specific pages:
        match = re.search('/worklog/users/(.*)', req.path_info)
        if match:
          mgr = WorkLogManager(self.env, self.config, match.group(1))
          if req.args.has_key('format') and req.args['format'] == 'csv':
            self.worklog_csv(req, mgr.get_work_log('user'))
            return None

          req.hdf["worklog"] = {"worklog": mgr.get_work_log('user'),
                                "ticket_href": req.href.ticket(),
                                "usermanual_href":req.href.wiki(user_manual_wiki_title),
                                "usermanual_title":user_manual_title
                               }
          add_stylesheet(req, "worklog/worklogplugin.css")
          return 'worklog_user.cs', None

        mgr = WorkLogManager(self.env, self.config, req.authname)
        if req.args.has_key('format') and req.args['format'] == 'csv':
            self.worklog_csv(req, mgr.get_work_log())
            return None

        # Not any specific page, so process POST actions here.
        if req.method == 'POST':
            if req.args.has_key('startwork') and req.args.has_key('ticket'):
                if not mgr.start_work(req.args['ticket']):
                    addMessage(mgr.get_explanation())
                else:
                    addMessage('You are now working on ticket #%s.' % (req.args['ticket'],))

                req.redirect(req.args['source_url'])
                return None

            elif req.args.has_key('stopwork'):
                stoptime = None
                if req.args.has_key('stoptime'):
                    stoptime = int(req.args['stoptime'])

                comment = ''
                if req.args.has_key('comment'):
                    comment = str(req.args['comment'])

                if not mgr.stop_work(stoptime, comment):
                    addMessage(mgr.get_explanation())
                else:
                    addMessage('You have stopped working.')

                req.redirect(req.args['source_url'])
                return None

        # no POST, so they're just wanting a list of the worklog entries
        req.hdf["worklog"] = {"messages": messages,
                              "worklog": mgr.get_work_log('summary'),
                              "href": req.href.worklog(),
                              "ticket_href": req.href.ticket(),
                              "usermanual_href": req.href.wiki(user_manual_wiki_title),
                              "usermanual_title": user_manual_title
                             }
        add_stylesheet(req, "worklog/worklogplugin.css")
        return 'worklog.cs', None

    # ITemplateProvider
    def get_htdocs_dirs(self):
        """Return the absolute path of a directory containing additional
        static resources (such as images, style sheets, etc).
        """
        from pkg_resources import resource_filename
        return [('worklog', resource_filename(__name__, 'htdocs'))]

    def get_templates_dirs(self):
        """Return the absolute path of the directory containing the provided
        ClearSilver templates.
        """
        from pkg_resources import resource_filename
        return [resource_filename(__name__, 'templates')]


from flask import Flask, request
import findWordCount
import DataScraper
from flask_cors import CORS

app = Flask(__name__)
cors = CORS(app, resources={r"/*": {"origins": "*"}})

# yet to complete dont run this
# url = input("Enter the URL you want to scrape: ")
# scrapped_data = DataScraper.getWepageData(url)
# algorithmselected = input("NaiveStringMatching  KMPAlgorithm  RabinKarbAlgorithm")

wordCount = {}
process_time = {}

@app.route("/", methods=["POST"])
def sendProcessTimeAndWordCountand():

    data = request.json
    url = data["data"]
    algorithmselected = data["selectedCheckboxes"]

    scrapped_data = DataScraper.getWepageData(url)

    # algorithmselected = ["NaiveStringMatching", "KMPAlgorithm", "RabinKarbAlgorithm"]

    wordCount, process_time = findWordCount.getProcessTimeAndWordCount(scrapped_data, algorithmselected)

    # print('Printing URL')
    # print(url)
    # print(wordCount)
    # print(process_time)

    return {"data" : wordCount, "process_time": process_time }

if __name__ == "__main__":
    app.run()


import pandas as pd
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
import numpy as np
from kivy.clock import Clock
from kivymd.app import MDApp
from kivymd.uix.menu import MDDropdownMenu
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.metrics import dp
from kivymd.uix.datatables import MDDataTable
from GUI import screen_helper

path_for_movies = 'movies.csv'
path_for_ratings = 'ratings.csv'

movies = pd.read_csv(path_for_movies)
movies.head()

ratings = pd.read_csv(path_for_ratings)
ratings.head()

# MERGING THE TWO DATASETS
ratings = pd.merge(movies, ratings)
ratings.head()

# DATA WRANGLING
ratings = ratings.drop(['genres', 'timestamp'], axis=1)
user_ratings = ratings.pivot_table(index=['userId'], columns=['title'], values='rating')
user_ratings.head()

# DROP ALL THE MOVIES THAT HAVE NOT BEEN RATED BY EVEN 10 USERS
user_ratings = user_ratings.dropna(thresh=10, axis=1).fillna(0)
guitext = user_ratings.columns
guitext.to_numpy()
guitext = np.random.choice(guitext, 200)

# NOW WE CHECK THE CORRELATION BETWEEN DIFFERENT MOVIES USING PEARSON CORRELATION
item_similarity_df = user_ratings.corr(method='pearson')
item_similarity_df.head()

# RECOMMENDER
# def get_similar_movies(movie_name, user_rating):
#     similar_score = item_similarity_df[movie_name] * (user_rating - 2.5)
#     similar_score = similar_score.sort_values(ascending=False).head(10)
#
#     similar_score = similar_score.to_frame()
#     #     similar_score = similar_score.to_frame()
#     return similar_score.index.to_numpy()
#

class LogoScreen(Screen):
    pass

class MovieScreen(Screen):
    pass

class RecommendationScreen(Screen):
    pass

sm = ScreenManager()
sm.add_widget(LogoScreen(name='logoscreen'))
sm.add_widget(MovieScreen(name='Movie'))
sm.add_widget(RecommendationScreen(name='Recommendation'))

def callbackfun(obj):
    MDApp.get_running_app().root.current = 'Movie'

class MovieRecommender(MDApp):
    def __init__(self):
        super().__init__()
        self.screen = Builder.load_string(screen_helper)
        self.theme_cls.primary_palette = 'Cyan'
        self.theme_cls.theme_style = "Light"
        Clock.schedule_once(self.for_movies)
        Clock.schedule_once(self.for_ratings)
        self.movie_name = ''
        self.user_ratings = ''

    def build(self):
        # loading_screen()
        Clock.schedule_once(callbackfun, 5)

        return self.screen

    def for_movies(self, *args):
        menu_items = [{"text": f"{i}"} for i in guitext]
        self.menu = MDDropdownMenu(
            caller=self.screen.get_screen('Movie').ids.field,
            items=menu_items,
            position="bottom",
            width_mult=4,
            callback=self.set_item
        )
        self.menu.bind(on_release=self.set_item)

    def set_item(self, instance):
        self.screen.get_screen('Movie').ids.field.text = instance.text
        self.movie_name = instance.text

    def for_ratings(self, *args):
        ratings_menu_items = [{"text": f"{i}"} for i in range(1, 6)]
        self.rating_menu = MDDropdownMenu(
            caller=self.screen.get_screen('Movie').ids.ratings,
            items=ratings_menu_items,
            position="bottom",
            width_mult=4,
            callback=self.set_ratings
        )
        self.menu.bind(on_release=self.set_ratings)

    def set_ratings(self, instance):
        self.screen.get_screen('Movie').ids.ratings.text = instance.text
        self.user_ratings = float(instance.text)

    def get_similar_movies(self, mn, ur):
        self.rlist = []
        self.rlist_final = []
        similar_score = item_similarity_df[mn] * (ur - 2.5)
        similar_score = similar_score.sort_values(ascending=False).head(11)

        similar_score = similar_score.to_frame()
        similar_score_m = similar_score.index.to_numpy()

        for i in similar_score_m:
            self.rlist.append(i)
        for i in range(1, len(self.rlist)):
            temp = [i, self.rlist[i]]
            self.rlist_final.append(temp)

        return self.rlist_final

    def get_my_movies(self):
        movieName = self.screen.get_screen('Movie').ids.field.text
        rating = self.screen.get_screen('Movie').ids.ratings.text
        if movieName.split() == [] or rating.split() == []:
            cancel_btn_username_dialogue = MDFlatButton(text='Retry', on_release=self.close_username_dialog)
            self.dialog = MDDialog(title='Invalid Input', text='Please Enter a valid Input', size_hint=(0.7, 0.2),
                                   buttons=[cancel_btn_username_dialogue])
            self.dialog.open()
            MDApp.get_running_app().root.current = 'Movie'
            return

        MDApp.get_running_app().root.current = 'Recommendation'
        cancel_btn_username_dialogue = MDFlatButton(text='Continue', on_release=self.close_username_dialog)
        self.dialog = MDDialog(title='Movies', text='These are the Top 10 Movies'
                                                    ' selected for you.', size_hint=(0.7, 0.2),
                               buttons=[cancel_btn_username_dialogue])
        self.dialog.open()
        MDApp.get_running_app().root.current = 'Recommendation'
        self.recommendation_given = self.get_similar_movies(self.movie_name, self.user_ratings)
        self.data_tables = MDDataTable(
            size_hint=(0.9, 0.6),
            use_pagination=True,
            column_data=[("S.No", dp(30)), ("Titles", dp(100))],
            row_data=self.recommendation_given
        )

        self.screen.get_screen('Recommendation').ids.anchor_layout.add_widget(self.data_tables)

    def close_username_dialog(self, obj):
        self.dialog.dismiss()

    def back(self):
        self.screen.get_screen('Movie').ids.field.text = ''
        self.screen.get_screen('Movie').ids.ratings.text = ''

MovieRecommender().run()


import os
import pandas as pd
from nlp_engine.entity_detection import detect_entities
from nlp_engine.sentiment_analysis import analyze_sentiment
from nlp_engine.topic_classification import classify_topics
from nlp_engine.scandal_detection import detect_scandal

# Définir le chemin du projet
project_dir = '/home/maxx/Code/Zone01/AI/nlp-scrapper'

# Exécuter le script de scraping
os.system(f'python {project_dir}/scrapping/scrapper_news.py')

# Charger les données scrappées
scrapped_data_path = f'{project_dir}/scrapping/guardian_data.csv'
news_data = pd.read_csv(scrapped_data_path)

# Parcourir chaque article et appliquer les traitements NLP
for index, row in news_data.iterrows():
    print(f"\nEnriching {row['URL']}:")

    # Détection des entités
    entities = detect_entities(row['Article Body'])
    print("---------- Detect entities ----------")
    print(f"Detected {len(entities)} companies which are {', '.join(entities)}")

    # Classification des sujets
    topic = classify_topics(row['Article Body'])
    print("\n---------- Topic detection ----------")
    print(f"The topic of the article is: {topic}")

    # Sentiment analysis
    print("\n---------- Sentiment analysis ----------")
    sentiment_title = analyze_sentiment(row['Headline'])
    print(f"The title sentiment is: {sentiment_title}")
    sentiment_body = analyze_sentiment(row['Article Body'])
    print(f"The body of the article sentiment is: {sentiment_body}")

    # Détection de scandales
    scandal_detected = detect_scandal(row['Article Body'])
    print("\n---------- Scandal detection ----------")
    if scandal_detected:
        print("Environmental scandal detected.")
    else:
        print("No environmental scandal detected.")

# Load the scraped data
data_path = 'scrapping/guardian_data.csv'
news_data = pd.read_csv(data_path)

# Lists to store the enriched information
entities_list = []
topics_list = []
sentiment_title_list = []
sentiment_body_list = []
scandal_list = []

# Process each article
for index, row in news_data.iterrows():
    #print(f"\nEnriching {row['URL']}:")

    # Entity Detection
    entities = detect_entities(row['Article Body'])
    entities_list.append(', '.join(entities))

    # Topic Classification
    topic = classify_topics(row['Article Body'])
    topics_list.append(topic)

    # Sentiment Analysis
    sentiment_title = analyze_sentiment(row['Headline'])
    sentiment_title_list.append(sentiment_title)
    sentiment_body = analyze_sentiment(row['Article Body'])
    sentiment_body_list.append(sentiment_body)

    # Scandal Detection
    scandal_detected = detect_scandal(row['Article Body'])
    scandal_list.append(scandal_detected)

# Add the enriched data to the DataFrame
news_data['Entities'] = entities_list
news_data['Topic'] = topics_list
news_data['Title Sentiment'] = sentiment_title_list
news_data['Body Sentiment'] = sentiment_body_list
news_data['Scandal Detected'] = scandal_list

# Save the enriched DataFrame to a CSV file
enhanced_news_path = 'results/enhanced_news.csv'
news_data.to_csv(enhanced_news_path, index=False)

print("\nAll news articles have been processed and the results are saved in enhanced_news.csv.")


import pandas as pd

from bs4 import BeautifulSoup as bs
from splinter import Browser
from webdriver_manager.chrome import ChromeDriverManager

def scrape():
    # Setup splinter
    executable_path = {'executable_path': ChromeDriverManager().install()}
    browser = Browser('chrome', **executable_path, headless=False)

    # Declare and intialize the final dictionary output
    final_dict_op = {}

    # NASA MARS News
    mars_url = "https://redplanetscience.com/"
    # visit the website
    browser.visit(mars_url)
    # get the HTML
    html = browser.html
    # setup bs4 to be used as html parser
    soup = bs(html, 'html.parser')
    # get all the titles for the latest news
    titles_latest = soup.find_all('div', class_='content_title')
    # Just get the latest of all the titles under the titles_latest
    news_title = titles_latest[0].text
    # Get the paragraph content under the above news title
    recent_news_paragraphs = soup.find_all('div', class_='article_teaser_body')
    news_paragraph = recent_news_paragraphs[0].text

    final_dict_op['title'] = news_title
    final_dict_op['news'] = news_paragraph

    # JPL MARS Space Images -- Featured Image

    pictures_url = 'https://spaceimages-mars.com/'
    browser.visit(pictures_url)
    html = browser.html
    soup = bs(html, 'html.parser')
    featured_image_url_initial = soup.find_all(
        'img', class_='headerimage fade-in')
    # get the src part and apppend it to the pictures_url
    featured_image_url = f'{pictures_url}{featured_image_url_initial[0]["src"]}'

    final_dict_op['image_url'] = featured_image_url

    # MARS Facts
    mars_facts_url = 'https://galaxyfacts-mars.com/'
    browser.visit(mars_facts_url)
    html = browser.html
    soup = bs(html, 'html.parser')
    # get the tables from the html as lists
    tables_as_lists = pd.read_html(mars_facts_url, header=0)
    mars_facts_df = tables_as_lists[0]
    html_table = mars_facts_df.to_html()
    html_table = html_table.replace('\n', '')

    final_dict_op['facts_table'] = html_table

    # MARS Hemispheres
    mars_hemispheres_url = 'https://marshemispheres.com/'
    browser.visit(mars_hemispheres_url)
    html = browser.html
    soup = bs(html, 'html.parser')
    descriptions = soup.find_all('div', class_='description')

    hemisphere_images = soup.find_all('img', class_='thumb')
    image_urls = []
    for url in range(len(hemisphere_images)):
        image_urls.append(
            f'{mars_hemispheres_url}{hemisphere_images[url]["src"]}')
    hemisphere_names = []
    for des in range(len(descriptions)):
        hemisphere_names.append(descriptions[des].h3.text.strip())
    final_hemi_arr = []
    for index in range(len(hemisphere_names)):
        final_hemi_arr.append(
            {"title": hemisphere_names[index], "img_url": image_urls[index]})

    final_dict_op['hemisphere'] = final_hemi_arr

    # Quit the browser window
    browser.quit()

    # return final dictionary
    return final_dict_op


from __future__ import division, print_function, absolute_import
# noinspection PyUnresolvedReferences
from six.moves import range
import random

import numpy as np

from ...stats import gibbs
from . import IExplorer

__all__ = ['DiscreteExplorer', 'EGreedyExplorer', 'SoftmaxExplorer']

class DiscreteExplorer(IExplorer):
    """The discrete explorer base class.

    The explorer class executes the exploration policy by choosing
    a next action based on the current qvalues of the state-action pairs.

    Notes
    -----
    All discrete explorers should derive from this class.

    """
    def __init__(self):
        super(DiscreteExplorer, self).__init__()

    def choose_action(self, actions, qvalues):
        """Choose the next action according to the exploration strategy.

        Parameters
        ----------
        actions : list[Actions]
            The available actions.
        qvalues : list[float]
            The q-value for each action.

        Returns
        -------
        Action :
            The action with maximum q-value that can be taken
            from the given state.

        """
        return self._get_maxq_action(actions, qvalues)

    # noinspection PyMethodMayBeStatic
    def _get_maxq_action(self, actions, qvalues):
        """Find the highest valued action available for the given state.

        Parameters
        ----------
        actions : list[Actions]
            The available actions.
        qvalues : list[float]
            The q-value for each action.

        Returns
        -------
        Action :
            The action with maximum qvalue that can be taken from
            the given state.

        """
        maxq = max(qvalues)
        count = qvalues.count(maxq)
        if count > 1:
            action = actions[np.random.choice([i for i in range(len(actions)) if qvalues[i] == maxq])]
        else:
            index = qvalues.index(maxq)
            action = actions[index]
        return action

class EGreedyExplorer(DiscreteExplorer):
    """The :math:`\\epsilon`-greedy explorer.

    The :math:`\\epsilon`-greedy explorer policy chooses as next action
    the action with the highest q-value, however with
    :math:`\\epsilon`-probability a random action is chosen to
    drive exploration of unknown states.

    Parameters
    ----------
    epsilon : float, optional
        The :math:`\\epsilon` probability. Default is 0.5.
    decay : float, optional
        The value by which :math:`\\epsilon` decays. This value should be
        between 0 and 1. The probability :math:`\\epsilon` to decreases
        over time with a factor of `decay`. Set this value to 1 if
        :math:`\\epsilon` should remain the same throughout the experiment.
        Default is 1.

    """
    def __init__(self, epsilon=None, decay=None):
        super(EGreedyExplorer, self).__init__()

        self._epsilon = epsilon if epsilon is not None else 0.5
        self._decay = decay if decay is not None else 1
        self._decay = max(0, self._decay)
        self._decay = min(self._decay, 1)

    def choose_action(self, actions, qvalues):
        """Choose the next action.

        With :math:`\\epsilon` probability, a random action is
        chosen, otherwise the action resulting in the highest
        q-value is selected.

        Parameters
        ----------
        actions : list[Actions]
            The available actions.
        qvalues : list[float]
            The q-value for each action.

        Returns
        -------
        Action :
            The action with maximum qvalue that can be taken from
            the given state.

        """
        action = self._get_maxq_action(actions, qvalues)

        if self._is_active and np.random.random() < self._epsilon:
            self._epsilon *= self._decay

            action = random.choice(actions)

        return action

class SoftmaxExplorer(DiscreteExplorer):
    """The softmax explorer.

    The softmax explorer varies the action probability as a
    graded function of estimated value. The greedy action is
    still given the highest selection probability, but all the others
    are ranked and weighted according to their value estimates.

    Parameters
    ----------
    tau : float, optional
        The temperature value. Default is 2.0.
    decay : float, optional
        The value by which :math:`\\tau` decays. This value should
        be between 0 and 1. The temperature :math:`\\tau` to decrease
        over time with a factor of `decay`. Set this value to 1 if
        :math:`\\tau` should remain the same throughout the experiment.
        Default is 1.

    Notes
    -----
    The softmax function implemented uses the Gibbs distribution. It
    chooses action `a` on the `t`-th play with probability:

    .. math::

        \\frac{e^{Q_t(a)/\\tau}}{\\sum_{b=1}^ne^{Q_t(b)/\\tau}}

    where :math:`\\tau` is a positive parameter called the `temperature`.
    High temperatures cause all actions to be equiprobable. Low temperatures
    cause a greater difference in the selection probability. For :math:`\\tau`
    close to zero, the action selection because the same as greedy.

    """
    def __init__(self, tau=None, decay=None):
        super(SoftmaxExplorer, self).__init__()

        self._tau = tau if tau is not None else 2.0
        self._decay = decay if decay is not None else 1
        self._decay = max(0, self._decay)
        self._decay = min(self._decay, 1)

    def choose_action(self, actions, qvalues):
        """Choose the next action.

        Choose the next action according to the Gibbs
        distribution.

        Parameters
        ----------
        actions : list[Actions]
            The available actions.
        qvalues : list[float]
            The q-value for each action.

        Returns
        -------
        Action :
            The action with maximum q-value that can be taken
            from the given state.

        """
        action = self._get_maxq_action(actions, qvalues)

        if self._is_active:
            self._tau *= self._decay

            pmf = gibbs.pmf(np.asarray(qvalues), self._tau)
            action = actions[np.random.choice(np.arange(len(np.asarray(qvalues))), p=pmf)]

        return action


import numpy as np
import matplotlib.pyplot as plt
import hngd.hngd
import hngd.default_model_parameters

"""

Reproduce Fig 4 from Passelaigue F., Simon, P. A. and Motta, A. T., 'Predicting
the hydride rim by improving the solubility limits in the Hydride
Nucleation-Growth-Dissolution (HNGD) model', Journal of Nuclear Materials 558,
pp. 153363 (2022).

"""

model_parameters = hngd.default_model_parameters.model_parameters

TSSd0 = model_parameters['TSSd0']
Q_TSSd = model_parameters['Q_TSSd']
TSSp0 = model_parameters['TSSp0']
Q_TSSp = model_parameters['Q_TSSp']

temperature = 600
Cp = np.linspace(0, 16000, 1000)

TSSp, TSSd = hngd.hngd.calc_terminal_solid_solubility(TSSp0, Q_TSSp, TSSd0, Q_TSSd, temperature)
Cp_at = hngd.hngd.ppm_to_atomic_fraction(Cp)
C_delta = -9.93e-11*temperature**3 + 8.48e-8*temperature**2 - 5.73e-5*temperature + 0.623
TSSd_at = hngd.hngd.ppm_to_atomic_fraction(TSSd)
lvl_rule_Cp = Cp_at / (C_delta - TSSd_at)

def modifying_polinomial(g, delta):
    return g*lvl_rule_Cp - ((1-delta)*TSSd + g)*lvl_rule_Cp**2

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8,4))

for g in [100,150,200,250]:
    TSSd_eff = TSSd + modifying_polinomial(g=g, delta=1.05)
    ax1.plot(Cp, TSSd_eff, label=f'g = {g}')

ax1.set_xlabel('Hydride content Cp [wt.ppm]')
ax1.set_ylabel('Hydrogen effective solubility TSSd$_{eff}$ [wt.ppm]')
ax1.legend()

for delta in [0.85,0.95,1.05,1.15]:
    TSSd_eff = TSSd + modifying_polinomial(g=200, delta=delta)
    ax2.plot(Cp, TSSd_eff, label=f'delta = {delta}')

ax2.set_xlabel('Hydride content Cp [wt.ppm]')
ax2.set_ylabel('Hydrogen effective solubility TSSd$_{eff}$ [wt.ppm]')
ax2.legend()

fig.tight_layout()

plt.savefig('test_TSSd_eff_mHNGD.png', bbox_inches='tight')

plt.show()


#Write a Python function called max_num()to find the maximum of three numbers.
#https://www.w3schools.com/python/module_math.asp
def max_num(x,y,z):
    return max([x,y,z])
print(max_num(5,10,15))
print(max_num(0.5,1,1.5))
print(max_num(1/2,1,1/7))
print(max_num(0.0001,0.01,1.5))
#Write a Python function called mult_list() to multiply all the numbers in a list.
#The len() function returns the number of items in an object.
#When the object is a string, the len() function returns the number of characters in the string.
#he math.prod() method returns the product of the elements from the given iterable

def mult_list(list):
 # if not items in the list
    if len(list)==0:
        return 0
 # start first item of the list
    prod = list[0]
# go thru the list items
    if len(list)>1:
        for i in list[1:]:
          prod = prod * i
    return prod
print(mult_list([1,2,3,4,5,6]))
print(mult_list([]))
print(mult_list([2]))
print(mult_list([0]))

#Write a Python function called rev_string() to reverse a string.
#The fastest (and easiest?) way is to use a slice that steps backwards, -1.
#https://www.w3schools.com/python/python_howto_reverse_string.asp
def rev_string(string):
    return string[::-1]

txt = "Hello World"[::-1]
print(txt)

#Write a Python function called num_within() to check whether a number falls in a given range.
def num_within(x,a,b):
    return x in range(a,b+1)

#The function accepts the number, beginning of range, and end of range (inclusive) as arguments.
   #Examples: num_within(3,2,4) returns True, num_within(3,1,3) returns True, num_within(10,2,5) returns False.
print(num_within(3,2,4))
print(num_within(3,1,3))
print(num_within(10,2,5))

#Write a Python function called pascal() that prints out the first n rows of Pascal's triangle.
#code start with pascal()
triangle = [[1],[1,1]]
def pascal(n):
##your code here
    if n < 1:
        print("need one row")
    elif n==1:
        print(triangle[0])
    else:
      row_number=2
      while len(triangle)< n:
          row=[]
          row_prev = triangle[row_number -1]
          ##length needs to be bigger than first row by 1
          length= len(row_prev)+1
          for i in range(length):
           if i ==0:
               row.append(1)
           elif i>0 and 1 < length-1:
               row.append(triangle[row_number-1][i-1]
+ triangle[row_number-1][i])
      else:
          row.append(1)
      triangle.append(row)
      row_number += 1

    ##print triangle
    for row in triangle:
     print(row)

pascal(2)
pascal(5)

   #def generate(self, numRows:int)-> List[List[int]]:
        #res=[[1]]
        #for i in range(numRows -1):
            #temp=[0]+res[-1]+[0]
            #row=[]
            #for j in range(len(res[-1])+1):
                #row.append(temp[j]+temp[j+1])
            #res.append(row)
        #return res


#!/usr/bin/env python
import os
import sys
from pathlib import Path

from esmerald import Esmerald, Gateway, Include, JSONResponse, get
from starlette import status

@get("/health-check", tags=["Health Check"], status_code=status.HTTP_200_OK)
async def health_check() -> JSONResponse:
    """
    Returns the status 200 used for render to verify the health of the application.
    """
    return JSONResponse({"detail": "healthy"})

def build_path():
    """
    Builds the path of the project and project root.
    """
    Path(__file__).resolve().parent.parent
    SITE_ROOT = os.path.dirname(os.path.realpath(__file__))

    if not SITE_ROOT in sys.path:
        sys.path.append(SITE_ROOT)
        sys.path.append(os.path.join(SITE_ROOT, "apps"))

def get_application():
    """
    This is optional. The function is only used for organisation purposes.
    """
    build_path()

    app = Esmerald(
        routes=[
            Gateway("/", handler=health_check, name="health-check"),
            Include(namespace="{{ cookiecutter.project_name }}.urls"),
        ],
    )
    return app

app = get_application()

{{ cookiecutter.project_name }}/{{ cookiecutter.project_name }}/main.py


from channels.middleware import BaseMiddleware

from core.utils.channels_helpers import get_user

class TokenAuthMiddleware(BaseMiddleware):
    """
    Token authorization middleware for channels
    """
    def __init__(self, inner):
            super().__init__(inner)

    async def __call__(self, scope, receive, send):
        try:
            token_key = (
                dict(
                    (
                        x.split('=')
                        for x in scope['query_string'].decode().split("&")
                    )
                )
            ).get('token')
        except ValueError:
            token_key = None
        scope['user'] = await get_user(token_key)
        return await super().__call__(scope, receive, send)


# Projeto Indicius 02/05
import pandas as pd

# Retire os comentários para ler a tabela inteira.
#pd.set_option('display.max_rows', 10000)
#pd.set_option('display.max_columns', 1000)

# ================= #
# Funções de I/O    #
# ================= #

# Função que recupera os dados do arquivo CSV
def csv_fetch(path):
    database = pd.read_csv(path, sep = ",")
    return database

# Função que gurda os dados no arquivo CSV
def csv_commit(database):
    planilha = database.copy()
    #planilha['rowNumber'] = planilha.index
    #planilha = planilha["rowNumber","failure_type"]
    #planilha.columns = ['rowNumber', 'predictedValues']

    planilha = planilha["failure_type"]
    planilha.columns = ['rowNumber','predictedValues']
    planilha.index.name = 'rowNumber'
    planilha.to_csv('predicted.csv', index=True)
    #print(planilha)

# =================== #
# Funções de Analise  #
# =================== #

# Função que ajusta a tabela input com as falhas prováveis de acordo com o dicionário.
def define_failure_limits(database):
    database['failure_type'] = "No Failure"

    # Percorrer todas as tuplas procurando possíveis falhas
    for index, row in database.iterrows():

        # Falha: Tool Wear Failure
        if (tool_wear_failure(row['tool_wear_min']) == True):
            database.loc[index, 'failure_type'] = "Tool Wear Failure"
            continue

        # Falha: Heat Dissipation Failure
        if (heat_dissipation_failure(row["air_temperature_k"], row["process_temperature_k"], row["rotational_speed_rpm"]) == True):
            database.loc[index, 'failure_type'] = "Heat Dissipation Failure"
            continue

        # Falha: Power Failure
        if (power_failure(row['torque_nm'], row['rotational_speed_rpm']) == True):
            database.loc[index, 'failure_type'] = "Power Failure"
            continue

        # Falha: Overstrain Failure
        if (overstrain_failure(row['tool_wear_min'], row['torque_nm'], row['type']) == True):
            database.loc[index, 'failure_type'] = "Overstrain Failure"
            continue

        #print(index, row['udi'], row['failure_type'])
    return database

# ================= #
# Funções de Falha  #
# ================= #

def tool_wear_failure(twm):
    if (240 > twm > 200):
        #print("tool_wear_failure:", twm)
        return True
    return False

def heat_dissipation_failure(air_temp, proc_temp, rpm):
    diff = air_temp - proc_temp
    if ((diff < 8.6) and (rpm < 1380)):
        #print("heat_dissipation_failure:", diff, rpm)
        return True
    return False

def power_failure(torque, rpm):
    rads = 2 * 3.14 * rpm / 60
    power = torque * rads
    if ((power > 9000) or (power < 3500)):
        #print("power_failure:", power)
        return True
    return False

def overstrain_failure(twm, torque, types):
    product = twm * torque
    if (types == "L" and product > 11000):
        return True
    elif (types == "M" and product > 12000):
        return True
    elif (types == "H" and product > 13000):
        return True
    return False

# ================= #
# Função Principal  #
# ================= #

def main():
    print("Idêntificador de falhas, digite o nome do arquivo.csv, caso nenhum seja inserido, o padrão do Desafio será utilizado.")
    csv_name = input("Digite o nome:")

    if (csv_name == ""):
        dadosTest = csv_fetch("desafio_manutencao_preditiva_teste.csv")
    else:
        dadosTest = csv_fetch(csv_name)

    print("| < Processando... > |")
    dados_finais = define_failure_limits(dadosTest)

    csv_commit(dados_finais)
    print("< Finalizado >")
    print("Resultados guardados em predicted.csv")
    return

main()


import glob
import logging
import os
import re

import matplotlib.pyplot as plt
import numpy as np
from cv2 import (
    COLOR_RGB2BGR,
    COLOR_RGBA2RGB,
    cvtColor,
    GaussianBlur,
    imwrite,
    IMWRITE_JPEG_QUALITY,
    IMWRITE_PNG_COMPRESSION,
    INTER_LINEAR,
    resize,
)
from matplotlib.path import Path
from PIL import Image
from scipy import optimize, spatial
from skimage.color import hed2rgb, hsv2rgb, lab2lch, lab2rgb, lch2lab, luv2rgb, rgb2hed, rgb2hsv, rgb2lab, rgb2luv
from skimage.exposure import rescale_intensity
from skimage.filters import threshold_otsu

#: threshold of tissue/background presence on potential cutting line
TISSUE_CONTENT = 0.01
#: supported image extensions
IMAGE_EXTENSIONS = ('.png', '.jpg', '.jpeg')
# https://github.com/opencv/opencv/issues/6729
# https://www.life2coding.com/save-opencv-images-jpeg-quality-png-compression
IMAGE_COMPRESSION_OPTIONS = (IMWRITE_JPEG_QUALITY, 98) + \
                            (IMWRITE_PNG_COMPRESSION, 9)
#: template for detecting/parsing scale from folder name
REEXP_FOLDER_SCALE = r'\S*scale-(\d+)pc'
# ERROR:root:error: Image size (... pixels) exceeds limit of ... pixels,
# could be decompression bomb DOS attack.
# SEE: https://gitlab.mister-muffin.de/josch/img2pdf/issues/42
Image.MAX_IMAGE_PIXELS = None
#: maximal image size for visualisations, larger images will be downscaled
MAX_IMAGE_SIZE = 5000
#: define pair of forward and backward color space conversion
CONVERT_RGB = {
    'rgb': (lambda img: img, lambda img: img),
    'hsv': (rgb2hsv, hsv2rgb),
    'lab': (rgb2lab, lab2rgb),
    'luv': (rgb2luv, luv2rgb),
    'hed': (rgb2hed, hed2rgb),
    'lch': (lambda img: lab2lch(rgb2lab(img)), lambda img: lab2rgb(lch2lab(img))),
}

def detect_binary_blocks(vec_bin):
    """ detect the binary object by beginning, end and length in !d signal

    :param list(bool) vec_bin: binary vector with 1 for an object
    :return tuple(list(int),list(int),list(int)):

    >>> vec = np.array([1] * 15 + [0] * 5 + [1] * 20)
    >>> detect_binary_blocks(vec)
    ([0, 20], [15, 39], [14, 19])
    """
    begins, ends, lengths = [], [], []

    # in case that it starts with an object
    if vec_bin[0]:
        begins.append(0)

    length = 0
    # iterate over whole array, skip the first one
    for i in range(1, len(vec_bin)):
        if vec_bin[i] > vec_bin[i - 1]:
            begins.append(i)
        elif vec_bin[i] < vec_bin[i - 1]:
            ends.append(i)
            lengths.append(length)
            length = 0
        elif vec_bin[i] == 1:
            length += 1

    # in case that it ends with an object
    if vec_bin[-1]:
        ends.append(len(vec_bin) - 1)
        lengths.append(length)

    return begins, ends, lengths

def find_split_objects(hist, nb_objects=2, threshold=TISSUE_CONTENT):
    """ find the N largest objects and set split as middle distance among them

    :param list(float) hist: input vector
    :param int nb_objects: number of desired objects
    :param float threshold: threshold for input vector
    :return list(int):

    >>> vec = np.array([1] * 15 + [0] * 5 + [1] * 20)
    >>> find_split_objects(vec)
    [17]
    """
    hist_bin = hist > threshold
    begins, ends, lengths = detect_binary_blocks(hist_bin)

    if len(lengths) < nb_objects:
        logging.error('not enough objects')
        return []

    # select only the number of largest objects
    obj_sorted = sorted(zip(lengths, range(len(lengths))), reverse=True)
    obj_select = sorted([o[1] for o in obj_sorted][:nb_objects])

    # compute the mean in the gup
    splits = [np.mean((ends[obj_select[i]], begins[obj_select[i + 1]])) for i in range(len(obj_select) - 1)]
    splits = list(map(int, splits))

    return splits

def find_largest_object(hist, threshold=TISSUE_CONTENT):
    """ find the largest objects and give its beginning end end

    :param list(float) hist: input vector
    :param float threshold: threshold for input vector
    :return list(int):

    >>> vec = np.array([1] * 15 + [0] * 5 + [1] * 20)
    >>> find_largest_object(vec)
    (20, 39)
    """
    hist_bin = hist > threshold
    begins, ends, lengths = detect_binary_blocks(hist_bin)

    if not lengths:
        raise ValueError('no object found')

    # select only the number of largest objects
    obj_sorted = sorted(zip(lengths, range(len(lengths))), reverse=True)
    obj_select = obj_sorted[0][1]

    return begins[obj_select], ends[obj_select]

def project_object_edge(img, dimension):
    """ scale the image, binarise with Othu and project to one dimension

    :param ndarray img:
    :param int dimension: select dimension for projection
    :return list(float):

    >>> img = np.zeros((20, 10, 3))
    >>> img[2:6, 1:7, :] = 1
    >>> img[10:17, 4:6, :] = 1
    >>> project_object_edge(img, 0).tolist()  # doctest: +NORMALIZE_WHITESPACE
    [0.0, 0.0, 0.7, 0.7, 0.7, 0.7, 0.0, 0.0, 0.0, 0.0,
     0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0]
    """
    if dimension not in (0, 1):
        raise ValueError('not supported dimension %i' % dimension)
    if img.ndim != 3:
        raise ValueError('unsupported image shape %r' % img.shape)
    img_gray = np.mean(img, axis=-1)
    img_gray = GaussianBlur(img_gray, (5, 5), 0)
    p_low, p_high = np.percentile(img_gray, (1, 95))
    img_gray = rescale_intensity(img_gray, in_range=(p_low, p_high))
    img_bin = img_gray > threshold_otsu(img_gray)
    img_edge = np.mean(img_bin, axis=1 - dimension)
    return img_edge

def load_large_image(img_path):
    """ loading very large images

    .. note:: For the loading we have to use matplotlib while ImageMagic nor other
     lib (opencv, skimage, Pillow) is able to load larger images then 64k or 32k.

    :param str img_path: path to the image
    :return ndarray: image
    """
    if not os.path.isfile(img_path):
        raise FileNotFoundError('missing image: %s' % img_path)
    img = plt.imread(img_path)
    if img.ndim == 3 and img.shape[2] == 4:
        img = cvtColor(img, COLOR_RGBA2RGB)
    if np.max(img) <= 1.5:
        np.clip(img, a_min=0, a_max=1, out=img)
        # this command split should reduce mount of required memory
        np.multiply(img, 255, out=img)
        img = img.astype(np.uint8, copy=False)
    return img

def save_large_image(img_path, img):
    """ saving large images more then 50k x 50k

    .. note:: For the saving we have to use openCV while other
     lib (matplotlib, Pillow, ITK) is not able to save larger images then 32k.

    :param str img_path: path to the new image
    :param ndarray img: image

    >>> img = np.zeros((2500, 3200, 4), dtype=np.uint8)
    >>> img[:, :, 0] = 255
    >>> img[:, :, 1] = 127
    >>> img_path = './sample-image.jpg'
    >>> save_large_image(img_path, img)
    >>> img2 = load_large_image(img_path)
    >>> img2[0, 0].tolist()
    [255, 127, 0]
    >>> img.shape[:2] == img2.shape[:2]
    True
    >>> os.remove(img_path)
    >>> img_path = './sample-image.png'
    >>> save_large_image(img_path, img.astype(np.uint16) * 255)
    >>> img3 = load_large_image(img_path)
    >>> img.shape[:2] == img3.shape[:2]
    True
    >>> img3[0, 0].tolist()
    [255, 127, 0]
    >>> save_large_image(img_path, img2 / 255. * 1.15)  # test overwrite message
    >>> os.remove(img_path)
    """
    # drop transparency
    if img.ndim == 3 and img.shape[2] == 4:
        img = img[:, :, :3]
    # for some reasons with linear interpolation some the range overflow (0, 1)
    if np.max(img) <= 1.5:
        np.clip(img, a_min=0, a_max=1, out=img)
        # this command split should reduce mount of required memory
        np.multiply(img, 255, out=img)
        img = img.astype(np.uint8, copy=False)
    # some tiff images have higher ranger int16
    elif np.max(img) > 255:
        img = img / 255
    # for images as integer clip the value range as (0, 255)
    if img.dtype != np.uint8:
        np.clip(img, a_min=0, a_max=255, out=img)
        img = img.astype(np.uint8, copy=False)
    if os.path.isfile(img_path):
        logging.debug('WARNING: this image will be overwritten: %s', img_path)
    # why cv2 imwrite changes the color of pics
    # https://stackoverflow.com/questions/42406338
    img = cvtColor(img, COLOR_RGB2BGR)
    imwrite(img_path, img, IMAGE_COMPRESSION_OPTIONS)

def generate_pairing(count, step_hide=None):
    """ generate registration pairs with an option of hidden landmarks

    :param int count: total number of samples
    :param int|None step_hide: hide every N sample
    :return list((int, int)), list(bool): registration pairs

    >>> generate_pairing(4, None)  # doctest: +NORMALIZE_WHITESPACE
    ([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
     [True, True, True, True, True, True])
    >>> generate_pairing(4, step_hide=3)  # doctest: +NORMALIZE_WHITESPACE
    ([(0, 1), (0, 2), (1, 2), (3, 1), (3, 2)],
     [False, False, True, False, False])
    """
    idxs_all = list(range(count))
    idxs_hide = idxs_all[::step_hide] if step_hide is not None else []
    # prune image on diagonal and missing both landmarks (target and source)
    idxs_pairs = [(i, j) for i in idxs_all for j in idxs_all if i != j and j not in idxs_hide]
    # prune symmetric image pairs
    idxs_pairs = [(i, j) for k, (i, j) in enumerate(idxs_pairs) if (j, i) not in idxs_pairs[:k]]
    public = [not (i in idxs_hide or j in idxs_hide) for i, j in idxs_pairs]
    return idxs_pairs, public

def parse_path_scale(path_folder):
    """ from given path with annotation parse scale

    :param str path_folder: path to the scale folder
    :return int: scale

    >>> parse_path_scale('scale-.1pc')
    nan
    >>> parse_path_scale('user-JB_scale-50pc')
    50
    >>> parse_path_scale('scale-10pc')
    10
    """
    folder = os.path.basename(path_folder)
    obj = re.match(REEXP_FOLDER_SCALE, folder)
    if obj is None:
        return np.nan
    scale = int(obj.groups()[0])
    return scale

def line_angle_2d(point_begin, point_end, deg=True):
    """ Compute direction of line with given two points

    the zero is horizontal in direction [1, 0]

    :param list(float) point_begin: starting line point
    :param list(float) point_end: ending line point
    :param bool deg: return angle in degrees
    :return float: orientation

    >>> [line_angle_2d([0, 0], p) for p in ((1, 0), (0, 1), (-1, 0), (0, -1))]
    [0.0, 90.0, 180.0, -90.0]
    >>> line_angle_2d([1, 1], [2, 3])  # doctest: +ELLIPSIS
    63.43...
    >>> line_angle_2d([1, 2], [-2, -3])  # doctest: +ELLIPSIS
    -120.96...
    """
    diff = np.asarray(point_end) - np.asarray(point_begin)
    angle = -np.arctan2(diff[0], diff[1]) + np.pi / 2.
    if deg:
        angle = angle / np.pi * 180.
    angle = norm_angle(angle, deg)
    return angle

def norm_angle(angle, deg=True):
    """ Normalise to be in range (-180, 180) degrees

    :param float angle: input angle
    :param bool deg: use degrees
    :return float: norma angle
    """
    alpha = 180. if deg else np.pi
    while angle > alpha:
        angle = -2 * alpha + angle
    while angle < -alpha:
        angle = 2 * alpha + angle
    return angle

def is_point_inside_perpendicular(point_begin, point_end, point_test):
    """ If point is left from line and perpendicularly in between line segment

    .. note:: negative response does not mean that that the point is on tight side

    :param list(float) point_begin: starting line point
    :param list(float) point_end: ending line point
    :param list(float) point_test: testing point
    :return int: gives +1 if it is above, -1 if bellow and 0 elsewhere

    >>> is_point_inside_perpendicular([1, 1], [3, 1], [2, 2])
    1
    >>> is_point_inside_perpendicular([1, 1], [3, 1], [2, 0])
    -1
    >>> is_point_inside_perpendicular([1, 1], [3, 1], [4, 2])
    0
    """
    angle_line = line_angle_2d(point_begin, point_end, deg=True)
    # compute angle of end - test compare to begin - end
    angle_a = norm_angle(line_angle_2d(point_end, point_test, deg=True) - angle_line, deg=True)
    # compute angle of begin - test compare to begin - end
    angle_b = norm_angle(line_angle_2d(point_begin, point_test, deg=True) - angle_line, deg=True)
    if angle_a >= 90 >= angle_b:
        state = 1
    elif (angle_a <= -90) and (angle_b >= -90):
        state = -1
    else:
        state = 0
    return state

def is_point_in_quadrant_left(point_begin, point_end, point_test):
    """ If point is left quadrant from line end point

    .. note:: negative response does not mean that that the point is on tight side

    :param list(float) point_begin: starting line point
    :param list(float) point_end: ending line point
    :param list(float) point_test: testing point
    :return int: gives +1 if it is above, -1 if bellow and 0 elsewhere

    >>> is_point_in_quadrant_left([1, 1], [3, 1], [2, 2])
    1
    >>> is_point_in_quadrant_left([3, 1], [1, 1], [2, 0])
    1
    >>> is_point_in_quadrant_left([1, 1], [3, 1], [2, 0])
    -1
    >>> is_point_in_quadrant_left([1, 1], [3, 1], [4, 2])
    0
    """
    angle_line = line_angle_2d(point_begin, point_end, deg=True)
    # compute angle of end - test compare to begin - end
    angle_pt = norm_angle(line_angle_2d(point_end, point_test, deg=True) - angle_line, deg=True)
    if (180 >= angle_pt >= 90) or angle_pt == -180:
        state = 1
    elif (-180 <= angle_pt <= -90) or angle_pt == 180:
        state = -1
    else:
        state = 0
    return state

def is_point_above_line(point_begin, point_end, point_test):
    """ If point is left from line

    :param list(float) point_begin: starting line point
    :param list(float) point_end: ending line point
    :param list(float) point_test: testing point
    :return bool: left from line

    >>> is_point_above_line([1, 1], [2, 2], [3, 4])
    True
    """
    # compute angle of end - test compare to begin - end
    angle_line = line_angle_2d(point_begin, point_end, deg=True)
    angle_test = line_angle_2d(point_end, point_test, deg=True)
    state = 0 <= norm_angle(angle_test - angle_line, deg=True) <= 180
    return state

def compute_half_polygon(landmarks, idx_start=0, idx_end=-1):
    """ compute half polygon path

    :param int idx_start: index of starting point
    :param int idx_end: index of ending point
    :param ndarray landmarks: set of points
    :return ndarray: set of points

    >>> pts = [(-1, 1), (0, 0), (0, 2), (1, 1), (1, -0.5), (2, 0)]
    >>> compute_half_polygon(pts, idx_start=0, idx_end=-1)
    [[-1.0, 1.0], [0.0, 2.0], [1.0, 1.0], [2.0, 0.0]]
    >>> compute_half_polygon(pts[:2], idx_start=-1, idx_end=0)
    [[-1, 1], [0, 0]]
    >>> pts = [[0, 2], [1, 5], [2, 4], [2, 5], [4, 4], [4, 6], [4, 8], [5, 8], [5, 8]]
    >>> compute_half_polygon(pts)
    [[0, 2], [1, 5], [2, 5], [4, 6], [4, 8], [5, 8]]
    """
    # the three or less are always minimal polygon
    if len(landmarks) < 3:
        return np.array(landmarks).tolist()
    # normalise indexes to be larger then 0
    while idx_start < 0:
        idx_start = len(landmarks) + idx_start
    while idx_end < 0:
        idx_end = len(landmarks) + idx_end
    # select points
    pt_begin, pt_end = landmarks[idx_start], landmarks[idx_end]
    del idx_start, idx_end
    # only unique points
    points = np.vstack({tuple(lnd) for lnd in landmarks})
    dists = spatial.distance.cdist(points, points, metric='euclidean')
    poly = [pt_begin]

    def _in(pt0, pts):
        return any(np.array_equal(pt, pt0) for pt in pts)

    def _disturbed(poly, pt_new, pt_test):
        last = is_point_in_quadrant_left(poly[-1], pt_new, pt_test) == 1
        path = sum(is_point_inside_perpendicular(pt0, pt1, pt_test) for pt0, pt1 in zip(poly, poly[1:] + [pt_new])) < 0
        return last and not path

    # iterated until you add the lst point to chain
    while not np.array_equal(poly[-1], pt_end):
        idx_last = np.argmax([np.array_equal(pt, poly[-1]) for pt in points])
        # walk over ordered list by distance starting with the closest
        pt_order = np.argsort(dists[idx_last])
        # iterate over all possible candidates not in chain already
        for pt0 in (pt for pt in points[pt_order] if not _in(pt, poly)):
            # find a point which does not have any point on the left perpendic
            if any(_disturbed(poly, pt0, pt) for pt in points if not _in(pt, poly + [pt0])):
                continue
            poly.append(pt0)
            break
    poly = np.array(poly).tolist()
    return poly

def get_close_diag_corners(points):
    """ finds points closes to the top left and bottom right corner

    :param ndarray points: set of points
    :return tuple(ndarray,ndarray): begin and end of imaginary diagonal

    >>> np.random.seed(0)
    >>> points = np.random.randint(1, 9, (20, 2))
    >>> get_close_diag_corners(points)
    (array([1, 2]), array([7, 8]), (12, 10))
    """
    pt_min = np.min(points, axis=0)
    pt_max = np.max(points, axis=0)
    dists = spatial.distance.cdist([pt_min, pt_max], points)
    idx_begin = np.argmin(dists[0])
    idx_end = np.argmin(dists[1])
    pt_begin = points[np.argmin(dists[0])]
    pt_end = points[np.argmin(dists[1])]
    return pt_begin, pt_end, (idx_begin, idx_end)

def simplify_polygon(points, tol_degree=5):
    """ simplify path, drop point on the same line

    :param ndarray points: point in polygon
    :param float tol_degree: tolerance on change in orientation
    :return list(list(float)): pints of polygon

    >>> pts = [[1, 2], [2, 4], [1, 5], [2, 8], [3, 8], [5, 8], [7, 8], [8, 7],
    ...     [8, 5], [8, 3], [8, 1], [7, 1], [6, 1], [4, 1], [3, 1], [3, 2], [2, 2]]
    >>> simplify_polygon(pts)
    [[1, 2], [2, 4], [1, 5], [2, 8], [7, 8], [8, 7], [8, 1], [3, 1], [3, 2]]
    """
    if len(points) < 3:
        return points
    path = [points[0]]
    for i in range(1, len(points)):
        angle0 = line_angle_2d(path[-1], points[i], deg=True)
        angle1 = line_angle_2d(points[i], points[(i + 1) % len(points)], deg=True)
        if abs(norm_angle(angle0 - angle1, deg=True)) > tol_degree:
            path.append(points[i])
    return np.array(path).tolist()

def compute_bounding_polygon(landmarks):
    """ get the polygon where all point lies inside

    :param ndarray landmarks: set of points
    :return ndarray: pints of polygon

    >>> np.random.seed(0)
    >>> points = np.random.randint(1, 9, (45, 2))
    >>> compute_bounding_polygon(points)  # doctest: +NORMALIZE_WHITESPACE
    [[1, 2], [2, 4], [1, 5], [2, 8], [7, 8], [8, 7], [8, 1], [3, 1], [3, 2]]
    """
    # the three or less are always mimimal polygon
    if len(landmarks) <= 3:
        return np.array(landmarks)
    points = np.array(landmarks)
    # split to two half by diagonal from [min, min] to [max, max]
    points = points[points[:, 0].argsort()]
    pt_begin, pt_end, _ = get_close_diag_corners(points)
    is_above = np.array([is_point_above_line(pt_begin, pt_end, pt) for pt in points])

    poly = []
    # compute one curve starting with [min, min] until [max, max] is added
    pts_above = [pt_begin] + [pt for i, pt in enumerate(points) if is_above[i]] + [pt_end]
    poly += compute_half_polygon(pts_above, idx_start=0, idx_end=-1)[:-1]
    # analogy got second curve from [max, max] to [min, min]
    pts_bellow = [pt_begin] + [pt for i, pt in enumerate(points) if not is_above[i]] + [pt_end]
    poly += compute_half_polygon(pts_bellow, idx_start=-1, idx_end=0)[:-1]
    return simplify_polygon(poly)

def compute_convex_hull(landmarks):
    """ compute convex hull around landmarks

    * http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.spatial.ConvexHull.html
    * https://stackoverflow.com/questions/21727199

    :param ndarray landmarks: set of points
    :return ndarray: pints of polygon

    >>> np.random.seed(0)
    >>> pts = np.random.randint(15, 30, (10, 2))
    >>> compute_convex_hull(pts)
    array([[27, 20],
           [27, 25],
           [22, 24],
           [16, 21],
           [15, 18],
           [26, 18]])
    """
    chull = spatial.ConvexHull(landmarks)
    chull_points = landmarks[chull.vertices]
    return chull_points

def inside_polygon(polygon, point):
    """ check if a point is strictly inside the polygon

    :param ndarray|list polygon: polygon contour
    :param tuple|list point: sample point
    :return bool: inside

    >>> poly = [[1, 1], [1, 3], [3, 3], [3, 1]]
    >>> inside_polygon(poly, [0, 0])
    False
    >>> inside_polygon(poly, [1, 1])
    False
    >>> inside_polygon(poly, [2, 2])
    True
    """
    path = Path(polygon)
    return path.contains_points([point])[0]

def list_sub_folders(path_folder, name='*'):
    """ list all sub folders with particular name pattern

    :param str path_folder: path to a particular folder
    :param str name: name pattern
    :return list(str): folders

    >>> from birl.utilities.data_io import update_path
    >>> paths = list_sub_folders(update_path('data-images'))
    >>> list(map(os.path.basename, paths))  # doctest: +ELLIPSIS
    ['images', 'landmarks', 'lesions_', 'rat-kidney_'...]
    """
    sub_dirs = sorted([p for p in glob.glob(os.path.join(path_folder, name)) if os.path.isdir(p)])
    return sub_dirs

def common_landmarks(points1, points2, threshold=1.5):
    """ find common landmarks in two sets

    :param ndarray|list(list(float)) points1: first point set
    :param ndarray|list(list(float)) points2: second point set
    :param float threshold: threshold for assignment (for landmarks in pixels)
    :return list(bool): flags

    >>> np.random.seed(0)
    >>> common = np.random.random((5, 2))
    >>> pts1 = np.vstack([common, np.random.random((10, 2))])
    >>> pts2 = np.vstack([common, np.random.random((15, 2))])
    >>> common_landmarks(pts1, pts2, threshold=1e-3)
    array([[0, 0],
           [1, 1],
           [2, 2],
           [3, 3],
           [4, 4]])
    >>> np.random.shuffle(pts2)
    >>> common_landmarks(pts1, pts2, threshold=1e-3)
    array([[ 0, 13],
           [ 1, 10],
           [ 2,  9],
           [ 3, 14],
           [ 4,  8]])
    """
    points1 = np.asarray(points1)
    points2 = np.asarray(points2)
    dist = spatial.distance.cdist(points1, points2, 'euclidean')
    ind_row, ind_col = optimize.linear_sum_assignment(dist)
    dist_sel = dist[ind_row, ind_col]
    pairs = [(i, j) for (i, j, d) in zip(ind_row, ind_col, dist_sel) if d < threshold]
    if len(pairs) > min([len(points1), len(points2)]):
        raise ValueError
    return np.array(pairs, dtype=int)

def args_expand_images(parser, nb_workers=1, overwrite=True):
    """ expand the parser by standard parameters related to images:
        * image paths
        * allow overwrite (optional)
        * number of jobs

    :param obj parser: existing parser
    :param int nb_workers: number threads by default
    :param bool overwrite: allow overwrite images
    :return obj:

    >>> import argparse
    >>> args_expand_images(argparse.ArgumentParser())  # doctest: +ELLIPSIS
    ArgumentParser(...)
    """
    parser.add_argument('-i', '--path_images', type=str, required=True, help='path (pattern) to the input image')
    parser.add_argument(
        '--nb_workers', type=int, required=False, default=nb_workers, help='number of processes running in parallel'
    )
    if overwrite:
        parser.add_argument(
            '--overwrite', action='store_true', required=False, default=False, help='allow overwrite existing images'
        )
    return parser

def args_expand_parse_images(parser, nb_workers=1, overwrite=True):
    """ expand the parser by standard parameters related to images:
        * image paths
        * allow overwrite (optional)
        * number of jobs

    :param obj parser: existing parser
    :param int nb_workers: number threads by default
    :param bool overwrite: allow overwrite images
    :return dict:
    """
    parser = args_expand_images(parser, nb_workers, overwrite)
    args = vars(parser.parse_args())
    args['path_images'] = os.path.expanduser(args['path_images'])
    return args

def estimate_scaling(images, max_size=MAX_IMAGE_SIZE):
    """ find scaling for given set of images and maximal image size

    :param list(ndarray) images: input images
    :param float max_size: max image size in any dimension
    :return float: scaling in range (0, 1)

    >>> estimate_scaling([np.zeros((12000, 300, 3))])  # doctest: +ELLIPSIS
    0.4...
    >>> estimate_scaling([np.zeros((1200, 800, 3))])
    1.0
    """
    sizes = [img.shape[:2] for img in images if img is not None]
    if not sizes:
        return 1.
    max_dim = np.max(sizes)
    scale = np.round(float(max_size) / max_dim, 1) if max_dim > max_size else 1.
    return scale

def scale_large_images_landmarks(images, landmarks):
    """ scale images and landmarks up to maximal image size

    :param list(ndarray) images: list of images
    :param list(ndarray) landmarks: list of landmarks
    :return tuple(list(ndarray),list(ndarray)): lists of images and landmarks

    >>> scale_large_images_landmarks([np.zeros((8000, 500, 3), dtype=np.uint8)],
    ...                              [None, None])  # doctest: +ELLIPSIS
    ([array(...)], [None, None])
    """
    if not images:
        return images, landmarks
    scale = estimate_scaling(images)
    if scale < 1.:
        logging.debug(
            'One or more images are larger then recommended size for visualisation,'
            ' an resize with factor %f will be applied', scale
        )
    # using float16 as image raise TypeError: src data type = 23 is not supported
    images = [
        resize(img, None, fx=scale, fy=scale, interpolation=INTER_LINEAR) if img is not None else None for img in images
    ]
    landmarks = [lnds * scale if lnds is not None else None for lnds in landmarks]
    return images, landmarks

def convert_landmarks_to_itk(lnds, image_size):
    """converting used landmarks to ITK format

    .. seealso:: https://github.com/SuperElastix/elastix/issues/156#issuecomment-511712784

    :param ndarray lnds: landmarks
    :param (int,int) image_size: image size - height, width
    :return ndarray: landmarks

    >>> convert_landmarks_to_itk([[5, 20], [100, 150], [0, 100]], (150, 200))
    array([[ 20, 145],
           [150,  50],
           [100, 150]])
    """
    height, _ = image_size
    # swap rows-columns to X-Y
    lnds = np.array(lnds)[:, [1, 0]]
    # revert the Y by height
    lnds[:, 1] = height - lnds[:, 1]
    return lnds

def convert_landmarks_from_itk(lnds, image_size):
    """converting ITK format to used in ImageJ

    .. seealso:: https://github.com/SuperElastix/elastix/issues/156#issuecomment-511712784

    :param ndarray lnds: landmarks
    :param (int,int) image_size: image height, width
    :return ndarray: landmarks

    >>> convert_landmarks_from_itk([[ 20, 145], [150,  50], [100, 150]], (150, 200))
    array([[  5,  20],
           [100, 150],
           [  0, 100]])
    >>> lnds = [[ 20, 145], [150,  50], [100, 150], [0, 0], [150, 200]]
    >>> img_size = (150, 200)
    >>> lnds2 = convert_landmarks_from_itk(convert_landmarks_to_itk(lnds, img_size), img_size)
    >>> np.array_equal(lnds, lnds2)
    True
    """
    height, _ = image_size
    # swap rows-columns to X-Y
    lnds = np.array(lnds)[:, [1, 0]]
    # revert the Y by height
    lnds[:, 0] = height - lnds[:, 0]
    return lnds

def image_histogram_matching(source, reference, use_color='hsv', norm_img_size=4096):
    """ adjust image histogram between two images

    Optionally transform the image to more continues color space.
    The source and target image does not need to be the same size, but RGB/gray.

    See cor related information:

    * https://www.researchgate.net/post/Histogram_matching_for_color_images
    * https://github.com/scikit-image/scikit-image/blob/master/skimage/transform/histogram_matching.py
    * https://stackoverflow.com/questions/32655686/histogram-matching-of-two-images-in-python-2-x
    * https://github.com/mapbox/rio-hist/issues/3

    :param ndarray source: 2D image to be transformed
    :param ndarray reference: reference 2D image
    :param str use_color: using color space for hist matching
    :param int norm_img_size: subsample image to this max size
    :return ndarray: transformed image

    >>> from birl.utilities.data_io import update_path, load_image
    >>> path_imgs = os.path.join(update_path('data-images'), 'rat-kidney_', 'scale-5pc')
    >>> img1 = load_image(os.path.join(path_imgs, 'Rat-Kidney_HE.jpg'))
    >>> img2 = load_image(os.path.join(path_imgs, 'Rat-Kidney_PanCytokeratin.jpg'))
    >>> image_histogram_matching(img1, img2).shape == img1.shape
    True
    >>> img = image_histogram_matching(img1[..., 0], np.expand_dims(img2[..., 0], 2))
    >>> img.shape == img1.shape[:2]
    True
    >>> # this should return unchanged source image
    >>> image_histogram_matching(np.random.random((10, 20, 30, 5)),
    ...                          np.random.random((30, 10, 20, 5))).ndim
    4
    """

    # in case gray images normalise dimensionality
    def _normalise_image(img):
        # normalise gray-scale images
        if img.ndim == 3 and img.shape[-1] == 1:
            img = img[..., 0]
        return img

    source = _normalise_image(source)
    reference = _normalise_image(reference)
    if source.ndim != reference.ndim:
        raise TypeError('the image dimensionality has to be equal')

    if source.ndim == 2:
        matched = histogram_match_cumulative_cdf(source, reference, norm_img_size=norm_img_size)
    elif source.ndim == 3:
        conv_from_rgb, conv_to_rgb = CONVERT_RGB.get(use_color.lower(), (None, None))
        if conv_from_rgb:
            source = conv_from_rgb(source[:, :, :3])
            reference = conv_from_rgb(reference[:, :, :3])
        matched = np.empty(source.shape, dtype=source.dtype)
        for ch in range(source.shape[-1]):
            matched[..., ch] = histogram_match_cumulative_cdf(
                source[..., ch],
                reference[..., ch],
                norm_img_size=norm_img_size,
            )
        if conv_to_rgb:
            matched = conv_to_rgb(matched)
    else:
        logging.warning('unsupported image dimensions: %r', source.shape)
        matched = source

    return matched

def histogram_match_cumulative_cdf(source, reference, norm_img_size=1024):
    """ Adjust the pixel values of a gray-scale image such that its histogram
    matches that of a target image

    :param ndarray source: 2D image to be transformed, np.array<height1, width1>
    :param ndarray reference: reference 2D image, np.array<height2, width2>
    :return ndarray: transformed image, np.array<height1, width1>

    >>> np.random.seed(0)
    >>> img = histogram_match_cumulative_cdf(np.random.randint(128, 145, (150, 200)),
    ...                                      np.random.randint(0, 18, (200, 180)))
    >>> img.astype(int)  # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
    array([[13, 16,  0, ..., 12,  2,  5],
           [17,  9,  1, ..., 16,  9,  0],
           [11, 12, 14, ...,  8,  5,  4],
           ...,
           [12,  6,  3, ..., 15,  0,  3],
           [11, 17,  2, ..., 12, 12,  5],
           [ 6, 12,  3, ...,  8,  0,  1]])
    >>> np.bincount(img.ravel()).astype(int)  # doctest: +NORMALIZE_WHITESPACE
    array([1705, 1706, 1728, 1842, 1794, 1866, 1771,    0, 1717, 1752, 1757,
           1723, 1823, 1833, 1749, 1718, 1769, 1747])
    >>> img_source = np.random.randint(50, 245, (2500, 3000)).astype(float)
    >>> img_source[-1, -1] = 255
    >>> img = histogram_match_cumulative_cdf(img_source / 255., img)
    >>> np.array(img.shape, dtype=int)
    array([2500, 3000])
    """
    # use smaller image
    step_src = max(1, int(np.max(source.shape) / norm_img_size))
    step_ref = max(1, int(np.max(reference.shape) / norm_img_size))

    # determine if we need remember that output should be float valued
    out_float = source.max() < 1.5
    # if the image is flout in range (0, 1) extend it
    source = np.round(source * 255) if source.max() < 1.5 else source
    # here we need convert to int values
    source = source.astype(np.int16)
    # work with just a small image
    src_small = source[::step_src, ::step_src]

    # here we need work with just a small image
    ref_small = reference[::step_ref, ::step_ref]
    # if the image is flout in range (0, 1) extend it
    ref_small = np.round(ref_small * 255) if reference.max() < 1.5 else ref_small
    # here we need convert to int values
    ref_small = ref_small.astype(np.int16)

    # some color spaces have also negative values, then shisfting to zero is needed
    offset = min(0, src_small.min(), ref_small.min())
    # get value histograms
    src_counts = np.bincount(src_small.ravel() - offset)
    # src_values = np.arange(0, len(src_counts))
    ref_counts = np.bincount(ref_small.ravel() - offset)
    ref_values = np.arange(0, len(ref_counts))
    # calculate normalized quantiles for each array
    src_quantiles = np.cumsum(src_counts) / float(source.size)
    ref_quantiles = np.cumsum(ref_counts) / float(reference.size)

    interp_values = np.round(np.interp(src_quantiles, ref_quantiles, ref_values))
    # in case that it overflows, due to sampling step may skip some high values
    if source.max() >= len(interp_values):
        logging.warning('source image max value %i overflow generated LUT of size %i', source.max(), len(interp_values))
        # then clip the source image values to fit ot the range
        source[source >= len(interp_values)] = len(interp_values) - 1
    matched = np.round(interp_values)[source - offset].astype(np.int16) + offset

    if out_float:
        matched = matched.astype(float) / 255.
    return matched


from criterions.multi_cls_my_loss import SpatialEmbLoss as MultiSpatialEmbLoss
from criterions.my_loss import SpatialEmbLoss
from criterions.weakly_loss import WeaklySpatialEmbLoss
from torch import nn
from train_utils import get_device
from criterions.my_loss import LovaszSoftMaxWithCE, FocalLoss
from criterions.spatial_density_loss import SpatialDensityEmbLoss
from criterions.multi_offset_loss import SpatialEmbLossMultiOffset

def get_segloss(args):
    if args.seg_criterion == 'l+ce':
        if args.n_class in (8, 15, 20):
            criterion = LovaszSoftMaxWithCE(n_class=args.n_class + 1)
        elif args.n_class in (9, 16, 21):
            criterion = LovaszSoftMaxWithCE(n_class=args.n_class)
        else:
            raise NotImplementedError
    elif args.seg_criterion == 'fbce':
        criterion = FocalLoss(n_class=args.n_class + 1, mode='bce')
    else:
        raise NotImplementedError

    criterion = nn.DataParallel(criterion).to(get_device(args))
    return criterion

def get_criterion(args):
    if 'weakly' in args.criterion:
        if args.n_class == 8:
            criterion = WeaklySpatialEmbLoss(n_class=8, to_center='to_center' in args.criterion,
                                             n_sigma=args.n_sigma, foreground_weight=args.foreground_weight,
                                             **args.loss_w)
        elif args.n_class == 9:
            raise NotImplementedError

    elif 'bce' in args.criterion:
        if args.n_class in (8, 15, 20):
            criterion = SpatialEmbLoss(args)
        elif args.n_class == 9:
            criterion = MultiSpatialEmbLoss(n_class=9, to_center='to_center' in args.criterion, mode='bce', strict=True,
                                            n_sigma=args.n_sigma, foreground_weight=args.foreground_weight,
                                            **args.loss_w)
        else:
            raise NotImplementedError

    elif 'ce' in args.criterion:
        assert args.n_class == 9
        criterion = MultiSpatialEmbLoss(n_class=9, to_center='to_center' in args.criterion, mode='ce', strict=False,
                                        n_sigma=args.n_sigma, foreground_weight=args.foreground_weight,
                                        **args.loss_w)
    elif 'density' in args.criterion:
        criterion = SpatialDensityEmbLoss(args)
    elif 'multioffset' in args.criterion:
        criterion = SpatialEmbLossMultiOffset(args)
    else:
        raise NotImplementedError
    print(f'===> instance criterion:{criterion.__class__.__name__} | n_class: {args.n_class}')
    device = get_device(args)
    criterion = nn.DataParallel(criterion)
    criterion = criterion.to(device)
    return criterion

def _test():
    from train_config import get_config
    args = get_config()
    criterion = get_criterion(args)

if __name__ == '__main__':
    _test()


#!/usr/bin/env python3
# -*- coding: utf-8 -*
# (c) Shrimadhav U K

"""Check Account Restrictions
.cr (.*)"""
from telethon.tl.types import Channel, Chat, User

from userbot.utils import lightning_cmd

@borg.on(lightning_cmd(pattern="cr (.*)"))
async def _(event):
    if event.fwd_from:
        return
    input_str = event.pattern_match.group(1)
    try:
        input_entity = await event.client.get_entity(input_str)
    except Exception:
        await event.edit("`Hmm...`")
        return
    else:
        await event.edit(get_restriction_string(input_entity))

def get_restriction_string(a) -> str:
    # logger.info(a.stringify())
    b = ""
    c = ""
    if isinstance(a, Channel):
        c = f"[{a.title}](https://t.me/c/{a.id}/{2})"
    elif isinstance(a, User):
        c = f"[{a.first_name}](tg://user?id={a.id})"
    elif isinstance(a, Chat):
        c = f"{a.title}"
        b = f"{c}: __basic groups do not have restriction in Telegram__, **to the best of our knowledge**"
        return b
    else:
        c = "something wnorgings while checking restriction_reason ðŸ˜’ðŸ˜’"
    if a.restriction_reason is None or len(a.restriction_reason) == 0:
        b = f"{c}: **Good News**! No Limitations are currently applied to this Group / Channel / Bot"
    else:
        tmp_string = f"{c} has the following restriction_reason(s): \n"
        for a_r in a.restriction_reason:
            tmp_string += f"ðŸ‘‰ {a_r.reason}-{a_r.platform}: {a_r.text}\n\n"
        b = tmp_string
    # b += "\n\n" + Translation.POWERED_BY_SE
    return b


"""
[21-01-08] 79. Word Search
https://leetcode.com/problems/word-search/

"""

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:

        m = len(board)
        n = len(board[0])

        def dfs(i, j, count):
            if count == len(word): return True
            key = str(i) + "_" + str(j)
            if i < 0 or j < 0 or i >= m or j >=n or board[i][j] != word[count]: return False
            temp, board[i][j] = board[i][j], '_'
            re = dfs(i, j + 1, count + 1) or dfs(i+1, j, count + 1) or dfs(i-1, j, count + 1) or  dfs(i, j - 1, count + 1)
            board[i][j] = temp
            return re

        for i in range(m):
            for j in range(n):
                if board[i][j] == word[0]:
                    seen = set()
                    result = dfs(i, j, 0)
                    if result: return True
        return False