import argparse
from enum import Enum
import time

import numpy as np
from keras.callbacks import TensorBoard, ModelCheckpoint, ReduceLROnPlateau, EarlyStopping
from keras.optimizers import Adam

from data.data_generator import yolov3_generator
from data.yolov3_load_dataset import YoloV3DataLoader
from model.yolo3.model import preprocess_true_boxes
from model.yolo3.yolov3_model import YoloV3Model

class ModelType(Enum):
    """Type of used model during training"""
    YOLO_V3 = 'yolo_v3'

def _read_args():
    """Read command-line arguments"""
    parser = argparse.ArgumentParser(description='Parameters for creating and training a model.')
    parser.add_argument('-t', '--type', nargs=1, help='Type of trained model.', required=True)
    parser.add_argument('-s', '--shape', nargs=2, help='Input shape (width height).', required=True)
    parser.add_argument('-a', '--anchors_path', nargs=1, help='File path of anchors.', required=True)
    parser.add_argument('-c', '--classes_path', nargs=1, help='File path of classes.', required=True)
    parser.add_argument('-w', '--weights_path', nargs=1,
                        help='File path of weights (serialized in Keras\' .h5 format).', required=True)
    parser.add_argument('-f', '--freeze_body', nargs=1, help='Layer-freezing parameter (0, 1 or 2).', default=0)
    parser.add_argument('-d', '--dataset_path', nargs=1, help='Dataset\'s path.', required=True)

    args = parser.parse_args()

    model_type = ModelType(args.type[0])
    input_shape = (int(args.shape[0]), int(args.shape[1]))
    anchors = _get_anchors(args.anchors_path[0])
    class_names = _get_classes(args.classes_path[0])
    weights_path = args.weights_path[0]
    freeze_body = int(args.freeze_body[0])
    dataset_path = args.dataset_path[0]

    return model_type, input_shape, anchors, class_names, weights_path, freeze_body, dataset_path

def _get_classes(classes_path):
    """Read class-names into an array"""
    with open(classes_path) as f:
        class_names = f.readlines()
    class_names = [c.strip() for c in class_names]
    return class_names

def _get_anchors(anchors_path):
    """Read anchors into an array in [ [x_1, y_1], ..., [x_n, y_n] ] format"""
    with open(anchors_path) as f:
        anchors = f.readline()
    anchors = [float(x) for x in anchors.split(',')]
    return np.array(anchors).reshape(-1, 2)

def _data_generator(dataset, batch_size, input_shape, anchors, num_classes, images_path):
    """Data generator for training the model on augmented mini-batches"""
    while True:
        # get images and bboxes
        image_data, box_data = yolov3_generator(images_path, dataset, batch_size, input_shape)

        image_data = np.array(image_data)
        box_data = np.array(box_data)
        # convert bounding boxes into yolo format
        y_true = preprocess_true_boxes(box_data, input_shape, anchors, num_classes)
        # return data in the needed format
        yield [image_data, *y_true], np.zeros(batch_size)

def data_generator_wrapper(dataset, batch_size, input_shape, anchors, num_classes, images_path):
    """Data generator wrapper"""
    n = len(dataset)
    if n == 0 or batch_size <= 0:
        return None
    return _data_generator(dataset, batch_size, input_shape, anchors, num_classes, images_path)

def main():
    """Load model, then train it with training data"""
    # read command line arguments
    model_type, input_shape, anchors, class_names, init_weights_path, freeze_body, dataset_path = _read_args()
    n_classes = len(class_names)

    # choose loaders according to the current network type
    model_loader, dataset_loader = None, None
    if model_type == ModelType.YOLO_V3:
        model_loader = YoloV3Model(input_shape, anchors, n_classes, init_weights_path, freeze_body)
        dataset_loader = YoloV3DataLoader()
    assert None not in (model_loader, dataset_loader)

    # load model
    model = model_loader.get_model()
    print('Model loaded.')
    model.summary()

    # load dataset
    dataset_loaded = dataset_loader.load_dataset(dataset_path, input_shape)
    datasets = dataset_loaded['train']
    validation = dataset_loaded['val']
    print('Dataset loaded.')

    # logging with tensorboard
    log_dir = '../logs'
    logging = TensorBoard(log_dir=log_dir)

    # save weights into folder
    weights_dir = '../trained_weights/'

    # save model weights periodically
    checkpoint = ModelCheckpoint(weights_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
                                 monitor='val_loss', save_weights_only=True, save_best_only=True, period=3)

    # reduce learning rate according to the loss measured on validation set
    reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1)

    # stop training after 'patience' number of epochs if there were no improvements
    early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)

    # number of training samples
    num_train = 0
    for dataset in datasets:
        num_train += len(dataset)

    # number of validation samples
    num_val = 0
    for dataset in validation:
        num_val += len(dataset)

    fine_tuning_only = False
    if not fine_tuning_only:

        # mini-batch size
        batch_size = 8

        # Train with frozen layers first, to get a stable loss.
        model.compile(optimizer=Adam(lr=0.001), loss={'yolo_loss': lambda y_true, y_pred: y_pred})

        print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))

        model.fit_generator(
            generator=data_generator_wrapper(datasets, batch_size, input_shape, anchors, n_classes, dataset_path),
            steps_per_epoch=max(1, num_train // batch_size // 20),
            validation_data=data_generator_wrapper(validation, batch_size, input_shape, anchors,
                                                   n_classes, dataset_path),
            validation_steps=max(1, min(50, num_val // batch_size)),
            epochs=75,
            initial_epoch=0,
            callbacks=[logging, checkpoint])

        # serialize weights
        model.save_weights(weights_dir + 'trained_weights_stage_1.h5')
        model.save_weights(weights_dir + 'trained_weights_stage_1' + str(time.time()) + '.h5')

    print('fine-tuning')

    # mini-batch size
    batch_size = 8  # note that more GPU memory is required after unfreezing the body

    # Unfreeze and continue training, to fine-tune.
    model.load_weights(weights_dir + 'trained_weights_stage_1.h5')

    # freeze2 = True
    # if not freeze2:
    #    for i in range(len(model.layers)):
    #        model.layers[i].trainable = True

    model.compile(optimizer=Adam(lr=0.0001), loss={'yolo_loss': lambda y_true, y_pred: y_pred})

    print('All layers are unfrozen now.')

    print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))

    model.fit_generator(
        generator=data_generator_wrapper(datasets, batch_size, input_shape, anchors, n_classes, dataset_path),
        steps_per_epoch=max(1, num_train // batch_size // 20),
        validation_data=data_generator_wrapper(validation, batch_size, input_shape, anchors,
                                               n_classes, dataset_path),
        validation_steps=max(1, min(50, num_val // batch_size)),
        epochs=100,
        initial_epoch=50,
        callbacks=[logging, checkpoint, reduce_lr, early_stopping])

    # serialize weights
    model.save_weights(weights_dir + 'trained_weights_final.h5')
    model.save_weights(weights_dir + 'trained_weights_final' + str(time.time()) + '.h5')

if __name__ == "__main__":
    main()


#!/usr/bin/env python
import sys
sys.path.append("..")

from utils import cv2_trans as transforms
from termcolor import cprint
import cv2
import torchvision
import torch.utils.data as data
import torch
import random
import numpy as np
import os
import warnings
import math

class MagTrainDataset(data.Dataset):
    def __init__(self, ann_file, transform=None, rank=None,num_replicas=None):
        self.ann_file = ann_file
        self.transform = transform
        self.num_replicas = num_replicas
        self.rank = rank
        self.init()

    def init(self):
        self.weight = {}
        self.im_names = []
        self.targets = []

        with open(self.ann_file) as f:
            lnum = sum(1 for _ in f)
            idxs = list(range(0, lnum, int(lnum/self.num_replicas)))
            # TODO: ugly code
            if len(idxs) == self.num_replicas:
                idxs.append(lnum)
            cidx = [idxs[self.rank], idxs[self.rank+1]
                    ] if self.num_replicas > 1 else [0, lnum]
        with open(self.ann_file) as f:
            for idx, line in enumerate(f):
                if idx < cidx[0] or idx > cidx[1]:
                    continue
                data = line.strip().split(' ')
                self.im_names.append(data[0])
                target = int(data[2])
                self.targets.append(target)

    def __getitem__(self, index):
        im_name = self.im_names[index]
        target = self.targets[index]
        img = cv2.imread(im_name)

        img = self.transform(img)
        return img, target

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

class ParallelDistributedSampler(torch.utils.data.Sampler):
    """Sampler that restricts data loading to a subset of the dataset.

    It is especially useful in conjunction with
    :class:`torch.nn.parallel.DistributedDataParallel`. In such case, each
    process can pass a DistributedSampler instance as a DataLoader sampler,
    and load a subset of the original dataset that is exclusive to it.

    Note:
        Dataset is assumed to be of constant size.

    Arguments:
        dataset: Dataset used for sampling.
        num_replicas (optional): Number of processes participating in
            distributed training.
        rank (optional): Rank of the current process within num_replicas.
        shuffle (optional): If true (default), sampler will shuffle the indices

    Warning:
        In distributed mode, calling the ``set_epoch`` method is needed to
        make shuffling work; each process will use the same random seed
        otherwise.

    Example::

        >>> sampler = DistributedSampler(dataset) if is_distributed else None
        >>> loader = DataLoader(dataset, shuffle=(sampler is None),
        ...                     sampler=sampler)
        >>> for epoch in range(start_epoch, n_epochs):
        ...     if is_distributed:
    """

    def __init__(self, dataset, num_replicas=None, rank=None, shuffle=True):
        self.dataset = dataset
        self.num_replicas = num_replicas
        self.rank = rank
        self.epoch = 0
        self.num_samples = int(math.ceil(len(self.dataset) * 1.0))
        self.total_size = self.num_samples
        self.shuffle = shuffle

    def __iter__(self):
        # deterministically shuffle based on epoch
        g = torch.Generator()
        g.manual_seed(self.epoch)
        if self.shuffle:
            indices = torch.randperm(len(self.dataset), generator=g).tolist()
        else:
            indices = list(range(len(self.dataset)))

        # add extra samples to make it evenly divisible
        indices += indices[:(self.total_size - len(indices))]
        assert len(indices) == self.total_size

        return iter(indices)

    def __len__(self):
        return self.num_samples

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

def train_loader(args):
    train_trans = transforms.Compose([
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
    ])
    train_dataset = MagTrainDataset(
        args.train_list,
        transform=train_trans,
        rank=args.rank,
        num_replicas=args.world_size
    )
    train_sampler = ParallelDistributedSampler(
                    train_dataset,
                    rank=args.rank,
                    num_replicas=args.world_size,
                    shuffle=True)

    train_loader = torch.utils.data.DataLoader(
        train_dataset,
        shuffle=(train_sampler is None),
        batch_size=args.batch_size,
        num_workers=args.workers,
        pin_memory=True,
        sampler=train_sampler,
        drop_last=(train_sampler is None))

    return train_loader


import matplotlib
matplotlib.use('Agg')
from data import *
from layers import *
from layers.box_utils import *
from utils.augmentations import SSDAugmentation
from ssd import build_ssd
import os, datetime
import sys
sys.path.append(os.path.expanduser("~/Documents"))
import omni_torch.visualize.basic as vb
from omni_torch.networks.optimizer import *
import time
import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.optim as optim
import torch.backends.cudnn as cudnn
import torch.nn.init as init
import torch.utils.data as data
import numpy as np
from args import prepare_args
import mmdet.ops.dcn as dcn
from layers.visualization import *

args = prepare_args(VOC_ROOT)
TMPJPG = os.path.expanduser("~/Pictures/tmp.jpg")
torch.set_default_tensor_type('torch.cuda.FloatTensor')

dt = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M")

def avg(list):
    return sum(list) / len(list)

def old_fit(args, cfg, net, train_set, optimizer, criterion):
    step_index = 0
    train_loader = data.DataLoader(train_set, args.batch_size,
                                  num_workers=args.num_workers,
                                  shuffle=True, collate_fn=detection_collate,
                                  pin_memory=True)
    # create batch iterator
    batch_iterator = iter(train_loader)
    loc_losses, conf_losses = [], []
    #progress = open(os.path.join(args.save_folder, "%s_train_prog.txt" % args.name), "w")
    for iteration in range(args.start_iter + args.ft_iter, args.max_iter):
        # print("iteration: %s"%iteration)
        args.curr_epoch = iteration
        if iteration in cfg['lr_steps']:
            step_index += 1
            adjust_learning_rate(optimizer, args.gamma, step_index)
        # load train data
        try:
            images, targets, _shape = next(batch_iterator)
        except StopIteration:
            batch_iterator = iter(train_loader)
            images, targets, _shape = next(batch_iterator)
        t0 = time.time()
        #images = Variable(images.cuda())
        #targets = [Variable(ann.cuda(), volatile=True) for ann in targets]

        images = images.cuda()
        targets = [ann.cuda() for ann in targets]
        targets_idx_ = torch.cuda.LongTensor([ann.size(0) for ann in targets])
        targets_idx = torch.cuda.LongTensor([sum(targets_idx_[:_idx]) for _idx in range(len(targets_idx_))])
        y_idx = torch.stack([targets_idx, targets_idx_], dim=1)
        y = torch.cat(targets, dim=0).repeat(torch.cuda.device_count(), 1)

        if iteration == 0 and args.visualize_box:
            # visualize_bbox(args, cfg, images, targets, net.module.priors[0], batch_idx)
            pass
        # forward
        #out = net(images)
        out1, out2 = net(images, deform_map=False, test=False)

        # backprop
        optimizer.zero_grad()
        out = (out1, out2, net.module.priors)
        loss_l, loss_c = criterion(out, targets)
        #loss_l, loss_c = out1, out2
        loss = loss_l + loss_c
        loss.backward()
        optimizer.step()
        #loc_loss += loss_l.data
        #conf_loss += loss_c.data
        loc_losses.append(float(loss_l.data))
        conf_losses.append(float(loss_c.data))
        #result = "--loc_loss: %.4f conf_loss: %.4f--\n" % (float(loss_l.data), float(loss_c.data))
        #progress.write(result)
        if iteration > 0 and iteration % 10 == 0:
            t1 = time.time()
            print('timer: %.4f sec.' % (t1 - t0))
            print('iter ' + repr(iteration) + ' || Loss: %.4f || Conf_Loss: %.4f || Loc_Loss: %.4f ||' % (
            loss.data, loss_c.data, loss_l.data), end=' ')

        if iteration - args.start_iter - args.ft_iter > 0 and iteration % 100 == 0:
            train_losses = [np.asarray(loc_losses), np.asarray(conf_losses)]
            vb.plot_curves(train_losses, ["location", "confidence"], save_path=args.val_log,
                           name=dt + "_" + args.name, window=25, fig_size=(18, 6),
                           bound={"low": 0.0, "high": 3.0}, title="Train Loss")

        #if iteration > 0 and iteration % 1000 == 0:
            #with torch.no_grad():
                #val(args, net, val_set, optimizer)

        if iteration > 5000 and iteration % 1000 == 0:
            print('Saving state, iter:', iteration)
            torch.save(net.module.state_dict(),
                       os.path.join(args.save_folder, '%s_%s_%s.pth' %
                                    (args.name, args.img_size, repr(iteration))))

        # 由于centroid可以向两个方向形成distortion，所以每个epoch后都需要重新创建一次
        # 以保证两个方向都能够受到distortion
        # net.module.create_centroid()

def val(args, net, val_set, optimizer):
    net.eval()
    val_loader = data.DataLoader(val_set, args.batch_size,
                                   num_workers=args.num_workers,
                                   shuffle=True, collate_fn=detection_collate,
                                   pin_memory=True)
    # create batch iterator
    batch_iterator = iter(val_loader)
    eval_results = []
    start_time = time.time()
    for iteration in range(args.start_iter + args.ft_iter, args.max_iter):
        try:
            images, targets, _shape = next(batch_iterator)
        except StopIteration:
            batch_iterator = iter(val_loader)
            images, targets, _shape = next(batch_iterator)

        images = Variable(images.cuda())
        targets = [Variable(ann.cuda(), volatile=True) for ann in targets]
        out1, out2 = net(images, deform_map=False, test=False)
        detections, reg_boxes = out1
        eval_result = evaluate(images, detections.data, targets, iteration, 0.1,
                               visualize=False, post_combine=True)
        eval_results.append(eval_result)
    eval_results = list(map(list, zip(*eval_results)))
    print(" --- accuracy=%.4f, precision=%.4f, recall=%.4f, f1-score=%.4f, cost %.2f seconds ---" %
          (avg(eval_results[0]), avg(eval_results[1]), avg(eval_results[2]),
           avg(eval_results[3]), time.time() - start_time))

def evaluate(img, detections, targets, batch_idx, threshold, visualize=False, post_combine=False):
    eval_result = {}
    #save_dir = os.path.expanduser("~/Pictures/")
    w = img.size(3)
    h = img.size(2)
    accu, pre, rec, f1 = [], [], [], []
    for i in range(detections.size(0)):
        gt_cls = targets[i][:, -1].data
        tar = targets[i][:, :-1].data
        gt_boxes = [[] for _ in range(20)]
        for _i, cls in enumerate(gt_cls):
            gt_boxes[int(cls)].append(tar[_i])

        # enumerate through all classes
        for j in range(1, detections.size(1)):
            idx = detections[i, j, :, 0] >= threshold
            boxes = detections[i, j, idx, 1:]
            if boxes.size(0) == 0 and len(gt_boxes[j-1]) == 0:
                continue
            elif boxes.size(0) != 0 and len(gt_boxes[j-1]) == 0:
                accuracy, precision, recall, f1_score = 0, 0, 1, 0
            elif boxes.size(0) == 0 and len(gt_boxes[j-1]) != 0:
                accuracy, precision, recall, f1_score = 0, 1, 0, 0
            else:
                gt = torch.stack(gt_boxes[j - 1], 0)
                jac = jaccard(boxes, gt)
                overlap, idx = jac.max(1, keepdim=True)
                # This is not DetEval
                positive_pred = boxes[overlap.squeeze(1) > 0.5]
                negative_pred = boxes[overlap.squeeze(1) <= 0.5]
                if negative_pred.size(0) == 0:
                    negative_pred = tuple()
                #print_box(blue_boxes=positive_pred, green_boxes=gt_boxes, red_boxes=negative_pred,
                          #img=vb.plot_tensor(args, img, margin=0), save_dir=save_dir)

                accuracy, precision, recall = measure(positive_pred, gt, width=w, height=h)
                if (recall + precision) < 1e-3:
                    f1_score = 0
                else:
                    f1_score = 2 * (recall * precision) / (recall + precision)
                if visualize and threshold == 0.1 and i == 0:
                    pred = [[float(coor) for coor in area] for area in positive_pred]
                    gt = [[float(coor) for coor in area] for area in gt_boxes]
                    #print_box(negative_pred, green_boxes=gt, blue_boxes=pred, idx=batch_idx,
                              #img=vb.plot_tensor(args, img, margin=0), save_dir=args.val_log)
            accu.append(accuracy)
            pre.append(precision)
            rec.append(recall)
            f1.append(f1_score)
        #eval_result.update({threshold: [avg(accu), avg(pre), avg(rec), avg(f1)]})
    return avg(accu), avg(pre), avg(rec), avg(f1)

def adjust_learning_rate(optimizer, gamma, step):
    """Sets the learning rate to the initial LR decayed by 10 at every
        specified step
    # Adapted from PyTorch Imagenet example:
    # https://github.com/pytorch/examples/blob/master/imagenet/main.py
    """
    lr = args.lr * (gamma ** (step))
    for param_group in optimizer.param_groups:
        param_group['lr'] = lr

def weights_init(m):
    if type(m) == torch.nn.Linear:
        torch.nn.init.xavier_normal_(m.weight)
    elif type(m) == torch.nn.Conv2d:
        torch.nn.init.kaiming_normal_(m.weight)
    elif isinstance(m, torch.nn.BatchNorm2d):
        torch.nn.init.constant_(m.weight, 1)
        torch.nn.init.constant_(m.bias, 0)
    else:
        if type(m) is nn.ModuleList:
            for _m in m:
                if type(_m) == torch.nn.Linear:
                    torch.nn.init.xavier_normal_(_m.weight)
                elif type(_m) == torch.nn.Conv2d:
                    torch.nn.init.kaiming_normal_(_m.weight)
                elif isinstance(_m, torch.nn.BatchNorm2d):
                    torch.nn.init.constant_(_m.weight, 1)
                    torch.nn.init.constant_(_m.bias, 0)
        elif type(m) is dcn.DeformConv:
            torch.nn.init.kaiming_normal_(m.weight)
        else:
            pass

def main():
    if args.dataset == 'COCO':
        if args.dataset_root == VOC_ROOT:
            if not os.path.exists(COCO_ROOT):
                parser.error('Must specify dataset_root if specifying dataset')
            print("WARNING: Using default COCO dataset_root because " +
                  "--dataset_root was not specified.")
            args.dataset_root = COCO_ROOT
        cfg = coco
        train_set = COCODetection(root=args.dataset_root,
                                transform=SSDAugmentation(cfg['min_dim'], MEANS))
        val_set = None
    elif args.dataset == 'VOC':
        #if args.dataset_root == COCO_ROOT:
            #parser.error('Must specify dataset if specifying dataset_root')
        cfg = voc
        train_set = VOCDetection(root=args.dataset_root,
                               transform=SSDAugmentation(cfg['min_dim'], MEANS))
        val_set = VOCDetection(args.voc_root, [('2007', "test")],
                               BaseTransform(args.img_size, (104, 117, 123)),
                               VOCAnnotationTransform())
        """
        train_set = data.DataLoader(train_set, args.batch_size,
                                      num_workers=args.num_workers,
                                      shuffle=False, collate_fn=detection_collate,
                                      pin_memory=True)
        val_set = None

        val_set = data.DataLoader(val_set, args.batch_size,
                                    num_workers=args.num_workers,
                                    shuffle=False, collate_fn=detection_collate,
                                    pin_memory=True)"""
    else:
        train_set, val_set = None, None

    ssd_net = build_ssd(args, 'train', cfg['min_dim'], cfg['num_classes'])

    args.curr_epoch = args.start_iter + args.ft_iter
    if args.resume:
        model_name = "%s_%s_%s.pth" % (args.basenet, args.img_size, args.ft_iter)
        print('Resuming training from %s...' % (model_name))
        weights = torch.load(os.path.join(args.save_folder, model_name))
        ssd_net.load_state_dict(weights)
    else:
        vgg_weights = torch.load(os.path.join(args.save_folder, args.basenet))
        print('Loading base network...')
        ssd_net.vgg.load_state_dict(vgg_weights)
        print('Initializing weights...')
        ssd_net.extras.apply(weights_init)
        if args.implementation in ["header", "190709"]:
            ssd_net.header.apply(weights_init)
        elif args.implementation == "vanilla":
            ssd_net.loc.apply(weights_init)
            ssd_net.conf.apply(weights_init)

    if args.cuda:
        net = torch.nn.DataParallel(ssd_net).cuda()
        cudnn.benchmark = True

    if args.optimizer.lower() == "adam":
        optimizer = optim.Adam(net.parameters(), lr=args.lr, weight_decay=args.weight_decay)
    elif args.optimizer.lower() == "sgd":
        optimizer = optim.SGD(net.parameters(), lr=args.lr, momentum=args.momentum,
                              weight_decay=args.weight_decay)
    elif args.optimizer.lower() == "super":
        optimizer = Adastand(net.parameters(), lr=args.lr, weight_decay=args.weight_decay)
    else:
        raise NotImplementedError()
    criterion = MultiBoxLoss(voc['num_classes'], args, True, 0, True, 3, 0.5, False, args.cuda)
    old_fit(args, cfg, net, train_set, optimizer, criterion)
    """
    loc_loss, conf_loss = [], []
    accuracy, precision, recall, f1_score = [], [], [], []
    for epoch in range(args.max_iter):
        loc_avg, conf_avg = fit(args, cfg, net, train_set, optimizer, is_train=True)
        loc_loss.append(loc_avg)
        conf_loss.append(conf_avg)
        train_losses = [np.asarray(loc_loss), np.asarray(conf_loss)]
        if val_set is not None:
            #fit(args, cfg, net, val_set, optimizer, is_train=False)
            accu, pre, rec, f1 = val(args, cfg, net, val_set, optimizer)
            accuracy.append(accu)
            precision.append(pre)
            recall.append(rec)
            f1_score.append(f1)
            val_losses = [np.asarray(accuracy), np.asarray(precision),
                          np.asarray(recall), np.asarray(f1_score)]

        if epoch + args.start_iter + args.ft_iter > 30 and epoch % 10 == 0:
            save_epoch = epoch + args.start_iter + args.ft_iter
            torch.save(net.module.state_dict(),
                       os.path.join(args.save_folder, '%s_%s_%s.pth' %
                                    (args.name, args.img_size, save_epoch)))
        if epoch >= 5:
            if val_set is None:
                vb.plot_curves(train_losses, ["location", "confidence"], save_path=args.val_log,
                               name=dt + "_" + args.name, window=5, fig_size=(18, 6),
                               bound={"low": 0.0, "high": 3.0}, title="Train Loss")
            else:
                vb.plot_multi_loss_distribution(
                    multi_line_data=[train_losses, val_losses],
                    multi_line_labels=[["location", "confidence"], ["Accuracy", "Precision", "Recall", "F1-Score"]],
                    save_path=args.val_log, window=5, name=dt + "_" + args.name, fig_size=(18, 12),
                    bound=[{"low": 0.0, "high": 3.0}, {"low": 0.0, "high": 1.0}],
                    titles=["Train Loss", "Validation Score"]
                )
        # 由于centroid可以向两个方向形成distortion，所以每个epoch后都需要重新创建一次
        # 以保证两个方向都能够受到distortion
        #net.module.create_centroid()
        """

if __name__ == '__main__':
    main()


#!/usr/bin/env python

import argparse
import numpy as np
import yaml
import os

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("config", help="configuration file (yaml)")
    parser.add_argument("name", help="project (folder) name")
    args = parser.parse_args()

    # read file
    with open(args.config) as configFile:
        data = yaml.safe_load(configFile)

    topicIdx = 0
    for topic in data["topics"]:
        print("Topic: " + topic["name"])
        numNodes = len(topic["nodes"])
        allNodeData = []
        for nodeIdx in range(0, numNodes):
            fileName = os.path.expanduser("~/.ros/{}_{}.txt".format(topicIdx, nodeIdx))
            nodeData = np.loadtxt(fileName, dtype=np.uint64, delimiter=",", ndmin=2)
            allNodeData.append(nodeData)
            # print(nodeData)
        for i in range(1, numNodes):
            numElements = min(allNodeData[i-1].shape[0], allNodeData[i].shape[0])
        dts = np.empty((numElements, numNodes - 1))
        for i in range(1, numNodes):
            dts[:,i-1] = allNodeData[i][0:numElements,0] - allNodeData[i-1][0:numElements,0]
        print("  Latencies [us]:")
        print("    " + str(dts).replace('\n','\n    '))
        for iteration in range(0, numElements):
            print("  Iteration {}:".format(iteration))
            for i in range(0, numNodes):
                fileName = os.path.join(os.path.abspath("."), "build/" + args.name + "/paths_{}_{}.txt".format(topicIdx, i))
                # fileName = "/home/whoenig/projects/phd/cs610/project/milestone2/ros_ws/build/test3/paths_{}_{}.txt".format(topicIdx, i)
                with open(fileName) as f:
                    lines = f.readlines()
                if allNodeData[i].shape[1] > 1:
                    entry = lines[allNodeData[i][iteration,1]]
                    entries = entry.split(",")[1:]
                    lastEntry = None
                    for e in entries:
                        if e != lastEntry:
                            print("    " + os.path.basename(e))
                        lastEntry = e
                else:
                    print("    " + lines[0])
                if i < numNodes - 1:
                    print("    Latency: {} us".format(dts[iteration, i]))

        topicIdx += 1


import pandas as pd
import numpy as np
import re
import os
import nltk
from tqdm import tqdm
import string
from sklearn.utils import shuffle
from sklearn.model_selection import train_test_split

class ReadData:
    def __init__(self, path, train_val_split, language_1, language_2):
        self.path = path
        self.train_val_split = train_val_split
        self.dataset = pd.read_csv(self.path)
        self.dataset = self.dataset[[language_1, language_2]]
        self.language_1 = language_1
        self.language_2 = language_2
        assert language_2 in self.dataset.columns and language_1 in self.dataset.columns, "Invalid format of {} file.".format(self.path)

    def prep_data(self):
        self.clean_data()
        self.get_info()

        print(self.dataset.head(10))

        self.language_1_text = list(self.dataset[self.language_1])
        self.language_2_text = list(self.dataset[self.language_2])

        x = []
        y = []
        for i in range(len(self.language_1_text)):
            x.append(str(self.language_1_text[i]))
            y.append(str(self.language_2_text[i]))

        X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=self.train_val_split)

        return (X_train, y_train), (X_test, y_test)

    def clean_data(self):
        # Uncasing all sentences pairs
        self.dataset[self.language_1] = self.dataset[self.language_1].apply(lambda x: x.lower())
        self.dataset[self.language_2] = self.dataset[self.language_2].apply(lambda x: x.lower())

        # Removing Punctuation
        exclude = set(string.punctuation)
        self.dataset[self.language_1] = self.dataset[self.language_1].apply(lambda x: ''.join(ch for ch in x if ch not in exclude))
        self.dataset[self.language_2] = self.dataset[self.language_2].apply(lambda x: ''.join(ch for ch in x if ch not in exclude))

        # Removing unwanted trailing characters
        self.dataset[self.language_1] = self.dataset[self.language_1].apply(lambda x: x.strip())
        self.dataset[self.language_2] = self.dataset[self.language_2].apply(lambda x: x.strip())

        self.dataset[self.language_1] = self.dataset[self.language_1].apply(lambda x: re.sub(" +", " ", x))
        self.dataset[self.language_2] = self.dataset[self.language_2].apply(lambda x:  re.sub(" +", " ", x))

        self.dataset[self.language_1] = self.dataset[self.language_1].apply(lambda x: re.sub("\?\?", "", x))
        self.dataset[self.language_2] = self.dataset[self.language_2].apply(lambda x:  re.sub("\?\?", "", x))

        # Adding "START_" and "_END" tokens
        self.dataset[self.language_2] = self.dataset[self.language_2].apply(lambda x: "START_ " + x + " _END")

    def get_info(self):
        vocab_language_1 = set()
        for sent in self.dataset[self.language_1]:
            for word in sent.split():
                if word not in vocab_language_1:
                    vocab_language_1.add(word)

        vocab_language_2 = set()
        for sent in self.dataset[self.language_2]:
            for word in sent.split():
                if word not in vocab_language_2:
                    vocab_language_2.add(word)

        length_list = []
        for l in self.dataset[self.language_1]:
            length_list.append(len(l.split(' ')))
        self.max_length_language_1 = np.max(length_list)

        length_list = []
        for l in self.dataset[self.language_2]:
            length_list.append(len(l.split(' ')))
        self.max_length_language_2 = np.max(length_list)

        input_words = sorted(list(vocab_language_1))
        target_words = sorted(list(vocab_language_2))

        self.num_encoder_tokens = len(vocab_language_1)
        self.num_decoder_tokens = len(vocab_language_2) + 1

        self.input_token_index = dict([(word, i+1) for i, word in enumerate(input_words)])
        self.target_token_index = dict([(word, i+1) for i, word in enumerate(target_words)])

        self.reverse_input_char_index = dict((i, word) for word, i in self.input_token_index.items())
        self.reverse_target_char_index = dict((i, word) for word, i in self.target_token_index.items())

        self.dataset = shuffle(self.dataset)

    def generate_batch(self, X, y, batch_size):
        while True:
            for j in range(0, len(X), batch_size):
                encoder_input_data = np.zeros((batch_size, self.max_length_language_1),dtype='float32')
                decoder_input_data = np.zeros((batch_size, self.max_length_language_2),dtype='float32')
                decoder_target_data = np.zeros((batch_size, self.max_length_language_2, self.num_decoder_tokens),dtype='float32')
                for i, (input_text, target_text) in enumerate(zip(X[j:j+batch_size], y[j:j+batch_size])):
                    for t, word in enumerate(input_text.split()):
                        encoder_input_data[i, t] = self.input_token_index[word] # encoder input seq
                    for t, word in enumerate(target_text.split()):
                        if t<len(target_text.split())-1:
                            decoder_input_data[i, t] = self.target_token_index[word] # decoder input seq
                        if t>0:
                            # decoder target sequence (one hot encoded)
                            # does not include the START_ token
                            # Offset by one timestep
                            decoder_target_data[i, t - 1, self.target_token_index[word]] = 1.
                yield([encoder_input_data, decoder_input_data], decoder_target_data)


import pandas as pd
import numpy as np
from ast import literal_eval
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
from sklearn.metrics.pairwise import linear_kernel, cosine_similarity
from nltk.stem.snowball import SnowballStemmer
from nltk.stem.wordnet import WordNetLemmatizer
from nltk.corpus import wordnet
from surprise import Reader, Dataset, SVD, evaluate
import copy
import datetime
import time
from multiprocessing.pool import ThreadPool
from movies_cinema_imdb import get_global_indices_map_for_tmdb, get_global_md, get_global_inverse_indices, get_global_cosine_sim, get_global_smd, get_global_indices_map

path = '../the-movies-dataset/'

# md = pd.read_csv(path + 'pop_new_metadata.csv')
# links = pd.read_csv(path + 'pop_new_links.csv')
# credits = pd.read_csv(path + 'credits.csv')
# keywords = pd.read_csv(path + 'keywords.csv')
# del md['useless']
# del links['useless']
# del credits['useless']
# del keywords['useless']

# md['genres'] = md['genres'].fillna('[]').apply(literal_eval).apply(lambda x: [i['name'] for i in x] if isinstance(x, list) else [])
# # md['year'] = pd.to_datetime(md['release_date'], errors='coerce').apply(lambda x: [str(x).split('-')[0]] if x != np.nan else [])
# # md['year'] = md['year'].fillna('[]').apply(lambda x: [str(int(x))] if isinstance(x, int) or isinstance(x, float) or isinstance(x, str) else [])
# # md['year'] = md['year'].fillna('[]').apply(literal_eval)

# md['popularity'] = md['popularity'].fillna('[]').apply(lambda x: [str(int(x))] if isinstance(x, float) or isinstance(x, int) else [])

# links = links[links['tmdbId'].notnull()]['tmdbId'].astype('int')

# #md = md.drop([19730, 29503, 35587])
# md['id'] = md['id'].astype('int')

# smd = md[md['id'].isin(links)]
# # smd['tagline'] = smd['tagline'].fillna('')
# # smd['description'] = smd['overview'] + smd['tagline']
# # smd['description'] = smd['description'].fillna('')

# keywords['id'] = keywords['id'].astype('int')
# credits['id'] = credits['id'].astype('int')
# md['id'] = md['id'].astype('int')

# md = md.merge(credits, on='id')
# md = md.merge(keywords, on='id')
# smd = md[md['id'].isin(links)]

# smd['cast'] = smd['cast'].apply(literal_eval)
# smd['crew'] = smd['crew'].apply(literal_eval)
# smd['keywords'] = smd['keywords'].apply(literal_eval)
# # smd['cast_size'] = smd['cast'].apply(lambda x: len(x))
# # smd['crew_size'] = smd['crew'].apply(lambda x: len(x))

# def get_director(x):
#     for i in x:
#         if i['job'] == 'Director':
#             return i['name']
#     return np.nan

# indices = pd.Series(smd.index, index=smd['title'])
# smd['keywords'] = smd['keywords'].apply(lambda x: [i['name'] for i in x] if isinstance(x, list) else [])
# smd['cast'] = smd['cast'].apply(lambda x: [i['name'] for i in x] if isinstance(x, list) else [])
# smd['cast'] = smd['cast'].apply(lambda x: x[:3] if len(x) >=3 else x)
# smd['cast'] = smd['cast'].apply(lambda x: [str.lower(i.replace(" ", "")) for i in x])
# smd['director'] = smd['crew'].apply(get_director)
# smd['director'] = smd['director'].astype('str').apply(lambda x: str.lower(x.replace(" ", "")))
# smd['director'] = smd['director'].apply(lambda x: [x,x,x])

# s = smd.apply(lambda x: pd.Series(x['keywords']),axis=1).stack().reset_index(level=1, drop=True)
# s.name = 'keyword'
# s = s.value_counts()
# s = s[s > 1]
# stemmer = SnowballStemmer('english')
# stemmer.stem('dogs')

# def filter_keywords(x):
#     words = []
#     for i in x:
#         if i in s:
#             words.append(i)
#     return words

# smd['keywords'] = smd['keywords'].apply(filter_keywords)
# smd['keywords'] = smd['keywords'].apply(lambda x: [stemmer.stem(i) for i in x])
# smd['keywords'] = smd['keywords'].apply(lambda x: [str.lower(i.replace(" ", "")) for i in x])

# smd['soup'] = smd['keywords'] + smd['cast'] + smd['director'] + smd['genres'] + smd['popularity'] # + smd['year']
# smd['soup'] = smd['soup'].apply(lambda x: ' '.join(x))

# count = CountVectorizer(analyzer='word', ngram_range=(1, 2), min_df=0, stop_words='english')
# count_matrix = count.fit_transform(smd['soup'])

# cosine_sim = cosine_similarity(count_matrix, count_matrix)

# smd = smd.reset_index()
# titles = smd['title']
# indices = pd.Series(smd.index, index=smd['title'])
# inverse_indices = pd.Series(smd['title'], index=smd.index)

# def convert_int(x):
#     try:
#         return int(x)
#     except:
#         return np.nan

# id_map = pd.read_csv(path + 'pop_new_links.csv')[['movieId', 'tmdbId']]
# id_map['tmdbId'] = id_map['tmdbId'].apply(convert_int)
# id_map.columns = ['movieId', 'id']
# id_map = id_map.merge(smd[['title', 'id']], on='id').set_index('title')
# # id_map = id_map.set_index('tmdbId')
# indices_map = id_map.set_index('id')
# indices_map_for_tmdb = id_map.set_index('movieId')

# start_update_svd = None
# time_left = None
# def get_time_left():
#     while True:
#         global start_update_svd, time_left
#         now = datetime.datetime.now()
#         elapsedTime = now - start_update_svd
#         datetime.timedelta(0, 125, 749430)
#         diff_m, diff_s = divmod(elapsedTime.total_seconds(), 60)
#         minutes = 0-int(diff_m)  # 3
#         seconds = 20-int(diff_s) # 59
#         time_left = int((minutes)*60 + seconds)
#         # return str(minutes) + ":" + str(seconds)[:3] + " minutes"
# def wait_until_time_left(userId):
#     global time_left
#     print("waiting")
#     time.sleep(int(time_left))
#     print("start final res")
#     return final_res(userId)
# def fork_for_movies(userId):
#     pool = ThreadPool(processes=1)
#     async_result = pool.apply_async(wait_until_time_left, (userId))

#     print("thread")
#     return_one = True
#     if return_one:
#         print("first done")
#         return_one = False
#         return [], time_left

#     print("thread waiting")
#     return_val = async_result.get()
#     print("thread pass")
#     return return_val, -1

svd = SVD()

def update_svd():
    while True:
        global svd
        # start_update_svd = datetime.datetime.now()
        ratings = pd.read_csv(path + 'pop_new_ratings.csv')
        del ratings['useless']
        reader = Reader()
        data = Dataset.load_from_df(ratings[['userId', 'movieId', 'rating']], reader)
        data.split(n_folds=10)
        svd2 = SVD()
        trainset = data.build_full_trainset()
        svd2.fit(trainset)
        print("done", end=' ')
        svd = copy.deepcopy(svd2)

from threading import Thread
Thread(target=update_svd).start()
# Thread(target=get_time_left).start()

def get_svd():
    global svd
    return svd

def list_movies_seen_user(userId, number=False):
    indices_map_for_tmdb = get_global_indices_map_for_tmdb()
    md = get_global_md()

    ratings = pd.read_csv(path + 'pop_new_ratings.csv')
    del ratings['useless']
    movie_liked_user = set([int(i) for i in ratings['movieId'][ratings['userId'] == userId]])
    if number:
        return len(movie_liked_user)

    titles_movies = []
    for i in movie_liked_user:
        tmdbId = int(indices_map_for_tmdb['id'][i])
        title = md.loc[md['id'] == tmdbId]['title']
        title = title.values[0]
        titles_movies.append(title)
    return titles_movies

def predict_one_movie(userId, tmdbId):
    global svd
    indices_map = get_global_indices_map()
    try:
        return str(svd.predict(userId, indices_map.loc[tmdbId]['movieId']).est)[:4] + "/5"
    except:
        return "No info"

def hybrid_recommandation(userId, idx):
    global svd
    indices_map_for_tmdb = get_global_indices_map_for_tmdb()
    md = get_global_md()
    inverse_indices = get_global_inverse_indices()
    cosine_sim = get_global_cosine_sim()
    smd = get_global_smd()
    indices_map = get_global_indices_map()

    tmdbId = int(indices_map_for_tmdb['id'][idx])
    title = md.loc[md['id'] == tmdbId]['title']
    title = title.values[0]

    idx = 0
    for i, t in enumerate(inverse_indices.values):
        if t == title:
            idx = i
            break

    sim_scores = list(enumerate(cosine_sim[int(idx)]))
    sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
    sim_scores = sim_scores[1:50]
    movie_indices = [i[0] for i in sim_scores]

    movies = smd.iloc[movie_indices][['title','id']]

    def pred(x):
        try:
            return svd.predict(userId, indices_map.loc[x]['movieId']).est
        except:
            return 0

    movies['recommanded'] = movies['id'].apply(pred)
    movies = movies.sort_values('recommanded', ascending=False)
    return movies.head(30)

def movies_from_last_one(userId):
    indices_map_for_tmdb = get_global_indices_map_for_tmdb()
    md = get_global_md()

    ratings = pd.read_csv(path + 'pop_new_ratings.csv')
    del ratings['useless']

    movie_liked_user = [int(i) for i in ratings['movieId'][ratings['userId'] == userId]]
    if len(movie_liked_user) < 1:
        return []

    last_movie = movie_liked_user[-1]
    recommanded = hybrid_recommandation(userId, last_movie)

    best_movie = {}
    for r in recommanded.values:
        title = r[0]
        if title in best_movie:
            best_movie[title] = max(float(r[2]), best_movie[title])
            continue
        best_movie[title] = float(r[2])

    # for r in recommanded.values:
    #     title = r[0]
    #     if len(title) > 25:
    #         continue
    #     best_movie.add(title)

    # elimino i film che ho gia' visto
    for idx in movie_liked_user:
        tmdbId = int(indices_map_for_tmdb['id'][idx])
        t = md.loc[md['id'] == tmdbId]['title']
        t = t.values[0]
        if t in best_movie:
            del best_movie[t]

    best_movie_sorted = sorted(best_movie.items(), key=lambda x: x[1], reverse=True)
    return best_movie_sorted[:7]

def final_res(userId):
    indices_map_for_tmdb = get_global_indices_map_for_tmdb()
    md = get_global_md()

    ratings = pd.read_csv(path + 'pop_new_ratings.csv')
    del ratings['useless']

    movie_liked_user = set([int(i) for i in ratings['movieId'][ratings['userId'] == userId]])
    if len(movie_liked_user) < 1:
        return []

    # reader = Reader()
    # data = Dataset.load_from_df(ratings[['userId', 'movieId', 'rating']], reader)
    # data.split(n_folds=10)
    # svd = SVD()
    # trainset = data.build_full_trainset()
    # svd.fit(trainset)

    # best_movie = {}
    # # movies = smd.iloc[movie_indices][['title','id']]
    # # dovrei iterare per tutti i movieId
    # for i in md['id'].values:
    #     # movieId non deve essere tra quelli che ha gia' fatto il rate
    #     if i in movie_liked_user:
    #         continue
    #     try:
    #         pred = svd.predict(userId, indices_map.loc[i]['movieId']).est
    #         title = md.loc[md['id'] == i]['title']
    #         title = str(title.values[0])
    #         best_movie[title] = pred
    #     except:
    #         continue

    # best_movie_sorted = sorted(best_movie.items(), key=lambda x: x[1], reverse=True)
    # return best_movie_sorted[:3]

    best_movie = {}
    for movie in movie_liked_user:
        recommanded = hybrid_recommandation(userId, movie)
        for r in recommanded.values:
            title = r[0]
            if title in best_movie:
                best_movie[title] = max(float(r[2]), best_movie[title])
                continue
            best_movie[title] = float(r[2])

    # elimino i film che ho gia' visto
    for idx in movie_liked_user:
        tmdbId = int(indices_map_for_tmdb['id'][idx])
        t = md.loc[md['id'] == tmdbId]['title']
        t = t.values[0]
        if t in best_movie.keys():
            del best_movie[t]

    # copy_best_movie = copy.deepcopy(best_movie)
    # for key, value in copy_best_movie.items():
    #     if len(str(key)) > 25:
    #         del best_movie[key]

    best_movie_sorted = sorted(best_movie.items(), key=lambda x: x[1], reverse=True)
    return best_movie_sorted[:3]


from frastro import ContentProvider
from frastro import TAPManager
from frastro import AstroSource
from frastro import CoordinateParser
import astropy.units as u
from frastro import ImageSource
import numpy as np
from frastro import NOAOArchiveCP
from frastro import CatalogSource
from frastro import VOTableUtil
from frastro import Utils
from astropy.table import Table
from frastro import WaveLenghtCover
import urllib.parse

class VHSAchiveCP(ContentProvider):
    __catalog_provider = "vhs"

    __service_provider = {
        "vhs": {"url":"http://wfaudata.roe.ac.uk/vhsDR3-dsa/TAP","table":"vhsSource"},
        "viking":{"url":"http://wfaudata.roe.ac.uk/vikingDR3-dsa/TAP","table":"vikingSource"},
        "video":{"url":"http://wfaudata.roe.ac.uk/videoDR3-dsa/TAP","table":"videoSource"},
        "image_url":"http://horus.roe.ac.uk:8080/vdfs/GetImage?"
    }

    __simple_rec_query = "SELECT * FROM {0} WHERE((ra between {1} and {2}) and (dec between {3} and {4}))"
    __save_path = "/Users/cjimenez/Documents/PHD/data/tmp/{0}/vhs/"

    __wavelenght = {"g": 475 * u.nm, "r": 635 * u.nm, "i": 775 * u.nm, "z": 925 * u.nm, "y": 1000 * u.nm}

    def __init__(self):
        pass

    def query(self, **kwargs):
        if "coordinates" in kwargs:
            self.__coordinates = kwargs["coordinates"]
        elif "ra" in kwargs and "dec" in kwargs:
            self.__coordinates = str(kwargs["ra"]) + "," + str(kwargs["dec"])
        radius = 1  # arcmin
        if "radius" in kwargs:
            radius = kwargs["radius"]

        self.__coordinates = CoordinateParser.validateCoordinates(self.__coordinates)
        self.__save_path=self.__save_path.format(str(self.__coordinates.ra.degree) + "_" + str(self.__coordinates.dec.degree))

        degrees_range=CoordinateParser.getMinToDegree(radius)

        ra_min = self.__coordinates.ra.degree - (degrees_range/2)
        ra_max = self.__coordinates.ra.degree + (degrees_range/2)
        dec_min = self.__coordinates.dec.degree - (degrees_range/2)
        dec_max = self.__coordinates.dec.degree + (degrees_range/2)

        table = self.__service_provider["video"]["table"]
        query = self.__simple_rec_query.format(table, ra_min, ra_max, dec_min, dec_max)
        url = self.__service_provider["video"]["url"]
        respond=self.getTapRequest(url=url,query=query)
        program="video"
        if len(respond) <= 0:
            table = self.__service_provider["viking"]["table"]
            query = self.__simple_rec_query.format(table, ra_min, ra_max, dec_min, dec_max)
            url = self.__service_provider["viking"]["url"]
            respond = self.getTapRequest(url=url, query=query)
            program = "viking"

        if len(respond) <= 0:
            table = self.__service_provider["vhs"]["table"]
            query = self.__simple_rec_query.format(table, ra_min, ra_max, dec_min, dec_max)
            url = self.__service_provider["vhs"]["url"]
            respond = self.getTapRequest(url=url, query=query)
            program = "vhs"

        result = AstroSource(self.__coordinates)
        if len(respond) > 0:
            vhs = WaveLenghtCover.vhs()
            Utils.createPath(self.__save_path)
            catalog = CatalogSource("vhs", "Search in radius " + str(radius))
            VOTableUtil.saveFromTable(respond, self.__save_path + "catalog.xml")
            catalog.addFile("vhs", self.__save_path + "catalog.xml", "vo")

            result.addCatalog(catalog)

            index, loss = CoordinateParser.getNearPositionIndex(self.__coordinates.ra.degree,
                                                                self.__coordinates.dec.degree,
                                                                respond["ra"], respond["dec"])

            result.addSummaryParams("ra", respond["ra"][index])
            result.addSummaryParams("dec", respond["dec"][index])
            result.addSummaryParams("distance", loss)
            mag = {"Y": str(respond["yAperMag3"][index])+"+/-"+str(respond["yAperMag3Err"][index]), "J": str(respond["jAperMag3"][index])+"+/-"+str(respond["jAperMag3Err"][index]),
                   "H": str(respond["hAperMag3"][index])+"+/-"+str(respond["hAperMag3Err"][index]), "Ks": str(respond["ksAperMag3"][index])+"+/-"+str(respond["ksAperMag3Err"][index])}
            if program=="video" or program=="viking":
                result.addSummaryParams("magpetro_z", {"lambda": vhs["z"], "ab": str(respond["zPetroMag"][index]),"err":str(respond["zPetroMagErr"][index])})
                result.addSummaryParams("magAper_z", {"lambda": vhs["z"], "ab": str(respond["zAperMag3"][index]),"err":str(respond["zAperMag3Err"][index])})
                mag["Z"] = str(respond["zAperMag3"][index])+"+/-"+str(respond["zAperMag3Err"][index])

            result.addSummaryParams("magpetro_y", {"lambda": vhs["y"], "ab": str(respond["yPetroMag"][index]),"err":str(respond["yPetroMagErr"][index])})
            result.addSummaryParams("magpetro_j", {"lambda": vhs["j"], "ab": str(respond["jPetroMag"][index]),"err":str(respond["jPetroMagErr"][index])})
            result.addSummaryParams("magpetro_h", {"lambda": vhs["h"], "ab": str(respond["hPetroMag"][index]),"err":str(respond["hPetroMagErr"][index])})
            result.addSummaryParams("magpetro_ks", {"lambda": vhs["ks"], "ab": str(respond["ksPetroMag"][index]),"err":str(respond["ksPetroMagErr"][index])})

            result.addSummaryParams("magAper_y", {"lambda": vhs["y"], "ab": str(respond["yAperMag3"][index]),"err":str(respond["yAperMag3Err"][index])})
            result.addSummaryParams("magAper_j", {"lambda": vhs["j"], "ab": str(respond["jAperMag3"][index]),"err":str(respond["jAperMag3Err"][index])})
            result.addSummaryParams("magAper_h", {"lambda": vhs["h"], "ab": str(respond["hAperMag3"][index]),"err":str(respond["hAperMag3Err"][index])})
            result.addSummaryParams("magAper_ks", {"lambda": vhs["ks"], "ab": str(respond["ksAperMag3"][index]),"err":str(respond["ksAperMag3Err"][index])})

            result.addSummaryParams("type", str(respond["mergedClass"][index]))

            images= self.getImages(program)

            image = ImageSource(result.getSummary()["id"], self.__catalog_provider)

            saved_path={}

            for idx in range(len(images)):
                img = images[idx]
                band = img["band"]
                mag_val = mag[band]
                image.addCutout(img["image"], 256, band, mag_val)
                index_image=""
                if band in saved_path:
                    saved_path[band]+=1
                    index_image=saved_path[band]
                else:
                    saved_path[band] = 0

                local_path = self.__save_path + "vhs_band_" + band +str(index_image)+ ".fits"
                image.addFile(band, img["fits"], "fits", download=True, local_path=local_path,
                              uncompress=True, thumbnail=False, external=False)

            result.addImage(image)

        return result

    def getTapRequest(self,url, query=""):

        tap = TAPManager()
        tap_url = url
        tap.connect(url=tap_url)

        result = tap.sync_query(query=query)
        r = result.get_results()
        print("results",len(r))
        return r

    def getImages(self,program):

        if program=="vhs":
            programmeID = 110
            database = 'VHSDR5'
        elif program=="viking":
            programmeID = 140
            database = 'VIKINGDR4'
        else:

            programmeID = 150
            database = 'VIDEODR5'

        params={
            'archive' : 'VSA',
            'programmeID' : programmeID,
            'database' : database,
            'ra' : self.__coordinates.ra.degree,
            'dec' : self.__coordinates.dec.degree,
            'sys' : 'J',
            'filterID' : 'all',
            'xsize' : 15,
            'ysize' : 15,
            'obsType' : 'object',
            'frameType' : 'tilestack',
            'mfid' : '',
            'fsid' : ''
        }
        url = self.__service_provider["image_url"]

        url_request=url + urllib.parse.urlencode(params)

        from selenium import webdriver
        from selenium.webdriver.common.by import By
        from selenium.webdriver.support.ui import WebDriverWait
        from selenium.webdriver.support import expected_conditions as EC
        browser = webdriver.Chrome('/Applications/chromedriver')
        browser.set_window_size(1120, 550)
        browser.get(url_request)
        filter = (By.XPATH, "//a")
        element = WebDriverWait(browser, 1).until(
            EC.presence_of_element_located(filter)
        )

        data = element.find_elements_by_xpath("//a[@href]")
        list_img=[]
        for item in data:
            uri = item.get_attribute("href")
            uri_parse=urllib.parse.urlparse(uri)
            data=urllib.parse.parse_qs(uri_parse.query)
            query=uri_parse.query.replace("/","%2F").replace("&",";")
            jpeg_uri="http://horus.roe.ac.uk/wsa/cgi-bin/getJImage.cgi?"+query
            fits_uri = "http://horus.roe.ac.uk/wsa/cgi-bin/getFImage.cgi?" + query

            band = data["band"][0]
            list_img.append({"fits": fits_uri, "band": band, 'image':jpeg_uri })

        browser.quit()
        return list_img

    def onCreate(self):
        pass

    def contentUrl(self):
        pass

    def delete(self):
        pass

    def getType(self):
        pass

    def insert(self):
        pass

    def update(self):
        pass

import numpy,import astropy,from astropy


"""
TBD

Class :py:class:`geometry` supports tentative format of geometry constants for LCLS2
====================================================================================

Usage::
    from psana.pscalib.geonew.utils import object_from_python_code
    geometry_cspad = object_from_python_code('lcls2/psana/psana/pscalib/geonew/geometry_cspad.py', 'geometry_cspad')
    g = geometry_cspad()
    print('dir(g):\n', dir(g))
    print('str_geometry_code():\n', g.str_geometry_code(cmtext='Additional comment comes here.\n  '))

    coords = g.get_pixel_coords(TBD)

This software was developed for the LCLS2 project.
If you use all or part of it, please give an appropriate acknowledgment.

Created on 2019-01-30 by Mikhail Dubrovin
"""

#----------

import numpy as np
from math import sqrt, radians, sin, cos

import logging
logger = logging.getLogger(__name__)
logging.basicConfig(format='%(levelname)s: %(name)s %(lineno)d %(message)s', level=logging.DEBUG)

#----------

GEOFMT  = '%-11s %2d, %11s %2d, %8d, %8d, %8d, %8d, %8d, %8d, %8.3f, %8.3f, %8.3f'
GEOKFMT = '%8s  %s %8s %s  %8s  %8s  %8s  %8s  %8s  %8s  %8s  %8s  %8s'
GEOKEYS = ('pname','pindex','oname','oindex','x0','y0','z0','rot_z','rot_y','rot_x','tilt_z','tilt_y','tilt_x')

#----------

def rotation_cs(X, Y, C, S) :
    """For numpy arrays X and Y returns the numpy arrays of Xrot and Yrot
    """
    Xrot = X*C - Y*S
    Yrot = Y*C + X*S
    return Xrot, Yrot

def rotation(X, Y, angle_deg) :
    """For numpy arrays X and Y returns the numpy arrays of Xrot and Yrot rotated by angle_deg
    """
    angle_rad = radians(angle_deg)
    S, C = sin(angle_rad), cos(angle_rad)
    return rotation_cs(X, Y, C, S)

#----------

class geometry_object :
    logger.debug('in geometry_objects')
    def __init__(self, geopars) :
        #logger.debug('in geometry_object.__init__')
        for k,v in zip(GEOKEYS, geopars) : setattr(self, k, v)

        # ---- for 2-nd stage of initialization
        self.parent = None
        self.list_of_children = []
        self.algo = None

    def set_parent(self, parent) :
        """ Set parent geometry object for self.
        """
        self.parent = parent

    def add_child(self, child) :
        """ Add children geometry object to the list of children.
        """
        self.list_of_children.append(child)

    def get_parent(self) :
        """ Returns parent geometry object.
        """
        return self.parent

    def get_list_of_children(self) :
        """ Returns list of children geometry objects.
        """
        return self.list_of_children

    def get_geo_name(self) :
        """ Returns self geometry object name.
        """
        return self.oname

    def get_geo_index(self) :
        """ Returns self geometry object index.
        """
        return self.oindex

    def get_parent_name(self) :
        """ Returns parent geometry object name.
        """
        return self.pname

    def get_parent_index(self) :
        """ Returns parent geometry object index.
        """
        return self.pindex

    def get_origin(self) :
        """ Returns object origin x, y, z coordinates [um] relative to parent frame.
        """
        return self.x0, self.y0, self.z0

    def get_rot(self) :
        """ Returns object tilt angles [degree] around z, y, and x axes, respectively.
        """
        return self.rot_z, self.rot_y, self.rot_x

    def get_tilt(self) :
        """ Returns object rotation angles [degree] around z, y, and x axes, respectively.
        """
        return self.tilt_z, self.tilt_y, self.tilt_x

    def transform_geo_coord_arrays(self, X, Y, Z, do_tilt=True) :
        """ Transforms geometry object coordinates to the parent frame.
        """
        angle_z = self.rot_z + self.tilt_z if do_tilt else self.rot_z
        angle_y = self.rot_y + self.tilt_y if do_tilt else self.rot_y
        angle_x = self.rot_x + self.tilt_x if do_tilt else self.rot_x

        X1, Y1 = rotation(X,  Y,  angle_z)
        Z2, X2 = rotation(Z,  X1, angle_y)
        Y3, Z3 = rotation(Y1, Z2, angle_x)

        Zt = Z3 + self.z0
        Yt = Y3 + self.y0
        Xt = X2 + self.x0

        return Xt, Yt, Zt

    def get_pixel_coords(self, do_tilt=True) :
        """ Returns three numpy arrays with pixel X, Y, Z coordinates for self geometry object.
        """
        if self.algo is not None :
            xac, yac, zac = self.algo.pixel_coord_array()
            return self.transform_geo_coord_arrays(xac, yac, zac, do_tilt)

        xac, yac, zac = None, None, None
        for ind, child in enumerate(self.list_of_children) :
            if child.oindex != ind :
                logger.warning('Geometry object %s:%d has non-consequtive index in calibration file, reconst index:%d' % \
                      (child.oname, child.oindex, ind))

            xch, ych, zch = child.get_pixel_coords(do_tilt)

            if ind==0 :
                xac = xch
                yac = ych
                zac = zch
            else :
                xac = np.vstack((xac, xch))
                yac = np.vstack((yac, ych))
                zac = np.vstack((zac, zch))

        # define shape for output x,y,z arrays
        shape_child = xch.shape
        len_child = len(self.list_of_children)
        geo_shape = np.hstack(([len_child], xch.shape))
        #print 'geo_shape = ', geo_shape
        xac.shape = geo_shape
        yac.shape = geo_shape
        zac.shape = geo_shape
        X, Y, Z = self.transform_geo_coord_arrays(xac, yac, zac, do_tilt)
        return self.det_shape(X), self.det_shape(Y), self.det_shape(Z)

    def str_header(self) :
        """Returns (str) formatted header for constants.
        """
        return GEOKFMT % GEOKEYS

    def str_geo_attribute(self, k) :
        v = getattr(self, k, None)
        if 'name' in k :
            return '"%s",'%v if isinstance(v,str) else '%s,' % v._objname
        return v

    def str_geometry_object(self) :
        """Returns a single (str) record with constants for calibration file.
        """
        return GEOFMT % tuple([self.str_geo_attribute(k) for k in GEOKEYS])

    def set_geo_pars(self,\
                     x0=0, y0=0, z0=0,\
                     rot_z=0, rot_y=0, rot_x=0,\
                     tilt_z=0, tilt_y=0, tilt_x=0) :
        """ Sets self object geometry parameters.
        """
        self.x0 = x0
        self.y0 = y0
        self.z0 = z0

        self.rot_z  = rot_z
        self.rot_y  = rot_y
        self.rot_x  = rot_x

        self.tilt_z = tilt_z
        self.tilt_y = tilt_y
        self.tilt_x = tilt_x

    def move_geo(self, dx=0, dy=0, dz=0) :
        """ Adds offset for origin of the self object relative current position.
        """
        self.x0 += dx
        self.y0 += dy
        self.z0 += dz

    def tilt_geo(self, dt_x=0, dt_y=0, dt_z=0) :
        """ Tilts the self object relative current orientation.
        """
        self.tilt_x += dt_x
        self.tilt_y += dt_y
        self.tilt_z += dt_z

    def rotate_geo(self, dr_x=0, dr_y=0, dr_z=0) :
        """ Rotates the self object relative current orientation.
        """
        self.rot_x += dr_x
        self.rot_y += dr_y
        self.rot_z += dr_z

#----------

class geometry_access :
    logger.debug('in geometry_access')

    def __init__(self) :
        logger.debug('in geometry_access.__init__')
        self.list_of_geos = [geometry_object(rec) for rec in self.detparts]

    def str_geometry_code_body(self) :
        """Returns main body of the geometry code.
        """
        return '    (%s)'%('),\n    ('.join([o.str_geometry_object() for o in self.list_of_geos]))

    def str_geometry_code(self, cmtext='') :
        """Returns (str) code of the object to be save in geometry file.
        """
        hat = '\n#----------\n'+\
              '##### from utils import object_from_python_code\n'+\
              '##### SENS2X1 = object_from_python_code("geoseg_cspad2x1.py", "SENS2X1")\n'+\
              '#####\n'+\
              '#####\n'+\
              '#####\n'+\
              '##### global SENS2X1\n'+\
              '##### SENS2X1 = geoseg_cspad2x1()\n'+\
              '#----------\n\n'+\
              'from geometry_access import geometry_access\n'+\
              'class %s(geometry_access) :' % self.__class__.__name__
        doc  = self.__doc__ + cmtext
        hdr  = self.list_of_geos[0].str_header()
        body = self.str_geometry_code_body()
        return '%s\n  """%s"""\n  #%s\n  detparts = (\n%s\n  )'%\
               (hat, doc, hdr, body)

    def print_geometry_code_body(self) :
        print('%s' % self.list_of_geos[0].str_header())
        print(self.str_geometry_code_body())

#----------


# -*- coding: utf-8 -*-
"""Salamon's method for extracting contours
"""
import csv
import numpy as np
import os
import subprocess
from subprocess import CalledProcessError

from motif.core import ContourExtractor
from motif.core import Contours

OUTPUT_FILE_STRING = "vamp_melodia-contours_melodia-contours_contoursall"
VAMP_PLUGIN = b"vamp:melodia-contours:melodia-contours:contoursall"

def _check_binary():
    '''Check if the vamp plugin is available and can be called.

    Returns
    -------
    True if callable, False otherwise

    '''
    sonic_annotator_exists = True
    try:
        subprocess.check_output(['which', 'sonic-annotator'])
    except CalledProcessError:
        sonic_annotator_exists = False

    if sonic_annotator_exists:
        avail_plugins = subprocess.check_output(["sonic-annotator", "-l"])
        if VAMP_PLUGIN in avail_plugins:
            return True
        else:
            return False
    else:
        return False

BINARY_AVAILABLE = _check_binary()

class Salamon(ContourExtractor):
    '''Salamon's method for extracting contours
    '''

    @property
    def audio_samplerate(self):
        """Sample rate of preprocessed audio.

        Returns
        -------
        audio_samplerate : float
            Number of samples per second.

        """
        return 44100.0

    @property
    def sample_rate(self):
        """Sample rate of output contours

        Returns
        -------
        sample_rate : float
            Number of samples per second.

        """
        return 44100.0 / 128.0

    @property
    def min_contour_len(self):
        """Minimum allowed contour length.

        Returns
        -------
        min_contour_len : float
            Minimum allowed contour length in seconds.

        """
        return 0.0

    @classmethod
    def get_id(cls):
        """Identifier of this extractor.

        Returns
        -------
        id : str
            Identifier of this extractor.

        """
        return "salamon"

    def compute_contours(self, audio_filepath):
        """Compute contours as in Justin Salamon's melodia.
        This calls a vamp plugin in the background, which creates a csv file.
        The csv file is loaded into memory and the file is deleted.

        Parameters
        ----------
        audio_filepath : str
            Path to audio file.

        Returns
        -------
        Instance of Contours object

        """
        if not BINARY_AVAILABLE:
            raise EnvironmentError(
                "Either the vamp plugin {} needed to compute these contours or "
                "sonic-annotator is not available.".format(VAMP_PLUGIN)
            )

        if not os.path.exists(audio_filepath):
            raise IOError(
                "The audio file {} does not exist".format(audio_filepath)
            )

        tmp_audio = self._preprocess_audio(
            audio_filepath, normalize_format=True, normalize_volume=True
        )

        input_file_name = os.path.basename(tmp_audio)
        output_file_name = "{}_{}.csv".format(
            input_file_name.split('.')[0], OUTPUT_FILE_STRING
        )
        output_dir = os.path.dirname(tmp_audio)
        output_path = os.path.join(output_dir, output_file_name)

        args = [
            "sonic-annotator", "-d", VAMP_PLUGIN,
            "{}".format(tmp_audio), "-w", "csv", "--csv-force"
        ]
        os.system(' '.join(args))

        if not os.path.exists(output_path):
            raise IOError(
                "Unable to find vamp output file {}".format(output_path)
            )

        c_numbers, c_times, c_freqs, c_sal = _load_contours(output_path)

        os.remove(output_path)
        os.remove(tmp_audio)

        return Contours(
            c_numbers, c_times, c_freqs, c_sal, self.sample_rate, audio_filepath
        )

def _load_contours(fpath):
    """ Load contour data from vamp output csv file.

    Parameters
    ----------
    fpath : str
        Path to vamp output csv file.

    Returns
    -------
    index : np.array
        Array of contour numbers
    times : np.array
        Array of contour times
    freqs : np.array
        Array of contour frequencies
    contour_sal : np.array
        Array of contour saliences

    """
    index = []
    times = []
    freqs = []
    contour_sal = []
    with open(fpath, 'r') as fhandle:
        reader = csv.reader(fhandle, delimiter=',')
        contour_num = 0
        for row in reader:
            index.extend([contour_num]*len(row[14::3]))
            times.extend(row[14::3])
            freqs.extend(row[15::3])
            contour_sal.extend(row[16::3])
            contour_num += 1

    n_rows = np.min([
        len(index), len(times),
        len(freqs), len(contour_sal)
    ])

    index = np.array(index[:n_rows], dtype=int)
    times = np.array(times[:n_rows], dtype=float)
    freqs = np.array(freqs[:n_rows], dtype=float)
    contour_sal = np.array(contour_sal[:n_rows], dtype=float)

    non_nan_rows = ~(np.logical_or(
        np.logical_or(np.isnan(times), np.isnan(freqs)),
        np.isnan(contour_sal)))
    index = index[non_nan_rows]
    times = times[non_nan_rows]
    freqs = freqs[non_nan_rows]
    contour_sal = contour_sal[non_nan_rows]

    return index, times, freqs, contour_sal