#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Apr 17 12:14:38 2018

@author: rita
"""

import numpy as np
import re
from collections import Iterable
from lxml.html import fromstring
import requests
import random
from nltk.tokenize import sent_tokenize

# Functions to preprocess the data for feature extraction
def get_proxies(url):

    try:
        r_obj = requests.get(url)
    except:
        proxy = 0
        while not type(proxy) == str:

            try:
                url = 'https://free-proxy-list.net/'
                response = requests.get(url)
                parser = fromstring(response.text)
                ip = random.choice(parser.xpath('//tbody/tr'))
                if ip.xpath('.//td[7][contains(text(),"yes")]'):
                    # Grabbing IP and corresponding PORT
                    proxy = ":".join([ip.xpath('.//td[1]/text()')[0], ip.xpath('.//td[2]/text()')[0]])
                    proxies = {"http": proxy, "https": proxy}

            except:
               continue

        r_obj = requests.get(url, proxies)

    return r_obj

def flatten(l):

    for el in l:
        if isinstance(el, Iterable) and not isinstance(el, str):
            for sub in flatten(el):
                yield sub
        else:
            yield el

def docs_normalizer(piece):

    if len(piece) < 3000:
        print("Sorry, the text couldn't be used, because it is too short.")
        return None

    else:
        sent = sent_tokenize(piece)
        length = 0
        batches = []
        pieces = []
        for s in range(len(sent)):
            try:
                if (((s < len(sent)-1) and (re.search(re.compile(sent[s]), sent[s+1])) and
                    (re.search(re.compile(sent[s]), sent[s+2]))) or (len(sent[s])>=3000)):
                    print("""
                          Sorry, the text couldn't be used,
                          because the sentences it is comprised of
                          are probably not meant to be read sequentially.
                          """)
                    return None
                # Adding the sentences' lengths together until the sum reaches 3k characters
                # and storing the sentences in batches
                else:
                    length += len(sent[s])
                    batches.append(sent[s])
                    if (length > 3000):
                        batch = ' '.join(batches)
                        pieces.append(batch)
                        length = 0
                        batches = []
            except:
                # Found a sentence with miss-matching parenthesis
                continue

        # Including only up to 5 excerpts per text
        if len(pieces) > 5:
            idx = np.random.choice(len(pieces), 5, replace = False)
            pieces = [pieces[i] for i in idx]

        return pieces


from zope.component import adapts
from zope.interface import implements

from Products.ZenRelations.RelSchema import ToOne, ToMany, ToManyCont

from Products.Zuul.decorators import info
from Products.Zuul.form import schema
from Products.Zuul.infos import ProxyProperty
from Products.Zuul.infos.component import ComponentInfo
from Products.Zuul.interfaces.component import IComponentInfo
from Products.Zuul.utils import ZuulMessageFactory as _t

from ZenPacks.zenoss.AWS import CLASS_NAME, MODULE_NAME
from ZenPacks.zenoss.AWS.AWSComponent import AWSComponent
from ZenPacks.zenoss.AWS.utils import updateToMany, updateToOne

class EC2ElasticIP(AWSComponent):
    '''
    Model class for EC2ElasticIP.
    '''

    meta_type = portal_type = 'EC2ElasticIP'

    public_ip = None
    private_ip_address = None
    instance_id = None
    domain = None
    network_interface_id = None
    network_interface_owner_id = None

    _properties = AWSComponent._properties + (
        {'id': 'public_ip', 'type': 'string'},
        {'id': 'private_ip_address', 'type': 'string'},
        {'id': 'instance_id', 'type': 'boolean'},
        {'id': 'domain', 'type': 'string'},
        {'id': 'network_interface_id', 'type': 'string'},
        {'id': 'network_interface_owner_id', 'type': 'string'},
    )

    _relations = AWSComponent._relations + (
        ('region', ToOne(
            ToManyCont, MODULE_NAME['EC2Region'], 'elastic_ips')),
    )

class IEC2ElasticIPInfo(IComponentInfo):
    '''
    API Info interface for EC2ElasticIP.
    '''

    account = schema.Entity(title=_t(u'Account'))
    region = schema.Entity(title=_t(u'Region'))
    public_ip = schema.TextLine(title=_t(u'Public IP'))
    private_ip_address = schema.TextLine(title=_t(u'Private IP address'))
    instance_id = schema.TextLine(title=_t(u'Instance ID'))
    domain = schema.TextLine(title=_t(u'Domain'))
    network_interface_id = schema.TextLine(title=_t(u'Network Interface ID'))
    network_interface_owner_id = schema.TextLine(
        title=_t(u'Network Interface Owner ID')
    )

class EC2ElasticIPInfo(ComponentInfo):
    '''
    API Info adapter factory for EC2ElasticIP.
    '''

    implements(IEC2ElasticIPInfo)
    adapts(EC2ElasticIP)

    public_ip = ProxyProperty('public_ip')
    private_ip_address = ProxyProperty('private_ip_address')
    instance_id = ProxyProperty('instance_id')
    domain = ProxyProperty('domain')
    network_interface_id = ProxyProperty('network_interface_id')
    network_interface_owner_id = ProxyProperty('network_interface_owner_id')

    @property
    @info
    def account(self):
        return self._object.device()

    @property
    @info
    def region(self):
        return self._object.region()


# https://atcoder.jp/contests/abc051/tasks/abc051_d
# 考えたこと
# 1.warshal floydからの経路復元でやってみる←御解答
# s→tまでは最短経路が2つ存在する場合がある。そのときうまくいかない

import sys
sys.setrecursionlimit(1 << 25)
read = sys.stdin.readline
ra = range
enu = enumerate
from copy import deepcopy

def read_ints():
    return list(map(int, read().split()))

from collections import defaultdict
INF = 10**9
N, M = read_ints()
D = [[INF] * N for _ in range(N)]

edges = defaultdict(lambda: INF)
for _ in range(M):
    a, b, c = read_ints()
    a -= 1
    b -= 1
    D[a][b] = c
    D[b][a] = c
    edges[b, a] = c
    edges[a, b] = c
for i in range(N):
    D[i][i] = 0

# warshal floyd
for k in range(N):
    for i in range(N):
        if D[i][k] >= INF:  # 定数倍高速化
            # ノード同士がつながってなければ更新しない
            continue
        for j in range(N):
            D[i][j] = min(D[i][j], D[i][k] + D[k][j])

# 経路復元

def restore_path(s: int, t: int, edges: dict, D: list):
    '''
    s→tまでの最短経路を復元する
    edgesはオリジナルの辺のリスト edges[a,b]=a,bを結ぶ辺の長さ。ないときは(INF、自分自身へもINF)
    Dは最短経路D[s][t]=s→tの最短経路
    '''
    ret = []  # 経路
    cur = s
    while cur != t:
        for i in range(N):  # Nはノード数 = len(D) or D.shape[0]
            if i != cur and edges[cur, i] + D[i][t] == D[cur][t]:
                ret.append((cur, i) if cur < i else (i, cur))  # 大小関係をはっきりさせておく
                cur = i
                break
    return ret

#
from itertools import combinations
edges_used = set()
for s, t in combinations(range(N), 2):
    edges_used |= set(restore_path(s, t, edges, D))
print(M - len(edges_used))


"""
检测车辆横向边缘
"""
import cv2
import numpy as np
import matplotlib.pyplot as plt
from detection_model.diamondSpace import DiamondSpace

def neighborhood(grad, winSize=9):
    l = []
    if winSize % 2 != 1:
        print("winSize should be an odd number.")
        raise TypeError
    low = int((winSize - 1) / 2)
    top = int((winSize + 1) / 2)
    for i in range(-low, top):
        l.append(np.stack([np.arange(-low, top).reshape(1, -1), np.full((1, winSize), i)], axis=2))
    weight = np.vstack(l)
    grad_pad = np.pad(grad, ((low, low), (low, low)), 'constant')
    height, width = grad_pad.shape[:2]
    orientations = np.zeros((height - 2 * low, width - 2 * low, 2))
    quality = np.zeros_like(grad)

    # y_index, x_index = np.where(grad_pad != 0)
    # start = time()
    # for y in y_index:
    #     for x in x_index:
    for y in range(height):
        for x in range(width):
            if grad_pad[y][x] == 0:
                continue
            window = grad_pad[y - low:y + top, x - low:x + top]
            x_weight = np.multiply(window, weight[:, :, 0]).ravel()
            y_weight = np.multiply(window, weight[:, :, 1]).ravel()

            o, q = calculate(np.stack([x_weight, y_weight], axis=1))
            orientations[y - low][x - low] = o
            quality[y - low][x - low] = q
    # print(f'time is {time()-start}')
    return orientations, quality

def accumulate_orientation(orientations, quality, winSize=9, threshold=0.25):
    if (quality == 0).all():
        raise
    mask = np.zeros_like(quality)
    thres = np.percentile(quality[quality != 0], 100 * (1 - threshold))
    y_index, x_index = np.where(quality >= thres)
    height, width = mask.shape[:2]
    for i, j in zip(y_index, x_index):
        dx, dy = orientations[i, j]
        if dx < 1e-6:
            continue
        slope = dy / dx
        x = np.arange(j - int((winSize - 1) / 2), j + int((winSize - 1) / 2))
        y = np.around(i - slope * j + slope * x).astype(np.int)
        if (x < width).all() and (y < height).all():
            try:
                mask[y, x] += quality[i, j]
            except Exception as e:
                print(e)
    return mask, thres

def calculate(X):
    u, s, v = np.linalg.svd(X.T @ X)
    W = v
    sigma = s.transpose() * s
    orientation = W[:, 0]
    quality = sigma[0] / sigma[1] if sigma[1] != 0 else 255
    return orientation, quality

def get_lines(edges, orientation, box=None):
    y, x = np.where(edges > 0)
    lines = []
    if box is not None:
        real_x = x + box[0]
        real_y = y + box[1]
        for i, j in zip(x, y):
            dx, dy = orientation[j, i]
            a = dy
            b = -dx
            c = (j + box[1]) * dx - dy * (i + box[0])
            # k = IPM[j, i][1] / IPM[j, i][0]
            # b = read_y - k * real_x
            lines.append([a, b, c])
    else:
        for i, j in zip(x, y):
            k = orientation[j, i][1] / orientation[j, i][0]
            b = j - k * i
            lines.append([k, -1, b])
            # start = np.round(index + 5 * IPM[j, i]).astype(np.int32)
            # end = np.round(index - 5 * IPM[j, i]).astype(np.int32)
            # cv2.line(img, start, end, (0, 0, 255), 1)
    return np.vstack(lines)

if __name__ == '__main__':
    DS = DiamondSpace(720)

    # path = './detection_model/car.jpg'
    path = 'pics/cars.jpg'
    img = cv2.imread(path)
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(img_gray, 700, 700, L2gradient=True)
    orientation, quality = neighborhood(edges)
    thres, edges = cv2.threshold(quality, 0, 255, cv2.THRESH_OTSU)
    lines = get_lines(edges, orientation)
    peak = (147.45110199, -238.7251731)
    DS.filter_lines_from_vp(peak, lines)
    p, v, p_ds = DS.find_peaks(t=0.9)

    A = DS.attach_spaces()
    _, ax = plt.subplots(1, 2, figsize=(20, 10))
    ax[0].imshow(A, cmap="Greys", extent=(
        (-DS.size + 0.5) / DS.scale, (DS.size - 0.5) / DS.scale, (DS.size - 0.5) / DS.scale,
        (-DS.size + 0.5) / DS.scale))
    ax[0].set(title="Accumulator", xticks=np.linspace(-DS.size + 1, DS.size - 1, 5) / DS.scale,
              yticks=np.linspace(-DS.size + 1, DS.size - 1, 5) / DS.scale)
    ax[0].plot(p_ds[:, 0] / DS.scale, p_ds[:, 1] / DS.scale, "r+")
    plt.figure()
    print(img.shape)
    print(p)
    ax[1].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    # ax.plot(peak[0], peak[1], 'r+')
    ax[1].plot(p[0, 0], p[0, 1], 'r+')
    ax[0].invert_yaxis()
    plt.show()


from django.shortcuts import render
from django.http import HttpResponse
from django.conf import settings
from ws4redis.redis_store import RedisMessage
from ws4redis.publisher import RedisPublisher
import pdb
import json

from .models import Video
from .tasks import sync_channel_videos,test
import sync

# Create your views here.
def index(request):
    #get access_token
    if not "access_token" in request.session:
        access_token,refresh_token = sync.get_access_token(sync.youku_user_dict)
        request.session["access_token"] = access_token
        request.session["refresh_token"] = refresh_token
    #get user playlists
    playlist_ids = list()
    playlists = dict()
    for video in Video.objects.all():
        if not video.playlist_id in playlist_ids:
            playlist_ids.append(video.playlist_id)
            playlists[video.playlist_id] = list()
    for playlist_id in playlist_ids:
        for video in Video.objects.all():
            if video.playlist_id == playlist_id:
                playlists[playlist_id].append(video.video_title)
    protocol = request.is_secure() and 'wss://' or 'ws://'
    WEBSOCKET_URI =protocol + request.get_host() + settings.WEBSOCKET_URL
    context = {"playlists": playlists,"WEBSOCKET_URI":WEBSOCKET_URI,"WS4REDIS_HEARTBEAT":settings.WS4REDIS_HEARTBEAT}
    return render(request,"sync_subscription/index.html",context)

#get youku existing videos and check for that are from youtube
#and show videos that are published failed
def check_youku_existing_youtube_video(request):
    if not "access_token" in request.session:
        access_token,refresh_token = sync.get_access_token(sync.youku_user_dict)
    playlists= sync.get_playlist(sync.youku_user_dict,request.session["access_token"],request.session["refresh_token"])
    response = {"playlists": playlists}
    request.session["playlists"] = playlists
    data = json.dumps(response)
    return HttpResponse(data,content_type='application/json')

#get youku video for each playlist
def get_youku_videos(request):
    playlist_id = request.GET["playlist_id"]
    if not "access_token" in request.session:
        access_token,refresh_token = sync.get_access_token(sync.youku_user_dict)
    videos = sync.get_playlist_videos(sync.youku_user_dict,playlist_id,request.session["access_token"],request.session["refresh_token"],request.session["playlists"])
    response = {"videos":videos}
    return HttpResponse(json.dumps(response),content_type='application/json')
#delete videos
def delete_videos(request):
    video_ids = request.POST.getlist("video_ids[]")
    playlist_ids = request.POST.getlist("playlist_ids[]")
    if not "access_token" in request.session:
        access_token,refresh_token = sync.get_access_token(sync.youku_user_dict)
    deleted_video_ids = sync.delete_videos(video_ids,playlist_ids,sync.youku_user_dict,request.session["access_token"],request.session["refresh_token"])

    if deleted_video_ids != "1002" and len(deleted_video_ids) == len(video_ids):
        return HttpResponse(json.dumps({"result":"success"}),content_type='application/json')
    else:
        return HttpResponse(json.dumps({"result":"failed"}),content_type='application/json')

def search_youtube_channel(request):
    query = request.GET["query"]
    search_type = request.GET["search_type"]
    if "result_more" in request.GET:
        channels,next_page_token = sync.youtube_search(query,search_type,request.session["next_page_token"],sync.google_user_dict)
        if next_page_token != "none":
            request.session["next_page_token"] = next_page_token
            return HttpResponse(json.dumps({"channels":channels}),content_type='application/json')
        else:
            return HttpResponse(json.dumps({"result":"no_more_results"}),content_type='application/json')
    else:
        channels,next_page_token = sync.youtube_search(query,search_type,"none",sync.google_user_dict)
        request.session["next_page_token"] = next_page_token
        return HttpResponse(json.dumps({"channels":channels}),content_type='application/json')

def get_channel_video_number(request):
    channel_id = request.GET["channel_id"]
    if "published_after" in request.GET:
        video_number = sync.get_channel_video_number(channel_id,request.GET["published_after"],sync.google_user_dict)
        return HttpResponse(json.dumps({"video_number":video_number}),content_type='application/json')

def sync_channel(request):
    if not "access_token" in request.session:
        access_token,refresh_token = sync.get_access_token(sync.youku_user_dict)
    channel_id = request.POST["channel_id"]
    published_after = request.POST["published_after"]
    sync_playlist_bool = request.POST["sync_playlist_bool"]
    videos = sync.get_channel_videos(channel_id,published_after,sync.google_user_dict)
    #redis_publisher = RedisPublisher(facility='uploading', broadcast=True)
    #uploading_status = RedisMessage("hello")
    #redis_publisher.publish_message(uploading_status)
    videos_trunked = trunks(videos,10) #split video_ids into 10 length trunk
    for videos_trunk in videos_trunked:
       sync_channel_videos.delay(videos_trunk,sync.youku_user_dict,sync.google_user_dict,request.session["access_token"],request.session["refresh_token"])

    return HttpResponse(json.dumps({"videos":videos}),content_type='application/json')

def trunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in xrange(0, len(l), n):
        yield l[i:i+n]


from collections import defaultdict
import numpy as np

def datatable_to_dict(table, job_id, db):
    """
    Reads dicts of numpy arrays from database
    args:
        table e.g dbLinkBudget.SAT
        job_id eg 1
        db eg. dbLinkBudget
    output is a dictionary of np arrays
    """
    keys = list(table.fields)
    keys.remove("Job_ID")
    keys.remove("id")
    mydict = defaultdict(list)
    row_list=db(table.Job_ID == job_id).iterselect(*keys).as_list()
    for row in range(0,len(row_list)):
        for key, value in row_list[row]['_extra'].items():
            mydict[key] = np.append(mydict[key],value)
    return mydict

def write_dict_to_table(table, dic, job_id, db):
    # type: (object, object, object) -> object
    """
    Used to read in dictionaries which contain
    np arrays created when reading from database

    Args:
        table: db table eg db.SAT
        dic: dictionary of arrays
        job_id: is the scenario ID

    """
    row = dic.fromkeys(dic)
    for v in range(dic.values()[0].size):
        for k in range(len(dic.keys())):
            row[dic.keys()[k]] = dic.values()[k][v]
        job_id_check = table.Job_ID == job_id
        if table is db.Gateway:
            table.update_or_insert((table.GW_ID == row['GW_ID']) & (job_id_check), Job_ID=job_id,
                                **row) #all these query if these fields are equal. If so then updates. Otherwise inserts
        elif table is db.SAT:
            table.update_or_insert((table.SAT_ID == row['SAT_ID']) & (job_id_check), Job_ID=job_id, **row)
        elif table is db.TRSP:
            table.update_or_insert((table.TRSP_ID == row['TRSP_ID']) & (job_id_check), Job_ID=job_id, **row)
        elif table is db.VSAT:
            table.update_or_insert((table.VSAT_ID == row['VSAT_ID']) & (job_id_check), Job_ID=job_id, **row)
        elif table is db.Earth_coord_GW:
            table.update_or_insert(
                (job_id_check) & (table.LON == row['LON']) & (table.LAT == row['LAT']) & (table.GW_ID == row['GW_ID']) & (
                    table.TRSP_ID == row['TRSP_ID']), Job_ID=job_id, **row)
        elif table is db.EARTH_coord_VSAT:
            table.update_or_insert((job_id_check) & (table.LON == row['LON']) & (table.LAT == row['LAT']) & (table.VSAT_ID == row['VSAT_ID']), Job_ID=job_id, **row)

        else:
            table.update_or_insert(Job_ID=job_id, **row)


from pyspark.sql import SparkSession
import datetime
import time

start=time.time()
spark = SparkSession.builder.appName("Query1_RDD").getOrCreate()

sc = spark.sparkContext

data= sc.textFile("hdfs://master:9000/project/yellow_tripdata_1m.csv"). \
				map(lambda line: (float(datetime.datetime.strptime(line.split(',')[1],"%Y-%m-%d %H:%M:%S").strftime("%H")),(float(line.split(',')[3]), float(line.split(',')[4]),1))). \
				filter(lambda line: (-75.0<=line[1][0] and line[1][0]<=-73.0) and (40.0<=line[1][1] and line[1][1]<=41.0)). \
				reduceByKey(lambda accum, data: (accum[0] + data[0], accum[1] + data[1], accum[2] + data[2])). \
				map(lambda x: (x[0],x[1][0]/x[1][2],x[1][1]/x[1][2]))

#data.cache()
for i in data.collect():
	print(i)

print("Execution Time: ", time.time()-start)


import sys
import os
import warnings
import random
import cv2
import numpy as np
import torchvision
import torchvision.transforms as T
import torch
import matplotlib.pyplot as plt
from PIL import Image

# ensure we are running on the correct gpu
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"] = "6"  # (xxxx is your specific GPU ID)
if not torch.cuda.is_available() or torch.cuda.device_count() != 1:
    print('exiting')
    sys.exit()
else:
    print('GPU is being properly used')

# set to evaluation mode
model = torch.load('../../DoE-Project/mask-rcnn-pebble.pt')
model.eval()
CLASS_NAMES = ['__background__', 'pebble']
device = torch.device(
    'cuda') if torch.cuda.is_available() else torch.device('cpu')
model.to(device)

def get_coloured_mask(mask):
    """
    random_colour_masks
      parameters:
        - image - predicted masks
      method:
        - the masks of each predicted object is given random colour for visualization
    """
    colours = [[0, 255, 0], [0, 0, 255], [255, 0, 0], [0, 255, 255], [255, 255, 0], [
        255, 0, 255], [80, 70, 180], [250, 80, 190], [245, 145, 50], [70, 150, 250], [50, 190, 190]]
    r = np.zeros_like(mask).astype(np.uint8)
    g = np.zeros_like(mask).astype(np.uint8)
    b = np.zeros_like(mask).astype(np.uint8)
    r[mask == 1], g[mask == 1], b[mask == 1] = colours[random.randrange(0, 10)]
    coloured_mask = np.stack([r, g, b], axis=2)
    return coloured_mask

def get_prediction(img, confidence):
    """
    get_prediction
      parameters:
        - img_path - path of the input image
        - confidence - threshold to keep the prediction or not
      method:
        - Image is obtained from the image path
        - the image is converted to image tensor using PyTorch's Transforms
        - image is passed through the model to get the predictions
        - masks, classes and bounding boxes are obtained from the model and soft masks are made binary(0 or 1) on masks
          ie: eg. segment of cat is made 1 and rest of the image is made 0

    """
    transform = T.Compose([T.ToTensor()])
    img = transform(img)

    img = img.to(device)
    pred = model([img])
    pred_score = list(pred[0]['scores'].detach().cpu().numpy())
    print('pred scores:', pred_score)
    pred_t = [pred_score.index(x) for x in pred_score if x > confidence]
    if len(pred_t) == 0:
        return None, None, None
    pred_t = pred_t[-1]
    masks = (pred[0]['masks'] > 0.5).detach().cpu().numpy()
    masks = masks.reshape(-1, *masks.shape[-2:])
    print(masks.shape)
    # print(pred[0]['labels'].numpy().max())
    pred_class = [CLASS_NAMES[i]
                  for i in list(pred[0]['labels'].cpu().numpy())]
    pred_boxes = [[(int(i[0]), int(i[1])), (int(i[2]), int(i[3]))]
                  for i in list(pred[0]['boxes'].detach().cpu().numpy())]
    masks = masks[:pred_t+1]
    pred_boxes = pred_boxes[:pred_t+1]
    pred_class = pred_class[:pred_t+1]
    return masks, pred_boxes, pred_class

def make_mask_image(img, masks, boxes, pred_cls, ind, rect_th=2, text_size=2, text_th=2):
    for i in range(len(masks)):
        rgb_mask = get_coloured_mask(masks[i])
        img = cv2.addWeighted(img, 1, rgb_mask, 0.5, 0)
        cv2.rectangle(img, boxes[i][0], boxes[i][1],
                      color=(0, 255, 0), thickness=rect_th)
        cv2.putText(img, pred_cls[i], boxes[i][0], cv2.FONT_HERSHEY_SIMPLEX,
                    text_size, (0, 255, 0), thickness=text_th)
    # save frame as JPG file
    # cv2.imwrite("./ceramicimages/image"+str(ind) + "_mask.jpg", img)

def crop_pebble(img, masks, boxes, ind):
    mask = np.asarray(masks[0], dtype="uint8")
    # obtain only the mask pixels from the image
    only_mask = cv2.bitwise_and(img, img, mask=mask)
    bbox = boxes[0]
    # crop the image to only contain the pebble
    crop = only_mask[bbox[0][1]:bbox[1][1], bbox[0][0]:bbox[1][0]]

    # put pebble on standard 1000x1000 image
    imgSize = 1100
    background = np.zeros((imgSize, imgSize, 3), np.uint8)
    ch, cw = crop.shape[:2]

    # compute xoff and yoff for placement of upper left corner of resized image
    yoff = round((imgSize-ch)/2)
    xoff = round((imgSize-cw)/2)

    background[yoff:yoff+ch, xoff:xoff+cw] = crop
    # save crop as JPG file
    # cv2.imwrite("./ceramicimages/image" + str(ind) + "/crop.jpg", background)

    return background

vidcap = cv2.VideoCapture(
    '../../DoE-Project/Moving Pebbles - Ceramic Paint.MOV')
frame_count = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
print('video has', str(frame_count), 'frames.')

rotations = [0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150,
             165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345]
sharpen_kernel = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])

count = 0
while (vidcap.isOpened()):
    hasFrames, image = vidcap.read()
    if hasFrames:
        if count >= 300 and count <= 305:
            # check if image has a pebble
            masks, boxes, pred_cls = get_prediction(image, .9)
            if masks is not None:
                if len(masks) == 1:
                    # make image directory
                    path = "./ceramicimages/image" + str(count) + "/"
                    if not os.path.isdir(path):
                        os.mkdir(path)
                        # save unmodified image
                    # save frame as JPG file
                    cv2.imwrite(path + "unmodified.jpg", image)
                    # make_mask_image(np.copy(image), masks,
                    #                 boxes, pred_cls, count)
                    image = crop_pebble(np.copy(image), masks, boxes, count)

                    # resize image
                    # image = cv2.resize(image, (100, 100))

                    for rotation in rotations:
                        image_center = tuple(np.array(image.shape[1::-1]) / 2)
                        rot_mat = cv2.getRotationMatrix2D(
                            image_center, rotation, 1.0)
                        result = cv2.warpAffine(
                            image, rot_mat, image.shape[1::-1], flags=cv2.INTER_LINEAR)
                        # sharpened = cv2.filter2D(result, -1, sharpen_kernel)

                        # deblurred = cv2.fastNlMeansDenoisingColored(
                        #     sharpened, None, 10, 10, 7, 21)
                        # save frame as JPG file
                        cv2.imwrite(path + str(rotation)+".jpg", result)
    else:
        break
    count += 1

# When everything done, release the capture
vidcap.release()
cv2.destroyAllWindows()


from statistics import mode
from menu import products

def get_product_by_id(id: int):
    if not type(id) == int:
        raise TypeError("product id must be an int")

    for element in products:
        if element["_id"] == id:
            return element

    return {}

def get_products_by_type(food_type: str):

    if not type(food_type) == str:
        raise TypeError("product type must be a str")

    new_list = []

    for element in products:
        if element["type"] == food_type:
            new_list.append(element)
    print(new_list)
    return new_list

def add_product(products, **menu: dict):
    if len(products) > 0:
        menu["_id"] = len(products)+1
        products.append(menu)

        return menu

    menu["_id"] = 1
    products.append(menu)
    return menu

def menu_report():
    count_product = 0
    average_price = 0
    most_common_type = []

    for item in products:
            count_product +=1
            average_price += item['price']
            most_common_type.append(item['type'])

    most_common_type_aux = mode(most_common_type)
    average_price_aux = round(average_price/count_product, 2)
    return f"Products Count: {count_product} - Average Price: ${average_price_aux} - Most Common Type: {most_common_type_aux}"


'''Defines the neural network, loss function and metrics'''
import math, copy
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import utils
from torch.nn.utils import weight_norm
import logging
from torch.autograd import grad
from typing import Tuple
from IPython import embed
import warnings

warnings.filterwarnings("ignore")

logger = logging.getLogger('Transformer.Net')

'''AutoEncoder'''
class VAEEncoderLayer(nn.Module):
    def __init__(self, params):
        super(VAEEncoderLayer, self).__init__()
        self.linear1 = nn.Sequential(
            nn.Linear(params.inter_dim, params.inter_dim),
            nn.LeakyReLU(),
        )
        self.linear2 = nn.Sequential(
            nn.Linear(params.inter_dim, params.inter_dim),
            nn.LeakyReLU(),
        )
        self.linear3 = nn.Sequential(
            nn.Linear(params.inter_dim, params.inter_dim),
            nn.LeakyReLU(),
        )
        self.encoder1 = nn.Sequential(
            nn.Linear(params.inputLen,params.inter_dim//2),
            nn.LeakyReLU(),
        )
        self.encoder1_pred = nn.Sequential(
            nn.Linear(params.input_dim, params.inter_dim//2),
            nn.LeakyReLU(),
        )
        self.encoder2 = nn.Linear(params.inter_dim, params.latent_dim * 2)
        self.init_weights()

    def init_weights(self):
        self.encoder1_pred[0].weight.data.normal_(0, 0.01)
        self.encoder1[0].weight.data.normal_(0, 0.01)
        self.encoder2.weight.data.normal_(0, 0.01)
        self.linear1[0].weight.data.normal_(0, 0.01)
        self.linear2[0].weight.data.normal_(0, 0.01)
        self.linear3[0].weight.data.normal_(0, 0.01)

    def forward(self, his_seq, pred_seq):
        his_seq = self.encoder1(his_seq.unsqueeze(1))
        pred_seq = self.encoder1_pred(pred_seq.unsqueeze(1))
        enc =  torch.cat([his_seq,pred_seq],-1)
        enc = enc + self.linear1(enc)
        enc = enc + self.linear2(enc)
        enc = enc + self.linear3(enc)
        enc = self.encoder2(enc)
        return enc

class VAEDecoderLayer(nn.Module):
    def __init__(self, params):
        super(VAEDecoderLayer, self).__init__()
        self.decoder1 = nn.Sequential(
            nn.Linear(params.latent_dim, params.inter_dim//2),
            nn.LeakyReLU(),
        )

        self.decoder1y = nn.Sequential(
            nn.Linear(params.inputLen, params.inter_dim//2),
            nn.LeakyReLU(),
        )

        self.linear1 = nn.Sequential(
            nn.Linear(params.inter_dim, params.inter_dim),
            nn.LeakyReLU(),
        )
        self.linear2 = nn.Sequential(
            nn.Linear(params.inter_dim, params.inter_dim),
            nn.LeakyReLU(),
        )
        self.decoder2 = nn.Linear(params.inter_dim, params.input_dim)

        self.CausalConv1d1 = nn.Sequential(
            CausalConv1d(1, 1, kernel_size=5),
            nn.LeakyReLU(),
        )
        self.CausalConv1d2 = nn.Sequential(
            CausalConv1d(1, 1, kernel_size=5),
            nn.LeakyReLU(),
        )
        self.CausalConv1d3 = nn.Sequential(
            CausalConv1d(1, 1, kernel_size=5),
            nn.LeakyReLU(),
        )
        self.decoder3 = nn.Sequential(
            nn.Linear(params.latent_dim, params.inter_dim//2),
            nn.LeakyReLU(),
        )
        self.decoder3y = nn.Sequential(
            nn.Linear(params.inputLen, params.inter_dim//2),
            nn.LeakyReLU(),
        )
        self.decoder4 = nn.Linear(params.inter_dim, params.input_dim)

        self.LinearDecoder = nn.Sequential(
            nn.Linear(params.inter_dim, params.inter_dim),
            nn.LeakyReLU(),
        )

        self.init_weights()

    def init_weights(self):
        self.linear1[0].weight.data.normal_(0, 0.01)
        self.linear2[0].weight.data.normal_(0, 0.01)
        self.decoder1[0].weight.data.normal_(0, 0.01)
        self.decoder2.weight.data.normal_(0, 0.01)
        self.decoder4.weight.data.normal_(0, 0.01)
        self.decoder3[0].weight.data.normal_(0, 0.01)
        self.decoder1y[0].weight.data.normal_(0, 0.01)
        self.decoder3y[0].weight.data.normal_(0, 0.01)

    def forward(self, hidden_z, his_seq):
        z_season = self.decoder1(hidden_z)
        h_season = self.decoder1y(his_seq)
        season =  torch.cat([z_season,h_season],-1)
        season = season + self.linear1(season)
        season = season + self.linear2(season)
        recon_season = self.decoder2(season)

        z_trend = self.decoder3(hidden_z)
        h_trend = self.decoder3y(his_seq)
        trend =  torch.cat([z_trend,h_trend],-1)
        trend = self.CausalConv1d1(trend.unsqueeze(1)).squeeze(1)
        trend = trend + self.CausalConv1d2(trend.unsqueeze(1)).squeeze(1)
        trend = trend + self.CausalConv1d3(trend.unsqueeze(1)).squeeze(1)
        recon_trend = self.decoder4(trend)

        recon_seq = recon_trend + recon_season
        return recon_seq, recon_trend, recon_season

class AutoEncoder(nn.Module):
    def __init__(self, params):
        super(AutoEncoder, self).__init__()
        self.encoder = VAEEncoderLayer(params)
        self.decoder = VAEDecoderLayer(params)

    def reparameterise(self, mu, logvar):
        epsilon = torch.randn_like(mu)
        return mu + epsilon * torch.exp(logvar / 2)

    def forward(self, his_seq, pred_seq):
        org_size = his_seq.size()
        batch = org_size[0]
        his_seq = his_seq.view(batch, -1)

        enc = self.encoder(his_seq, pred_seq)

        mu, logvar = enc.chunk(2, dim=-1)
        hidden_z = self.reparameterise(mu.squeeze(1), logvar.squeeze(1))

        recon_seq, recon_trend, recon_season = self.decoder(hidden_z, his_seq)

        return recon_seq, recon_trend, recon_season, mu, logvar

'''Generator'''
class Generator(nn.Module):
    def __init__(self, params):
        super(Generator, self).__init__()
        # self.q50 = nn.Linear(params.d_model, 1)
        # self.q90 = nn.Linear(params.d_model, 1)
        self.distribution_mu = nn.Sequential(
            # nn.LayerNorm(params.d_model+params.latent_dim, eps=1e-6),
            # # nn.Linear(params.d_model+params.latent_dim, params.latent_dim),
            # nn.Linear(params.d_model+params.latent_dim, 1),
            nn.LayerNorm(params.d_model, eps=1e-6),
            nn.Linear(params.d_model, 1),
        )
        self.distribution_sigma = nn.Sequential(
            # nn.LayerNorm(params.d_model+params.latent_dim, eps=1e-6),
            # # nn.Linear(params.d_model+params.latent_dim, params.latent_dim),
            # nn.Linear(params.d_model+params.latent_dim, 1),
            # nn.Softplus(),
            nn.LayerNorm(params.d_model, eps=1e-6),
            nn.Linear(params.d_model, 1),
            nn.Softplus(),
        )
        # self.embedding = nn.Embedding(params.latent_dim, 5)
        self.para = params
        # self.emdedding = nn.Embedding(params.laten_dim, params.d_model)

    def forward(self, x, mu, logvar):

        # mu = mu.repeat(1,x.shape[1],1)
        # logvar = logvar.repeat(1,x.shape[1],1)

        # # x_cat = torch.cat((x,onehot_embed),-1)
        # mu_cat = torch.cat((x,mu),-1)
        # sigma_cat = torch.cat((x,logvar.exp()),-1)
        # distribution_mu = self.distribution_mu(mu_cat)
        # distribution_sigma = self.distribution_sigma(sigma_cat)

        distribution_mu = self.distribution_mu(x)
        distribution_sigma = self.distribution_sigma(x)
        return  distribution_mu.squeeze(-1), distribution_sigma.squeeze(-1)

class CausalConv1d(torch.nn.Conv1d):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1, dilation=1, groups=1, bias=True):
        super(CausalConv1d, self).__init__(in_channels,out_channels,kernel_size=kernel_size,stride=stride,padding=0,dilation=dilation,groups=groups,bias=bias)
        self.__padding = (kernel_size - 1) * dilation

    def forward(self, input):#(Batch, input_channel, seq_len)
        pad = (self.__padding-self.__padding//2, self.__padding//2)
        inputs = F.pad(input, pad)
        return super(CausalConv1d, self).forward(inputs)

'''Transformer EncoderDecoder'''
class EncoderDecoder(nn.Module):
    def __init__(self, params, emb, encoder, decoder, generator):
        super(EncoderDecoder, self).__init__()
        self.emb = emb  #class-Embedding
        self.encoder = encoder     #class-Encoder --EncoderLayer
        self.decoder = decoder   #class-Decoder
        self.generator = generator #class-Genarator
        self.predict_steps = params.predict_steps

    ### Encoder: [0,y1~y167]; Decoder: [y168~y191]_Mask   ------>    [y169~y192]
    def forward(self, x, idx, mu, logvar):
        #x[:,:-self.predict_steps,:]
        src_mask, encoder_out = self.encode(x[:,:-self.predict_steps,:], idx)  #[0,y1~y167]
        #mu_en, sigma_en = self.generator(encoder_out)
        decoder_out = self.decode(encoder_out, x[:,-self.predict_steps:,:], idx, src_mask)
        distribution_mu, distribution_sigma = self.generator(decoder_out, mu, logvar)

        #mu = torch.cat((mu_en, mu_de), 1)
        #sigma = torch.cat((sigma_en, sigma_de), 1)
        return distribution_mu, distribution_sigma

    def encode(self, x, idx):  #x[:,:-self.predict_steps,:]
        src_mask = (x[:,:,0]!=0).unsqueeze(-2)
        embeded = self.emb(x, idx)  #outout=[x;idx_embedding]+PE
        encoder_out = self.encoder(embeded, None)

        return src_mask, encoder_out

    def decode(self, memory, x, idx, src_mask):    #memory = encoder_out
        tgt_mask = make_std_mask(x[:,:,0], 0)  #mask the forecasting series
        embeded = self.emb(x, idx)   #outout=[x;idx_embedding]+PE
        decoder_out = self.decoder(embeded, memory, None, tgt_mask)  #embeding + encoder_out + mask
        return decoder_out

def make_std_mask(tgt, pad):
    "Create a mask to hide padding and future words."
    tgt_mask = (tgt != pad).unsqueeze(-2)
    tgt_mask = tgt_mask & Variable(subsequent_mask(tgt.size(-1)).type_as(tgt_mask.data))
    return tgt_mask
    # [ True, False, False,  ..., False, False, False],
    # [ True,  True, False,  ..., False, False, False],
    # [ True,  True,  True,  ..., False, False, False],
    # ...
    # [ True,  True,  True,  ...,  True, False, False],
    # [ True,  True,  True,  ...,  True,  True, False],
    # [ True,  True,  True,  ...,  True,  True,  True]

def subsequent_mask(size):   #sequence mask
    "Mask out subsequent positions."
    attn_shape = (1, size, size)
    subsequent_mask = np.triu(np.ones(attn_shape), k=1).astype('uint8')
    return torch.from_numpy(subsequent_mask) == 0

def clones(module, N):
    "Produce N identical layers."
    return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])

'''Transformer Encoder'''
class Encoder(nn.Module):
    "Core encoder is a stack of N layers"
    def __init__(self, params, layer):  #layer: class-EncoderLayer
        super(Encoder, self).__init__()
        self.layers = clones(layer, params.N)   #duplicate N times (number of layers)
        self.norm = LayerNorm(layer.size)

    def forward(self, x, src_mask):
        "Pass the input (and mask) through each layer in turn."
        for layer in self.layers:
            x = layer(x, src_mask)   #caluculate self-attention based on x and mask
        encoder_output = self.norm(x)
        return encoder_output

class EncoderLayer(nn.Module):
#(1)Multi-head self-attention + Residual Connection +  Norm
#(2)Feed forward + Residual Connection + Norm
    def __init__(self, params, self_attn, feed_forward, dropout):
        super(EncoderLayer, self).__init__()
        self.params = params
        self.self_attn = self_attn   #class - MultiHeadedAttention
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(params.d_model, dropout), 2)  #residual connection
        self.size = params.d_model

    def forward(self, x, mask):
        "Follow Figure 1 (left) for connections."
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask)) #Multi-head self-attention + ResCon &  Norm
        x = self.sublayer[1](x, self.feed_forward)  #Feed forward + ResCon &  Norm
        return x

'''Transformer Decoder'''
class Decoder(nn.Module):
    "Generic N layer decoder with masking."
    def __init__(self, params, layer):
        super(Decoder, self).__init__()
        self.layers = clones(layer, params.N) #duplicate N times (number of layers) #Attention is all your need.
        self.norm = LayerNorm(layer.size)

    def forward(self, x, memory, src_mask, tgt_mask):
        for layer in self.layers:
            x = layer(x, memory, src_mask, tgt_mask)
        decoder_output = self.norm(x)
        return decoder_output

class DecoderLayer(nn.Module):
#(1)Masked multi-head self-attention + Residual Connection +  Norm
#(2)Multi-head self-attention + Residual Connection +  Norm
#(3)Feed forward + Residual Connection + Norm
    "Decoder is made of self-attn, src-attn, and feed forward (defined below)"
    def __init__(self, params, self_attn, src_attn, feed_forward, dropout):
        super(DecoderLayer, self).__init__()
        self.size = params.d_model
        self.self_attn = self_attn    #Multi-head attention
        self.src_attn = src_attn
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(self.size, dropout), 3)

    def forward(self, x, memory, src_mask, tgt_mask):
        "Follow Figure 1 (right) for connections."
        m = memory    #memory = encoder_out ； x = embedding
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, tgt_mask))
        x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, src_mask))
        src_mask=None
        x = self.sublayer[2](x, self.feed_forward)
        return x

class SublayerConnection(nn.Module):
    """
    A residual connection followed by a layer norm.
    Note for code simplicity the norm is first as opposed to last.
    """
    def __init__(self, size, dropout):
        super(SublayerConnection, self).__init__()
        self.norm = LayerNorm(size)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, sublayer):
        "Apply residual connection to any sublayer with the same size."
        return x + self.dropout(sublayer(self.norm(x)))

'''Embedding'''
class Embedding(nn.Module):
    def __init__(self, params, position):
        super(Embedding, self).__init__()
        self.params = params
        ##torch.nn.Embedding(num_embeddings, embedding_dim)
        self.embedding = nn.Embedding(params.num_class, params.embedding_dim)
        '''
        if(params.dataset == "wind"):
            self.embed1 = nn.Linear(6, params.d_model)
        else:
        '''
        self.embedding
        # self.embed1 = nn.Linear(params.embedding_dim + params.cov_dim+ 3, params.d_model)
        self.embed1 = nn.Linear(params.embedding_dim + params.cov_dim+ 1, params.d_model)
        self.embed2 = position  #class-PositionalEncoding

    def forward(self, x, idx):
        "Pass the input (and mask) through each layer in turn.  x : [bs, window_len, 5] "

        idx = idx.repeat(1, x.shape[1]) # idx is the store id of this batch , [bs, window_len]
        '''
        if(self.params.dataset=="wind"):
            idx = torch.unsqueeze(idx, -1)
            output = torch.cat((x, idx.float()), dim=2) # [bs, widow_len, 25]  [bs, window]  wind dataset!!!
        else:
        '''
        onehot_embed = self.embedding(idx) #[bs, windows_len, embedding_dim(default 20)]
        try:
            output = torch.cat((x, onehot_embed), dim=-1)
            # output = self.embed2(self.embed1(output))
            output = self.embed1(output)  #embedding
            output = self.embed2(output)  #position encoding  ; outout=idx_embedding+PE
        except:
            embed()
        return output

class PositionalEncoding(nn.Module):
    "Implement the PE function."
    def __init__(self, d_model, dropout=0.1, max_len=500): # TODO:max_len
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)

        # Compute the positional encodings once in log space.
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len).unsqueeze(1).float()

        # div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
        div_term = torch.exp(torch.arange(0, d_model, 2).float() *
                             -(math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0)
        self.register_buffer('pe', pe)

    def forward(self, x):
        x = x + Variable(self.pe[:, :x.size(1)],
                         requires_grad=False)
        return self.dropout(x)

class PositionwiseFeedForward(nn.Module):
    "Implements FFN equation."
    def __init__(self, d_model, d_ff, dropout=0.1):
        super(PositionwiseFeedForward, self).__init__()
        self.w_1 = nn.Linear(d_model, d_ff)
        self.w_2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        return self.w_2(self.dropout(F.relu(self.w_1(x))))

'''self-attention'''
def attention(query, key, value, params, mask=None, dropout=None, alpha=None):
    "Compute 'Scaled Dot Product Attention'"
    d_k = query.size(-1)
    #operation MatMul and Scale
    scores = torch.matmul(query, key.transpose(-2, -1)) \
             / math.sqrt(d_k)
    if mask is not None:
        try:
            scores = scores.masked_fill(mask == 0, -1e9)
        except:
            embed()
    #operation Softmax
    p_attn = F.softmax(scores, dim = -1)
    if dropout is not None:
        p_attn = dropout(p_attn)
    p_attn = p_attn.to(torch.float32)
    #operation MatMul
    return torch.matmul(p_attn, value), scores, p_attn

class MultiHeadedAttention(nn.Module):
    def __init__(self, params, dropout=0.2):  # TODO : h , dropout
        "Take in model size and number of heads."
        super(MultiHeadedAttention, self).__init__()
        assert params.d_model % params.h == 0

        self.d_k = params.d_model // params.h
        self.h = params.h  #head
        self.linears = clones(nn.Linear(params.d_model, params.d_model), 4)
        self.attn = None
        self.dropout = nn.Dropout(p=dropout)
        self.params = params
        self.scores = None
        # self.alpha_choser = AlphaChooser(params.h)
        self.alpha = None
        self.attn_type = params.attn_type

    def forward(self, query, key, value, mask=None):
        "Implements Figure 2"
        if mask is not None:
            # Same mask applied to all h heads.
            mask = mask.unsqueeze(1)
        nbatches = query.size(0)
        # 1) Do all the linear projections in batch from d_model => h x d_k
        #  d_model -> h*d_k
        query, key, value = \
            [l(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2)
             for l, x in zip(self.linears, (query, key, value))]

        # 2) Apply attention on all the projected vectors in batch.
        # if self.attn_type=='entmax':
        #     self.alpha = self.alpha_choser()
        x, self.scores, self.attn = attention(query, key, value, self.params, mask=mask,
                                     dropout=self.dropout, alpha=self.alpha)
        # 3) "Concat" using a view and apply a final linear.
        x = x.transpose(1, 2).contiguous() \
             .view(nbatches, -1, self.h * self.d_k)
        return self.linears[-1](x)

class LayerNorm(nn.Module):
    "Construct a layernorm module (See citation for details)."
    def __init__(self, features, eps=1e-6):
        super(LayerNorm, self).__init__()
        self.a_2 = nn.Parameter(torch.ones(features))
        self.b_2 = nn.Parameter(torch.zeros(features))
        self.eps = eps

    def forward(self, x):
        mean = x.mean(-1, keepdim=True)
        std = x.std(-1, keepdim=True)
        return self.a_2 * (x - mean) / (std + self.eps) + self.b_2

def test(model,autoencoder, params, x,  id_batch,mu, logvar):
    batch_size = x.shape[0]
    sample_mu = torch.zeros(batch_size, params.predict_steps, device=params.device)
    sample_q90 = torch.zeros(batch_size, params.predict_steps, device=params.device)
    src_mask, memory = model.encode(x[:, :params.predict_start,:], id_batch)
    for t in range(params.predict_steps):
        ys = x[:, params.predict_start:params.predict_start+t+1,:]
        out = model.decode(memory, ys, id_batch, src_mask)
        distribution_mu, distribution_sigma = model.generator(out,mu, logvar)
        if t < (params.predict_steps - 1):
            x[:, params.predict_start+t+1, 0] = distribution_mu[:, -1]
    # return sample_mu, sample_q90
    return distribution_mu, distribution_sigma

def loss_fn(mu: Variable, sigma: Variable, labels: Variable, predict_start):
    labels = labels[:,predict_start:]
    zero_index = (labels != 0)
    mask = sigma == 0
    sigma_index = zero_index * (~mask)
    distribution = torch.distributions.normal.Normal(mu[sigma_index], sigma[sigma_index])
    likelihood = distribution.log_prob(labels[sigma_index])
    # likelihood = -(labels-mu)**2/(2*sigma**2)-torch.log(sigma)-0.5*math.log(2*math.pi)
    return -torch.mean(likelihood)

def kl_loss(mu: Variable, logvar: Variable):
    KL = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
    return KL

# if relative is set to True, metrics are not normalized by the scale of labels
def accuracy_ND(mu: torch.Tensor, labels: torch.Tensor, relative = False):
    zero_index = (labels != 0)
    if relative:
        diff = torch.mean(torch.abs(mu[zero_index] - labels[zero_index])).item()
        return [diff, 1]
    else:
        diff = torch.sum(torch.abs(mu[zero_index] - labels[zero_index])).item()
        summation = torch.sum(torch.abs(labels[zero_index])).item()
        return [diff, summation]

def accuracy_MAPE(mu: torch.Tensor, labels: torch.Tensor, relative = False):
    zero_index = (labels != 0)
    if relative:
        diff = torch.mean(torch.abs(mu[zero_index] - labels[zero_index])).item()
        return [diff, 1]
    else:
        diff = torch.sum(torch.abs(mu[zero_index] - labels[zero_index])).item()
        summation = torch.sum(torch.abs(labels[zero_index])).item()
        return [diff, summation]

def accuracy_RMSE(mu: torch.Tensor, labels: torch.Tensor, relative = False):
    zero_index = (labels != 0)
    diff = torch.sum(torch.mul((mu[zero_index] - labels[zero_index]), (mu[zero_index] - labels[zero_index]))).item()
    if relative:
        return [diff, torch.sum(zero_index).item(), torch.sum(zero_index).item()]
    else:
        summation = torch.sum(torch.abs(labels[zero_index])).item()
        if summation == 0:
            logger.error('summation denominator error! ')
        return [diff, summation, torch.sum(zero_index).item()]

def accuracy_ROU(rou: float, mu: torch.Tensor, labels: torch.Tensor, relative = False):
    numerator = 0
    denominator = 0
    #pred_samples = samples.shape[0]
    rou_pred = mu
    abs_diff = labels - rou_pred
    numerator += 2 * (torch.sum(rou * abs_diff[labels > rou_pred]) - torch.sum(
        (1 - rou) * abs_diff[labels <= rou_pred])).item()
    denominator += torch.sum(labels).item()
    if relative:
        return [numerator, torch.sum(labels != 0).item()]
    else:
        return [numerator, denominator]

def MAPE(mu:torch.Tensor, labels:torch.Tensor):
    zero_index = (labels != 0)
    diff = mu[zero_index] -labels[zero_index]
    lo = torch.mean(torch.abs(diff / labels[zero_index])) *100
    return lo

def accuracy_ND_(mu: torch.Tensor, labels: torch.Tensor, relative = False):
    mu = mu.cpu().detach().numpy()
    labels = labels.cpu().detach().numpy()

    mu[labels == 0] = 0.

    diff = np.sum(np.abs(mu - labels), axis=1)
    if relative:
        summation = np.sum((labels != 0), axis=1)
        mask = (summation == 0)
        summation[mask] = 1
        result = diff / summation
        result[mask] = -1
        return result
    else:
        summation = np.sum(np.abs(labels), axis=1)
        mask = (summation == 0)
        summation[mask] = 1
        result = diff / summation
        result[mask] = -1
        return result

def accuracy_RMSE_(mu: torch.Tensor, labels: torch.Tensor, relative = False):
    mu = mu.cpu().detach().numpy()
    labels = labels.cpu().detach().numpy()

    mask = labels == 0
    mu[mask] = 0.

    diff = np.sum((mu - labels) ** 2, axis=1)
    summation = np.sum(np.abs(labels), axis=1)
    mask2 = (summation == 0)
    if relative:
        div = np.sum(~mask, axis=1)
        div[mask2] = 1
        result = np.sqrt(diff / div)
        result[mask2] = -1
        return result
    else:
        summation[mask2] = 1
        result = (np.sqrt(diff) / summation) * np.sqrt(np.sum(~mask, axis=1))
        result[mask2] = -1
        return result

def accuracy_ROU_(rou: float, mu: torch.Tensor, labels: torch.Tensor, relative = False):
    mu = mu.cpu().detach().numpy()
    labels = labels.cpu().detach().numpy()
    mask = labels == 0
    mu[mask] = 0.

    abs_diff = np.abs(labels - mu)
    abs_diff_1 = abs_diff.copy()
    abs_diff_1[labels < mu] = 0.
    abs_diff_2 = abs_diff.copy()
    abs_diff_2[labels >= mu] = 0.

    numerator = 2 * (rou * np.sum(abs_diff_1, axis=1) + (1 - rou) * np.sum(abs_diff_2, axis=1))
    denominator = np.sum(labels, axis=1)

    mask2 = (denominator == 0)
    denominator[mask2] = 1
    result = numerator / denominator
    result[mask2] = -1
    return result


NORTH = 0
EAST = 1
SOUTH = 2
WEST = 3

# question 1 actions
def MoveAction(ship, direction, amount):
    if direction == NORTH:
        ship["pos"] = (ship["pos"][0], ship["pos"][1] + amount)         # maybe I should have just used a list for positions even if it makes the dimensions mutable, tuples overcomplicated this because immutability means you need to unpack & reassign each time

    if direction == EAST:
        ship["pos"] = (ship["pos"][0] + amount, ship["pos"][1])         # oh or an object! Duh

    if direction == SOUTH:
        ship["pos"] = (ship["pos"][0], ship["pos"][1] - amount)

    if direction == WEST:
        ship["pos"] = (ship["pos"][0] - amount, ship["pos"][1])

def RotateAction(ship, direction, amount):
    if direction == "R":
        ship["dir"] += int(amount / 90)

    else:
        ship["dir"] -= int(amount / 90)

    ship["dir"] %= 4

actions = {
    "N" : lambda ship, value : MoveAction(ship, NORTH, value),
    "E" : lambda ship, value : MoveAction(ship, EAST, value),
    "S" : lambda ship, value : MoveAction(ship, SOUTH, value),
    "W" : lambda ship, value : MoveAction(ship, WEST, value),
    "R" : lambda ship, value : RotateAction(ship, "R", value),
    "L" : lambda ship, value : RotateAction(ship, "L", value),
    "F" : lambda ship, value : MoveAction(ship, ship["dir"], value)
}

#question 2 actions
def waypointMove(ship, waypoint, amount):
    ship["pos"] = (ship["pos"][0] + (waypoint["pos"][0] * amount), ship["pos"][1] + (waypoint["pos"][1] * amount))

def waypointRotate(ship, waypoint, direction, amount):
    # rotate 180 degrees by negating x and y
    if amount == 180:
        waypoint["pos"] = (-waypoint["pos"][0], -waypoint["pos"][1])

    else:
        # rotate 90 degrees right and then another 180 degrees if the rotation was left
        waypoint["pos"] = (waypoint["pos"][1], -waypoint["pos"][0])

        if (direction == "R" and amount == 270) or (direction == "L" and amount == 90):
            waypoint["pos"] = (-waypoint["pos"][0], -waypoint["pos"][1])

waypointActions = {
    "N" : lambda ship, waypoint, value : MoveAction(waypoint, NORTH, value),
    "E" : lambda ship, waypoint, value : MoveAction(waypoint, EAST, value),
    "S" : lambda ship, waypoint, value : MoveAction(waypoint, SOUTH, value),
    "W" : lambda ship, waypoint, value : MoveAction(waypoint, WEST, value),
    "R" : lambda ship, waypoint, value : waypointRotate(ship, waypoint, "R", value),
    "L" : lambda ship, waypoint, value : waypointRotate(ship, waypoint, "L", value),
    "F" : lambda ship, waypoint, value : waypointMove(ship, waypoint, value)
}

with open("day12input.txt") as file:
    directions = [ (line[0], int(line[1:])) for line in file]

    # check the range of turning angles, should be multiples of 90
    turiningAngles = {direction[1] for direction in directions if direction[0] == "L" or direction[0] == "R"}
    print ("Range of turning angles, should be multiples of 90: " + str(turiningAngles))

    ship = {
        "pos" : (0, 0),
        "dir" : EAST
    }

    loops = 0

    print("Starting ship movements")
    for direction in directions:
        actions[direction[0]](ship, direction[1])
        print("Step: " + str(loops) + "\r", end="")
        loops +=1

    print("\nMovements finished")
    print("Finished ship state: " + str(ship))
    print("Manhattan distance: " + str(abs(ship["pos"][0]) + abs(ship["pos"][1])))

    ship = {
        "pos" : (0, 0)
    }

    waypoint = {
        "pos" : (10, 1)
    }

    loops = 0

    print("\nStarting ship and waypoint movements")
    for direction in directions:
        waypointActions[direction[0]](ship, waypoint, direction[1])
        print("Step: " + str(loops) + "\r", end="")
        loops +=1

    print("\nMovements finished")
    print("Finished ship state: " + str(ship))
    print("Manhattan distance: " + str(abs(ship["pos"][0]) + abs(ship["pos"][1])))


from selenium import webdriver
import allure
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

# def test_open_page():
#     driver.get('https://openweathermap.org/')
#     driver.maximize_window()
#     assert 'openweathermap' in driver.current_url
#     print('browser.current_url')

@allure.suite('Forms')
def test_search_city_field():
    driver.get('https://openweathermap.org/')
    driver.maximize_window()
    search_city_field = driver.find_element(By.CSS_SELECTOR, "input[placeholder='Search city']")
    search_city_field.send_keys('New York')
    search_button = driver.find_element(By.CSS_SELECTOR,'button[class="button-round dark"]')
    time.sleep(5)
    search_button.click()
    time.sleep(5)
    search_option = driver.find_element(By.CSS_SELECTOR,'.search-dropdown-menu>li:nth-child(1)')
    time.sleep(5)
    search_option.click()
    expected_city = 'New York City, US'
    time.sleep(5)
    displayed_city = WebDriverWait(driver,15).until(EC.presence_of_element_located((By.XPATH, '//*[@id="weather-widget"]/div[2]/div[1]/div[1]/div[1]/h2')))
    displayed_city_text = displayed_city.text
    print(displayed_city_text)
    assert expected_city == displayed_city_text


from django.shortcuts import render
from .models import Banks,Branches
from .forms import IfscForm, BranchForm

def index(request):
    return render(request, 'Bank/index.html')

def get_name(request):
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form1 = IfscForm(request.POST)
        # check whether it's valid:
        if form1.is_valid():
            obj = Branches.objects.all().filter(ifsc=form1.cleaned_data['Bank_ifsc'])
            my_dic = {'banks': obj}
            return render(request, 'Bank/results.html', my_dic)
    else:
        form1 = IfscForm()

    return render(request, 'Bank/form_page.html', {'form1': form1})

def branch_name(request):
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:

        form2 = BranchForm(request.POST)
        # check whether it's valid:
        if  form2.is_valid():
            # a = Bank.objects.all().filter(city=form2.cleaned_data['Bank_city'])
            # my_dic = {'e': a }
            obj = Branches.objects.all().filter(city=form2.cleaned_data['Bank_city'])
            my_dic = {'bank': obj }
            return render(request, 'Bank/results1.html', my_dic)
    else:

        form2 = BranchForm()
    return render(request, 'Bank/form_page.html', {'form2': form2})


from graphviz import Digraph

state_set = [['initial'],
    ['show_tutorial', 'to_en_ja', 'to_zh_ja', 'to_zh_en'],
    ['zh_to_en_result', 'zh_to_ja_result', 'en_to_zh_result', 'en_to_ja_result', 'ja_to_zh_result', 'ja_to_en_result'],
    ['zh_en_ex', 'zh_ja_ex', 'en_zh_ex', 'en_ja_ex', 'ja_zh_ex', 'ja_en_ex'],
    ['check_zh_ex', 'check_en_ex', 'check_ja_ex'],
    ['no_ex']
]
colors = ['black', 'blue', 'green', 'orange', 'yellow', 'red']

def e(a, b, l, c):
    dot.edge(a, b, label=l, color=c)

dot = Digraph(format="png")
dot.attr(rankdir='LR', size='8,5')

for states, color in zip(state_set, colors):
    for state in states:
        dot.node(name=state, label=state, color=color)
#
e('initial', 'show_manual', '嗨', 'black')
e('show_tutorial', 'to_en_ja', ':中文', 'black')
e('show_tutorial', 'to_zh_ja', ':英文', 'black')
e('show_tutorial', 'to_zh_en', ':日文', 'black')
e('initial', 'to_en_ja', ':中文', 'black')
e('initial', 'to_zh_ja', ':英文', 'black')
e('initial', 'to_zh_en', ':日文', 'black')
#
e('to_en_ja', 'zh_to_en_result', '帮我翻英文', 'black')
e('to_en_ja', 'zh_to_ja_result', '帮我翻日文', 'black')

e('to_zh_ja', 'en_to_zh_result', '帮我翻中文', 'black')
e('to_zh_ja', 'en_to_ja_result', '帮我翻日文', 'black')

e('to_zh_en', 'ja_to_zh_result', '帮我翻中文', 'black')
e('to_zh_en', 'ja_to_en_result', '帮我翻英文', 'black')
#
e('zh_to_en_result', 'check_zh_ex', '看例句', 'black')
e('check_zh_ex', 'no_ex', '如果没例句', 'black')
e('zh_en_ex', 'check_zh_ex', '下一句', 'black')
e('check_zh_ex', 'zh_en_ex', '如果有中文例句', 'black')
e('zh_en_ex', 'to_en_ja', '尝试其他翻译方式', 'black')

e('zh_to_ja_result', 'check_zh_ex', '看例句', 'black')
e('zh_ja_ex', 'check_zh_ex', '下一句', 'black')
e('check_zh_ex', 'zh_ja_ex', '如果有中文例句', 'black')
e('zh_ja_ex', 'to_en_ja', '尝试其他翻译方式', 'black')

e('en_to_zh_result', 'check_en_ex', '看例句', 'black')
e('check_en_ex', 'no_ex', '如果没例句', 'black')
e('en_zh_ex', 'check_en_ex', '下一句', 'black')
e('check_en_ex', 'en_zh_ex', '如果有英文例句', 'black')
e('en_zh_ex', 'to_zh_ja', '尝试其他翻译方式', 'black')

e('en_to_ja_result', 'check_en_ex', '看例句', 'black')
e('en_ja_ex', 'check_en_ex', '下一句', 'black')
e('check_en_ex', 'en_ja_ex', '如果有英文例句', 'black')
e('en_ja_ex', 'to_zh_ja', '尝试其他翻译方式', 'black')

e('ja_to_zh_result', 'check_ja_ex', '看例句', 'black')
e('check_ja_ex', 'no_ex', '如果没例句', 'black')
e('ja_zh_ex', 'check_ja_ex', '下一句', 'black')
e('check_ja_ex', 'ja_zh_ex', '如果有日文例句', 'black')
e('ja_zh_ex', 'to_zh_en', '尝试其他翻译方式', 'black')

e('ja_to_en_result', 'check_ja_ex', '看例句', 'black')
e('check_ja_ex', 'ja_en_ex', '下一句', 'black')
e('check_ja_ex', 'ja_zh_ex', '如果有日文例句', 'black')
e('ja_en_ex', 'to_zh_en', '尝试其他翻译方式', 'black')
#
e('no_ex', 'initial', '换一个', 'black')
#

print(dot.source)
dot.render(filename='fsm', directory="./imgs",view=False)
dot.render(filename='fsm', directory="./static",view=False)


from rest_framework import serializers, validators
from djoser.serializers import AbstractUserRegistrationSerializer

from .models import JOIE, Employer, User, Industry, Company, SocialLink, Financial
from joieAPI.adhoc import ModelChoiceField, ImageField

class UserRegistrationSerializer(AbstractUserRegistrationSerializer):
    """
    override default registration serializer, separate the the need of REQUIRED_FIELDS
    """
    class Meta(AbstractUserRegistrationSerializer.Meta):
        model = User
        fields = (
            User.USERNAME_FIELD,
            User._meta.pk.name,
            'password',
            'app_user_type',
        )
        write_only_fields = (
            'password',
        )

    def create(self, validated_data):
            return User.objects.create_user(**validated_data)

class SocialLinkSerializer(serializers.ModelSerializer):
    """
    used by serializer of user
    """

    class Meta:
        model = SocialLink
        fields = ('network_name', 'link')

class AccountSerializer(serializers.ModelSerializer):
    """
    first_name and last_name can be updated by user
    """
    socialLinks = SocialLinkSerializer(many=True, required=False)

    # required fields
    first_name = serializers.CharField(max_length=30, required=True)
    last_name = serializers.CharField(max_length=30, required=True)

    class Meta:
        model = User
        first_sign_in = serializers.SerializerMethodField(method_name='first_sign_in')

        fields = (
            'email', 'first_name', 'last_name', 'app_user_type', 'socialLinks', 'create_at', 'create_by', 'update_at',
            'update_by', 'last_login', 'first_sign_in', 'status', )
        read_only_fields = (
            'email', 'create_at', 'create_by', 'update_at', 'update_by', 'app_user_type', 'last_login')

    def first_sign_in(self, obj):
        return obj.first_sign_in()

    def update(self, instance, validated_data):
        user = instance
        socialLinks_data = validated_data.pop('socialLinks', 'empty')
        if socialLinks_data and socialLinks_data != 'empty':
            # user.socialLinks.clear()
            SocialLink.objects.filter(user=user).delete()
            for socialLink_data in socialLinks_data:
                try:
                    socialLink = SocialLink.objects.get(user=user, network_name=socialLink_data['network_name'])
                    socialLink.link = socialLink_data['link']
                    socialLink.save()
                except SocialLink.DoesNotExist:
                    SocialLink.objects.create(user=user, **socialLink_data)
        elif socialLinks_data != 'empty':
            # got the key and the value is null, means the use want to remove the values
            SocialLink.objects.filter(user=user).delete()
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

class AccountMeSerializer(AccountSerializer):
    """
    user cannot update the status by his own
    """
    status = serializers.CharField(read_only=True)

class AccountAdminSerializer(AccountSerializer):
    """
    use by admin users themselves
    """

    class Meta(AccountSerializer.Meta):
        model = User
        fields = (
            'email', 'first_name', 'last_name', 'create_at', 'create_by', 'update_at',
            'update_by', 'last_login')
        read_only_fields = (
            'email', 'create_at', 'create_by', 'update_at', 'update_by', 'last_login')

class IndustrySerializer(serializers.HyperlinkedModelSerializer):
    """
    used by serializer of company
    """

    class Meta:
        model = Industry
        fields = ('url', 'name', 'description')

class CompanySerializer(serializers.ModelSerializer):
    """
    used by serializer of employer
    """

    industry = ModelChoiceField(choices=Industry.objects.all().values_list('name', flat=True))
    logo = ImageField(allow_null=True, required=False)

    # required fields
    name = serializers.CharField(max_length=100, required=True)
    roc = serializers.CharField(max_length=40, required=True)
    business_type = serializers.ChoiceField(choices=[('Direct', 'Direct'), ('Agency', 'Agency')], required=True)
    ea = serializers.CharField(max_length=40, required=False, allow_null=True, allow_blank=True)
    credit_amount = serializers.FloatField(required=True)

    class Meta:
        model = Company

    def get_validation_exclusions(self):
        exclusions = super(CompanySerializer, self).get_validation_exclusions()
        return exclusions + ['postal_code']

    def create(self, validated_data):
        industry_name = validated_data.pop('industry')
        industry = Industry.objects.get(name=industry_name)
        if validated_data['business_type'] == 'Agency':
            if 'ea' not in validated_data or not validated_data['ea']:
                raise serializers.ValidationError('EA is Mandatory for Agency Type.')
        company = Company.objects.create(industry=industry, **validated_data)
        return company

    def update(self, instance, validated_data):
        name = validated_data.get('name')
        filter_set = Company.objects.filter(name=name).exclude(pk=instance.pk)
        if filter_set.exists():
            raise validators.ValidationError('company name must be unique.')
        industry_name = validated_data.pop('industry', instance.industry)
        industry = Industry.objects.get(name=industry_name)
        instance.industry = industry
        # deleting the image because has a None value
        if 'image' in validated_data and not validated_data['image']:
            validated_data.pop('image')
        if 'ea' in validated_data and not validated_data['ea']:
            if instance.business_type == 'Agency' or validated_data['business_type'] == 'Agency':
                raise serializers.ValidationError('EA is Mandatory for Agency Type.')
        if 'business_type' in validated_data:
            if instance.business_type == 'Agency' or validated_data['business_type'] == 'Agency':
                if not instance.ea and not validated_data['ea']:
                    raise serializers.ValidationError('EA is Mandatory for Agency Type.')
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

class CompanyMeSerializer(CompanySerializer):
    # user cannot update the credit_amount by his own.
    credit_amount = serializers.FloatField(read_only=True)

class BaseEmployerSerializer(serializers.HyperlinkedModelSerializer):
    """
    BaseEmployerSerializer for super admin user.
    super admin user can update all of the necessary fields
    """

    user = AccountSerializer(required=True)
    company = CompanySerializer(required=True)

    class Meta:
        model = Employer

    def update(self, instance, validated_data):
        user_data = validated_data.get('user', None)
        company_data = validated_data.get('company', None)
        if user_data:
            instance.user = AccountSerializer().update(instance.user, user_data)
        if company_data:
            instance.company = CompanySerializer().update(instance.company, company_data)
        instance.save()
        return instance

class EmployerAdminSerializer(serializers.HyperlinkedModelSerializer):
    """
    for admin user.
    super admin user can update all of the necessary fields
    """

    user = AccountSerializer(required=True)
    company = CompanyMeSerializer(required=True)

    class Meta:
        model = Employer

    def update(self, instance, validated_data):
        user_data = validated_data.get('user', None)
        company_data = validated_data.get('company', None)
        if user_data:
            instance.user = AccountSerializer().update(instance.user, user_data)
        if company_data:
            instance.company = CompanyMeSerializer().update(instance.company, company_data)
        instance.save()
        return instance

class EmployerMeSerializer(BaseEmployerSerializer):
    """
    For /me viewset

    """
    user = AccountMeSerializer(required=True)
    company = CompanyMeSerializer(required=True)

    def update(self, instance, validated_data):
        user_data = validated_data.get('user', None)
        company_data = validated_data.get('company', None)
        if user_data:
            instance.user = AccountMeSerializer().update(instance.user, user_data)
        if company_data:
            instance.company = CompanyMeSerializer().update(instance.company, company_data)
        instance.save()
        return instance

class FinancialSerializer(serializers.ModelSerializer):
    """
    used by serializer of joie
    """

    class Meta:
        model = Financial
        fields = ('bank_code', 'branch_code', 'account_number')

class BaseJOIESerializer(serializers.HyperlinkedModelSerializer):
    """
    for super admin user. the admin user cannot update the financial detail
    admin user can update all of the necessary fields
    """
    user = AccountSerializer(required=True)
    financial_detail = FinancialSerializer(required=False)

    # required fields
    nric = serializers.CharField(max_length=20, required=True)
    nric_name = serializers.CharField(max_length=40, required=True)
    nric_type = serializers.ChoiceField(choices=[('Singaporean', 'Singaporean'), ('PR', 'PR')], required=True)
    date_of_birth = serializers.DateField(required=True)
    gender = serializers.ChoiceField(choices=[(0, 'Male'), (1, 'Female')], required=True)
    contact_number = serializers.CharField(max_length=20, required=True)

    class Meta:
        model = JOIE

    def update(self, instance, validated_data):
        user_data = validated_data.pop('user', None)  # in case of partial update, it could be None
        financial_data = validated_data.pop('financial_detail', None)
        if user_data:
            instance.user = AccountSerializer().update(instance.user, user_data)
        # financial object created during JOIE object creation
        if financial_data:
            instance.financial_detail = FinancialSerializer().update(instance.financial_detail, financial_data)
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

class JOIEAdminSerializer(BaseJOIESerializer):
    """
    admin user cannot update the financial detail
    """
    financial_detail = FinancialSerializer(read_only=True)

class JOIEMESerializer(BaseJOIESerializer):
    """
    For /me viewset
    """
    user = AccountMeSerializer(required=True)
    financial_detail = FinancialSerializer(required=False)

    punctuality = serializers.FloatField(read_only=True)
    performance = serializers.FloatField(read_only=True)
    attitude = serializers.FloatField(read_only=True)
    rating = serializers.FloatField(read_only=True)

class StaffRegistrationSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = (
            User.USERNAME_FIELD,
            User._meta.pk.name,
            'password',
        )
        write_only_fields = (
            'password',
        )

    def create(self, validated_data):
        return User.objects.create_admin(**validated_data)


#############################################################
# TRUE PARAMS
#############################################################

framesPerVid = 75
wordsPerVideo = 6
framesPerWord = 14
nOfMouthPixels = 1600
mouthW = 40
mouthH = 40
nOfUniqueWords = 53     # including silent and short pause
# excluding 'sil' and 'sp', +1 for padding
wordsVocabSize = (nOfUniqueWords - 2) + 1

# # Unique Words Idx
# uniqueWordsFile = os.path.join('uniqueWords.npy')
# uniqueWords = np.load(uniqueWordsFile)
# # num of unique words, including 'sil' and 'sp'
# nOfUniqueWords = len(uniqueWords)
# # Remove silent
# uniqueWords = np.delete(
#     uniqueWords, np.argwhere(uniqueWords == 'sil'))
# # Remove short pauses
# uniqueWords = np.delete(
#     uniqueWords, np.argwhere(uniqueWords == 'sp'))
# # Vocabulary size
# # excluding 'sil' and 'sp', +1 for padding
# wordsVocabSize = len(uniqueWords) + 1
# # Word indices
# wordIdx = {}
# # Start word indices from 1 (0 for padding)
# for i, word in enumerate(uniqueWords):
#     wordIdx[word] = i+1

# np.save("wordIdx", wordIdx)


"""
This is a Twitter snowflake like scheme generator that fits in 53 bits (for JavaScript, Redis sorted sets, ...)
Its scheme is:
    timestamp | worker_id | sequence
     41 bits  |  8 bits   |  4 bits
Timestamp is in milliseconds since the epoch allowing for 69 years of ids.
Each id generated per millisecond receives a unique auto incrementing sequence
number. Each worker can produce 16 ids per millisecond.
The scheme allows for 256 unique workers, so at most 4 million ids
(256*16*1000) can be produced per second with this scheme.
"""

import time

from logging import getLogger

log = getLogger(__name__)

__version__ = '1.0'

# Tuesday, February 18, 2020 3:00:00 AM GMT
fitepoch = 1581994800000

worker_id_bits = 8
max_worker_id = -1 ^ (-1 << worker_id_bits)
sequence_bits = 4
worker_id_shift = sequence_bits
timestamp_left_shift = sequence_bits + worker_id_bits
sequence_mask = -1 ^ (-1 << sequence_bits)
timestamp_mask = -1 >> timestamp_left_shift << timestamp_left_shift

def to_timestamp(_id):
    _id = _id >> timestamp_left_shift  # strip the lower bits
    _id += fitepoch                    # adjust for epoch
    _id = _id / 1000                   # convert from milliseconds to seconds
    return _id

def generator(
        worker_id=1,
        sleep=lambda x: time.sleep(x/1000.0),
        now=lambda: int(time.time()*1000)):
    """
    worker_id: a unique for your *entire* environment number between 0 and 255
               to identify this generator.
    sleep(n):  function to pause this worker for n milliseconds. you usually
               want to supply a custom method for this in asynchronous
               processes.
    now():     function to return a current unix timestamp in milliseconds.
               useful for testing.
    returns an iterator which yields a series of increasing integers,
    guaranteed to be unique for this worker_id.
    """
    #log.info("snowflake generator.")
    assert worker_id >= 0 and worker_id <= max_worker_id

    last_timestamp = -1
    sequence = 0

    while True:
        timestamp = now()

        if last_timestamp > timestamp:
            log.warning(
                "clock is moving backwards. waiting until %i" % last_timestamp)
            sleep(last_timestamp-timestamp)
            continue

        if last_timestamp == timestamp:
            sequence = (sequence + 1) & sequence_mask
            if sequence == 0:
                log.warning("sequence overrun")
                sequence = -1 & sequence_mask
                sleep(1)
                continue
        else:
            sequence = 0

        last_timestamp = timestamp

        yield (
            ((timestamp-fitepoch) << timestamp_left_shift) |
            (worker_id << worker_id_shift) |
            sequence)

if __name__ == "__main__":
    import datetime
    print(int(datetime.datetime.utcnow().timestamp()))
    print(int(datetime.datetime.now().timestamp() ))
    print(int(time.time() ))
    sf = generator(now=lambda: int(datetime.datetime.utcnow().timestamp() * 1000))
    sfid = next(sf)
    print(f'id {sfid}')


from django.contrib.contenttypes.fields import GenericRelation
from django.db import models

from b2c.misc.models import Media
from .merchant import Merchant

class MerchantLegalPerson(models.Model):
    """
    机构法人信息
    """

    realname = models.CharField(
        verbose_name='法人姓名', max_length=10, null=True, blank=True)

    idcard_number = models.CharField(
        verbose_name='身份证号', max_length=20, null=True, blank=True)

    idcard_valid_from = models.DateField(
        verbose_name='身份证有效期起始',
        null=True, blank=True)

    idcard_valid_to = models.DateField(
        verbose_name='身份证有效期结束',
        null=True, blank=True)

    idcard_front_image = GenericRelation(
        Media,
        verbose_name='身份证正面', null=True, blank=True)

    idcard_back_image = GenericRelation(
        Media,
        verbose_name='身份证反面', null=True, blank=True)

    merchant = models.OneToOneField(
        Merchant,
        on_delete=models.CASCADE,
        verbose_name='法人信息',
        related_name='legal_person'
    )

    class Meta:
        verbose_name_plural = verbose_name = "法人信息"

    def __str__(self):
        return self.realname


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time    : 19-4-23 下午3:53
# @Author  : MaybeShewill-CV
# @Site    : https://github.com/MaybeShewill-CV/lanenet-lane-detection
# @File    : tf_io_pipline_tools.py
# @IDE: PyCharm
"""
tensorflow io pip line tools
"""
import os
import os.path as ops

import cv2
import numpy as np
import loguru
import tensorflow as tf

from local_utils.config_utils import parse_config_utils

CFG = parse_config_utils.lanenet_cfg
LOG = loguru.logger

RESIZE_IMAGE_HEIGHT = CFG.AUG.TRAIN_CROP_SIZE[1] + CFG.AUG.CROP_PAD_SIZE
RESIZE_IMAGE_WIDTH = CFG.AUG.TRAIN_CROP_SIZE[0] + CFG.AUG.CROP_PAD_SIZE
CROP_IMAGE_HEIGHT = CFG.AUG.TRAIN_CROP_SIZE[1]
CROP_IMAGE_WIDTH = CFG.AUG.TRAIN_CROP_SIZE[0]

def int64_feature(value):
    """

    :return:
    """
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

def bytes_feature(value):
    """

    :param value:
    :return:
    """
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

def write_example_tfrecords(gt_images_paths, gt_binary_images_paths, gt_instance_images_paths, tfrecords_path):
    """
    write tfrecords
    :param gt_images_paths:
    :param gt_binary_images_paths:
    :param gt_instance_images_paths:
    :param tfrecords_path:
    :return:
    """
    _tfrecords_dir = ops.split(tfrecords_path)[0]
    os.makedirs(_tfrecords_dir, exist_ok=True)

    LOG.info('Writing {:s}....'.format(tfrecords_path))

    with tf.python_io.TFRecordWriter(tfrecords_path) as _writer:
        for _index, _gt_image_path in enumerate(gt_images_paths):

            # prepare gt image
            _gt_image = cv2.imread(_gt_image_path, cv2.IMREAD_UNCHANGED)
            if _gt_image.shape != (RESIZE_IMAGE_WIDTH, RESIZE_IMAGE_HEIGHT, 3):
                _gt_image = cv2.resize(
                    _gt_image,
                    dsize=(RESIZE_IMAGE_WIDTH, RESIZE_IMAGE_HEIGHT),
                    interpolation=cv2.INTER_LINEAR
                )
            _gt_image_raw = _gt_image.tostring()

            # prepare gt binary image
            _gt_binary_image = cv2.imread(gt_binary_images_paths[_index], cv2.IMREAD_UNCHANGED)
            if _gt_binary_image.shape != (RESIZE_IMAGE_WIDTH, RESIZE_IMAGE_HEIGHT):
                _gt_binary_image = cv2.resize(
                    _gt_binary_image,
                    dsize=(RESIZE_IMAGE_WIDTH, RESIZE_IMAGE_HEIGHT),
                    interpolation=cv2.INTER_NEAREST
                )
                _gt_binary_image = np.array(_gt_binary_image / 255.0, dtype=np.uint8)
            _gt_binary_image_raw = _gt_binary_image.tostring()

            # prepare gt instance image
            _gt_instance_image = cv2.imread(gt_instance_images_paths[_index], cv2.IMREAD_UNCHANGED)
            if _gt_instance_image.shape != (RESIZE_IMAGE_WIDTH, RESIZE_IMAGE_HEIGHT):
                _gt_instance_image = cv2.resize(
                    _gt_instance_image,
                    dsize=(RESIZE_IMAGE_WIDTH, RESIZE_IMAGE_HEIGHT),
                    interpolation=cv2.INTER_NEAREST
                )
            _gt_instance_image_raw = _gt_instance_image.tostring()

            _example = tf.train.Example(
                features=tf.train.Features(
                    feature={
                        'gt_image_raw': bytes_feature(_gt_image_raw),
                        'gt_binary_image_raw': bytes_feature(_gt_binary_image_raw),
                        'gt_instance_image_raw': bytes_feature(_gt_instance_image_raw)
                    }))
            _writer.write(_example.SerializeToString())

    LOG.info('Writing {:s} complete'.format(tfrecords_path))

    return

def decode(serialized_example):
    """
    Parses an image and label from the given `serialized_example`
    :param serialized_example:
    :return:
    """
    features = tf.parse_single_example(
        serialized_example,
        # Defaults are not specified since both keys are required.
        features={
            'gt_image_raw': tf.FixedLenFeature([], tf.string),
            'gt_binary_image_raw': tf.FixedLenFeature([], tf.string),
            'gt_instance_image_raw': tf.FixedLenFeature([], tf.string)
        })

    # decode gt image
    gt_image_shape = tf.stack([RESIZE_IMAGE_HEIGHT, RESIZE_IMAGE_WIDTH, 3])
    gt_image = tf.decode_raw(features['gt_image_raw'], tf.uint8)
    gt_image = tf.reshape(gt_image, gt_image_shape)

    # decode gt binary image
    gt_binary_image_shape = tf.stack([RESIZE_IMAGE_HEIGHT, RESIZE_IMAGE_WIDTH, 1])
    gt_binary_image = tf.decode_raw(features['gt_binary_image_raw'], tf.uint8)
    gt_binary_image = tf.reshape(gt_binary_image, gt_binary_image_shape)

    # decode gt instance image
    gt_instance_image_shape = tf.stack([RESIZE_IMAGE_HEIGHT, RESIZE_IMAGE_WIDTH, 1])
    gt_instance_image = tf.decode_raw(features['gt_instance_image_raw'], tf.uint8)
    gt_instance_image = tf.reshape(gt_instance_image, gt_instance_image_shape)

    return gt_image, gt_binary_image, gt_instance_image

def central_crop(image, crop_height, crop_width):
    """
    Performs central crops of the given image
    :param image:
    :param crop_height:
    :param crop_width:
    :return:
    """
    shape = tf.shape(input=image)
    height, width = shape[0], shape[1]

    amount_to_be_cropped_h = (height - crop_height)
    crop_top = amount_to_be_cropped_h // 2
    amount_to_be_cropped_w = (width - crop_width)
    crop_left = amount_to_be_cropped_w // 2

    return tf.slice(image, [crop_top, crop_left, 0], [crop_height, crop_width, -1])

def augment_for_train(gt_image, gt_binary_image, gt_instance_image):
    """

    :param gt_image:
    :param gt_binary_image:
    :param gt_instance_image:
    :return:
    """
    # convert image from uint8 to float32
    gt_image = tf.cast(gt_image, tf.float32)
    gt_binary_image = tf.cast(gt_binary_image, tf.float32)
    gt_instance_image = tf.cast(gt_instance_image, tf.float32)

    # apply random color augmentation
    gt_image, gt_binary_image, gt_instance_image = random_color_augmentation(
        gt_image, gt_binary_image, gt_instance_image
    )

    # apply random flip augmentation
    gt_image, gt_binary_image, gt_instance_image = random_horizon_flip_batch_images(
        gt_image, gt_binary_image, gt_instance_image
    )

    # apply random crop image
    return random_crop_batch_images(
        gt_image=gt_image,
        gt_binary_image=gt_binary_image,
        gt_instance_image=gt_instance_image,
        cropped_size=[CROP_IMAGE_WIDTH, CROP_IMAGE_HEIGHT]
    )

def augment_for_test(gt_image, gt_binary_image, gt_instance_image):
    """

    :param gt_image:
    :param gt_binary_image:
    :param gt_instance_image:
    :return:
    """
    # apply central crop
    gt_image = central_crop(
        image=gt_image, crop_height=CROP_IMAGE_HEIGHT, crop_width=CROP_IMAGE_WIDTH
    )
    gt_binary_image = central_crop(
        image=gt_binary_image, crop_height=CROP_IMAGE_HEIGHT, crop_width=CROP_IMAGE_WIDTH
    )
    gt_instance_image = central_crop(
        image=gt_instance_image, crop_height=CROP_IMAGE_HEIGHT, crop_width=CROP_IMAGE_WIDTH
    )

    return gt_image, gt_binary_image, gt_instance_image

def normalize(gt_image, gt_binary_image, gt_instance_image):
    """
    Normalize the image data by substracting the imagenet mean value
    :param gt_image:
    :param gt_binary_image:
    :param gt_instance_image:
    :return:
    """

    if gt_image.get_shape().as_list()[-1] != 3 \
            or gt_binary_image.get_shape().as_list()[-1] != 1 \
            or gt_instance_image.get_shape().as_list()[-1] != 1:
        LOG.error(gt_image.get_shape())
        LOG.error(gt_binary_image.get_shape())
        LOG.error(gt_instance_image.get_shape())
        raise ValueError('Input must be of size [height, width, C>0]')

    gt_image = tf.cast(gt_image, dtype=tf.float32)
    gt_image = tf.subtract(tf.divide(gt_image, tf.constant(127.5, dtype=tf.float32)),
                           tf.constant(1.0, dtype=tf.float32))

    return gt_image, gt_binary_image, gt_instance_image

def random_crop_batch_images(gt_image, gt_binary_image, gt_instance_image, cropped_size):
    """
    Random crop image batch data for training
    :param gt_image:
    :param gt_binary_image:
    :param gt_instance_image:
    :param cropped_size:
    :return:
    """
    concat_images = tf.concat([gt_image, gt_binary_image, gt_instance_image], axis=-1)

    concat_cropped_images = tf.image.random_crop(
        concat_images,
        [cropped_size[1], cropped_size[0], tf.shape(concat_images)[-1]],
        seed=tf.random.set_random_seed(1234)
    )

    cropped_gt_image = tf.slice(
        concat_cropped_images,
        begin=[0, 0, 0],
        size=[cropped_size[1], cropped_size[0], 3]
    )
    cropped_gt_binary_image = tf.slice(
        concat_cropped_images,
        begin=[0, 0, 3],
        size=[cropped_size[1], cropped_size[0], 1]
    )
    cropped_gt_instance_image = tf.slice(
        concat_cropped_images,
        begin=[0, 0, 4],
        size=[cropped_size[1], cropped_size[0], 1]
    )

    return cropped_gt_image, cropped_gt_binary_image, cropped_gt_instance_image

def random_horizon_flip_batch_images(gt_image, gt_binary_image, gt_instance_image):
    """
    Random horizon flip image batch data for training
    :param gt_image:
    :param gt_binary_image:
    :param gt_instance_image:
    :return:
    """
    concat_images = tf.concat([gt_image, gt_binary_image, gt_instance_image], axis=-1)

    [image_height, image_width, _] = gt_image.get_shape().as_list()

    concat_flipped_images = tf.image.random_flip_left_right(
        image=concat_images,
        seed=tf.random.set_random_seed(1)
    )

    flipped_gt_image = tf.slice(
        concat_flipped_images,
        begin=[0, 0, 0],
        size=[image_height, image_width, 3]
    )
    flipped_gt_binary_image = tf.slice(
        concat_flipped_images,
        begin=[0, 0, 3],
        size=[image_height, image_width, 1]
    )
    flipped_gt_instance_image = tf.slice(
        concat_flipped_images,
        begin=[0, 0, 4],
        size=[image_height, image_width, 1]
    )

    return flipped_gt_image, flipped_gt_binary_image, flipped_gt_instance_image

def random_color_augmentation(gt_image, gt_binary_image, gt_instance_image):
    """
    andom color augmentation
    :param gt_image:
    :param gt_binary_image:
    :param gt_instance_image:
    :return:
    """
    # first apply random saturation augmentation
    gt_image = tf.image.random_saturation(gt_image, 0.8, 1.2)
    # sencond apply random brightness augmentation
    gt_image = tf.image.random_brightness(gt_image, 0.05)
    # third apply random contrast augmentation
    gt_image = tf.image.random_contrast(gt_image, 0.7, 1.3)

    gt_image = tf.clip_by_value(gt_image, 0.0, 255.0)

    return gt_image, gt_binary_image, gt_instance_image


#Autor: RodrigoTenorio

def ex_01():
    ips = open("ip.txt","r")
    ip_invalidos=[]
    ip_validos=[]
    for ip in ips:
        numero=ip.split('.')
        if( int(numero[0]) <= 255 and int(numero[1]) <= 255 and int(numero[2]) <= 255 and int(numero[3]) <= 255):
            ip_validos.append(ip)
        else:
            ip_invalidos.append(ip)

    arq = open("lista_ip.txt","w")
    texto ="[Endereços válidos:]"+"\n"
    arq.write(texto)
    for ip_ in ip_validos:
        arq.write(ip_)
    texto ="\n"+"\n"+"[Endereços inválidos:]"+"\n"
    arq.write(texto)
    for ip_ in ip_invalidos:
        arq.write(ip_)

    ips.close()
    arq.close()

def ex_02():
    relatrio =open("usuario.txt","r")
    usuarios = []
    espaco  = []
    for palavra in relatrio:
        palavra= palavra.split()
        usuarios.append(palavra[0])
        espaco.append(converter_byte_mega( int(palavra[1])))
    relatrio.close()

    arq=open("relatorio.txt","w")
    cabecalho = "ACME Inc.                 Uso do espaço em disco pelos usuários    "+"\n"
    linhas="------------------------------------------------------------------------"+"\n"
    enunciado="Nr. Usuário                       Espaço utilizado           % do uso"+"\n"+"\n"
    arq.write(cabecalho)
    arq.write(linhas)
    arq.write(enunciado)
    con = 0
    while con < len(usuarios):
        arq.write("{}  {}                     {} MB        {}% \n".format(con+1, usuarios[con], espaco[con], porcento_do_uso(sum(espaco), espaco[con]) ) )
        con +=1
    arq.write("\n")
    arq.write("Espaço total ocupado: {} MB\n".format(sum(espaco)))
    arq.write("Espaço médio ocupado: {} MB".format( round(sum(espaco)/len(espaco),2 )))

    arq.close()

def converter_byte_mega(num_bute):
    num_mega = ( num_bute / 1024)/1024
    return  round(num_mega,2)

def porcento_do_uso(total,uso):
    valor = (uso * 100)/total
    return round(valor,2)

def run():
    ex_01()
    ex_02()


#!/usr/bin/python3
"""
 contains pascal_triangle function
"""

def pascal_triangle(n):
    """ returns a list of lists of integers
     representing the Pascals triangle of n """
    pascal = []
    if n <= 0:
        return pascal
    pascal.append([1])
    if n == 1:
        return pascal
    pascal.append([1, 1])
    if n == 2:
        return pascal
    for i in range(2, n):
        aux = []
        aux.append(1)
        aux2 = [pascal[i - 1][j] + pascal[i - 1][j + 1]\
            for j in range(len(pascal) - 1)]
        aux += aux2
        aux.append(1)
        pascal.append(aux)
    return pascal


from selenium import webdriver
import time

option = webdriver.ChromeOptions()
option.add_argument("-incognito")
option.add_experimental_option("excludeSwitches", ['enable-automation']);
#option.add_argument("--headless")
#option.add_argument("disable-gpu")
browser = webdriver.Chrome(executable_path=r'C:\Users\sheik\Downloads\chromedriver_win32/chromedriver.exe', options=option)

browser.get("https://docs.google.com/forms/d/e/1FAIpQLSeRP3-IvxiGvLNoHL5efeXoukWIzehU7g-beVtVKQf_eJBqYQ/viewform")

##final = browser.find_element_by_class_name("quantumWizTextinputPapertextareaContentArea.exportContentArea")

for i in range(1500):
    textboxes = browser.find_elements_by_class_name("quantumWizTextinputPaperinputInput")
    radiobuttons = browser.find_elements_by_class_name("docssharedWizToggleLabeledLabelWrapper")
    ##time.sleep(1)
    textboxes[0].send_keys("Muhammad Mujtaba")
    textboxes[1].send_keys("muhammad.mujtaba@centre.edu")
    radiobuttons[1].click()
    textboxes[2].send_keys("sheikhmujtaba")
    ##final.send_keys("Physical workouts, extreme sports")
    submit = browser.find_element_by_xpath('//*[@id="mG61Hd"]/div[2]/div/div[3]/div[1]/div/div/span/span')
    submit.click()
    another_response = browser.find_element_by_xpath('/html/body/div[1]/div[2]/div[1]/div/div[4]/a')
    another_response.click()

browser.close()


###
#This file is a part of the NVDA project.
#URL: http://www.nvda-project.org/
#Copyright 2010-2012 NV Access Limited
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License version 2.0, as published by
#the Free Software Foundation.
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#This license can be found at:
#http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
###

import os
import sys

# Get the path to msgfmt.
MSGFMT = os.path.abspath(os.path.join("miscDeps", "tools", "msgfmt.exe"))

def exists(env):
	return True

def generate(env):
	env['BUILDERS']['gettextMoFile']=env.Builder(
		action=env.Action([[MSGFMT,"-o","$TARGET","$SOURCE"]],
			lambda t,s,e: 'Compiling gettext template %s'%s[0].path),
		suffix='.mo',
		src_suffix='.po'
	)


from datetime import datetime
from pathlib import Path
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponseBadRequest
from fragview.projects import Project
from fragview.projects import current_project
from fragview.forms import KillJobForm
from jobs import client

def _elapsed_time(start: datetime, end: datetime) -> dict[str, int]:
    """
    calculate time elapsed from start to end time,
    returns elapsed time divided into hours, minutes and seconds
    """
    delta = end - start

    # let's use seconds precision
    seconds_delta = delta.seconds

    seconds = seconds_delta % 60
    minutes = (seconds_delta // 60) % 60
    hours = seconds_delta // 3600

    return dict(hours=hours, minutes=minutes, seconds=seconds)

def _get_jobs(project: Project) -> list[dict]:
    def _run_time(start_time):
        if start_time is None:
            return None

        return _elapsed_time(start_time, now)

    now = datetime.now()

    #
    # convert Jobs table into a format that is more
    # convenient for presenting to the user
    #
    jobs = []
    for job in project.get_running_jobs():
        jobs.append(
            dict(
                id=job.id,
                name=job.description,
                stdout=Path(job.stdout).name,
                stderr=Path(job.stderr).name,
                run_time=_run_time(job.started),
            )
        )

    return jobs

def status(request):
    return render(
        request, "hpcstatus.html", {"jobs": _get_jobs(current_project(request))}
    )

def kill(request):
    form = KillJobForm(request.POST)

    if not form.is_valid():
        return HttpResponseBadRequest(f"{form.errors}")

    project = current_project(request)
    client.cancel_jobs(project.id, form.get_job_ids())

    return redirect(reverse("hpcstatus"))

def jobhistory(request):
    def _finished_jobs():
        for job in project.get_finished_jobs():
            stdout = Path(job.stdout).name
            stderr = Path(job.stderr).name
            yield stdout, job.finished, stdout, stderr

    project = current_project(request)
    return render(request, "jobhistory.html", {"logHistory": _finished_jobs()})