import time
import numpy as np
import os
import logging
from . import utils
from skimage.morphology import skeletonize_3d
from .scores import correctness_completeness_quality

logger = logging.getLogger(__name__)

class Trainer(object):

    def __init__(self,training_step, validation=None, valid_every=None,
                 print_every=None, save_every=None, save_path=None, save_objects={},
                 save_callback=None):

        self.training_step = training_step
        self.validation = validation
        self.valid_every = valid_every
        self.print_every = print_every
        self.save_every = save_every
        self.save_path = save_path
        self.save_objects = save_objects
        self.save_callback = save_callback

        self.starting_iteration = 0

        if self.save_path is None or self.save_path in ["", ".", "./"]:
            self.save_path = os.getcwd()

        self.results = {"training":{"epochs":[], "results":[]},
                        "validation": {"epochs":[], "results":[]}}

    def save_state(self, iteration):

        for name, object in self.save_objects.items():
            utils.torch_save(os.path.join(self.save_path, "{}_final.pickle".format(name, iteration)),
                             object.state_dict())

        utils.pickle_write(os.path.join(self.save_path, "results_final.pickle".format(iteration)),
                           self.results)

        if self.save_callback is not None:
            self.save_callback(iteration)

    def run_for(self, iterations):

        start_time = time.time()
        block_iter_start_time = time.time()

        for iteration in range(self.starting_iteration, self.starting_iteration+iterations+1):

            train_step_results = self.training_step(iteration)
            self.results['training']["results"].append(train_step_results)
            self.results['training']["epochs"].append(iteration)

            if self.print_every is not None:
                if iteration%self.print_every==0:
                    elapsed_time = (time.time() - start_time)//60
                    block_iter_elapsed_time = time.time() - block_iter_start_time

                    loss_v1 = train_step_results["loss"] if "loss" in train_step_results.keys() else None
                    loss_v2 = train_step_results["loss_2"] if "loss_2" in train_step_results.keys() else 0
                    to_print = "[{:0.0f}min][{:0.2f}s] - Epoch: {} (Train-batch Loss: {:0.6f}, {:0.6f})"
                    to_print = to_print.format(elapsed_time, block_iter_elapsed_time, iteration, loss_v1, loss_v2)

                    logger.info(to_print)
                    block_iter_start_time = time.time()

            if self.validation is not None and self.valid_every is not None:
                if iteration%self.valid_every==0 and iteration!=self.starting_iteration:
                    logger.info("validation...")
                    start_valid = time.time()

                    validation_results = self.validation(iteration)

                    self.results['validation']["results"].append(validation_results)
                    self.results['validation']["epochs"].append(iteration)
                    logger.info("Validation time: {:.2f}s".format(time.time()-start_valid))

            if self.save_every is not None:
                if iteration%self.save_every==0 and iteration!=self.starting_iteration:
                    start_saving = time.time()
                    self.save_state(iteration)
                    logger.info("Saving time: {:.2f}s".format(time.time()-start_saving))

class TrainingEpoch(object):

    def __init__(self, dataloader, ours=False, ours_start=0, neg_coeff=1e-2, pos_coeff=1e-4):

        self.dataloader = dataloader
        self.ours = ours
        self.ours_start = ours_start
        self.neg_coeff = neg_coeff
        self.pos_coeff = pos_coeff

    def __call__(self, iterations, network, optimizer, lr_scheduler, base_loss, our_loss):

        mean_loss = 0
        mean_loss2 = 0
        for images, labels, graphs, slices in self.dataloader:

            images = images.cuda()
            labels = labels.cuda()

            preds = network(images.contiguous())

            loss = base_loss(preds, labels)
            loss_v = float(utils.from_torch(loss))
            loss_v2 = float(0)

            if self.ours and iterations >= self.ours_start:
                loss2 = our_loss(preds, labels, self.neg_coeff, self.pos_coeff)
                loss_v2 = float(utils.from_torch(loss2))
                loss += loss2

            if np.isnan(loss_v) or np.isinf(loss_v):
                return {"loss": loss_v,
                        "pred": utils.from_torch(preds),
                        "labels": utils.from_torch(labels)}

            mean_loss += loss_v
            mean_loss2 += loss_v2
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            if lr_scheduler is not None:
                lr_scheduler.step()

        return {"loss"   : float(mean_loss/len(self.dataloader)),
                "loss_2" : float(mean_loss2/len(self.dataloader))}

class Validation(object):

    def __init__(self, crop_size, margin_size, dataloader_val, out_channels, output_path):
        self.crop_size = crop_size
        self.margin_size = margin_size
        self.dataloader_val = dataloader_val
        self.out_channels = out_channels
        self.output_path = output_path

        self.bestqual = 0
        self.bestcomp = 0
        self.bestcorr = 0

    def __call__(self, iteration, network, loss_function):

        losses = []
        preds = []
        scores = {"corr":[], "comp":[], "qual":[]}

        network.train(False)
        with utils.torch_no_grad:
            for i, (image, label) in enumerate(self.dataloader_val):

                image  = image.cuda()[:,None]
                label  = label.cuda()[:,None]

                out_shape = (image.shape[0],self.out_channels,*image.shape[2:])
                pred = utils.to_torch(np.empty(out_shape, np.float32), volatile=True).cuda()
                pred = utils.process_in_chuncks(image, pred,
                                            lambda chunk: network(chunk),
                                            self.crop_size, self.margin_size)

                loss = loss_function(pred, label)
                loss_v = float(utils.from_torch(loss))
                losses.append(loss_v)

                pred_np = utils.from_torch(pred)[0]
                preds.append(pred_np)
                label_np = utils.from_torch(label)[0]

                pred_mask = skeletonize_3d((pred_np < 5)[0])//255
                label_mask = (label_np==0)

                corr, comp, qual = correctness_completeness_quality(pred_mask, label_mask, slack=3)

                scores["corr"].append(corr)
                scores["comp"].append(comp)
                scores["qual"].append(qual)

                # save the prediction here
                output_valid = os.path.join(self.output_path, "output_valid")
                utils.mkdir(output_valid)
                np.save(os.path.join(output_valid, "pred_{:06d}_final.npy".format(i,iteration)), pred_np)

        scores["qual"] = np.nan_to_num(scores["qual"])

        qual_total = np.mean(scores["qual"],axis=0)
        corr_total = np.mean(scores["corr"],axis=0)
        comp_total = np.mean(scores["comp"],axis=0)

        if self.bestqual < qual_total:
            self.bestqual = qual_total
            for i in range(len(self.dataloader_val)):
                np.save(os.path.join(output_valid, "pred_{:06d}_bestqual.npy".format(i,iteration)), preds[i])
            utils.torch_save(os.path.join(self.output_path, "network_bestqual.pickle"),
                             network.state_dict())

        logger.info("\tMean loss: {}".format(np.mean(losses)))
        logger.info("\tMean qual: {:0.3f}".format(qual_total))
        logger.info("Best quality score is {}".format(self.bestqual))

        network.train(True)

        return {"loss": np.mean(losses),
                "mean_corr": corr_total,
                "mean_comp": comp_total,
                "mean_qual": qual_total,
                "scores": scores}


#!python3
#lower = int(args.lower)

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import sys
import argparse
import signal
import math
from scipy.stats import zscore
from bisect import bisect_left
parser = argparse.ArgumentParser(description='Creates normal heatmaps and creates bullseye plots which account for the manhattan distance between each site.')
parser.add_argument('-i', action = 'store', dest = 'input_file', required = True, type=str, help='input file')
parser.add_argument('-o', action = 'store', dest = 'output_file', required = True, type=str, help='prefix for output plots')
parser.add_argument('-c', action = 'store', dest = 'colorscheme', required = False, help='matplotlib_colors', default = "Red")
parser.add_argument('-z', dest='znorm', action='store_true', help='znorm each ring in bullseye plot', default = False)
parser.add_argument('-s', dest='squareBullseye', action='store_true', help='Trim edges to make a square bullseye plot', default = False)
parser.add_argument('-l', dest='lower', action='store', required = False, help='minvalue for color scale', type=float)
parser.add_argument('-u', dest='upper', action='store', required = False, help='maxvalue for color scale', type=float)
args = parser.parse_args()

spot = str(args.input_file)
outpre = str(args.output_file)
heatcol = str(args.colorscheme)

#lower = int(args.lower)
#upper = int(args.upper)
znorm = args.znorm
square = args.squareBullseye

def takeClosest(myList, myNumber):
    """
    Assumes myList is sorted. Returns closest value to myNumber.

    If two numbers are equally close, return the smallest number.
    """
    pos = bisect_left(myList, myNumber)
    if pos == 0:
        return 0
    if pos == len(myList):
        return -1
    before = myList[pos - 1]
    after = myList[pos]
    if after - myNumber < myNumber - before:
       return pos
    else:
       return pos - 1
mat = np.loadtxt(spot)

#this is the size of the starting matrix
matsize = len(mat)
if (matsize % 2) == 0:
    print("Bullseye requires a central bin, therefore the number of rows and columns in the input matrix should be odd")
    sys.exit(0)

#this is the number of rings in the plot which corresponds to the Manhattan distance
rings = math.floor(math.sqrt(((matsize//2)**2)/2))

#This is the maximum number of rings that will be needed if square is set to True.
maxrings = math.ceil(math.sqrt(rings**2 + rings**2))

#this is the number of radial slices the circle is divided in to. Too few and the segments will be distorted.
#slices = 2000
slices = (matsize-1)*100
#this toggles on z-score normalization of each ring.
#znorm = False

#this toggles between "closest-angle" mapping and "uniform segment length" mapping
uniform = True

#this makes a square plot by trimming the edges of the plot.
#square = False

#First we compute the Manhattan distance and arctan of every point in the starting matrix
arctan = np.zeros((matsize,matsize))
manhattandistance = np.zeros((matsize,matsize))
for i in range(0,len(manhattandistance)):
    for j in range(0,len(manhattandistance)):
        manhattandistance[i,j] = abs(j-int((matsize-1)/2)) + abs(i-((matsize-1)/2))
        arctan[i,j] = np.arctan2(((j-int((matsize-1)/2))),((i-((matsize-1)/2))))

#We rearrange the Manhattan distances and arctans into lists based on the Manhattan distances.
#This means each ring is now represented by a list of scores and arctans.
manhat_arctans = []
manhat_scores = []
for a in range(matsize):
    manhat_arctans.append([])
    manhat_scores.append([])
    for b in range(matsize):
        for c in range(matsize):
            if (manhattandistance[b,c] == a):
                manhat_scores[a].append(mat[b,c])
                manhat_arctans[a].append(arctan[b,c])

#Next we construct the meshgrid for the circular plot.
rad = np.linspace(0, maxrings, maxrings+1)
azm = np.linspace(0, 2 * np.pi, slices)
r, th = np.meshgrid(rad, azm)

#now we trim the edges of the meshgrid to make a square
if square:
    for i in range(slices):
        for j in range(maxrings+1):
            #compute the cartesian coordinates of each quad to see if they need trimming
            nrad = r[i,j]
            theta = th[i,j]
            cosine = math.cos(theta)
            x = nrad * cosine
            sine = math.sin(theta)
            y = nrad * sine
            if abs(x) > abs(y):
                if abs(x) > rings:
                    nrad = abs(rings/cosine)
            else:
                if abs(y) > rings:
                    nrad = abs(rings/sine)
            r[i,j] = nrad

#This will be the new matrix that will be plotted. We need to fill it with the appropriate values.

C = np.zeros([slices,int(maxrings)+1])

dscoretmp1 = 0
dscoretmp2 = 0
dscore = 0
#For each ring we convert the arctans into thetas by making them all positive
#and adjusting for the slices.
for ring in range(int(maxrings)):
    arctans = manhat_arctans[ring]
    thetas = [(x + np.pi) * ((slices/2)/np.pi) for x in arctans]
    scores = manhat_scores[ring]
    #Z-score normalization toggled by boolean value above.
    if znorm:
        if ring:
            scores = zscore(scores).tolist()
        else:
            scores = [0]

    #In order for the values to appropriately wrap around to 0 we need to add the last score to the beginning.
    thetas.append(0)
    scores.append(scores[0])
    #Here we sort the scores and thetas by the theta in order to help finding the closest one.
    sorted_scores = [x for _,x in sorted(zip(thetas,scores))]
    if len(sorted_scores) > 1:
        quatrang = int(float(len(sorted_scores)-1)/4)
        quatstart = quatrang+1
        quatend = quatrang+quatrang
        for hloc in sorted_scores:

            dscoretmp1+=1

        for hloc in sorted_scores[quatstart:quatend]:
            if hloc >= 1:
                dscoretmp2+=1
    sorted_thetas = sorted(thetas)
    #Here to produce a uniform map we map the actual angles to idealized angles.
    if uniform:
        if ring:
            slices_per_segment = slices/(len(scores)-1)
            for t in range(len(sorted_thetas)):
                sorted_thetas[t] = slices_per_segment * t

    #Now for each slice in the ring we find the score corresponding to the nearest theta
    #and assign it to the appropriate coordinate in our matrix to be plotted: C.
    for i in range(slices):
        idx = takeClosest(sorted_thetas,i)
        C[i,ring] = sorted_scores[idx]

#create a juicebox-like red to white colormap
if heatcol == "Red":
    br = LinearSegmentedColormap.from_list("bright_red",[(1,1,1),(1,0,0)])
else:
    br = heatcol

ax = plt.subplot(projection="polar")

if znorm:
    plt.pcolormesh(th,r,C,cmap="seismic",vmin=-4,vmax=4)
    if (dscoretmp1 > 0):
        dscore=float(dscoretmp2/dscoretmp1)*100
    print("ADA score is %s\n" % (str(round(dscore,2))))
else:
    if args.lower is not None and args.upper is not None:
        plt.pcolormesh(th,r,C,cmap=br,vmin=args.lower,vmax=args.upper)
    elif args.lower is not None:
        plt.pcolormesh(th,r,C,cmap=br,vmin=args.lower)
    elif  args.upper is not None:
        plt.pcolormesh(th,r,C,cmap=br,vmax=args.upper)
    else:
        plt.pcolormesh(th,r,C,cmap=br)
if square:
    ax.axis('off')
else:
    xlabels=["","","Left Anchor","","Right Anchor","","",""]
    ax.set_xticklabels(xlabels, rotation=40, ha="right")
    ax.set_yticklabels([])
    plt.colorbar()

#Rotate the plot so that the anchors go back to their original positions.
ax.set_theta_offset(np.pi/2)

plt.savefig(outpre + '_bullseye.png')

plt.clf()
center = (matsize-1)//2

left = center-rings

right = center+rings+1

newmat = (mat[int(left):int(right),int(left):int(right)])
if args.lower is not None and args.upper is not None:
    plt.imshow(newmat,cmap=br,vmin=args.lower,vmax=args.upper)
elif args.lower is not None:
    plt.imshow(newmat,cmap=br,vmin=args.lower)
elif  args.upper is not None:
    plt.imshow(newmat,cmap=br,vmax=args.upper)
else:
    plt.imshow(newmat,cmap=br)

plt.colorbar()
plt.savefig(outpre + '_normal.png')


import gzip
import json

from flask import Blueprint, Response, request, escape

from lib import db
from lib.gwpcc import consts
from lib.gwpcc.colonies.colony import Colony
from lib.gwpcc.consts import KEY_THING_LOCALE_THING_NAMES, USER_TYPES
from lib.gwpcc.things.thing import Thing

colony_module = Blueprint('v4_colony_module', __name__, url_prefix='/v4/colonies')

@colony_module.route('/create', methods=['PUT'])
def colony_create():
    incoming_data = request.json
    connection = db.get_redis_db_from_context()
    colony = new_colony_from_request(incoming_data, connection)
    print("Saving new colony to db")
    if not any(allowed_type == colony.OwnerType for allowed_type in USER_TYPES):
        print("Not allowed - not saving - sending error")
        return Response(consts.ERROR_INVALID, status=consts.HTTP_INVALID)

    colony.save_to_database(connection)

    colony.ping()
    print("Colony Saved")
    return Response(json.dumps({'Hash': colony.Hash}), status=201, mimetype='application/json')

def new_colony_from_request(incoming_data, connection):
    new_colony = {'BaseName': escape(incoming_data['BaseName']),
                  'FactionName': escape(incoming_data['FactionName']),
                  'Planet': escape(incoming_data['Planet']),
                  'OwnerType': escape(incoming_data['OwnerType']),
                  'OwnerID': escape(incoming_data['OwnerID']),
                  'LastGameTick': escape(incoming_data['LastGameTick'])}

    return Colony.from_dict(new_colony, connection=connection)

@colony_module.route('/<string:colony_hash>', methods=['PUT'])
def colony_update_data(colony_hash):
    connection = db.get_redis_db_from_context()
    colony = Colony.get_from_database_by_hash(colony_hash, connection)

    print("Saving colony data")
    incoming_data = request.json

    created = False

    if not colony or colony.OwnerID != incoming_data['OwnerID']:
        # If the Owner ID's don't match. Create a new colony.
        # And assign it a new ID. Might have happened if save sharing.
        colony = new_colony_from_request(incoming_data, connection)
        created = True
    else:
        colony.BaseName = escape(incoming_data['BaseName'])
        colony.FactionName = escape(incoming_data['FactionName'])
        colony.Planet = escape(incoming_data['Planet'])
        colony.LastGameTick = escape(incoming_data['LastGameTick'])
        if incoming_data['HasSpawned']:
            colony.Ban()

    pipe = connection.pipeline()

    if colony.IsBanned():
        return Response(consts.ERROR_BANNED, status=consts.HTTP_FORBIDDEN)

    # Make sure add the owners to the correct sets.
    if colony.OwnerType == 'Steam':
        key = consts.KEY_USER_INDEX_BY_STEAM_ID
    elif colony.OwnerType == 'Normal':
        key = consts.KEY_USER_INDEX_BY_NORMAL_ID
    else:
        return Response(consts.ERROR_INVALID, status=consts.HTTP_INVALID)

    pipe.sadd(key, colony.OwnerID)

    colony.save_to_database(pipe)

    pipe.execute()

    colony.ping()
    print("Sending colony hash")
    return Response(json.dumps({'Hash': colony.Hash}), status=201 if created else 200, mimetype='application/json')

@colony_module.route('/<string:colony_hash>', methods=['GET'])
def colony_get_data(colony_hash):
    print("Getting colony data")
    colony = Colony.get_from_database_by_hash(colony_hash, db.get_redis_db_from_context())

    if not colony:
        return Response(consts.ERROR_NOT_FOUND, status=consts.HTTP_NOT_FOUND)
    print("Sending colony data")
    return Response(json.dumps(colony.to_dict()), status=200, mimetype='application/json')

@colony_module.route('/<string:colony_hash>/thing_metadata', methods=['PUT'])
def colony_set_supported_things(colony_hash: str):
    print("Received Supported things list from colony")

    colony = Colony.get_from_database_by_hash(colony_hash, db.get_redis_db_from_context())
    if not colony:
        return Response(consts.ERROR_NOT_FOUND, status=consts.HTTP_NOT_FOUND)

    try:
        gz_post_data = request.files['things']

        # Decompress payload
        thing_file = gzip.GzipFile(fileobj=gz_post_data, mode='r')

        # Deserialize JSON back in to {'Locale': str, 'Things': List[Dict[str, str]]}
        payload = json.loads(thing_file.read().decode('UTF8'))

        # Set locale
        locale = payload['Locale'].lower()

        # This is a List[Dict[str, str]]
        supported_things_json = payload['Things']

        print("Supported things parsed")

    except json.JSONDecodeError:
        print("Error in things list found")
        return Response(consts.ERROR_INVALID, status=consts.HTTP_INVALID)

    # Need to construct things then add Localized name to index
    pipe = db.get_redis_db_from_context().pipeline()

    pipe.sadd(consts.KEY_THING_LOCALE_KNOWN_LANGUAGES, locale)

    for thing_json in supported_things_json:
        thing = Thing.from_dict(thing_json)
        pipe.zincrby(KEY_THING_LOCALE_THING_NAMES.format(locale, thing.Hash), 1, thing.LocalizedName)
    pipe.execute()

    # This is immediately saved.
    colony.SupportedThings = supported_things_json
    print("Supported things saved")
    return Response('OK', status=200, mimetype='application/json')

@colony_module.route('/<string:colony_hash>/mod_metadata', methods=['PUT'])
def colony_set_mods(colony_hash: str):
    print("Received List of Mods from colony")
    colony = Colony.get_from_database_by_hash(colony_hash, db.get_redis_db_from_context())
    if not colony:
        return Response(consts.ERROR_NOT_FOUND, status=consts.HTTP_NOT_FOUND)

    try:
        gz_post_data = request.files['mods']
        mod_file = gzip.GzipFile(fileobj=gz_post_data, mode='r')
        payload = json.loads(mod_file.read().decode('UTF8'))
        mod_list = payload['ModList']

    except json.JSONDecodeError:
        print("Error in mod list found")
        return Response(consts.ERROR_INVALID, status=consts.HTTP_INVALID)

    # This is immediately saved.
    colony.ModList = mod_list
    print("List of Mods Saved")
    return Response('OK', status=200, mimetype='application/json')


# -*- coding: utf-8 -*-
from contextlib import contextmanager
from math import floor, log10
from timeit import default_timer as timer
from typing import Tuple

import torch
import torch.nn as nn
from torch.utils.data import DataLoader, SubsetRandomSampler, TensorDataset

# +
from torchvision.datasets import MNIST, CIFAR10
from torchvision.transforms import Compose, ConvertImageDtype, Normalize, ToTensor

from fastprogress.fastprogress import progress_bar

# -

def mnist_target_to_binary(target, B):
    """Convert target digits into a zeros (class A) and ones (class B)."""
    new_target = torch.zeros_like(target, dtype=torch.float)
    new_target[target == B] = 1.0
    return new_target.unsqueeze(-1)

def get_binary_mnist_dataloader(
    path: str, train: bool, A: int, B: int, bs: int
) -> Tuple[DataLoader, int]:
    """Helper method returning either a training or validation data loader."""

    # Convert images to normalized tensors with mean 0 and standard deviation 1
    image_xforms = Compose(
        [
            ConvertImageDtype(torch.float),  # ToTensor(),
            Normalize((0.1307,), (0.3081,)),
        ]
    )

    dataset = MNIST(root=path, train=train, download=True)  # , transform=image_xforms)

    # Grab indices for the two requested classes
    idx_classA = [i for i, t in enumerate(dataset.targets) if t == A]
    idx_classB = [i for i, t in enumerate(dataset.targets) if t == B]

    idxs = idx_classA + idx_classB
    size = len(idxs)

    if bs == 0:
        bs = size

    X = image_xforms(dataset.data[idxs])
    y = mnist_target_to_binary(dataset.targets[idxs], B)

    dataset = TensorDataset(X, y)

    dataset.classes = [A, B]
    dataset.data = X
    dataset.targets = y

    loader = DataLoader(dataset, batch_size=bs)

    return loader, size

def get_binary_mnist_dataloaders(
    path: str, A: int, B: int, bs_train: int = 0, bs_valid: int = 0
) -> Tuple[DataLoader, int, DataLoader, int]:
    """Return an MNIST dataloader for the two specified classes.

    Args:
        path (str): location in which to store downloaded data
        A (int): class A, a number in [0, 9] denoting a MNIST class/number
        B (int): class B, a number in [0, 9] denoting a MNIST class/number
        bs_train (int): batch size for training data loader
        bs_valid (int): batch size for validation data loader

    Returns:
        Tuple[DataLoader, int, DataLoader, int]: [description]
    """
    train_loader, train_size = get_binary_mnist_dataloader(path, True, A, B, bs_train)
    valid_loader, valid_size = get_binary_mnist_dataloader(path, False, A, B, bs_valid)
    return train_loader, train_size, valid_loader, valid_size

def get_binary_mnist_one_batch(path: str, A: int, B: int, flatten: bool):
    """Return entire MNIST training and validation partitions as single batches.

    Args:
        path (str): location in which to store downloaded data
        A (int): class A, a number in [0, 9] denoting a MNIST class/number
        B (int): class B, a number in [0, 9] denoting a MNIST class/number
    """
    train_dl, _, valid_dl, _ = get_binary_mnist_dataloaders(path, A, B)

    # Convenient method for turning a data loader into batch
    train_x, train_y = next(iter(train_dl))
    valid_x, valid_y = next(iter(valid_dl))

    if flatten:
        train_x = torch.flatten(train_x, start_dim=1)
        valid_x = torch.flatten(valid_x, start_dim=1)

    train_y = mnist_target_to_binary(train_y, B)
    valid_y = mnist_target_to_binary(valid_y, B)

    return train_x, train_y, valid_x, valid_y

def get_mnist_data_loaders(path, batch_size, valid_batch_size):

    # MNIST specific transforms
    image_xforms = Compose([ToTensor(), Normalize((0.1307,), (0.3081,))])

    # Training data loader
    train_dataset = MNIST(root=path, train=True, download=True, transform=image_xforms)

    tbs = len(train_dataset) if batch_size == 0 else batch_size
    train_loader = DataLoader(train_dataset, batch_size=tbs, shuffle=True)

    # Validation data loader
    valid_dataset = MNIST(root=path, train=False, download=True, transform=image_xforms)

    vbs = len(valid_dataset) if valid_batch_size == 0 else valid_batch_size
    valid_loader = DataLoader(valid_dataset, batch_size=vbs, shuffle=True)

    return train_loader, valid_loader

# +
def get_cifar10_data_loaders(path, batch_size, valid_batch_size):

#     std = (0.4941, 0.4870, 0.5232)
#     mean = (-0.0172, -0.0357, -0.1069)
    std = (0.2470, 0.2435, 0.2616)
    mean =(0.4914, 0.4822, 0.4465)
    image_xforms = Compose([ToTensor(), Normalize(mean, std)])

    train_dataset = CIFAR10(root=path, train=True, download=True, transform=image_xforms)

    tbs = len(train_dataset) if batch_size == 0 else batch_size
    train_loader = DataLoader(train_dataset, batch_size=tbs, shuffle=True)

    valid_dataset = CIFAR10(root=path, train=False, download=True, transform=image_xforms)

    vbs = len(valid_dataset) if valid_batch_size == 0 else valid_batch_size
    valid_loader = DataLoader(valid_dataset, batch_size=vbs, shuffle=True)

    return train_loader, valid_loader

# -

class NN_FC_CrossEntropy(nn.Module):
    def __init__(self, layer_sizes, activation=nn.ReLU):
        super(NN_FC_CrossEntropy, self).__init__()

        first_layer = nn.Flatten()
        middle_layers = [
            nn.Sequential(nn.Linear(nlminus1, nl), activation())
            for nl, nlminus1 in zip(layer_sizes[1:-1], layer_sizes)
        ]
        last_layer = nn.Linear(layer_sizes[-2], layer_sizes[-1])

        all_layers = [first_layer] + middle_layers + [last_layer]

        self.layers = nn.Sequential(*all_layers)

    def forward(self, X):
        return self.layers(X)

def compute_validation_accuracy_multi(dataloader, model, criterion, device, mb, epoch):

    model.eval()

    N = len(dataloader.dataset)
    num_batches = len(dataloader)

    valid_loss, num_correct = 0, 0

    with torch.no_grad():

        for X, Y in dataloader:

            X, Y = X.to(device), Y.to(device)
            output = model(X)

            valid_loss += criterion(output, Y).item()
            num_correct += (output.argmax(1) == Y).type(torch.float).sum().item()

        valid_loss /= num_batches
        valid_accuracy = num_correct / N

    mb.write(
        f"{epoch:>3}: validation accuracy={(100*valid_accuracy):5.2f}% and loss={valid_loss:.3f}"
    )
    return valid_loss, valid_accuracy

def train_one_epoch(dataloader, model, criterion, optimizer, device, mb):

    model.train()

    num_batches = len(dataloader)
    dataiter = iter(dataloader)

    for batch in progress_bar(range(num_batches), parent=mb):

        X, Y = next(dataiter)
        X, Y = X.to(device), Y.to(device)

        output = model(X)

        loss = criterion(output, Y)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

def format_duration_with_prefix(duration, sig=2):
    # Round to significant digits
    duration = round(duration, -int(floor(log10(abs(duration)))) + (sig - 1))

    if duration < 1e-6:
        return f"{duration*1e9:.1f} ns"
    elif duration < 1e-3:
        return f"{duration*1e6:.1f} μs"
    elif duration < 1:
        return f"{duration*1e3:.1f} ms"
    else:
        return f"{duration:.1f} s"

@contextmanager
def stopwatch(label: str):
    start = timer()
    try:
        yield
    finally:
        print(f"{label}: {timer() - start:6.2f}s")

class DataLoaderProgress(object):
    def __init__(self, dataloader):
        self.dataloader = dataloader
        self.length = len(dataloader)

    def __iter__(self):
        return zip(range(self.length), self.dataloader)

    def __len__(self):
        return self.length


import gzip
import math
import numpy as np
import os
import random
import sys
import torch
import torch.nn as nn
import torch.utils.data as data
import torch.utils.model_zoo as model_zoo
import torchvision.transforms as transforms

if sys.version_info[0] == 2:
    from urllib import urlretrieve
else:
    from urllib.request import urlretrieve

import progressbar

from collections import OrderedDict

pbar = None

def mmnist_data_loader(collate_fn=None, n_frames=10, num_digits=1, with_target=False,
                       batch_size=100, n_workers=8, is_train=True, drop_last=True,
                       dset_path=os.path.dirname(os.path.realpath(__file__))):
    dset = MovingMNIST(dset_path, is_train, n_frames, num_digits, with_target=with_target)
    dloader = data.DataLoader(dset, batch_size=batch_size, shuffle=is_train, collate_fn=collate_fn,
                              num_workers=n_workers, drop_last=drop_last, pin_memory=True)
    # Returns images of size [1, 64, 64] in [-1, 1]
    return dloader

class ToTensor(object):
    """Converts a numpy.ndarray (... x H x W x C) in the range
    [0, 255] to a torch.FloatTensor of shape (... x C x H x W) in the range [0.0, 1.0].
    """
    def __init__(self, scale=True):
        self.scale = scale
    def __call__(self, arr):
        if isinstance(arr, np.ndarray):
            video = torch.from_numpy(np.rollaxis(arr, axis=-1, start=-3))
            if self.scale:
                return video.float().div(255)
            else:
                return video.float()
        else:
            raise NotImplementedError

# def load_mnist(root):
#     # Load MNIST dataset for generating training data.
#     path = os.path.join(root, 'train-images-idx3-ubyte.gz')
#     with gzip.open(path, 'rb') as f:
#         mnist = np.frombuffer(f.read(), np.uint8, offset=16)
#         mnist = mnist.reshape(-1, 28, 28)
#     return mnist

# def load_fixed_set(root, is_train):
#     # Load the fixed dataset
#     filename = 'mnist_test_seq.npy'
#     path = os.path.join(root, filename)
#     dataset = np.load(path)
#     dataset = dataset[..., np.newaxis]
#     return dataset

# loads mnist from web on demand
def load_mnist(root, is_train=True):

    def load_mnist_images(filename):
        if not os.path.exists(os.path.join(root, filename)):
            download(root, filename)
        with gzip.open(os.path.join(root, filename), 'rb') as f:
            data = np.frombuffer(f.read(), np.uint8, offset=16)
        data = data.reshape(-1, 28, 28)
        return data

    if is_train:
        return load_mnist_images('http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz')
    return load_mnist_images('http://www.cs.toronto.edu/~nitish/unsupervised_video/mnist_test_seq.npy')

def download(root, filename):
    def show_progress(block_num, block_size, total_size):
        global pbar
        if pbar is None:
            pbar = progressbar.ProgressBar(maxval=total_size)
            pbar.start()

        downloaded = block_num * block_size
        if downloaded < total_size:
            pbar.update(downloaded)
        else:
            pbar.finish()
            pbar = None
    print("Downloading %s" % os.path.basename(filename))
    os.makedirs(root, exist_ok=True)
    urlretrieve(filename, os.path.join(root, os.path.basename(filename)), show_progress)

def load_mnist(root):
    # Load MNIST dataset for generating training data.
    path = os.path.join(root, 'train-images-idx3-ubyte.gz')
    if not os.path.exists(path):
        download(root, 'http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz')
    with gzip.open(path, 'rb') as f:
        mnist = np.frombuffer(f.read(), np.uint8, offset=16)
        mnist = mnist.reshape(-1, 28, 28)
    return mnist

def load_fixed_set(root):
    # Load the fixed dataset
    path = os.path.join(root, 'mnist_test_seq.npy')
    if not os.path.exists(path):
        download(root, 'http://www.cs.toronto.edu/~nitish/unsupervised_video/mnist_test_seq.npy')
    dataset = np.load(path)
    dataset = dataset[..., np.newaxis]
    return dataset

class MovingMNIST(data.Dataset):
    def __init__(self, root, is_train, n_frames, num_digits, transform=transforms.Compose([ToTensor()]), step_length=0.1, with_target=False):
        super(MovingMNIST, self).__init__()

        self.dataset = None
        if is_train:
            self.mnist = load_mnist(root)
        else:
            if num_digits != 2:
                self.mnist = load_mnist(root)
            else:
                self.dataset = load_fixed_set(root)
        self.length = int(1e4) if self.dataset is None else self.dataset.shape[1]

        self.is_train = is_train
        self.num_digits = num_digits
        self.n_frames = n_frames
        self.transform = transform
        self.with_target = with_target

        # For generating data
        self.image_size_ = 64
        self.digit_size_ = 28
        self.step_length_ = step_length

    def get_random_trajectory(self, seq_length):
        ''' Generate a random sequence of a MNIST digit '''
        canvas_size = self.image_size_ - self.digit_size_
        x = random.random()
        y = random.random()
        theta = random.random() * 2 * np.pi
        v_y = np.sin(theta)
        v_x = np.cos(theta)

        start_y = np.zeros(seq_length)
        start_x = np.zeros(seq_length)
        for i in range(seq_length):
            # Take a step along velocity.
            y += v_y * self.step_length_
            x += v_x * self.step_length_

            # Bounce off edges.
            if x <= 0:
                x = 0
                v_x = -v_x
            if x >= 1.0:
                x = 1.0
                v_x = -v_x
            if y <= 0:
                y = 0
                v_y = -v_y
            if y >= 1.0:
                y = 1.0
                v_y = -v_y
            start_y[i] = y
            start_x[i] = x

        # Scale to the size of the canvas.
        start_y = (canvas_size * start_y).astype(np.int32)
        start_x = (canvas_size * start_x).astype(np.int32)
        return start_y, start_x

    def generate_moving_mnist(self, num_digits=2):
        '''
        Get random trajectories for the digits and generate a video.
        '''
        data = np.zeros((self.n_frames, self.image_size_, self.image_size_), dtype=np.float32)
        for n in range(num_digits):
            # Trajectory
            start_y, start_x = self.get_random_trajectory(self.n_frames)
            ind = random.randint(0, self.mnist.shape[0] - 1)
            digit_image = self.mnist[ind]
            for i in range(self.n_frames):
                top    = start_y[i]
                left   = start_x[i]
                bottom = top + self.digit_size_
                right  = left + self.digit_size_
                # Draw digit
                data[i, top:bottom, left:right] = np.maximum(data[i, top:bottom, left:right], digit_image)

        data = data[..., np.newaxis]

        return data

    def __getitem__(self, idx):
        if self.is_train or self.num_digits != 2:
            # Generate data on the fly
            images = self.generate_moving_mnist(self.num_digits)
        else:
            images = self.dataset[:, idx, ...]

        if self.with_target:
            targets = np.array(images > 127, dtype=float) * 255.0

        if self.transform is not None:
            images = self.transform(images)
            if self.with_target:
                targets = self.transform(targets)

        if self.with_target:
            return images, targets
        else:
            return images

    def __len__(self):
        return self.length


#!/usr/bin/env python3
import numpy as np

"""This module contains MISMIP constants and parameters, as well as functions
computing theoretical steady state profiles corresponding to various MISMIP
experiments.

It should not be cluttered with plotting or NetCDF output code.
"""

def secpera():
    "Number of seconds per year."
    return 3.15569259747e7

def L():
    "The length of the MISMIP domain."
    return 1800e3

def N(mode):
    "Number of grid spaces corresponding to a MISMIP 'mode.'"
    if mode == 1:
        return 150

    if mode == 2:
        return 1500

    raise ValueError("invalid mode (%s)" % mode)

def A(experiment, step):
    """Ice softness parameter for given experiment and step."""
    A1 = np.array([4.6416e-24,  2.1544e-24,  1.0e-24,
                   4.6416e-25,  2.1544e-25,  1.0e-25,
                   4.6416e-26,  2.1544e-26,  1.0e-26])
    # Values of A to be used in experiments 1 and 2.

    A3a = np.array([3.0e-25, 2.5e-25, 2.0e-25,
                    1.5e-25, 1.0e-25, 5.0e-26,
                    2.5e-26, 5.0e-26, 1.0e-25,
                    1.5e-25, 2.0e-25, 2.5e-25,
                    3.0e-25])
    # Values of A to be used in experiment 3a.

    A3b = np.array([1.6e-24, 1.4e-24, 1.2e-24,
                    1.0e-24, 8.0e-25, 6.0e-25,
                    4.0e-25, 2.0e-25, 4.0e-25,
                    6.0e-25, 8.0e-25, 1.0e-24,
                    1.2e-24, 1.4e-24, 1.6e-24])
    # Values of A to be used in experiment 3b.

    try:
        if experiment in ("1a", "1b", "2a", "2b"):
            return A1[step - 1]

        if experiment == "3a":
            return A3a[step - 1]

        if experiment == "3b":
            return A3b[step - 1]
    except:
        raise ValueError("invalid step (%s) for experiment %s" % (step, experiment))

    raise ValueError("invalid experiment (%s)" % experiment)

def run_length(experiment, step):
    """Returns the time interval for an experiment 3 step."""
    T3a = np.array([3.0e4, 1.5e4, 1.5e4,
                    1.5e4, 1.5e4, 3.0e4,
                    3.0e4, 1.5e4, 1.5e4,
                    3.0e4, 3.0e4, 3.0e4,
                    1.5e4])
    # Time intervals to be used in experiment 3a.

    T3b = np.array([3.0e4, 1.5e4, 1.5e4,
                    1.5e4, 1.5e4, 1.5e4,
                    1.5e4, 3.0e4, 1.5e4,
                    1.5e4, 1.5e4, 1.5e4,
                    1.5e4, 3.0e4, 1.5e4])
    # Time intervals to be used in experiment 3b.

    try:
        if experiment == "3a":
            return T3a[step - 1]

        if experiment == "3b":
            return T3b[step - 1]
    except:
        raise ValueError("invalid step (%s) for experiment %s" % (step, experiment))

    return 3e4

def rho_i():
    "Ice density"
    return 900.0

def rho_w():
    "Water density"
    return 1000.0

def g():
    """Acceleration due to gravity. (Table 2 on page 19 of mismip_4.pdf
    uses this value, i.e. g = 9.8 m s-2.)"""
    return 9.8

def n():
    "Glen exponent"
    return 3.0

def a():
    "Accumulation rate (m/s)"
    return 0.3 / secpera()

def m(experiment):
    "Sliding law exponent"
    if experiment in ("1a", "2a", "3a"):
        return 1 / 3.0

    if experiment in ("1b", "2b", "3b"):
        return 1.0

    raise ValueError("invalid experiment (%s)" % experiment)

def C(experiment):
    "Sliding law coefficient"
    if experiment in ("1a", "2a", "3a"):
        return 7.624e6

    if experiment in ("1b", "2b", "3b"):
        return 7.2082e10

    raise ValueError("invalid experiment (%s)" % experiment)

def b(experiment, x):
    "Bed depth below sea level. (-b(x) = topg(x))"

    if experiment in ("1a", "1b", "2a", "2b"):
        return -720. + 778.5 * (x / 7.5e5)

    if experiment in ("3a", "3b"):
        xx = x / 7.5e5
        return -(729. - 2184.8 * xx ** 2. + 1031.72 * xx ** 4. - 151.72 * xx ** 6.)

    raise ValueError("invalid experiment (%s)" % experiment)

def b_slope(experiment, x):
    """The x-derivative of b(experiment, x)."""

    if experiment in ("1a", "1b", "2a", "2b"):
        return 778.5 / 7.5e5

    if experiment in ("3a", "3b"):
        xx = x / 7.5e5
        return -(- 2184.8 * (2. / 7.5e5) * xx
                 + 1031.72 * (4. / 7.5e5) * xx ** 3.
                 - 151.72 * (6. / 7.5e5) * xx ** 5.)

    raise ValueError("invalid experiment (%s)" % experiment)

def cold_function(experiment, step, x, theta=0.0):
    """Evaluates function whose zeros define x_g in 'cold' steady marine sheet problem."""
    r = rho_i() / rho_w()
    h_f = r ** (-1.) * b(experiment, x)
    b_x = b_slope(experiment, x)
    s = a() * x
    rho_g = rho_i() * g()
    return (theta * a()
            + C(experiment) * s ** (m(experiment) + 1.0) / (rho_g * h_f ** (m(experiment) + 2.))
            - theta * s * b_x / h_f
            - A(experiment, step) * (rho_g * (1.0 - r) / 4.0) ** n() * h_f ** (n() + 1.0))

def x_g(experiment, step, theta=0.0):
    """Computes the theoretical grounding line location using Newton's method."""

    # set the initial guess
    if experiment in ("3a", "3b"):
        x = 800.0e3
    else:
        x = 1270.0e3

    delta_x = 10.  # Finite difference step size (metres) for gradient calculation
    tolf = 1.e-4  # Tolerance for finding zeros
    eps = np.finfo(float).eps
    normf = tolf + eps
    toldelta = 1.e1                     # Newton step size tolerance
    dx = toldelta + 1.0

    # this is just a shortcut
    def F(x):
        return cold_function(experiment, step, x, theta)

    while (normf > tolf) or (abs(dx) > toldelta):
        f = F(x)
        normf = abs(f)
        grad = (F(x + delta_x) - f) / delta_x
        dx = -f / grad
        x = x + dx

    return x

def thickness(experiment, step, x, theta=0.0):
    """Compute ice thickness for x > 0.
    """
    # compute the grounding line position
    xg = x_g(experiment, step, theta)

    def surface(h, x):
        b_x = b_slope(experiment, x)
        rho_g = rho_i() * g()
        s = a() * np.abs(x)
        return b_x - (C(experiment) / rho_g) * s ** m(experiment) / h ** (m(experiment) + 1)

    # extract the grounded part of the grid
    x_grounded = x[x < xg]

    # We will integrate from the grounding line inland. odeint requires that
    # the first point in x_grid be the one corresponding to the initial
    # condition; append it and reverse the order.
    x_grid = np.append(xg, x_grounded[::-1])

    # use thickness at the grounding line as the initial condition
    h_f = b(experiment, xg) * rho_w() / rho_i()

    import scipy.integrate
    thk_grounded = scipy.integrate.odeint(surface, [h_f], x_grid, atol=1.e-9, rtol=1.e-9)

    # now 'result' contains thickness in reverse order, including the grounding
    # line point (which is not on the grid); discard it and reverse the order.
    thk_grounded = np.squeeze(thk_grounded)[:0:-1]

    # extract the floating part of the grid
    x_floating = x[x >= xg]

    # compute the flux through the grounding line
    q_0 = a() * xg

    # Calculate ice thickness for shelf from van der Veen (1986)
    r = rho_i() / rho_w()
    rho_g = rho_i() * g()
    numer = h_f * (q_0 + a() * (x_floating - xg))
    base = q_0 ** (n() + 1) + h_f ** (n() + 1) * ((1 - r) * rho_g / 4) ** n() * A(experiment, step) \
        * ((q_0 + a() * (x_floating - xg)) ** (n() + 1) - q_0 ** (n() + 1)) / a()
    thk_floating = numer / (base ** (1.0 / (n() + 1)))

    return np.r_[thk_grounded, thk_floating]

def plot_profile(experiment, step, out_file):
    from pylab import figure, subplot, hold, plot, xlabel, ylabel, text, title, axis, vlines, savefig

    if out_file is None:
        out_file = "MISMIP_%s_A%d.pdf" % (experiment, step)

    xg = x_g(experiment, step)

    x = np.linspace(0, L(), N(2) + 1)
    thk = thickness(experiment, step, x)
    x_grounded, thk_grounded = x[x < xg],  thk[x < xg]
    x_floating, thk_floating = x[x >= xg], thk[x >= xg]

    figure(1)
    ax = subplot(111)
    hold(True)
    plot(x / 1e3, np.zeros_like(x), ls='dotted', color='red')
    plot(x / 1e3, -b(experiment, x), color='black')
    plot(x / 1e3, np.r_[thk_grounded - b(experiment, x_grounded),
                        thk_floating * (1 - rho_i() / rho_w())],
         color='blue')
    plot(x_floating / 1e3, -thk_floating * (rho_i() / rho_w()), color='blue')
    _, _, ymin, ymax = axis(xmin=0, xmax=x.max() / 1e3)
    vlines(xg / 1e3, ymin, ymax, linestyles='dashed', color='black')

    xlabel('distance from the summit, km')
    ylabel('elevation, m')
    text(0.6, 0.9, "$x_g$ (theory) = %4.0f km" % (xg / 1e3),
         color='black', transform=ax.transAxes)
    title("MISMIP experiment %s, step %d" % (experiment, step))
    savefig(out_file)

if __name__ == "__main__":
    from optparse import OptionParser

    parser = OptionParser()

    parser.usage = "%prog [options]"
    parser.description = "Plots the theoretical geometry profile corresponding to MISMIP experiment and step."
    parser.add_option("-e", "--experiment", dest="experiment", type="string",
                      default='1a',
                      help="MISMIP experiments (one of '1a', '1b', '2a', '2b', '3a', '3b')")
    parser.add_option("-s", "--step", dest="step", type="int", default=1,
                      help="MISMIP step number")
    parser.add_option("-o", dest="out_file", help="output file name")

    (opts, args) = parser.parse_args()

    plot_profile(opts.experiment, opts.step, opts.out_file)


from products.models import Products,Category
from .models import Customers
from django.views.generic import CreateView, DeleteView,UpdateView,ListView,FormView,View,DetailView
from .forms import *
from django.contrib.auth import get_user_model
from django.db.models import Q
from cart.cart import Cart
from cart.models import Item
from django.http import HttpResponse,HttpResponseRedirect,JsonResponse
from django.urls import reverse,reverse_lazy
from django.shortcuts import render_to_response,redirect
from django.db.models import F
from django.contrib import messages
from django.db.models import Sum,Count
from django.views.generic import View
import datetime
from django.core.files.storage import FileSystemStorage
from django.template.loader import render_to_string

def add_to_cart(request):
    quantity = int(request.POST.getlist('quantity')[0])
    if(quantity !=0):
        product_id = request.POST['product_id']
        product = Products.objects.get(id=product_id)
        cart = Cart(request)
        cart.add(product, product.price, quantity)
        return HttpResponseRedirect(reverse('order_list_products'))
    else:
        messages.error(request,"Order fail Qty can't null")
        return HttpResponseRedirect(reverse('order_list_products'))

def remove_from_cart(request, product_id):
    product = Products.objects.get(id=product_id)
    cart = Cart(request)
    cart.remove(product)
    return HttpResponseRedirect(reverse('order_list_products'))

def generateInvoice():
    order = Orders.objects.only('created_at').order_by('-created_at')
    if order.count() > 0:
        order   = order.first()
        explode = order.invoice.split('-')
        count   = str(int(explode[1]) + 1)
        return 'INV-'+count
    return 'INV-1'
def get_cart(request):
    return render(request, 'orders/products/cart.html', {'cart': Cart(request)})

class ProductsViews(FormView):
    form_class = OrdersForm
    success_url = 'order_list_products'
    def get(self, request, *args, **kwargs):
        """Handle GET requests: instantiate a blank version of the form."""
        if request.method == 'GET' and 'search' in request.GET:
            if request.GET['search'] != '':
                kwargs['products']          = Products.objects.filter(Q(name=request.GET['search']) | Q(code=request.GET['search']))
            else:
                kwargs['products']          = Products.objects.all()
        elif request.method == 'GET' and 'categories' in request.GET:
            if request.GET['categories'] != '':
                kwargs['products']          = Products.objects.filter(Q(categories=request.GET['categories']))
            else:
                kwargs['products']          = Products.objects.all()
        else:
                kwargs['products']          = Products.objects.all()
        kwargs['cart']                      = Cart(request)
        return self.render_to_response(self.get_context_data(**kwargs))
    def get_context_data(self, **kwargs):
        kwargs['heading_title']     = "Order Transaction"
        kwargs['list_categories']   = Category.objects.all()
        kwargs['breadcrumb']        = {'content':'Order Product','class':'active'}
        # Call the base implementation first to get a context
        context = super().get_context_data(**kwargs)
        # Add in a QuerySet of all the books
        return context

    def get_success_url(self):
        """Return the URL to redirect to after processing a valid form."""
        if not self.success_url:
            raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
        return reverse_lazy(self.success_url)  # success_url may be lazy

class Checkout(CreateView):
    model = Orders
    form_class = OrdersForm
    success_url = 'ordes_list'
    def get(self, request, *args, **kwargs):
        """Handle GET requests: instantiate a blank version of the form."""
        self.object = None
        User = get_user_model()
        kwargs['cart']                   = Cart(request)
        kwargs['customers']              = Customers.objects.all()
        kwargs['barista']                = User.objects.all().filter(groups__name='Barista')
        return self.render_to_response(self.get_context_data(**kwargs))
    def get_context_data(self, **kwargs):
        kwargs['heading_title']     = "Order Transaction"
        kwargs['card_title']        = "Customers data"
        kwargs['breadcrumb']        = {'content':'Order Product','url':'order_list_products'}
        kwargs['breadcrumb2']       = {'content':'Checkout','class':'active'}
        # Call the base implementation first to get a context
        context = super().get_context_data(**kwargs)
        # Add in a QuerySet of all the books
        return context
    def post(self, request, *args, **kwargs):
        """
        Handle POST requests: instantiate a form instance with the passed
        POST variables and then check if it's valid.
        """
        self.object = None # assign the object to the view
        User = get_user_model()
        method = self.request.POST['customer']
        cart   = Cart(request)
        if(method):
            try:
                qty = []
                product = []
                orders = True
                for x in Item.objects.all():
                    product.append(x.object_id)
                    qty.append(x.quantity)
                queryset = Products.objects.filter(id__in=product).values('stock')
                for i in range(len(queryset)):
                    if queryset[i]['stock'] < qty[i]:
                        orders = False
                if not Item.objects.all():
                    orders = False
                if int(request.POST['payment']) < int(cart.summary()):
                    orders = False
                if request.POST['payment'] == "":
                    orders = False

                name = None
                if(request.POST['name'] == "others"):
                    name = request.POST['name_input']
                else:
                    name = request.POST['name']

                if name == "":
                    orders = False
                if request.POST['barista'] == "":
                    orders = False

                if orders:
                    barista  = User.objects.get(id=request.POST['barista']).id
                    customer = Customers.objects.create(
                        name=name,
                        email=request.POST['email'],
                        address=request.POST['address'],
                        phone=request.POST['phone'],
                        payment=request.POST['payment'],
                        barista_id=barista,
                    )

                    order = Orders(
                        invoice=generateInvoice(),
                        total=cart.summary(),
                        user_id=request.user.id,
                        customer_id=customer.id)
                    order.save()
                    if(order):
                        for x in Item.objects.all():
                            orderdetail = OrderDetails(
                                order_id = order.id,
                                product_id = x.object_id,
                                qty = x.quantity,
                                price = x.unit_price
                            )
                            orderdetail.save()
                            if(orderdetail):
                                product = Products.objects.get(id=x.object_id)
                                product.stock = F('stock') - x.quantity
                                product.save()
                                cart.clear()
                    return HttpResponseRedirect(self.get_success_url())
                else:
                    messages.error(request,'Order fail out of stock or form important null or payment minus')
                    return redirect(reverse('check_out_product'))
            except Exception as e:
                messages.error(request,'Order fail out of stock or form important null or cart null or payment minus')
                return redirect(reverse('check_out_product'))
    def get_success_url(self):
        """Return the URL to redirect to after processing a valid form."""
        if not self.success_url:
            raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
        return reverse_lazy(self.success_url)  # success_url may be lazy

class OrdersView(FormView):
    form_class = OrdersForm
    success_url = 'order_list'
    def get(self, request, *args, **kwargs):
        """Handle GET requests: instantiate a blank version of the form."""
        total_order_details  = OrderDetails.objects.aggregate(total_item=Sum('qty'))
        total_order          = Orders.objects.aggregate(total_omset=Sum('total'))
        total_customers      = Customers.objects.all().count()
        kwargs['total_item']      = total_order_details['total_item']
        kwargs['total_omset']     = total_order['total_omset']
        kwargs['total_customers'] = total_customers
        kwargs['orders']          = Orders.objects.all().order_by('-created_at')
        return self.render_to_response(self.get_context_data(**kwargs))

    def get_context_data(self, **kwargs):
        kwargs['heading_title']     = "Order List"
        kwargs['breadcrumb']        = {'content':'Order List','class':'active'}
        # Call the base implementation first to get a context
        context = super().get_context_data(**kwargs)
        # Add in a QuerySet of all the books
        return context

class GenerateInvoicePrint(DetailView):
    def get(self, request, *args, **kwargs):
        """Handle GET requests: instantiate a blank version of the form."""
        self.object = None
        kwargs['totalPrice']           = OrderDetails.objects.filter(order_id=self.kwargs['pk']).aggregate(totalPrice=Sum('price'))
        kwargs['totalQty']             = OrderDetails.objects.filter(order_id=self.kwargs['pk']).aggregate(totalQty=Sum('qty'))
        kwargs['total']                = OrderDetails.objects.filter(order_id=self.kwargs['pk']).aggregate(total=Sum(F('qty') * F('price')))
        kwargs['order']                = OrderDetails.objects.all().filter(order_id=self.kwargs['pk'])
        kwargs['orders']               = Orders.objects.all().get(pk=self.kwargs['pk'])
        kwargs['date_order']           = Orders.objects.get(pk=self.kwargs['pk']).created_at
        return self.render_to_response(self.get_context_data(**kwargs))

    def get_context_data(self, **kwargs):
        # Call the base implementation first to get a context
        context = super().get_context_data(**kwargs)
        # Add in a QuerySet of all the books
        return context

#def GenerateInvoicePDF(request,pk):

#     orders = Orders.objects.filter(id=pk)
#     html_string = render_to_string('invoice/invoice_pdf.html', {'orders': orders})

    # html = HTML(string=html_string)
    # html.write_pdf(target='/tmp/mypdf.pdf');

    # fs = FileSystemStorage('/tmp')
    # with fs.open('mypdf.pdf') as pdf:
    #     response = HttpResponse(pdf, content_type='application/pdf')
    #     response['Content-Disposition'] = 'attachment; filename="mypdf.pdf"'
    #     return response

    # return response