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

import os,random,glob,time,shutil
os.environ['KMP_DUPLICATE_LIB_OK']='True'

import json,gc
import numpy as np
from PIL import Image
from datetime import datetime as dt
import socket
import subprocess,sys

import torch
import torchvision
import torch.nn as nn
import torch.optim as optim
import torch.backends.cudnn as cudnn
import torchvision.models as models
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import torch.distributed as dist
import torch.multiprocessing as mp
from torch.nn.parallel import DistributedDataParallel as DDP

from dataset import DatasetFolderPH
from arguments import arguments
from train_val import train, validate
from model_select import model_select
from torch.utils.tensorboard import SummaryWriter

args = arguments()

class Experiment:
    def __init__(self, log_dir: str):
        self.logger = None
        self.log_dir = log_dir
        self.val_rec = []

    def setup_logger(self):
        if self.logger is None:
            self.logger = SummaryWriter(self.log_dir)

def worker_init_fn(worker_id):
    np.random.seed(np.random.get_state()[1][0] + worker_id)

def dpp_train(rank, world_size, args, mode="", exp=None):
    # setup parallelisation environment
    use_cuda = torch.cuda.is_available()
    device = torch.device("cuda" if use_cuda else "cpu")
    # create default process group
    if args.dist_backend == "nccl":
        # os.environ['MASTER_ADDR'] = 'localhost'
        # os.environ['MASTER_PORT'] = '8888'
        dist.init_process_group(backend=args.dist_backend, init_method=args.dist_url, rank=rank, world_size=world_size)
        torch.cuda.set_device(rank)
        args.batch_size = int(args.batch_size / args.world_size)
        args.num_workers = int((args.num_workers + args.world_size - 1) / args.world_size)
    elif args.dist_backend == "gloo":
        pidf = os.path.join(args.output,"temp") ## check write permission!
        if os.path.isfile(pidf):
            os.remove(pidf)
        pid = "file:///{}".format(pidf)
        dist.init_process_group("gloo", rank=rank, world_size=world_size, init_method=pid)
        torch.cuda.set_device(rank)
        args.batch_size = int(args.batch_size / args.world_size)
        args.num_workers = int((args.num_workers + args.world_size - 1) / args.world_size)
    elif args.dist_backend is None:
        pass
    else:
        print("Unknown parallelisation backend")
        exit()
    if rank == 0:
        print("Phase: ", mode)
        print("paralell trainer: ",args.dist_backend)
        exp.setup_logger()

    # training dataset
    if mode=="pretraining":
        train_data_path = args.train_pt
        val_data_path = args.val_pt
    elif mode=="evaluation":
        train_data_path = args.val
        val_data_path = args.val
        args.epochs=1
    else:
        train_data_path = args.train
        val_data_path = args.val

    # training transform
    img_size = args.img_size_pt if mode=="pretraining" else args.img_size
    if train_data_path=="generate" or "random" in train_data_path:
        normalize = transforms.Normalize(mean=[0.5,0.5,0.5], std=[0.32,0.32,0.32])
    else:
        #normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        normalize = transforms.Normalize(mean=[x / 255.0 for x in [125.3, 123.0, 113.9]],
                                        std=[x / 255.0 for x in [63.0, 62.1, 66.7]])

    if args.aug_plus:
        # MoCo v2's aug: similar to SimCLR https://arxiv.org/abs/2002.05709
        train_transform = transforms.Compose([
            transforms.RandomResizedCrop(224, scale=(0.2, 1.)),
            transforms.RandomApply([
                transforms.ColorJitter(0.4, 0.4, 0.4, 0.1)  # not strengthened
            ], p=0.8),
            transforms.RandomGrayscale(p=0.2),
            transforms.RandomApply([transforms.GaussianBlur(5)], p=0.5),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            normalize
        ])
    else:
        train_transform = transforms.Compose([])
        if args.random_scale > 0:
            train_transform.transforms.append(transforms.RandomResizedCrop(args.crop_size,scale=(1.0-args.random_scale,1.0)))
        elif img_size > args.crop_size:
            train_transform.transforms.append(transforms.Resize(img_size, interpolation=transforms.InterpolationMode.BILINEAR))
        if args.random_rotation > 0:
            train_transform.transforms.append(transforms.RandomRotation(args.random_rotation))
        if args.affine:
            train_transform.transforms.append(transforms.RandomAffine(degrees=(-180,180), scale=(0.5,2), shear=(-100,100,-100,100)))
        train_transform.transforms.append(transforms.RandomCrop(args.crop_size, padding=args.crop_padding))
        train_transform.transforms.append(transforms.RandomHorizontalFlip())
        train_transform.transforms.append(transforms.ToTensor())
        train_transform.transforms.append(normalize)

    # dataset loading
    if args.learning_mode == "simultaneous": # use a single dataset but with two tasks: classification and PH
        train_datasets = [DatasetFolderPH(train_data_path, transform=train_transform, args=args)]
        if rank == 0:
            print(len(train_datasets[0]), "first train images loaded (used for both classification and PH regression).")
        numof_classes = train_datasets[0].n_classes
        outdim = numof_classes+args.numof_dims_pt
        parts = [(0,numof_classes),(numof_classes,outdim)]
        criterions=[nn.CrossEntropyLoss(reduction='mean').to(rank),nn.MSELoss(reduction='mean').to(rank)]
    else:
        if args.label_type_pt == "class" or mode in ["finetuning","evaluation"]:
            train_datasets = [datasets.ImageFolder(train_data_path, transform=train_transform)]
            outdim = len(train_datasets[0].classes)
            if rank == 0:
                print(len(train_datasets[0]), "first train images loaded from ", train_data_path)
                print("number of classes: ", outdim)
            criterions = [nn.CrossEntropyLoss(reduction='mean').to(rank)]
        else: ## PH related tasks
            if rank == 0:
                if os.path.isdir(args.path2PHdir):
                    print("PH will be loaded from: ",args.path2PHdir)
                # caching
                if args.cachedir is not None:
                    if os.path.exists(args.cachedir):
                        print("clearning cache in {}".format(args.cachedir))
                        shutil. rmtree(args.cachedir)
                    os.makedirs(args.cachedir,exist_ok=True)
                    if train_data_path=="generate":
                        os.makedirs(os.path.join(args.cachedir,'train'),exist_ok=True)
                        os.makedirs(os.path.join(args.cachedir,'val'),exist_ok=True)
                    print("PH computation will be cached in ",args.cachedir)
            if train_data_path=="generate":
                imdir = os.path.join(args.cachedir,'train') if args.cachedir is not None else "train"
                train_datasets = [DatasetFolderPH(root=imdir, transform=train_transform, generate_on_the_fly=True, args=args)]
                if rank == 0:
                    print(len(train_datasets[0]), "training images are generated on the fly.")
            else:
                train_datasets = [DatasetFolderPH(train_data_path, transform=train_transform, args=args)]
                if rank == 0:
                    print(len(train_datasets[0]), "first train images loaded from ", train_data_path)
            criterions = [nn.MSELoss(reduction='mean').to(rank)]
            outdim = args.numof_dims_pt
        # second dataset
        if args.learning_mode == "alternating":
            train_datasets.append(DatasetFolderPH(args.train_pt, transform=train_transform, args=args, PH_vect_dim=args.numof_dims_pt))
            if rank == 0:
                print(len(train_datasets[0]), len(train_datasets[1]), "first and second train images loaded.")
            numof_classes = train_datasets[0].n_classes
            outdim = numof_classes+args.numof_dims_pt
            parts = [(0,numof_classes),(numof_classes,outdim)]
            criterions.append(nn.MSELoss(reduction='mean').to(rank))
        else:
            parts = [None]

    # save transformed sample training images to file
    for i in range(args.output_training_images):
        img = (train_datasets[0][i][0]).numpy()
        img = (255*(img-img.min())/np.ptp(img)).astype(np.uint8).transpose(1,2,0)
        Image.fromarray(img).save(os.path.join(args.output,"{:0>5}.jpg".format(i)))

    # data sampler
    if args.dist_backend in ["nccl","gloo"]:
        train_samplers = [torch.utils.data.distributed.DistributedSampler(train_datasets[0],num_replicas=world_size,rank = rank)]
        train_loaders = [torch.utils.data.DataLoader(train_datasets[0], batch_size=args.batch_size, shuffle=False,
                                    num_workers=args.num_workers, pin_memory=True, drop_last=False, worker_init_fn=worker_init_fn, sampler = train_samplers[0])]
        if args.learning_mode == "alternating":
            train_samplers.append(torch.utils.data.distributed.DistributedSampler(train_datasets[1],num_replicas=world_size,rank = rank))
            train_loaders.append(torch.utils.data.DataLoader(train_datasets[1], batch_size=args.batch_size, shuffle=False,
                                      num_workers=args.num_workers, pin_memory=True, drop_last=False, worker_init_fn=worker_init_fn, sampler = train_samplers[1]))
    else:
        train_loaders = [torch.utils.data.DataLoader(dataset=train_datasets[0], batch_size=args.batch_size,shuffle=True,
                                            num_workers=args.num_workers,pin_memory=True, drop_last=False, worker_init_fn=worker_init_fn)]
        if args.learning_mode == "alternating":
            train_loaders.append(torch.utils.data.DataLoader(dataset=train_datasets[1], batch_size=args.batch_size,shuffle=True,
                                            num_workers=args.num_workers,pin_memory=True, drop_last=False, worker_init_fn=worker_init_fn))

    # validation dataset
    if val_data_path is not None and rank==0:
        test_transform = transforms.Compose([])
        if img_size > args.crop_size:
            test_transform.transforms.append(transforms.Resize(img_size, interpolation=transforms.InterpolationMode.BILINEAR))
        test_transform.transforms.append(transforms.CenterCrop(args.crop_size))
        test_transform.transforms.append(transforms.ToTensor())
        test_transform.transforms.append(normalize)
        if args.label_type_pt == "class" or mode in ["finetuning","evaluation"]:
            test_datasets = [datasets.ImageFolder(val_data_path, transform=test_transform)]
            if rank == 0:
                print(len(test_datasets[0]), f"first validation images loaded from {val_data_path}.")
        else:
            if val_data_path=="generate":
                imdir = os.path.join(args.cachedir,'val') if args.cachedir is not None else ""
                test_datasets = [DatasetFolderPH(root=imdir, transform=train_transform, generate_on_the_fly=True, args=args)]
                if rank == 0:
                    print(len(test_datasets[0]), "validation images are generated on the fly.")
            else:
                test_datasets = [DatasetFolderPH(val_data_path, transform=test_transform,args=args)]
                if rank == 0:
                    print(len(test_datasets[0]), f"first validation images loaded from {val_data_path}.")

        test_loaders = [torch.utils.data.DataLoader(test_datasets[0], batch_size=args.batch_size, shuffle=False,
                                                 num_workers=args.num_workers, pin_memory=True, drop_last=False, worker_init_fn=worker_init_fn)]
        if args.learning_mode == "alternating":
            test_datasets.append(DatasetFolderPH(args.val_pt, transform=test_transform,args=args, PH_vect_dim=args.numof_dims_pt))
            test_loaders.append(torch.utils.data.DataLoader(test_datasets[1], batch_size=args.batch_size, shuffle=False,
                                                 num_workers=args.num_workers, pin_memory=True, drop_last=False, worker_init_fn=worker_init_fn))
            if rank == 0:
                print(len(test_datasets[1]), "second validation images loaded.")

    # setup model and optimiser
    if args.learning_mode == "evaluation":
        model = model_select(args, outdim, renew_fc=False)
    else:
        model = model_select(args, outdim)

    if args.world_size != 1:
        if args.dist_backend in ["nccl","gloo"]:
            model = DDP(model.to(rank), device_ids=[rank],output_device=rank)
        else:
            model = nn.DataParallel(model)
            model = model.to(device)
    else:
        model = model.to(device)
    num_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
    if rank == 0:
        print("NN output dimension: ", outdim)
        print('The number of parameters of model is', num_params)

    lr = args.lr_fine if mode=="finetuning" else args.lr_pre
    if "sgd" in args.optimizer:
        optimizer = optim.SGD(model.parameters(), lr=lr, momentum=args.momentum, nesterov=("nes" in args.optimizer), weight_decay=args.weight_decay)
    elif "adam" in args.optimizer:
        optimizer = optim.Adam(model.parameters(), lr=lr)
        print("using Adam.")

    if "cos" in args.optimizer:
        scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=args.epochs)
        print("using cosine annealing.")
    else:
        if args.epochs == 200: # TEMP: resnet18
            scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[60, 120, 160], gamma=0.2)
        else:
            if rank == 0:
                print("LR drops at: ", [(i+1) * args.epochs//args.lr_drop for i in range(args.lr_drop-1)])
            scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[(i+1) * args.epochs//args.lr_drop for i in range(args.lr_drop-1)], gamma=0.1)

    # checkpointing
    if args.resume:
        if args.dist_backend in ["nccl","gloo"]:
            dist.barrier()
            map_location = {'cuda:%d' % 0: 'cuda:%d' % rank}
            checkpoint = torch.load(args.resume, map_location=map_location)
        else:
            checkpoint = torch.load(args.resume, map_location=lambda storage, loc: storage)
        args.start_epoch = checkpoint['epoch']
        model.load_state_dict(checkpoint['state_dict'])
        optimizer.load_state_dict(checkpoint['optimizer'])
        scheduler.load_state_dict(checkpoint['scheduler'])
        print("checkpoint loaded: {} (epoch {})".format(args.resume, checkpoint['epoch']))
    else:
        args.start_epoch=1

    # Training and Validation
    for epoch in range(args.start_epoch, args.epochs + 1):
        if args.learning_mode != "simultaneous":
            for i in range(len(criterions)):
                if args.learning_mode == "evaluation":
                    loss, acc = 0,0
                else:
                    if args.dist_backend in ["nccl","gloo"]:
                        train_samplers[i].set_epoch(epoch)
                        loss, acc, _ = train(args, model, rank, train_loaders[i], optimizer, epoch, [criterions[i]], part=parts[i], quiet=(rank!=0))
                    else:
                        loss, acc, _ = train(args, model, device, train_loaders[i], optimizer, epoch, [criterions[i]], part=parts[i])
                if rank == 0 and val_data_path is not None:
                    validation_loss, validation_accuracy, _ = validate(args, model, device, test_loaders[i], [criterions[i]], part=parts[i])
                    if i==0 and mode != "pretraining":
                        exp.val_rec.append(validation_accuracy.item())
                        exp.logger.add_scalar("Loss/train", loss, epoch)
                        exp.logger.add_scalar("Acc/train", acc, epoch)
                        exp.logger.add_scalar("Loss/val", validation_loss, epoch)
                        exp.logger.add_scalar("Acc/val", validation_accuracy, epoch)
                    else: # stat for pretraining task
                        exp.logger.add_scalar("Loss/train2", loss, epoch)
                        exp.logger.add_scalar("Loss/val2", validation_loss, epoch)
        else: #simultaneous
            if args.dist_backend in ["nccl","gloo"]:
                train_samplers[0].set_epoch(epoch)
                loss, acc, loss2 = train(args, model, rank, train_loaders[0], optimizer, epoch, criterions, part=parts, quiet=(rank!=0))
            else:
                loss, acc, loss2 = train(args, model, device, train_loaders[0], optimizer, epoch, criterions, part=parts)
            if rank == 0 and val_data_path is not None:
                validation_loss, validation_accuracy, validation_loss2 = validate(args, model, device, test_loaders[0], criterions, part=parts)
                exp.val_rec.append(validation_accuracy.item())
                exp.logger.add_scalar("Loss/train", loss, epoch)
                exp.logger.add_scalar("Acc/train", acc, epoch)
                exp.logger.add_scalar("Loss/val", validation_loss, epoch)
                exp.logger.add_scalar("Acc/val", validation_accuracy, epoch)
                exp.logger.add_scalar("Loss/train2", loss2, epoch)
                exp.logger.add_scalar("Loss/val2", validation_loss2, epoch)

        scheduler.step()
        if ((args.save_interval>0 and epoch % args.save_interval == 0) or epoch == args.epochs) and rank==0 and args.learning_mode != "evaluation":
            print("saving checkpoint...")
            if args.world_size>1:
                model_state = model.module.state_dict()
            else:
                model_state = model.state_dict()
            # Save checkpoint
            model_type = "pt" if mode=="pretraining" else args.learning_mode
            saved_weight = "{}/{}_{}_epoch{}.pth".format(args.output, args.usenet, model_type, epoch)
            torch.save(model_state, saved_weight)
            checkpoint = "{}/checkpoint_epoch{}.pth.tar".format(args.output, epoch)
            torch.save({'epoch': epoch + 1,
                        'state_dict': model_state,
                        'optimizer' : optimizer.state_dict(),
                        'scheduler' : scheduler.state_dict(),}, checkpoint)

    if args.dist_backend in ["nccl","gloo"]:
        dist.destroy_process_group()

    model_type = "pt" if mode=="pretraining" else args.learning_mode
    with open(os.path.join(args.logdir,"args_{}.txt".format(model_type)), 'w') as fh:
        fh.write(" ".join(sys.argv))
        fh.write("\n")
        fh.write("\n".join([str(f) for f in exp.val_rec]))

#####################################
if __name__== "__main__":

    # number of GPUs
    ngpus_per_node = torch.cuda.device_count()
    if args.world_size is None:
        args.world_size = max(ngpus_per_node,1)
    if ngpus_per_node <= 1 or args.learning_mode == "evaluation":
        #print(torch.cuda.device_count(), "GPUs available")
        args.world_size = 1
        args.dist_backend = None

    # create directories for log and output
    dtstr = dt.now().strftime('%Y_%m%d_%H%M')
    if args.learning_mode == "finetuning":
        dtstr += "_finetuning"
        if args.path2weight is None:
            dtstr += "_Scratch"
        elif "persistence_image" in args.path2weight:
            dtstr += "_PH-PI"
        elif "persistence_landscape" in args.path2weight:
            dtstr += "_PH-LS"
        elif "persistence_betticurve" in args.path2weight:
            dtstr += "_PH-BC"
        elif "persistence_histogram" in args.path2weight:
            dtstr += "_PH-HS"
        elif "Fractal" in args.path2weight:
            dtstr += "_FDB"
        elif "imagenet" in args.path2weight:
            dtstr += "_IMN"
        elif "class" in args.path2weight:
            dtstr += "_Label"
    else:
        grd ="grad_" if args.gradient else ""
        if args.label_type_pt != 'class':
            dtstr += '_{}{}_ml{}_n{}'.format(grd,args.label_type_pt,args.max_life[0],args.numof_dims_pt)
        else:
            dtstr += '_{}{}'.format(grd,args.label_type_pt)
    if args.dataset_name_pt:
        dtstr += "_"+args.dataset_name_pt
    if args.dataset_name:
        dtstr += "_ft-"+args.dataset_name
    if args.suffix:
        dtstr += "_"+args.suffix

    args.logdir = os.path.join(os.path.dirname(__file__),"runs/{}/{}".format(socket.gethostname(),dtstr))
    exp = Experiment(args.logdir)
    args.output = os.path.join(os.path.expanduser(args.output), dtstr)  #.replace("result","weight")
    print(args)

    os.makedirs(args.output, exist_ok=True)
    os.makedirs(args.logdir, exist_ok=True)
    with open(os.path.join(args.output, "args.json"), mode="w") as f:
        json.dump(args.__dict__, f, indent=4)
    with open(os.path.join(args.logdir, "args.json"), mode="w") as f:
        json.dump(args.__dict__, f, indent=4)

    cudnn.benchmark = True
    # to be deterministic
    if args.seed >= 0:
        cudnn.deterministic = True
        random.seed(args.seed)
        torch.manual_seed(args.seed)
        torch.cuda.manual_seed(args.seed)

    # pretraining and finetuing iterations
    if args.learning_mode=="combined":
        modes = ["pretraining","finetuning"]
    else:
        modes = [args.learning_mode]

    for mode in modes:
        starttime = time.time()
        if args.dist_backend in ["nccl","gloo"]:
            mp.spawn(dpp_train,args=(args.world_size,args,mode,exp),nprocs=args.world_size,join=True)
        else:
            dpp_train(0, args.world_size, args, mode,exp)
        endtime = time.time()
        interval = endtime - starttime
        print("{0} elapsed time = {1:d}h {2:d}m {3:d}s".format(mode, int(interval/3600), int((interval%3600)/60), int((interval%3600)%60)))
        print(args.logdir)
        # clear memory
        gc.collect()
        torch.cuda.empty_cache()
        # set weight file
        fns = sorted(glob.glob(os.path.join(args.output,'*.pth')), key=lambda f: os.stat(f).st_mtime, reverse=True)
        if len(fns)>0:
            args.path2weight = fns[0]

    print(args)


'''
Created on Feb 24, 2020

@author: Carla Castaneda
'''
import binascii
A = 12345
B = 67890
TRACE = 1
MAXDATASIZE = 20   # This constant controls the maximum size of the buffer in a Message and in a Packet

def checksumCalc(payload):
    #The implemention of function that calculates checksum goes here
    res = ''.join(format(i, 'b') for i in bytearray(payload, encoding ='utf-8'))
    while len(res) < 16:
        res = '0' + res

    #default will change later
    check = "0000000000000000"
    list1 = [int(x) for x in str(res)]
    list2 = [int(x) for x in str(check)]

    result = ""
    carry = 0

    i = 15
    while i > -1:
        total = list1[i] + list2[i]

        if (carry == 1):
            if (total == 1):
                carry = 1
                total = 0
            elif (total == 0):
                total == 1
                carry = 0
            else:
                total = 1
                carry = 1

        if(total == 2):
            carry = 1
            total = 0

        if total == 0:
            total = "1"
        else:
            total = "0"
        i = i - 1
        result = str(total) + result

        return result

class Packet:
    def __init__(self, s, a, c, p=''):
        self.seqNum = s
        self.ackNum = a
        self.checksum = c #checks if receiver got whole message
        self.payload = p

    def toStr(self):
        return "seqNum: "+str(self.seqNum) + " ackNum: "+ str(self.ackNum) + " checksum: " + str(self.checksum) + " payload: "+ str(self.payload)

class Message:
    data = ''
    def __init__(self,inputData):
        self.data = inputData

class Event:
    def __init__(self, t, ty, ent, p=None):
        self.self = self
        self.time = t
        self.event_type = ty
        self.entity = ent
        self.packet = p

class EventType:
    TIMERINTERRUPT = 0
    FROMAPP = 1
    FROMNETWORK = 2

class EventList:
    def __init__(self):
        self.self = self
        self.event_list = []

    def add(self, e):
        return self.event_list.append(e)

    def removeNext(self):
        if len(self.event_list) == 0:
            return None
        #print(len(self.event_list))
        index = 0
        soonest = self.event_list[index].time

        for i in range(0,len(self.event_list)):
            if self.event_list[i].time < soonest:
                soonest = self.event_list[i].time
                index = i
        #print(" i ======" + str(i))
        next_event = self.event_list[index]
        self.event_list.pop(index)
        return next_event

    def removeTimer(self, entity):
        timerIndex = -1
        timerEvent = None

        for  i in range(0, len(self.event_list)):
            if self.event_list[i].event_type == EventType.TIMERINTERRUPT and self.event_list[i].entity == entity:
                timerIndex = i
                break

        if timerIndex != -1:
            timerEvent = self.event_list[timerIndex]
            self.event_list.pop(timerIndex)

        return timerEvent

    def getLastPacketTime(self, entityTo):
        time = 0.0

        for  i in range(0, len(self.event_list)):
            if self.event_list[i].event_type == EventType.FROMNETWORK:
                if self.event_list[i].entity == entityTo:
                    time = self.event_list[i].time

        return time


from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from tutorial.auth_helper import get_sign_in_url, get_token_from_code, store_token, store_user, remove_user_and_token, get_token
from tutorial.graph_helper import get_user, get_calendar_events, post_findmeetingtime, post_freeorbusy, post_findmeetingtime_forC
import dateutil.parser
from tutorial.ggcalendar_helper import get_ggevents
from tutorial.order_helper import bubbleSort
from tutorial.mail_helper import create_and_sendEmail, create_and_sendEmailto_B, create_and_sendEmailto_C
from tutorial.event_helper import create_and_addEvent, getEvent_and_extension, create_and_addEvent_byinvitee, delete_event
from tutorial.strategy_helper import high_rank
# session
global_var = {
'eventinfo':[]
}
old_event = {}

# home page
def home(request):

    context = initialize_context(request)

    return render(request, 'tutorial/home.html', context)

# calendar page
def calendar(request):
    context = initialize_context(request)
    token = get_token(request)
    events = get_calendar_events(token)

    if events:
        events['value'].extend(get_ggevents())
        events['value'] = bubbleSort(events['value'])
        # Convert the ISO 8601 date times to a datetime object
        # This allows the Django template to format the value nicely
        for event in events['value']:
            event['start']['dateTime'] = dateutil.parser.parse(event['start']['dateTime'])
            event['end']['dateTime'] = dateutil.parser.parse(event['end']['dateTime'])

        context['events'] = events['value']
        # print("events: ", events['value'])
        # bubbleSort(events['value'])
    return render(request, 'tutorial/calendar.html', context)

# find meeting time page
def schedule_findmeetingtime(request):
    global global_var
    global old_event
    context = initialize_context(request)
    token = get_token(request)

    result = post_findmeetingtime(request, token)
    if result['emptySuggestionsReason']:
        # print('there is no suitable time slots.\n')
        # context['noSuggestionReasion'] = result['emptySuggestionsReason']
        context['suggestion'] = '0'
        request.session['eventinfo'] = global_var['eventinfo']

        old_event_info = high_rank(request, token)

        # if there is old event for replace
        if old_event_info:
            # old_event = old_event_info
            time = {
                'start': old_event_info['event_start'].split('T')[0]+' '+old_event_info['event_start'].split('T')[1],
                'end': old_event_info['event_end'].split('T')[0]+' '+old_event_info['event_end'].split('T')[1]
            }

            context['invitee_addr'] = request.POST['mail-addr']
            context['meeting-when'] = request.POST['when']
            context['meeting-howlong'] = request.POST['howlong']
            context['meeting-type'] = request.POST['type']
            context['meeting-movable'] = request.POST['movable']
            context['meeting-priority'] = request.POST['priority']

            context['old_event_time'] = time

            # send mail to ask delete old event
            old_reply = post_findmeetingtime_forC(old_event_info,token)
            if old_reply['emptySuggestionsReason']:
                # the old event can not be rescheduled (with no suitable slot)
                context['replaceable'] = "0"
                context['reason'] = "There is one previous event but it cannot be rescheduled."
                return render(request, 'tutorial/schedule.html', context)
            else:
                context['replaceable'] = "1"
                old_event = old_event_info
                old_suggestions = old_reply['meetingTimeSuggestions']

                context['old_suggestions_times'] = []
                for suggestion in old_suggestions:
                    time = {
                        'start': str(dateutil.parser.parse(suggestion['meetingTimeSlot']['start']['dateTime'])),
                        'end': str(dateutil.parser.parse(suggestion['meetingTimeSlot']['end']['dateTime']))
                    }
                    context['old_suggestions_times'].append(time)
                old_event['suggestions'] = context['old_suggestions_times']
                # print("global old_event: ", old_event)

        # if there is no old event for replace
        else:
            context['replaceable'] = "0"
            context['reason'] = 'canot find a previous event that can be replaced.'
            return render(request, 'tutorial/schedule.html', context)

    else:
        context['suggestion'] = '1'
        # print('there are some suitable time slots.\n')
        suggestions = result['meetingTimeSuggestions']
        num = len(suggestions)
        context['suggestions_num'] = num
        context['invitee_addr'] = request.POST['mail-addr']
        context['meeting-when'] = request.POST['when']
        context['meeting-howlong'] = request.POST['howlong']
        context['meeting-type'] = request.POST['type']
        context['meeting-movable'] = request.POST['movable']
        context['meeting-priority'] = request.POST['priority']
        context['suggestions_times'] = []
        for suggestion in suggestions:
            time = {
                'start': str(dateutil.parser.parse(suggestion['meetingTimeSlot']['start']['dateTime'])),
                'end': str(dateutil.parser.parse(suggestion['meetingTimeSlot']['end']['dateTime']))
            }
            context['suggestions_times'].append(time)

    # print("context: ", context)

    global_var['findmeetingtime'] = context
    request.session['findmeetingtime'] = context
    # print('gloval variable: ', global_var)
    return render(request, 'tutorial/schedule.html', context)

# page to handle response to invitations
def inviteeReply_handle(request):
    global global_var
    context = initialize_context(request)
    token = get_token(request)
    request.session['findmeetingtime'] = global_var['findmeetingtime']

    # create_and_addEvent(request, token)
    accept = request.GET['accept']
    # print('the accept of invitee is ', accept)
    if accept == '1':
        #i nvitee accept the request
        # print("the invitee has accepted the request")
        # print("all request session in invitee: ", request.session)
        # print("all request session[timechoice] in invitee: ", request.session['timeChoice'])
        # print("all request session[findmeetingtime] in invitee: ", request.session['findmeetingtime'])
        reply = create_and_addEvent(request, token)
        if reply.status_code == 200 or reply.status_code ==201 or reply.status_code == 202:
            print("the event has been created and added to Outlook. Request has been sent.")
            reply_json = reply.json()
            # print('reply json: ', reply_json)

            event = {
                'event_id': reply_json['id'],
                'extension_name': reply_json['extensions'][0]['extensionName']
            }
            global_var['eventinfo'].append(event)

            context['page_info'] = 'You have accepted the time slot of meeting. Meeting request mail has been sent to your email.'

        else:
            print("there may be some error happened. status code: ", reply.status_code)

    elif accept == '0':
        # invitee reject the request, do nothing
        # print("the invitee has rejected the request")
        context['page_info'] = 'You have rejected the time slot of meeting'

    elif accept == '2':
        # invitee selects another time slot
        # print("the invitee has selected another time slot")
        reply = create_and_addEvent_byinvitee(request, token)
        if reply.status_code == 200 or reply.status_code == 201 or reply.status_code == 202:
            # print("the event has been created and added to Outlook. Request has been sent.")
            reply_json = reply.json()
            event = {
                'event_id': reply_json['id'],
                'extension_name': reply_json['extensions'][0]['extensionName']
            }
            global_var['eventinfo'].append(event)

            context['page_info'] = 'You have chosen another time slot of meeting. Meeting request mail has been sent to your email.'
            # getEvent_and_extension(request, token)
    else:
        print("something wrong happened in accept.")

    return render(request, 'tutorial/invitee.html', context)

# page to handle multiple invitations
def inviteeB_handle(request):

    global global_var
    context = initialize_context(request)
    token = get_token(request)

    request.session['findmeetingtime'] = global_var['findmeetingtime']
    print('this is in inviteeB handle')
    # create_and_addEvent(request, token)
    accept = request.GET['accept']
    # print('the accept of invitee is ', accept)
    if accept == '1':
        #invitee accept the request
        print("the invitee has accepted the request")

        reply = create_and_addEvent_byinvitee(request, token)
        if reply.status_code == 200 or reply.status_code ==201 or reply.status_code == 202:
            print("the event has been created and added to Outlook. Request has been sent.")
            reply_json = reply.json()

            event = {
                'event_id': reply_json['id'],
                'extension_name': reply_json['extensions'][0]['extensionName']
            }
            global_var['eventinfo'].append(event)
            context['page_info'] = 'You have accepted the time slot of meeting. Meeting request mail has been sent to your email.'
        else:
            print("there may be some error happened. status code: ", reply.status_code)

    elif accept == '0':
        # invitee B reject the request, do nothing
        print("the invitee has rejected the request")

        context['page_info'] = 'You have rejected the time slot of meeting'

    else:
        print("something wrong happened in accept.")

    return render(request, 'tutorial/invitee.html', context)

# pages to handle multiple invitations
def inviteeC_handle(request):

    global global_var
    global old_event
    context = initialize_context(request)
    token = get_token(request)

    request.session['findmeetingtime'] = old_event
    print('this is in inviteeC handle')
    # create_and_addEvent(request, token)
    accept = request.GET['accept']
    # print('the accept of invitee is ', accept)
    if accept == '1':
        #invitee accept the request
        print("the invitee has accepted the request")
        print('old event id: ', old_event['eventid'])
        # delete the old event need event id old_event['eventid']
        delete_status = delete_event(old_event['eventid'], token)

        if delete_status == 204:
            # delete successfully and create event
            reply = create_and_addEvent_byinvitee(request, token)
            if reply.status_code == 200 or reply.status_code ==201 or reply.status_code == 202:
                print("the event has been created and added to Outlook. Request has been sent.")
                reply_json = reply.json()

                event = {
                    'event_id': reply_json['id'],
                    'extension_name': reply_json['extensions'][0]['extensionName']
                }

                global_var['eventinfo'].append(event)

                # getEvent_and_extension(request, token)
                context['page_info'] = 'You have accepted the time slot of meeting. Meeting request mail has been sent to your email.'
            else:
                print("there may be some error happened when creating event. status code: ", reply.status_code)
        else:
            print('deleting old event failed')

    elif accept == '0':
        # invitee reject the request, delete B if B exist
        print("the invitee has rejected the request")

        context['page_info'] = 'You have rejected the time slot of meeting'

    else:
        print("something wrong happened in accept.")

    return render(request, 'tutorial/invitee.html', context)

# page to mail to invitee
def mail(request):

    global global_var
    context = initialize_context(request)
    token = get_token(request)

    time_choice = request.POST['timeChoice']
    request.session['timeChoice'] = time_choice
    print('time choice:', time_choice)
    global_var['findmeetingtime']['timeChoice'] = time_choice
    status_code = create_and_sendEmail(request, token)
    context['status_code'] = status_code

    return render(request, 'tutorial/mail.html', context)

def mailtoBC(request):
    print('this is in mailtoBC function')
    # new event to B
    global global_var
    global old_event
    context = initialize_context(request)
    token = get_token(request)

    time_choice = global_var['findmeetingtime']['old_event_time']['start']+' - ' + global_var['findmeetingtime']['old_event_time']['end']

    request.session['timeChoice'] = time_choice
    print('time choice:', time_choice)
    global_var['findmeetingtime']['timeChoice'] = time_choice

    status_code = create_and_sendEmailto_B(request, token)
    context['status_code_B'] = status_code

    # old event to C
    request.session['old_event'] = old_event
    status_code = create_and_sendEmailto_C(request, token)
    context['status_code_C'] = status_code

    return render(request, 'tutorial/mail.html', context)

# page to get other's free or busy status
def schedule_getfreeorbusy(request):
    context = initialize_context(request)
    token = get_token(request)

    events = post_freeorbusy(token)
    context['errors'] = [
        {'message': 'Events', 'debug': format(events)}
    ]

    get_ggevents()
    return render(request, 'tutorial/schedule.html', context)

# page to get events from google calendar
def get_googleevents(request):
    context = initialize_context(request)
    get_ggevents()
    return render(request, 'tutorial/schedule.html', context)

# page to schedule meetings
def schedule(request):
    try:
    # context = initialize_context(request)
        pref_wh = request.POST['when']
        pref_hl = request.POST['howlong']
        pref_im = request.POST['important']
    except:
        return render(request, 'tutorial/preference.html', {
            'error_message': "You didn't select a choice.",
        })
    else:
        request.session['pref_when'] = pref_wh
        request.session['pref_howlong'] = pref_hl
        request.session['pref_important'] = pref_im

        return render(request, 'tutorial/schedule.html', {
            'pref_when': pref_wh,
            'pref_howlong': pref_hl,
            'pref_important': pref_im,
        })

# page for users to set preference.
def preference(request):
    # context = initialize_context(request)

    return render(request, 'tutorial/preference.html')

def initialize_context(request):
  context = {}

  # Check for any errors in the session
  error = request.session.pop('flash_error', None)

  if error != None:
    context['errors'] = []
    context['errors'].append(error)

  # Check for user in the session
  context['user'] = request.session.get('user', {'is_authenticated': False})
  return context

# The signin action generates the Azure AD signin URL,
# saves the state value generated by the OAuth client,
# then redirects the browser to the Azure AD signin page.
def sign_in(request):
  # Get the sign-in URL
  sign_in_url, state = get_sign_in_url()
  # Save the expected state so we can validate in the callback
  request.session['auth_state'] = state
  # Redirect to the Azure sign-in page
  return HttpResponseRedirect(sign_in_url)

def callback_p(request):
  # Get the state saved in session
  expected_state = request.session.pop('auth_state', '')
  # Make the token request
  token = get_token_from_code(request.get_full_path(), expected_state)
  # Temporary! Save the response in an error so it's displayed
  request.session['flash_error'] = { 'message': 'Token retrieved', 'debug': format(token) }
  return HttpResponseRedirect(reverse('home'))

def callback(request):
  # Get the state saved in session
  expected_state = request.session.pop('auth_state', '')
  # Make the token request
  token = get_token_from_code(request.get_full_path(), expected_state)

  # Get the user's profile
  user = get_user(token)

  # Save token and user
  store_token(request, token)
  store_user(request, user)

  return HttpResponseRedirect(reverse('home'))
  # return HttpResponseRedirect("http://7f6f44efd0e7.ngrok.io")

def sign_out(request):
  # Clear out the user and token
  remove_user_and_token(request)

  return HttpResponseRedirect(reverse('home'))


def f(u, t):
    return [u[1], 3.*(1. - u[0]*u[0])*u[1] - u[0]]

u0 = [2.0, 0.0]
import odespy, numpy, matplotlib.pyplot as plt

for method in odespy.Lsode, odespy.DormandPrince, odespy.Vode:
    solver = method(f, rtol=0.0, atol=1e-6,
                    adams_or_bdf='adams', order=10)
    solver.set_initial_condition(u0)
    t_points = numpy.linspace(0, 30, 150)
    u, t = solver.solve(t_points)

    plt.plot(t, u[:,0])
    plt.hold('on')
plt.show()


import json
import random

from django.conf import settings
from django.core.mail import send_mail
from django.http import HttpResponse, Http404
from django.template import loader
from django.views.generic import View

from pmo2015.forms import MessageForm, BattleForm
from pmo2015.models import MainComment, Player, Vote

def _return_me(error_code, **kwargs):
    kwargs["error"] = error_code
    return kwargs

def _guestbook(request, *args, **kwargs):
    form = MessageForm(request.POST)
    if not form.is_valid():
        if 'captcha' in form.errors:
            return _return_me(2)
        return _return_me(1)
    ip_address = request.META.get("REMOTE_ADDR")
    if ip_address is None:
        return _return_me(-1)
    try:
        MainComment.create(
            nickname=form.cleaned_data['nickname'],
            content=form.cleaned_data['message'],
            email=form.cleaned_data['email'],
            ip_address=ip_address
        )
    except:
        return _return_me(-1)
    return _return_me(0)

def _battle(request, *args, **kwargs):
    form = BattleForm(request.POST)
    if not form.is_valid():
        if 'captcha' in form.errors:
            return _return_me(4)
        if 'taobao' in form.errors:
            return _return_me(5)
        return _return_me(1, errors=form.errors)

    player_name = form.cleaned_data['nickname']
    email = form.cleaned_data['email']
    taobao_id = form.cleaned_data['taobao']
    team = form.cleaned_data['team']
    team = (random.choice(Vote.TEAM_CHOICES)[0] if team == 'random' else team).upper()
    if team not in {Vote.TEAM_AQUA, Vote.TEAM_MAGMA}:
        return _return_me(-1)
    ip_address = request.META.get("REMOTE_ADDR")
    if ip_address is None:
        return _return_me(-1)
    elif any(Player.objects.filter(email=email, status=0)) or any(Player.objects.filter(email=email, status=1)):
        return _return_me(2)
    else:
        try:
            player = Player.create(
                player_name=player_name,
                email=email,
                taobao_id=taobao_id,
                signup_ip=ip_address,
                team=team
            )
        except AssertionError:
            return _return_me(3)
        except:
            return _return_me(-1)
    send_mail(
        '%s对战报名' % settings.EMAIL_SUBJECT_PREFIX, "",
        settings.EMAIL_HOST_USER, [email], fail_silently=False,
        html_message=loader.get_template('pmo2015/mails/battle_signup.html').render({
            'player': player,
            'base_url': settings.BASE_URL,
            'weibo_url': settings.WEIBO_URL,
            'contact_mail': settings.CONTACT_EMAIL
        }),
    )
    return _return_me(0)

def _vote(request, *args, **kwargs):
    choice = request.POST.get("team").upper()
    ip_address = request.META.get("REMOTE_ADDR")
    if not all((choice, ip_address)) or choice not in {Vote.TEAM_AQUA, Vote.TEAM_MAGMA}:
        return _return_me(-1)
    if Vote.objects.filter(ip_address=ip_address).count() > 0 or request.session.get('vote'):
        return _return_me(1)
    vote = Vote.objects.create(
        choice=choice,
        ip_address=ip_address
    )
    request.session['vote'] = vote.id
    return _return_me(0, vote=0 if choice == Vote.TEAM_AQUA else 1)

_api_list = {
    'guestbook': _guestbook,
    'battle': _battle,
    'vote': _vote
}

class ApiView(View):

    @staticmethod
    def post(request, sub=None, *args, **kwargs):
        if sub not in _api_list:
            raise Http404
        func = _api_list[sub]
        return HttpResponse(json.dumps(func(
            request, *args, **kwargs
        )), content_type='application/json')

    @staticmethod
    def get(*args, **kwargs):
        raise Http404


import time
import os
from pyspark import SparkConf
from pyspark import SparkContext
from pyspark.sql import SparkSession
from pyspark.sql.functions import from_json, avg, col, concat, desc, explode, lit, min, max, sum, count, split, udf, to_date, from_unixtime, datediff, when, countDistinct, date_add
from pyspark.sql.types import StructType, StructField, StringType, LongType, IntegerType, FloatType, DateType, TimestampType
from pyspark.sql.functions import explode, sum, split, col, expr, when
from pyspark.sql.utils import ForeachBatchFunction

from util.utils import add_date_field, Normalize_Data_ToUser, extract_past_N_days, Choose_Features, remove_invalid_record, stop_stream_query
from util.config import *

def f_merge_df(microdf, batchid):

    # print(f"inside forEachBatch for batchid:{batchid}. Rows in passed dataframe: {microdf.count()}")

    microdf = microdf.dropDuplicates(['ts', 'userId'])

    if (DeltaTable.isDeltaTable(spark, silver_data_path) == False):

        microdf \
        .write \
        .partitionBy(["event_year", "event_month", "event_day"]) \
        .format("delta") \
        .mode("overwrite") \
        .save(silver_data_path)

    else:

        deltadf = DeltaTable.forPath(spark, silver_data_path)

        (deltadf.alias("t")
                .merge(
                    microdf.alias("s"),
                    """ s.event_year  = t.event_year
                    and s.event_month = t.event_month
                    and s.event_day   = t.event_day
                    and s.ts = t.ts
                    and s.userId = t.userId
                    """)
                .whenNotMatchedInsertAll()
                .execute()
        )

if __name__ == '__main__':

    schema = StructType([
        StructField("ts",TimestampType(),True),
        StructField("userId",StringType(),True),
        StructField("sessionId",IntegerType(),True),
        StructField("page", StringType(), True),
        StructField("auth", StringType(), True),
        StructField("method", StringType(), True),
        StructField("status", IntegerType(), True),
        StructField("level", StringType(), True),
        StructField("itemInSession", IntegerType(), True),
        StructField("location", StringType(), True),
        StructField("userAgent", StringType(), True),
        StructField("lastName", StringType(), True),
        StructField("firstName", StringType(), True),
        StructField("registration", LongType(), True),
        StructField("gender", StringType(), True),
        StructField("artist", StringType(), True),
        StructField("song", StringType(), True),
        StructField("length", FloatType(), True)
    ])

    spark = SparkSession.builder \
        .appName(__file__) \
        .config("spark.sql.shuffle.partitions", shuffle_partitions) \
        .config("spark.jars.packages", "io.delta:delta-core_2.12:0.8.0") \
        .config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") \
        .config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog") \
        .getOrCreate()

    spark.sparkContext.setLogLevel("ERROR")

    from delta.tables import *

    # Step 1: Receive data via socket and injest into as Text format.
    # Base on experiment, TEXT consumes less memory

    queryRaw = spark \
            .readStream \
            .format("socket") \
            .option("host", "localhost") \
            .option("port", 3001) \
            .load() \
            .writeStream \
            .queryName("queryRaw") \
            .outputMode("append") \
            .format("text") \
            .option("path", raw_data_path) \
            .option("checkpointLocation", raw_data_ckpt) \
            .trigger(processingTime=trigger_time) \
            .start()

    # Step 2: Convert text into delta format
    df_text = spark \
            .readStream \
            .format("text") \
            .load(raw_data_path)

    df_text = df_text.withColumn("jsonValue", from_json(df_text.value, schema)) \
                 .select(col("jsonValue.ts").alias('ts'),
                         col("jsonValue.userId").alias('userId'),
                         col("jsonValue.sessionId").alias('sessionId'),
                         col("jsonValue.page").alias('page'),
                         col("jsonValue.auth").alias('auth'),
                         col("jsonValue.method").alias('method'),
                         col("jsonValue.status").alias('status'),
                         col("jsonValue.level").alias('level'),
                         col("jsonValue.itemInSession").alias('itemInSession'),
                         col("jsonValue.location").alias('location'),
                         col("jsonValue.userAgent").alias('userAgent'),
                         col("jsonValue.lastName").alias('lastName'),
                         col("jsonValue.firstName").alias('firstName'),
                         col("jsonValue.registration").alias('registration'),
                         col("jsonValue.gender").alias('gender'),
                         col("jsonValue.artist").alias('artist'),
                         col("jsonValue.song").alias('song'),
                         col("jsonValue.length").alias('length')
                         )

    df_text = add_date_field(remove_invalid_record(df_text))

    queryBronze = df_text \
            .writeStream \
            .queryName("queryBronze") \
            .partitionBy(["event_year", "event_month", "event_day"]) \
            .format("delta") \
            .outputMode("append") \
            .option("checkpointLocation", bronze_data_ckpt) \
            .option("path", bronze_data_path) \
            .trigger(processingTime=trigger_time) \
            .start()

    ReadyForNext = False
    while(not ReadyForNext):

        if (os.path.exists(bronze_data_path)
            and
            DeltaTable.isDeltaTable(spark, bronze_data_path)):
            ReadyForNext = True
        else:
            time.sleep(10)

    querySilver = spark \
            .readStream \
            .format("delta") \
            .load(bronze_data_path) \
            .writeStream \
            .queryName("querySilver") \
            .outputMode("append") \
            .format("delta") \
            .foreachBatch(f_merge_df) \
            .option("checkpointLocation", silver_data_ckpt) \
            .trigger(processingTime=trigger_time) \
            .start()

    while spark.streams.active != []:

        for stream_query in spark.streams.active:
            stop_stream_query(stream_query)

        time.sleep(trigger_seconds)

    exit(0)


from scipy.stats import norm
import matplotlib.pyplot as plt
import numpy as np

plt.rcdefaults()
values = [ 12, 55, 4, 32, 14]
colors = ['r', 'g', 'b', 'c', 'm']
explode = [0, 0, 0.2, 0, 0]
labels = ['India', 'United States', 'Rusia', 'China', 'Europe']
plt.pie(values, colors=colors, labels=labels, explode=explode)
plt.title('Nacionalidad Trabajadores')
plt.savefig("/home/hadoop/SCProjects/0_SCProjects_github.com_SCelisV/python/SC.Code/pue-cca175/pictures/figura10_tarta.png", format="png")
plt.show()


import pygame as pyg
import time, random, sys
pyg.init()
run = True
gameover = False

# Game window
screenSize = 600
win = pyg.display.set_mode((screenSize, screenSize))

# Colors
black = (0, 0, 0)
white = (255, 255, 255)
blue = (0, 0, 255)
red = (255, 0, 0)

transparent = pyg.Surface((screenSize, screenSize))
transparent.fill(black)
transparent.set_alpha(200)

# Grid array
grid = [['', '', ''], ['', '', ''], ['', '', '']]
total = 0
tie = False
width = 6 # Width of all lines drawn

# Players
p1 = 'X'
p1_turn = True
p2 = 'O'
p2_turn = False

# Generates text
def create_text(text, name, font_size, text_color, location):
    font = pyg.font.SysFont(name, font_size, bold = True)
    ren = font.render(text, True, text_color)
    win.blit(ren, (screenSize/2 - ren.get_rect().width/2, screenSize/2 - location))

# Uses mouse input to determine location of player moves
def take_turn(x, y):
    global grid, p1_turn, p2_turn, total
    if grid[x][y] == '':
        if p1_turn:
            grid[x][y] = 'X'
            p1_turn = False
            p2_turn = True
        else:
            grid[x][y] = 'O'
            p1_turn = True
            p2_turn = False
        total += 1

# Draws an X given an x,y position for its center
def draw_X(center_x, center_y):
    x1 = center_x - (screenSize/3)/4
    y1 = center_y - (screenSize/3)/4
    x2 = center_x + (screenSize/3)/4
    y2 = center_y + (screenSize/3)/4
    pyg.draw.line(win, blue, (x1, y1), (x2, y2), width = width)

    x1 = center_x + (screenSize/3)/4
    y1 = center_y - (screenSize/3)/4
    x2 = center_x - (screenSize/3)/4
    y2 = center_y + (screenSize/3)/4
    pyg.draw.line(win, blue, (x1, y1), (x2, y2), width = width)

# Updates display
def draw():
    pyg.display.set_caption("Tic-Tac-Toe")
    win.fill(black)

    # Grid
    pyg.draw.line(win, white, (screenSize/3, 0), (screenSize/3, screenSize), width = width)
    pyg.draw.line(win, white, (screenSize/3 * 2, 0), (screenSize/3 * 2, screenSize), width = width)
    pyg.draw.line(win, white, (0, screenSize/3), (screenSize, screenSize/3), width = width)
    pyg.draw.line(win, white, (0, screenSize/3 * 2), (screenSize, screenSize/3 * 2), width = width)

    # Player moves
    for r in range(3):
        for c in range(3):
            if grid[r][c] == None:
                pass
            elif grid[r][c] == 'X':
                center_x = c*(screenSize/3) + (screenSize/3)//2
                center_y = r*(screenSize/3) + (screenSize/3)//2
                draw_X(center_x, center_y)
            elif grid[r][c] == 'O':
                pyg.draw.circle(win, red, (c*(screenSize/3) + (screenSize/3)//2, r*(screenSize/3) + (screenSize/3)//2), screenSize//8, width = width)

# Checks if game is over
def check_win():
    global gameover, tie
    if grid[0][0] == grid[0][1] and grid[0][1] == grid[0][2] and grid[0][0] != '':
        gameover = True
    elif grid[1][0] == grid[1][1] and grid[1][1] == grid[1][2] and grid[1][0] != '':
        gameover = True
    elif grid[2][0] == grid[2][1] and grid[2][1] == grid[2][2] and grid[2][0] != '':
        gameover = True
    elif grid[0][0] == grid[1][0] and grid[1][0] == grid[2][0] and grid[0][0] != '':
        gameover = True
    elif grid[0][1] == grid[1][1] and grid[1][1] == grid[2][1] and grid[0][1] != '':
        gameover = True
    elif grid[0][2] == grid[1][2] and grid[1][2] == grid[2][2] and grid[0][2] != '':
        gameover = True
    elif grid[0][0] == grid[1][1] and grid[1][1] == grid[2][2] and grid[0][0] != '':
        gameover = True
    elif grid[0][2] == grid[1][1] and grid[1][1] == grid[2][0] and grid[0][2] != '':
        gameover = True
    elif total == 9:
        gameover = True
        tie = True

def gameloop():
    global grid, p1_turn, p2_turn, gameover, total, tie, run
    # Resetting variables if another game is played
    grid = [['', '', ''], ['', '', ''], ['', '', '']]
    p1_turn = True
    p2_turn = False
    gameover = False
    total = 0
    tie = False

    while run:
        # Gameover screen
        while gameover:
            win.fill(black)
            for e in pyg.event.get():
                if e.type == pyg.QUIT:
                    run = False
                    gameover = False
                    pyg.quit()
                    sys.exit()
                if e.type == pyg.MOUSEBUTTONUP:
                    gameover = False
                    gameloop()
            victory = str()

            if tie:
                victory = "DRAW"
            elif not p1_turn:
                victory = "Player 1 wins!"
            elif not p2_turn:
                victory = "Player 2 wins!"
            draw()
            win.blit(transparent, (0, 0))
            create_text("GAME OVER", "Courier", 48, white, 150)
            create_text(victory, "Courier", 36, white, 60)
            create_text("Click anywhere to play again", "Courier", 18, white, -25)
            pyg.display.update()

        # Main game
        for e in pyg.event.get():
            # Clicking X to quit
            if e.type == pyg.QUIT:
                run = False
                pyg.quit()
                sys.exit()

            # Mouse position
            m_x,m_y = pyg.mouse.get_pos()

            # Mouse position relative to grid; determines locations of moves made
            if e.type == pyg.MOUSEBUTTONUP:
                # Row 1
                if m_x < screenSize/3 - width/2 and m_y < screenSize/3 - width/2:
                    take_turn(0, 0)
                elif m_x > screenSize/3 + width/2 and m_x < (screenSize/3 * 2) - width/2 and m_y < screenSize/3 - width/2:
                    take_turn(0, 1)
                elif m_x > (screenSize/3 * 2) + width/2 and m_y < screenSize/3 - width/2:
                    take_turn(0, 2)
                # Row 2
                elif m_x < screenSize/3 - width/2 and m_y > screenSize/3 + width/2 and m_y < (screenSize/3 * 2) - width/2:
                    take_turn(1, 0)
                elif m_x > screenSize/3 + width/2 and m_x < (screenSize/3 * 2) - width/2 and m_y > screenSize/3 + width/2 and m_y < (screenSize/3 * 2) - width/2:
                    take_turn(1, 1)
                elif m_x > (screenSize/3 * 2) + width/2 and m_y > screenSize/3 + width/2 and m_y < (screenSize/3 * 2) - width/2:
                    take_turn(1, 2)
                # Row 3
                elif m_x < screenSize/3 - width/2 and m_y > (screenSize/3 * 2) + width/2:
                    take_turn(2, 0)
                elif m_x > screenSize/3 + width/2 and m_x < (screenSize/3 * 2) - width/2 and m_y > (screenSize/3 * 2) + width/2:
                    take_turn(2, 1)
                elif m_x > (screenSize/3 * 2) + width/2 and m_y > (screenSize/3 * 2) + width/2:
                    take_turn(2, 2)
        draw()
        pyg.display.update()
        check_win()

gameloop()


# -*- coding: utf-8 -*-
import scrapy
from ..items import ItcastItem

class DemoSpider(scrapy.Spider):
    # 识别名称，必须是唯一的
    name = 'Demo'
    # 搜索的域名范围，也就是爬虫的约束区域
    allowed_domains = ['itcast.cn']
    start_urls = [
        'http://www.itcast.cn/channel/teacher.shtml',
    ]
    def parse(self, response):
        # 爬取网页源码保存到teacher.html
        # filename = "teacher.html"
        # html = open(filename, 'wb+')
        # html.write(response.body)
        items = []
        #

        #
吴老师

        #
高级讲师

        #
有多年PHP开发和项目管理经验，开发过聊天在线系统、客户关系管理系统、智能SEM竞价系统等项目，精通PHP、mysql、linux、javascript、html5、css3等主流的web开发技术，讲课风格风趣幽默有激情，善于启发学生自我思考来解决问题。

        #

        for each in response.xpath("//div[@class='li_txt']"):
        # 将得到的数据封装到一个 `ItcastItem` 对象
            item = ItcastItem()
        # extract()方法返回的都是unicode字符串
            name = each.xpath("h3/text()").extract()
            title = each.xpath("h4/text()").extract()
            info = each.xpath("p/text()").extract()
        # xpath返回的是包含一个元素的列表
            item['name'] = name[0]
            item['level'] = title[0]
            item['info'] = info[0]
            items.append(item)
        # 直接返回最后数据
        return items
# scrapy startproject Demo
# scrapy crawl Demo
# scrapy crawl Demo -o teachers.csv


import numpy as np
x = np.array([1,2,3])
print(np.exp(x))

def sigmoid(x):
    s = 1 / (1+ np.exp(-x))
    return s

print(sigmoid(x))

def sigmoid_derivative(x):
    s = 1/(1+np.exp(-x))
    ds = s*(1-s)
    return ds

print(sigmoid_derivative(x))

def image2vector(image):
    v = image.reshape((image.shape[0] * image.shape[1]*image.shape[2]),1)
    return v

def normalizeRows(x):
    x_norm = np.linalg.norm(x, axis=1, keepdims=True)
    return x/x_norm

x = np.array([[0,3,4],[1,6,4]])
print("normalizeRow(x) = " + str(normalizeRows(x)))

def softmax(x):
    x_exp = np.exp(x)
    x_sum = np.sum(x_exp, axis = 1, keepdims=True)
    s = x_exp/x_sum
    return s

print(softmax(x))

# vectorization
import time
x1 = [9, 2, 5, 0, 0, 7, 5, 0, 0, 0, 9, 2, 5, 0, 0]
x2 = [9, 2, 2, 9, 0, 9, 2, 5, 0, 0, 9, 2, 5, 0, 0]

tic = time.process_time()
dot = 0
for i in range(len(x1)):
    dot += x1[i] *x2[i]
toc = time.process_time()
print("dot = " + str(dot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

tic = time.process_time()
outer = np.zeros((len(x1), len(x2)))
for i in range(len(x1)):
    for j in range(len(x2)):
        outer[i,j] = x1[i]*x2[j]
toc = time.process_time()
print ("outer = " + str(outer) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

tic = time.process_time()
mul = np.zeros(len(x1))
for i in range(len(x1)):
    mul[i] = x1[i]*x2[i]
toc = time.process_time()
print ("elementwise multiplication = " + str(mul) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

W = np.random.rand(3,len(x1)) # Random 3*len(x1) numpy array
tic = time.process_time()
gdot = np.zeros(W.shape[0])
for i in range(W.shape[0]):
    for j in range(len(x1)):
        gdot[i] += W[i,j]*x1[j]
toc = time.process_time()
print ("gdot = " + str(gdot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

def L1(yhat, y):
    loss = sum(abs(y-yhat))
    return loss

yhat = np.array([0.9, 0.2,0.1,0.4,0.9])
y = np.array([1,0,0,1,1])
print("l1 = " + str(L1(yhat,y)))

def L2(yhat, y):
    loss = np.dot(y-yhat, y-yhat)
    return loss

yhat = np.array([.9,.2,.1,.4,.9])
y = np.array([1,0,0,1,1])
print("l2 = " + str(L2(yhat,y)))

import matplotlib.pyplot as plt
import h5py
import scipy
from PIL import Image
from scipy import ndimage
import dp_ai.seaborn.lr_utils
from dp_ai.seaborn.lr_utils import load_dataset

train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()
index = 25
plt.imshow(train_set_x_orig[index])
print ("y = " + str(train_set_y[:, index]) + ", it's a '" + classes[np.squeeze(train_set_y[:, index])].decode("utf-8") + "'picture.")

train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0],-1).T
test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0],-1).T

def initialize_with_zeros(dim):
    w = np.zeros((dim,1))
    b = 0
    assert (w.shape == (dim,1))
    assert (isinstance(b, float) or isinstance(b,int))
    return w, b

def propagate(w,b,X,Y):

    m = X.shape[1]
    A = sigmoid(np.dot(w.T, X) + b)
    cost = -(np.dot(Y, np.log(A.T)) + np.dot(np.log(1-A), (1-Y).T))/m
    dw = np.dot(X, (A-Y).T)/m
    db = np.sum(A-Y)/m

    assert(dw.shape == w.shape)
    assert(db.dtype == float)
    cost = np.squeeze(cost)
    assert(cost.shape == ())

    grads = {"dw": dw,
             "db": db}
    return grads, cost

def optimize(w,b,X,Y,num_iterations, learning_rate, print_cost = False):
    costs = []
    for i in range(num_iterations):
        grads, cost = propagate(w,b,X,Y)

    dw = grads["dw"]
    db = grads["db"]

    w = w - learning_rate*dw
    b = b - learning_rate * db

    if i % 100 == 0:
        costs.append(cost)

    if print_cost and i % 100 == 0:
        print("Cost after iteration %i: %f" % (i, cost))

    params = {"w": w,
              "b": b}
    grads = {"dw": dw,
             "db": db}
    return params, grads, costs

w, b, X, Y = np.array([[1.],[2.]]), 2., np.array([[1.,2.,-1.],[3.,4.,-3.2]]), np.array([[1,0,1]])
params, grads, costs = optimize(w, b, X, Y, num_iterations= 100,learning_rate = 0.009, print_cost = False)
print ("w = " + str(params["w"]))
print ("b = " + str(params["b"]))
print ("dw = " + str(grads["dw"]))
print ("db = " + str(grads["db"]))

def predict(w,b,X):
    m = X.shape[1]
    Y_prediction = np.zeros((1,m))
    w = w.reshape(X.shape[0],1)

    A = sigmoid(np.dot(w.T, X), b)
    for i in range(A.shape[1]):
        if A[0][j] <= 0.5:
            A[0][j] = 0
        else:
            A[0][j] = 1
    Y_prediction = A
    assert(Y_prediction.shape == (1,m))
    return Y_prediction

def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5,print_cost = False):
    w, b = initialize_with_zeros(X_train.shape[0])

    parameters, grads, costs = optimize(w,b,X_train, Y_train, num_iterations, learning_rate, print_cost)
    w = parameters["w"]
    b = parameters["b"]

    Y_prediction_test = predict(w,b,X_test)
    Y_prediction_train = predict(w, b, X_train)

    print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
    print("test accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))

    d = {"costs": costs,
         "Y_prediction_test": Y_prediction_test,
         "Y_prediction_train": Y_prediction_train,
         "w": w,
         "b": b,
         "learning_rate": learning_rate,
         "num_iterations": num_iterations}
    return d

train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0],-1).T
test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0],-1).T

# d = model(train_set_x, train_set_y, test_set_x, test_set_y,num_iterations = 2000, learning_rate = 0.005, print_cost = True)


import logging
from clarity_ext.domain.validation import ValidationType, UsageError
from clarity_ext.service.step_logger_service import AggregatedStepLoggerService

class ValidationService:

    def __init__(self, step_logger_service, logger=None):
        self.logger = logger or logging.getLogger(__name__)
        self.step_logger_service = AggregatedStepLoggerService(step_logger_service)
        self.warning_count = 0
        self.error_count = 0
        self.messages = set()

    def add_separate_warning_step_log(self, step_logger_service):
        self.step_logger_service.warnings_step_logger_service = step_logger_service

    def add_separate_error_step_log(self, step_logger_service):
        self.step_logger_service.errors_step_logger_service = step_logger_service

    @property
    def log_file_names(self):
        return self.step_logger_service.log_file_names

    def handle_validation(self, results, tailored_error_message=None):
        """
        Pushes validation results to the logging framework
        """
        if len(results) > 0:
            self._log_debug("Validation errors, len = {}".format(len(results)))
            for result in results:
                self._log_debug(result)
                self.handle_single_validation(result)
        # If any of the validation results were errors, raise an exception:
        if any(result for result in results if result.type == ValidationType.ERROR):
            msg = tailored_error_message if tailored_error_message is not None \
                else "Errors during validation. See the step log for further details."
            raise UsageError(msg, results)

    def handle_single_validation(self, result):
        msg_row = "{}".format(result)
        if msg_row in self.messages:
            # Quick fix: the messages per transfers are often duplicated. Skip adding them several times to the log.
            return
        self.messages.add(msg_row)
        self._log_debug("{}".format(msg_row))

        if result.type == ValidationType.ERROR:
            self.step_logger_service.error(msg_row)
            self.error_count += 1
        elif result.type == ValidationType.WARNING:
            self.step_logger_service.warning(msg_row)
            self.warning_count += 1
        else:
            self.step_logger_service.log(msg_row)

    def _log_debug(self, msg):
        if self.logger is not None:
            self.logger.debug(msg)


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

from sanic import Sanic
from sanic.log import logger

app = Sanic("card_games")

app.static('/static', './static')

@app.listener('before_server_start')
async def before_server_start(app, loop):
    app.ctx.rooms = {}
    """
    # base no db
    app.ctx.rooms = {
        'room1': {
            'users': {
                'user1': {
                    'name': 'user1',
                    'ws': websocket,
                    'room': 'room1',
                    'ready': False,
                    'cards': [],
                    'landlord': False,
                    'score': 0,
                    'type': 'player',   # player, npc
                    'master': False,
                },
            },
            'status': 'waiting',    # waiting, playing, end
            'landlord': None,
            'last_cards': [],
            'last_user': None,
        }
    }
    """

@app.route('/')
async def index(request):
    return await app.send_file('./static/index.html')

@app.route('/ddz')
async def ddz(request):
    if not request.args.get('name'):
        # not login
        # TODO login(input name)
        return await app.send_file('./static/ddz.html')
    _room = request.args.get('room')
    if not _room:
        # login but not in room
        # TODO create room or join room(input room)
        return await app.send_file('./static/ddz.html')
    if not app.ctx.rooms.get(_room, None):
        # login but room not exist
        # TODO room list? or create room
        return await app.send_file('./static/ddz.html')

    return await app.send_file('./static/ddz.html', room=app.ctx.rooms[_room])

@app.route('/ddz/create')
async def ddz_create(request):
    """create room"""
    # TODO to websocket?
    _user = request.args.get('name')
    if not _user:
        # TODO login
        return await app.send_file('./static/ddz.html')

    # TODO room configs
    _room = 'room1'     # TODO create room
    if app.ctx.rooms.get(_room, None):
        # TODO room exist random room
        _room = _room + '1'

    app.ctx.rooms[_room] = {
        'users': {
            _user: {
                'name': _user,
                'ws': None,
                'room': _room,
                'ready': False,
                'cards': [],
                'landlord': False,
                'score': 0,
                'type': 'player',
                'master': True,
            },
        },
        'status': 'waiting',
        'landlord': None,
        'last_cards': [],
        'last_user': None,
    }

    return await app.send_file('./static/ddz.html', room=app.ctx.rooms[_room])

@app.websocket('/ddz/ws')
async def ddz_ws(request, ws):
    _room = request.args.get('room')
    _user = request.args.get('name')
    if not _room or not _user:
        # TODO login
        return

    if not app.ctx.rooms.get(_room, None):
        # TODO room not exist
        return

    if not app.ctx.rooms[_room]['users'].get(_user, None):
        # TODO user not exist
        return

    app.ctx.rooms[_room]['users'][_user]['ws'] = ws

    while True:
        data = await ws.recv()
        # TODO parse data
        # TODO do something
        # TODO send data to other users
        # TODO send data to self
        logger.info(data)

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8000, debug=True)


import argparse
import logging
import random

import gym
import numpy as np
from datetime import datetime
from os.path import join, basename, abspath
from agent_score import agent_score
from disagreement import save_disagreements, get_top_k_disagreements, disagreement, \
    DisagreementTrace, State
from merge_and_fade import merge_and_fade
from utils import load_agent_config, load_agent_aux, mark_agent

def get_logging(args):
    name = '_'.join([basename(args.a1_dir), basename(args.a2_dir)])
    file_name = '_'.join([name, datetime.now().strftime("%d-%m %H:%M:%S").replace(' ', '_')])
    log_name = join('logs', file_name)
    args.output = join('results', file_name)
    logging.basicConfig(filename=log_name + '.log', filemode='w',
                        format='%(name)s - %(levelname)s - %(message)s',
                        level=logging.INFO)
    return name

def online_comparison(args):
    """Compare two agents running online, search for disagreements"""

    """logging"""
    name = get_logging(args)
    logging.info(f'Comparing Agents: {name}')
    logging.info(f'Disagreement importance by: {args.importance_type}')
    if args.verbose:
        print(f'Comparing Agents: {name}')
        print(f'Disagreement importance by: {args.importance_type}')

    """agent assessment"""
    # a1_overall = agent_score(args.a1_config)
    # a2_overall = agent_score(args.a2_config)
    # if a1_overall < 0 and a2_overall > 0:
    #     a2_overall += abs(a1_overall)
    #     a1_overall = 1
    # if a2_overall < 0 and a1_overall > 0:
    #     a1_overall += abs(a2_overall)
    #     a2_overall = 1
    # agent_ratio = a1_overall / a2_overall
    # logging.info(f'A1 score: {a1_overall}, A2 score: {a2_overall}, agent_ration: {agent_ratio}')
    # if args.verbose:
    #     print(f'A1 score: {a1_overall}, A2 score: {a2_overall}, agent_ration: {agent_ratio}')
    # for testing
    agent_ratio = 1

    """get agents"""
    a1_config, a1_agent_dir = load_agent_config(args.a1_dir)
    a2_config, a2_agent_dir = load_agent_config(args.a2_dir)
    seed = a1_config.seed
    agent_rng = np.random.RandomState(seed)
    a1_env, a1_helper, a1_agent, a1_behavior_tracker, a1_output_dir, video_callable = \
        load_agent_aux(a1_config, 5, a1_agent_dir, 0, seed, agent_rng, args)
    a2_env, a2_helper, a2_agent, _, _, _ = \
        load_agent_aux(a2_config, 5, a2_agent_dir, 1, seed, agent_rng, args, no_output=True)
    a1_Qmax, a2_Qmax = max([max(x) for x in a1_agent.q]), max([max(x) for x in a2_agent.q])
    a1_agent.q, a2_agent.q = a1_agent.q / a1_Qmax, a2_agent.q / a2_Qmax

    """skip first games"""
    # [a1_env.reset() for _ in range(5)]
    # [a2_env.reset() for _ in range(5)]

    """Run"""
    traces = []
    for e in range(args.num_episodes):
        logging.info(f'Running Episode number: {e}')
        if args.verbose: print(f'Running Episode number: {e}')
        t = 0
        trace = DisagreementTrace(args.horizon, a1_agent.q, a2_agent.q, agent_ratio, e)
        a1_done = False
        a1_curr_obs = a1_env.reset()
        _ = a2_env.reset()
        """first state"""
        a1_curr_s = a1_helper.get_state_from_observation(a1_curr_obs, 0, False)
        frame = a1_env.render()
        a1_position = [int(x) for x in a1_env.env.game_state.game.frog.position]
        trace.a1_states.append(
            State(t, e, a1_curr_obs, a1_curr_s, a1_agent.q[a1_curr_s], frame, a1_position))

        while not a1_done:
            a1_a = a1_agent.act(a1_curr_s)
            a2_a = a2_agent.act(a1_curr_s)
            """check for disagreement"""
            if a1_a != a2_a and t != 0:  # dont allow da on first state
                if args.verbose: print(f'\tDisagreement at frame {t}')
                logging.info(f'\tDisagreement at frame {t}')
                preceding_actions = a1_behavior_tracker.s_a[e]
                a2_env, a2_helper, a2_agent, _ = \
                    disagreement(e, trace, a2_env, a2_agent, a2_helper, t, a1_curr_s, a2_config,
                                 a2_agent_dir, agent_rng, preceding_actions, args)
                a2_agent.q = a2_agent.q / a2_Qmax
            """observe transition"""
            a1_new_obs, a1_r, a1_done, _ = a1_env.step(a1_a)
            _ = a2_env.step(a1_a)  # dont need returned values
            a1_new_s = a1_helper.get_state_from_observation(a1_new_obs, a1_r, a1_done)
            if a1_new_s == 1036:
                trace.lilies_reached += 1
                a1_done = True if trace.lilies_reached == 2 else False  # if lvl completed
            a1_r = a1_helper.get_reward(a1_curr_s, a1_a, a1_r, a1_new_s, a1_done)
            """update agent and stats"""
            a1_agent.update(a1_curr_s, a1_a, a1_r, a1_new_s)
            a2_agent.update(a1_curr_s, a1_a, a1_r, a1_new_s)
            a1_helper.update_stats(e, t, a1_curr_obs, a1_new_obs, a1_curr_s, a1_a, a1_r, a1_new_s)
            a1_behavior_tracker.add_sample(a1_curr_s, a1_a)
            """save state"""
            t += 1
            frame = a1_env.render()
            a1_curr_s = a1_new_s
            a1_curr_obs = a1_new_obs
            a1_position = [int(x) for x in a1_env.env.game_state.game.frog.position]
            trace.a1_states.append(
                State(t, e, a1_curr_obs, a1_curr_s, a1_agent.q[a1_curr_s], frame, a1_position))
            trace.a1_scores.append(a1_env.env.previous_score)

        """end of episode"""
        trace.get_trajectories(e, args.importance_type, args.state_importance,
                               args.trajectory_importance)
        traces.append(trace)
        a1_behavior_tracker.new_episode()

    """top k diverse disagreements"""
    disagreements = get_top_k_disagreements(traces, args)
    if args.verbose: print(f'Obtained {len(disagreements)} disagreements')
    logging.info(f'Obtained {len(disagreements)} disagreements')

    """randomize order"""
    if args.randomized: random.shuffle(disagreements)

    """mark disagreement frames"""
    a1_disagreement_frames, a2_disagreement_frames = [], []
    for d in disagreements:
        d_state = d.a1_states[(args.horizon // 2) - 1]
        d_state.image = mark_agent(d_state.image, d_state.agent_position)
        a1_frames, a2_frames = d.get_frames()
        for i in range(args.horizon // 2, args.horizon):
            a1_position = d.a1_states[i].agent_position
            a2_position = d.a2_states[i].agent_position
            a1_frames[i] = mark_agent(a1_frames[i], position=a1_position, color=(255, 0,))
            a2_frames[i] = mark_agent(a2_frames[i], position=a2_position, color=(0, 0, 0))
        a1_disagreement_frames.append(a1_frames)
        a2_disagreement_frames.append(a2_frames)

    """save disagreement frames"""
    video_dir = save_disagreements(a1_disagreement_frames, a2_disagreement_frames, a1_output_dir,
                                   args.fps)
    if args.verbose: print(f'Disagreements saved')
    logging.info(f'Disagreements saved')

    """generate video"""
    fade_duration = 2
    fade_out_frame = args.horizon - fade_duration
    merge_and_fade(video_dir, args.n_disagreements, fade_out_frame, name)
    if args.verbose: print(f'DAs Video Generated')
    logging.info(f'DAs Video Generated')

    """ writes results to files"""
    a1_agent.save(a1_output_dir)
    if args.verbose: print(f'\nResults written to:\n\t\'{a1_output_dir}\'')
    logging.info(f'\nResults written to:\n\t\'{a1_output_dir}\'')

    """close environments"""
    a1_env.close()
    a2_env.close()
    del gym.envs.registration.registry.env_specs[a1_env.spec.id]
    del gym.envs.registration.registry.env_specs[a2_env.spec.id]

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='RL Agent Comparisons')
    parser.add_argument('-r1', '--a1_dir', help='agent 1 configuration file directory')
    parser.add_argument('-r2', '--a2_dir', help='agent 2 configuration file directory')
    parser.add_argument('-n', '--num_episodes', help='number of episodes to run', type=int,
                        default=3)
    parser.add_argument('-fps', '--fps', help='summary video fps', type=int, default=1)
    parser.add_argument('-l', '--horizon', help='number of frames to show per highlight',
                        type=int, default=10)
    parser.add_argument('-sb', '--show_score_bar', help='score bar', type=bool, default=False)
    parser.add_argument('-rand', '--randomized', help='randomize order of summary trajectories',
                        type=bool, default=True)
    parser.add_argument('-k', '--n_disagreements', help='# of disagreements in the summary',
                        type=int, default=5)
    parser.add_argument('-overlaplim', '--similarity_limit', help='# overlaping',
                        type=int, default=3)
    parser.add_argument('-impMeth', '--importance_type',
                        help='importance by state or trajectory', default='trajectory')
    parser.add_argument('-impTraj', '--trajectory_importance',
                        help='method calculating trajectory importance', default='last_state_val')
    parser.add_argument('-impState', '--state_importance',
                        help='method calculating state importance', default='bety')
    parser.add_argument('-v', '--verbose', help='print information to the console', default=True)
    args = parser.parse_args()

    """experiment parameters"""
    args.a1_dir = abspath('agents/HighVision_Mid')
    args.a2_dir = abspath('agents/LimitedVision_Low')
    # args.fps = 1
    # args.horizon = 10
    # args.show_score_bar = False
    # args.n_disagreements = 5
    # args.num_episodes = 10
    # args.randomized = True

    """get more/less trajectories"""
    # args.similarity_limit = 3  # int(args.horizon * 0.66)

    """importance measures"""
    # args.state_importance = "bety"  # "sb" "bety"
    # args.trajectory_importance = "last_state_val"  # last_state_loc,  last_state_val, max_min, max_avg, avg, avg_delta
    # args.importance_type = 'trajectory'  # state/trajectory

    """RUN"""
    online_comparison(args)


import numpy as np  # linear algebra
import pandas as pd  # data processing, CSV file I/O (e.g. pd.read_csv)
from pydantic import BaseModel
import pickle
import catboost as cb

from sklearn.preprocessing import QuantileTransformer

def map_2_cols(df, col1, col2, group, gby):
    return df.loc[:, [col1, col2]].astype(str).sum(axis=1).map(group[gby])

def group_2_cols(df,col1,col2,gby,func='mean'):
    group = df[[col1, col2,gby]].groupby([col1, col2],as_index=False).agg('mean')
    group['comp'] = group[[col1,col2]].astype(str).sum(axis=1)
    group.set_index('comp',inplace = True)
    group.drop([col1, col2],axis=1,inplace = True)
    return group

def cyclical(df, column, max_value):
    """
    The function is encoding time series cyclical features with sin and cos.
    Input:
    ---------
    df - pandas DataFrame
    column - column name
    max_value - column max value
    Output:
    -----------
    -same dataframe with _sin and _cos columns added
    """
    print('>>>>>>>>>>>>>>>>>>>>>', column, "\tmax: ", max_value, '\tdf: ',df[column].values)
    print(df.shape)
    df[column + '_sin'] = np.sin(2 * np.pi * df[column] / max_value)
    df[column + '_cos'] = np.cos(2 * np.pi * df[column] / max_value)
    df.fillna(0, inplace=True)
    return df

def shift_col(df, col_name, shift_num=1):
    df[f"{col_name}_shifted{shift_num}"]= df[col_name] - df[col_name].shift(shift_num)
    df[f"{col_name}_shifted{shift_num}"].fillna(method='bfill',inplace=True)
    df[f"{col_name}_shifted{shift_num}**"]= df[f"{col_name}_shifted{shift_num}"] * df[f"{col_name}_shifted{shift_num}"]
    df[f"{col_name}_shifted{shift_num}**"].fillna(method='bfill',inplace=True)
    df[f"{col_name}_shifted{shift_num}**"].fillna(method='ffill',inplace=True)
    df[f"{col_name}_shifted{shift_num}**"].fillna(method='ffill',inplace=True)
    df.fillna(0, inplace=True)
    return df

def preprocess(df, seasons_Hour_3cut=None, seaons_mean=None, test_set=False):
    df['Holiday'].replace({"Holiday": 0, "No Holiday": 1}, inplace=True)
    df['Functioning Day'].replace({"Yes": 0, "No": 1}, inplace=True)
    df['Seasons'].replace({"Autumn": 2, "Spring": 3, "Summer": 1, "Winter": 4}, inplace=True)
    df['Date'] = pd.to_datetime(df['Date'])
    df['year'] = df['Date'].apply(lambda x:1 if x.year == 2018 else 0)
    df['Hour_3cut'] = pd.cut(df['Hour'],
                       bins=[-np.inf,7,18,np.inf],
                       labels=[1,2,3]).astype('int')
    dummies = pd.get_dummies(df, columns=['Hour', 'Seasons'], prefix=['col1', 'col2'])
#     print(dummies)

    # split the fractions
    df['snow_frac'] = df['Snowfall (cm)'].apply(lambda x: x - int(x))
    df['rain_frac'] = df['Rainfall(mm)'].apply(lambda x: x - int(x))
    df['solar_frac'] = df['Solar Radiation (MJ/m2)'].apply(lambda x: x - int(x))
    df['wind_frac'] = df['Wind speed (m/s)'].apply(lambda x: x - int(x))
#     df['wind_log'] = df['Wind speed (m/s)'].apply(lambda x: np.log(x+1))

    # create dates columns
    df['month'] = df['Date'].apply(lambda x:x.month)
    df['Week Days'] = df['Date'].apply(lambda x:x.dayofweek+1)

    # shift columns -1
    df = shift_col(df, 'Temperature(�C)', -1)
    df = shift_col(df, 'Rainfall(mm)', -1)
    df = shift_col(df, 'Humidity(%)', -1)
    df = shift_col(df, 'Wind speed (m/s)', -1)

    # shift columns
    df = shift_col(df, 'Temperature(�C)')
    df = shift_col(df, 'Rainfall(mm)')
    df = shift_col(df, 'Humidity(%)')
    df = shift_col(df, 'Wind speed (m/s)')

    df = cyclical(df, "month", 12)
    df = cyclical(df, "Hour", 23)
#     df = pd.concat([df, dummies], axis=1)
#     print(type(df))

    return df.drop(["ID", 'Date', 'Temperature(�C)_shifted-1', 'Temperature(�C)_shifted1', 'Functioning Day'], axis=1)

def pipeline(df, model, qt):
    df['Date'] = pd.to_datetime(df['Date'], dayfirst=True).dt.date
    test_X = preprocess(df, test_set=True)
    test_X = pd.DataFrame(qt.transform(test_X))
    y_cbm = model.predict(test_X)
    y_hat = np.exp(y_cbm) - 1
    return y_hat

def predict_file(test_path: str):
    df = pd.read_csv(test_path)
    columns = np.load('bin/columns.npy')
    df.columns = columns
    print('################################', df.info())
    model = cb.CatBoostRegressor()
    model.load_model('bin/cb_model', 'cbm')
    qt = pickle.load(open('bin/qt.pkl', 'rb'))
    predictions = pipeline(df, model, qt)
    ret = pd.DataFrame({'ID': df['ID'].to_numpy(), 'y': predictions}).to_numpy()
    return ret

def predict_single(p_id, date, hour, temperature, humidity, wind_speed, visibility, dew_point, solar_rad, rain_fall,
                   snow_fall, season, holiday, functioning_day):
    columns = np.load('bin/columns.npy')
    df = pd.DataFrame(columns=columns)

    df = df.append({columns[0]: p_id, columns[1]: date, columns[2]: hour, columns[3]: temperature, columns[4]: humidity,
                   columns[5]: wind_speed, columns[6]: visibility, columns[7]: dew_point, columns[8]: solar_rad,
                   columns[9]: rain_fall, columns[10]: snow_fall, columns[11]: season, columns[12]: holiday,
                   columns[13]: functioning_day}, ignore_index=True)
    df['Hour'] = df['Hour'].astype('int')
    model = cb.CatBoostRegressor()
    model.load_model('bin/cb_model', 'cbm')
    qt = pickle.load(open('bin/qt.pkl', 'rb'))
    predictions = pipeline(df, model, qt)
    ret = pd.DataFrame({'ID': df['ID'].to_numpy(), 'y': predictions}).to_numpy()
    return ret

class PredictionItem(BaseModel):
    p_id: str
    date: str
    hour: int
    temperature: float
    humidity: float
    wind_speed: float
    visibility: int
    dew_point: float
    solar_rad: float
    rain_fall: float
    snow_fall: float
    season: str
    holiday: str
    functioning_day: str


def add(s, W):
    for c in W:
        s.append(c)
    return s

def remove(s, k):
    return s[:-k]

s = []
undos = []

Q = int(raw_input())
for _ in range(Q):
    ops = raw_input().split(' ')
    if ops[0] == '1':
        s = add(s, ops[1])
        undos.append(['2', len(ops[1])])
    elif ops[0] == '2':
        k = int(ops[1])
        undos.append(['1', s[-k:]])
        s = remove(s, k)
    elif ops[0] == '3':
        k = int(ops[1])
        print(s[k - 1])
    else:  # undo
        op, val = undos.pop(-1)
        func = add if op == '1' else remove
        s = func(s, val)


# Author: Niharika Singhal
#
# For license information, see LICENSE.txt

"""
Functions for estimating the parameters (alpha_t, sigma^2_t, beta^1_t, beta^2_t, beta^3_t) of the linear regression with
degree (LR-N)

References
----------
.. *Kobayashi and Lambiotte, ICWSM, pp. 191-200, 2016; Zhao et al., KDD, pp. 1513-1522, 2015*.
"""

import numpy as np
from scipy import linalg

def parameter_estimation_lr_n(follower_orig, total_follower_t, no_events, event_pred):
    """
    Fit the parameters of the linear regression with degree
    :param follower_orig: array, containing the number of followers of the original tweeting person for all tweets (d_0).
    :param total_follower_t: array, containing the cumulative number of followers at an observation time for all tweets (D(T)).
    :param no_events: array, containing the total number of retweets at an observation time (R(T) ) for all tweets.
    :param event_pred: array, containing the total number of retweets at a prediction time (R(t) ) for all tweets.
    :return: parameters of the linear regression with degree.
    """

    a = np.matrix([
        [len(follower_orig), sum(no_events), sum(total_follower_t), sum(follower_orig)],
        [sum(no_events), sum(no_events * no_events), sum(total_follower_t * no_events),
         sum(follower_orig * no_events)],
        [sum(total_follower_t), sum(no_events * total_follower_t), sum(total_follower_t * total_follower_t),
         sum(total_follower_t * follower_orig)],
        [sum(follower_orig), sum(no_events * follower_orig), sum(total_follower_t * follower_orig),
         sum(follower_orig * follower_orig)]
    ])

    # y1 = sum(event_pred)
    # y2 = sum([(no_events[i] * event_pred[i]) for i in range(len(event_pred))])
    # y3 = sum([(total_follower_t[i] * event_pred[i]) for i in range(len(event_pred))])
    # y4 = sum([(follower_orig[i] * event_pred[i]) for i in range(len(event_pred))])

    # same as above with numpy to avoid loops
    y1 = np.sum(event_pred, axis=0)
    y2 = np.sum((event_pred.T * no_events).T, axis=0)
    y3 = np.sum((event_pred.T * total_follower_t).T, axis=0)
    y4 = np.sum((event_pred.T * follower_orig.T).T, axis=0)

    if isinstance(y1, np.float64):
        theta = linalg.solve(a, [y1, y2, y3, y4])
        sigma_val = [(event_pred[i] - theta[0] - (theta[1] * no_events[i]) - (theta[2] * total_follower_t[i]) -
                      (theta[3] * follower_orig[i])) ** 2 for i in range(len(event_pred))]
        sigma_sq = sum(sigma_val) / len(event_pred)

        return [theta], [sigma_sq]

    else:
        theta = [linalg.solve(a, [y1[i], y2[i], y3[i], y4[i]]) for i in range(len(sum(event_pred)))]
        sigma_sq_list = []
        for j in range(len(theta)):
            val = [(event_pred[i][j] - theta[j][0] - (theta[j][1] * no_events[i]) -
                    (theta[j][2] * total_follower_t[i]) - (theta[j][3] * follower_orig[i])) ** 2
                   for i in range(len(no_events))]
            sigma_sq = sum(val) / len(no_events)
            sigma_sq_list.append(sigma_sq)

        return theta, sigma_sq_list