"""
    Multiple Kilobots move directly to the light.
    They learn to push an object in a single direction.
    The light is moved based on a policy provided by this learner.
"""

from numpy import *

import matplotlib.pyplot as plt
from matplotlib.pyplot import cm
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.patches import Rectangle

import os
import gc
import pprint
import time
import sys

from zmq import Context, PAIR
import pickle

from Helper import Helper
from LeastSquaresTD import LeastSquaresTD
from AC_REPS import AC_REPS
from Kernel import ExponentialQuadraticKernel, KilobotKernel
from SparseGPPolicy import SparseGPPolicy

class MazeLearner:
    def __init__(self, port):
        self.context = Context()
        self.socket = self.context.socket(PAIR)
        self.port = port

        # s: light.x light.y kb.x1 kb.y1 ... kb.xn kb.yn
        # a: light movement (dx, dy)
        self.NUM_NON_KB_DIM = 2

        # initial random range for actions
        self.aRange = array([[-0.015, 0.015], [-0.015, 0.015]])
        self.policy = SparseGPPolicy(KilobotKernel(self.NUM_NON_KB_DIM),
                self.aRange)

        # kernels used for LSTD
        self.kernelS = KilobotKernel(self.NUM_NON_KB_DIM)
        self.kernelSA = KilobotKernel(self.NUM_NON_KB_DIM + 2)

        self.lstd = LeastSquaresTD()
        self.reps = AC_REPS()

        self.it = 0
        self.S = None

    def _sendPolicyModules(self):
        msg = {'message': 'sentPolicyModules',
               'modules': [
                   ('Helper.py', open('Helper.py').read()),
                   ('Kernel.py', open('Kernel.py').read()),
                   ('SparseGPPolicy.py', open('SparseGPPolicy.py').read())
                   ],
               'policyModule': 'SparseGPPolicy'}
        self.socket.send(pickle.dumps(msg, protocol=2))

    def _getSamples(self):
        msg = {'message': 'getSamples',
               'policyDict': self.policy.getSerializableDict(),
               'objectShape': self.objectShape,
               'numKilobots': self.numKilobots,
               'numEpisodes': self.numEpisodes,
               'numStepsPerEpisode': self.numStepsPerEpisode,
               'stepsPerSec': self.stepsPerSec,
               'epsilon': self.epsilon,
               'useMean': False}
        self.socket.send(pickle.dumps(msg, protocol=2))

        msg = pickle.loads(self.socket.recv(), encoding='latin1')
        if not msg['message'] == 'sentSamples':
            print('received unexpected message')
        else:
            return msg['samples']

    def testPolicy(self, objectShape = 'quad', numKilobots = 4,
            numEpisodes = 100, numStepsPerEpisode = 50, stepsPerSec = 8):
        msg = {'message': 'getSamples',
               'policyDict': self.policy.getSerializableDict(),
               'objectShape': objectShape,
               'numKilobots': numKilobots,
               'numEpisodes': numEpisodes,
               'numStepsPerEpisode': numStepsPerEpisode,
               'stepsPerSec': stepsPerSec,
               'epsilon': 0.0,
               'useMean': True}
        self.socket.send(pickle.dumps(msg, protocol=2))

        msg = pickle.loads(self.socket.recv(), encoding='latin1')
        _, _, R, _ = msg['samples']
        return R.sum()

    def _getStateActionMatrix(self, S, A):
        # states without kilobot positions + actions + kilobot positions
        return c_[S[:, 0:self.NUM_NON_KB_DIM], A, S[:, self.NUM_NON_KB_DIM:]]

    def _unpackSARS(self, SARS):
        return SARS[:, 0:self.sDim],\
               SARS[:, self.sDim:(self.sDim + 2)],\
               SARS[:, (self.sDim + 2):(self.sDim + 3)],\
               SARS[:, (self.sDim + 3):(2 * self.sDim + 3)]

    def _reshapeKbPositions(self, X):
        return c_[X.flat[0::2].T, X.flat[1::2].T]

    def _updateKernelParameters(self, S, A, random=True, normalize=True):
        SA = self._getStateActionMatrix(S, A)

        if random:
            self.MuS = Helper.getRandomSubset(S, self.numFeatures)
            self.MuSA = Helper.getRandomSubset(SA, self.numFeatures)
        else:
            self.MuS = Helper.getRepresentativeRows(S, self.numFeatures, normalize)
            self.MuSA = Helper.getRepresentativeRows(SA, self.numFeatures, normalize)

        NUM_SAMPLES_FOR_BW_ESTIMATE = 500

        # bandwidth for PHI_S
        bwNonKbS = Helper.getBandwidth(self.MuS[:, 0:self.NUM_NON_KB_DIM],
                NUM_SAMPLES_FOR_BW_ESTIMATE, self.bwFactorNonKbS)

        kbPosS = self._reshapeKbPositions(self.MuS[:, self.NUM_NON_KB_DIM:])
        bwKbS = Helper.getBandwidth(kbPosS, NUM_SAMPLES_FOR_BW_ESTIMATE,
                self.bwFactorKbS)

        self.kernelS.setBandwidth(bwNonKbS, bwKbS)
        self.kernelS.setWeighting(self.weightNonKbS)

        # bandwidth for PHI_SA
        bwNonKbSA = Helper.getBandwidth(self.MuSA[:, 0:(self.NUM_NON_KB_DIM + 2)],
                NUM_SAMPLES_FOR_BW_ESTIMATE, self.bwFactorNonKbSA)

        kbPosSA = self._reshapeKbPositions(self.MuSA[:, (self.NUM_NON_KB_DIM + 2):])
        bwKbSA = Helper.getBandwidth(kbPosSA, NUM_SAMPLES_FOR_BW_ESTIMATE,
                self.bwFactorKbSA)

        self.kernelSA.setBandwidth(bwNonKbSA, bwKbSA)
        self.kernelSA.setWeighting(self.weightNonKbSA)

    def _getSubsetForGP(self, S, random=True, normalize=True):
        Nsubset = min(self.numSamplesSubsetGP, S.shape[0])

        if random:
            return Helper.getRandomSubset(S, Nsubset)
        else:
            return Helper.getRepresentativeRows(S, Nsubset, normalize)

    def _updateBandwidthsGP(self, Ssub):
        bwNonKb = Helper.getBandwidth(Ssub[:, 0:self.NUM_NON_KB_DIM],
                Ssub.shape[0], self.bwFactorNonKbGP)

        kbPos = Ssub[:, self.NUM_NON_KB_DIM:]
        bwKb = Helper.getBandwidth(self._reshapeKbPositions(kbPos),
                Ssub.shape[0], self.bwFactorKbGP)

        self.policy.kernel.setBandwidth(bwNonKb, bwKb)
        self.policy.kernel.setWeighting(self.weightNonKbGP)

    def _getFeatureExpectation(self, S, N, MuSA):
        SA = self._getStateActionMatrix(S, self.policy.sampleActions(S))

        PHI = self.kernelSA.getGramMatrix(SA, MuSA)

        for i in range(N - 1):
            SA = self._getStateActionMatrix(S, self.policy.sampleActions(S))
            PHI += self.kernelSA.getGramMatrix(SA, MuSA)

        return PHI / N

    def connect(self):
        self.socket.bind('tcp://*:{}s'.format(self.port))

        self._sendPolicyModules()

    def savePolicyAndSamples(self, fileName):
        d = self.policy.getSerializableDict()
        d['SARS'] = c_[self.S, self.A, self.R, self.S_]
        d['numIt'] = self.it
        d['sDim'] = self.sDim

        s = pickle.dumps(d)
        with open(fileName, 'wb') as f:
            f.write(s)

    def loadPolicyAndSamples(self, fileName):
        with open(fileName, 'rb') as f:
            s = f.read()
        d = pickle.loads(s)

        self.policy = SparseGPPolicy.fromSerializableDict(d)

        self.it = d['numIt']
        self.sDim = d['sDim']

        SARS = d['SARS']
        self.S, self.A, self.R, self.S_ = self._unpackSARS(SARS)

    def saveParams(self, fileName):
        params = {
            'general': {
                'numLearnIt': self.numLearnIt,
                'startEpsilon': self.startEpsilon,
                'epsilonFactor': self.epsilonFactor},
            'sampling': {
                'objectShape': self.objectShape,
                'numKilobots': self.numKilobots,
                'numEpisodes': self.numEpisodes,
                'numStepsPerEpisode': self.numStepsPerEpisode},
            'LSTD': {
                'discountFactor': self.lstd.discountFactor,
                'numFeatures': self.numFeatures,
                'S': {
                    'bwNonKb': self.bwFactorNonKbS,
                    'bwKb': self.bwFactorKbS,
                    'weightNonKb': self.weightNonKbS},
                'SA': {
                    'bwNonKb': self.bwFactorNonKbSA,
                    'bwKb': self.bwFactorKbSA,
                    'weightNonKb': self.weightNonKbSA}},
            'REPS': {
                'epsilonAction': self.reps.epsilonAction},
            'GP': {
                'minVariance': self.policy.GPMinVariance,
                'regularizer': self.policy.GPRegularizer,
                'numSamplesSubset': self.numSamplesSubsetGP,
                'bwNonKb': self.bwFactorNonKbGP,
                'bwKb': self.bwFactorKbGP,
                'weightNonKb': self.weightNonKbGP}}

        with open(fileName, 'w') as f:
            f.write(pprint.pformat(params, width=1))

    def learn(self, savePrefix, numSampleIt, continueLearning = True,
            numLearnIt = 1, startEpsilon = 0.0, epsilonFactor = 1.0):

        """ sampling """
        self.objectShape = 'quad'
        self.numKilobots = 4
        self.numEpisodes = 25
        self.numStepsPerEpisode = 100

        self.stepsPerSec = 4096

        self.sDim = self.NUM_NON_KB_DIM + 2 * self.numKilobots

        if continueLearning:
            # dont reset sample or policy
            if self.S is None:
                self.S, self.A, self.R, self.S_ = empty((0, self.sDim)),\
                    empty((0, 2)), empty((0, 1)), empty((0, self.sDim))
        else:
            # reset samples, policy and number of iterations
            self.S, self.A, self.R, self.S_ = empty((0, self.sDim)),\
                    empty((0, 2)), empty((0, 1)), empty((0, self.sDim))
            self.policy = SparseGPPolicy(KilobotKernel(self.NUM_NON_KB_DIM),
                    self.aRange)
            self.it = 0

        """ LSTD """
        self.lstd.discountFactor = 0.99

        factor = 1.0
        factorKb = 1.0
        weightNonKb = 0.5

        self.bwFactorNonKbSA = factor
        self.bwFactorKbSA = factorKb
        self.weightNonKbSA = weightNonKb

        self.bwFactorNonKbS = factor
        self.bwFactorKbS = factorKb
        self.weightNonKbS = weightNonKb

        self.numFeatures = 200

        """ REPS """
        self.reps.epsilonAction = 0.5

        """ GP """
        self.policy.GPMinVariance = 0.0
        self.policy.GPRegularizer = 0.05

        self.numSamplesSubsetGP = 200

        self.bwFactorNonKbGP = factor
        self.bwFactorKbGP = factorKb
        self.weightNonKbGP = weightNonKb

        self.numLearnIt = numLearnIt

        self.startEpsilon = startEpsilon
        self.epsilon = startEpsilon
        self.epsilonFactor = epsilonFactor

        # make data dir and save params
        if savePrefix == '':
            savePath = ''
        else:
            savePath = os.path.join(savePrefix, Helper.getSaveName())
            os.makedirs(savePath)

            self.saveParams(os.path.join(savePath, 'params'))

        rewards = []

        for i in range(numSampleIt):
            t = time.time()

            # get new samples
            St, At, Rt, S_t = self._getSamples()
            print('sum reward for last samples: {}'.format(Rt.sum()))
            rewards += [Rt.sum()]

            # add samples
            self.S = r_[self.S, St]
            self.A = r_[self.A, At]
            self.R = r_[self.R, Rt]
            self.S_ = r_[self.S_, S_t]

            # only keep 10000 samples
            SARS = c_[self.S, self.A, self.R, self.S_]
            SARS = Helper.getRandomSubset(SARS, 10000)

            self.S, self.A, self.R, self.S_ = self._unpackSARS(SARS)

            self._updateKernelParameters(self.S, self.A, random=True,
                    normalize=True)

            self.PHI_S = self.kernelS.getGramMatrix(self.S, self.MuS)

            SA = self._getStateActionMatrix(self.S, self.A)
            self.PHI_SA = self.kernelSA.getGramMatrix(SA, self.MuSA)

            for j in range(numLearnIt):
                # LSTD to estimate Q function / Q(s,a) = phi(s, a).T * theta
                self.PHI_SA_ = self._getFeatureExpectation(self.S_, 5, self.MuSA)
                self.theta = self.lstd.learnLSTD(self.PHI_SA, self.PHI_SA_, self.R)

                # AC-REPS
                self.Q = self.PHI_SA * self.theta
                self.w = self.reps.computeWeighting(self.Q, self.PHI_S)

                # GP
                Ssub = self._getSubsetForGP(self.S, random=True, normalize=True)
                self._updateBandwidthsGP(Ssub)
                self.policy.train(self.S, self.A, self.w, Ssub)

                self.it += 1
                print('finished learning iteration {}'.format(self.it))

                # save results
                if savePath != '':
                    figV = self.getValueFunctionFigure(50, 25, 4)
                    figP = self.getPolicyFigure(50, 25)

                    figV.savefig(os.path.join(savePath, 'V_{}.svg'.format(self.it)))
                    figP.savefig(os.path.join(savePath, 'P_{}.svg'.format(self.it)))

                    self.savePolicyAndSamples(os.path.join(savePath,
                        'policy_samples_{}'.format(self.it)))

                    plt.close(figV)
                    plt.close(figP)

            self.epsilon *= self.epsilonFactor

            print('sampling iteration took: {}s'.format(time.time() - t))

            gc.collect()

        with open(os.path.join(savePath, 'rewards'), 'w') as f:
            for r in rewards:
                f.write('{}\n'.format(r))

    def getValueFunctionFigure(self, stepsX = 50, stepsY = 25, N = 4):
        [X, Y] = meshgrid(linspace(-0.25, 0.25, stepsX), linspace(-0.25, 0.25, stepsY))
        X = X.flatten()
        Y = -Y.flatten()

        lightX = X
        lightY = Y
        alpha = zeros(size(lightX))

        # kilobots at light position
        KB = tile(c_[X, Y], [1, self.numKilobots])

        Srep = repeat(c_[lightX, lightY, KB], N, axis=0)
        Arep = 0.05 * random.random((X.size * N, 2))

        SA = self._getStateActionMatrix(Srep, Arep)
        PHI_SA_rep = self.kernelSA.getGramMatrix(SA, self.MuSA)
        Qrep = PHI_SA_rep * self.theta

        # max over each N rows
        V = asarray(Qrep).reshape(-1, N, Qrep.shape[1]).max(1).reshape(stepsY, stepsX)

        fig = plt.figure()
        plt.imshow(V)

        plt.colorbar()
        plt.title('value function, iteration {}'.format(self.it))

        return fig

    def getPolicyFigure(self, stepsX = 50, stepsY = 25):
        [X, Y] = meshgrid(linspace(-0.25, 0.25, stepsX), linspace(-0.25, 0.25, stepsY))
        X = X.flatten()
        Y = Y.flatten()

        lightX = X
        lightY = Y
        alpha = zeros(size(lightX))

        # kilobots at light position
        KB = tile(c_[X, Y], [1, self.numKilobots])

        A = asarray(self.policy.getMeanAction(c_[lightX, lightY, KB]))
        A /= linalg.norm(A, axis=1).reshape((A.shape[0], 1))

        U = A[:, 0]
        V = A[:, 1]

        fig = plt.figure()

        # draw the object
        ax = plt.gca()
        ax.add_patch(Rectangle((-0.075, -0.075), 0.15, 0.15, facecolor='grey'))

        plt.quiver(X, Y, U, V)
        plt.title('policy, iteration {}'.format(self.it))

        return fig

learner = MazeLearner(2357)
learner.connect()


from django.core.validators import EmailValidator
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from rest_framework.validators import UniqueValidator

from .models import (Brand, Catalogue, Client, Entry, Exit, Product, Provider,
                     Purchase, Sale)

class BrandSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Brand
        fields = (
            'pk',
            'name'
        )

class CatalogueSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Catalogue
        fields = (
            'pk',
            'name'
        )

class ProductSerializer(serializers.HyperlinkedModelSerializer):

    brand = serializers.SlugRelatedField(queryset=Brand.objects.all(),
		slug_field='name', required=True, error_messages = {
            'does_not_exist': _("Brand with name {value} doesn't exit"),
        }
    )
    catalogue = serializers.SlugRelatedField(queryset=Catalogue.objects.all(),
		slug_field='name', required=True, error_messages = {
            'does_not_exist': _("Catalogue with name {value} doesn't exit"),
        }
    )

    code = serializers.CharField(max_length=80 , validators=[
        UniqueValidator(
            queryset=Product.objects.all(),
            message = _("Product code is unique")
        )],
    )

    class Meta:
        model = Product
        fields = (
            'pk',
            'name',
            'code',
            'details',
            'sale_price',
            'purchase_price',
            'brand',
            'catalogue',
            'stock')

    def validate_sale_price(self, data):
        if data <= 0:
            raise serializers.ValidationError(
                _("Price must be a positive number")
            )
        return data

    def validate_purchase_price(self, data):
        if data <= 0:
            raise serializers.ValidationError(
                _("Price must be a positive number")
            )
        return data

    def validate_stock(self, data):
        if data < 0:
            raise serializers.ValidationError(
                _("Product stock can't be negative")
            )
        return data

class ProviderSerializer(serializers.ModelSerializer):

    email = serializers.CharField(max_length=80 ,validators=[
        UniqueValidator(
            queryset=Provider.objects.all(),
            message = _('provider with this email already exists.')
        ),
        EmailValidator()
        ],
        allow_blank = True
    )

    class Meta:
        model = Provider
        fields = (
            'pk',
            'name',
            'phone',
            'email')

class SaleSerializer(serializers.ModelSerializer):

    product = serializers.SlugRelatedField(queryset=Product.objects.all(),
		slug_field='code', required=True, error_messages = {
            'does_not_exist': _("Product with code {value} doesn't exit"),
        }
    )

    sold = serializers.ReadOnlyField(
        source='get_amount_sold'
    )

    exit = serializers.SlugRelatedField(queryset=Exit.objects.all(),
		slug_field='pk', required=True, error_messages = {
            'does_not_exist': _("Exit with id {value} doesn't exit"),
        }
    )

    class Meta:
        model = Sale
        fields = (
            'pk',
            'exit',
            'product',
            'amount',
            'unit_price',
            'sold',
        )

    def validate_amount(self, amount):
        if amount < 0:
            raise serializers.ValidationError(
                _("Amount must be a positive number")
            )
        return amount

    def validate(self, data):

        curr_amount = self.instance.amount if self.instance else 0

        if self.instance:
            curr_product = self.instance.product
        else:
            curr_product = data['product']

        amount = data['amount']

        curr_stock = curr_product.stock

        if curr_amount + curr_stock - amount < 0:
            raise serializers.ValidationError(
                _("There is not enough stock for this product")
            )

        return data

class PurchaseSerializer(serializers.ModelSerializer):

    product = serializers.SlugRelatedField(queryset=Product.objects.all(),
		slug_field='code', required=True, error_messages = {
            'does_not_exist': _("Product with code {value} doesn't exit"),
        }
    )

    spent = serializers.ReadOnlyField(
        source='get_amount_spent'
    )

    entry = serializers.SlugRelatedField(queryset=Entry.objects.all(),
		slug_field='pk', required=True, error_messages = {
            'does_not_exist': _("Entry with id {value} doesn't exit"),
        }
    )

    class Meta:
        model = Purchase
        fields = (
            'pk',
            'entry',
            'product',
            'amount',
            'unit_price',
            'spent',
        )

    def validate_amount(self, amount):
        if amount < 0:
            raise serializers.ValidationError(
                _("Amount must be a positive number")
            )
        return amount

class ExitSerializer(serializers.HyperlinkedModelSerializer):

    user = serializers.ReadOnlyField(source='user.username')
    exit_sales = SaleSerializer(many=True, read_only=True)

    units_sold = serializers.ReadOnlyField(
        source='get_units_sold'
    )

    total_sold = serializers.ReadOnlyField(
        source='total_amount_sold'
    )

    client = serializers.SlugRelatedField(queryset=Client.objects.all(),
		slug_field='tice', required=False, allow_null=True, error_messages = {
            'does_not_exist': _("Client with name {value} doesn't exit"),
        }
    )

    class Meta:
        model = Exit
        fields = (
            'pk',
            'created_date',
            'user',
            'client',
            'units_sold',
            'total_sold',
            'exit_sales'
        )
        read_only_fields = ('created_date', 'user')

class EntrySerializer(serializers.HyperlinkedModelSerializer):

    user = serializers.ReadOnlyField(source='user.username')
    provider = serializers.SlugRelatedField(queryset=Provider.objects.all(),
		slug_field='name', required=True, error_messages = {
            'does_not_exist': _("Provider with name {value} doesn't exit"),
        }
    )
    entry_purchases = PurchaseSerializer(many=True, read_only=True)

    units_bought = serializers.ReadOnlyField(
        source='get_units_bought'
    )

    total_spent = serializers.ReadOnlyField(
        source='total_amount_spent'
    )

    class Meta:
        model = Entry
        fields = (
            'pk',
            'created_date',
            'user',
            'provider',
            'units_bought',
            'total_spent',
            'entry_purchases'
        )
        read_only_fields = ('created_date', 'user')

class ClientSerializer(serializers.ModelSerializer):

    class Meta:
        model = Client
        fields = (
            'pk',
            'name',
            'phone',
            'tice',
        )


import datetime
from collections import OrderedDict

from django.core.paginator import Paginator, EmptyPage, InvalidPage
from django.db.models import ProtectedError
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import render

from appTienda.formularios import Articulo_form
from appTienda.models import Articulo, Unidad, Prenda_precio_compra, Prenda_precio_venta, Tipo_articulo
from appTienda.views.controller import borrar_sessiones

#Listado de articulo
def listado(request, pagina, orden):
    pagina = 1
    # Guardo sessiones
    request.session['articulo_pagina'] = pagina
    request.session['articulo_orden'] = orden
    borrar_sessiones(request, 'articulo_pagina', 'articulo_orden', 'articulo_numero', 'aritculo_busqueda', 'articulo_opcion')
    # Si hay mensajes para mostrar
    if 'msjlista' in request.session:
        mensaje = request.session["msjlista"]
        del request.session['msjlista']
    else:
        mensaje = ''
    # Funcionalidad
    if request.method == "POST":
        busqueda = str(request.POST.get('busqueda'))
        opcion = str(request.POST.get('opcion_busqueda'))
        request.session['articulo_busqueda'] = busqueda
        request.session['articulo_opcion'] = opcion
    else:
        if 'articulo_busqueda' not in request.session:
            busqueda = ''
        else:
            busqueda = request.session['articulo_busqueda']
        if 'articulo_opcion' not in request.session:
            opcion = ''
        else:
            opcion = request.session['articulo_opcion']
    if len(busqueda) != 0:
        if opcion == 'codigo':
            lista = Articulo.objects.filter(codigo__icontains=busqueda).order_by(orden)
        elif opcion == 'descripcion':
            lista = Articulo.objects.filter(descripcion__icontains=busqueda).order_by(orden)
        elif opcion == 'categoria':
            lista = Articulo.objects.filter(Q(categoria__codigo__icontains=busqueda)
                                            | Q(categoria__nombre__icontains=busqueda)).order_by(orden)
    else:
        if opcion == 'valuar':
            lista = Articulo.objects.filter(estado__estado__icontains='Valuar').order_by(orden)
        elif opcion == 'stock':
            lista = Articulo.objects.filter(estado__estado__icontains='Stock').order_by(orden)
        elif opcion == 'venta':
            lista = Articulo.objects.filter(estado__estado__icontains='Venta').order_by(orden)
        elif opcion == 'vendido':
            lista = Articulo.objects.filter(estado__estado__icontains='Vendido').order_by(orden)
        else:
            lista = Articulo.objects.all().order_by(orden)
    busqueda = ''
    opciones = OrderedDict([
        ('descripcion', 'Descripción'), ('codigo', 'Código'),
        ('categoria', 'Categoría'),
        ('venta', 'Venta'), ('vendido', 'Vendido'),
        ('valuar', 'Valuar'), ('stock', 'Stock'),
    ])
    campos = OrderedDict([
        ('codigo', 'Código'), ('descripcion', 'Descripción'),
        ('precio_compra', 'Precio compra'),
        ('precio_venta', 'Precio venta'), ('estado', 'Estado')
    ])
    ctx = {'articulo_lista': lista,
           'mensaje': mensaje,
           'busqueda': busqueda,
           'seleccionado': opcion,
           'pagina': pagina,
           'orden': orden,
           'campos': campos,
           'opciones': opciones,
           }
    return render(request,'mantenimientos/articulo/lista.html', ctx)

# Alta de articulo
def alta(request):
    pagina = str(request.session['articulo_pagina'])
    orden = str(request.session['articulo_orden'])
    if request.method == "POST":
        form = Articulo_form(request.POST)
        #form_precio = Prenda_precio_form(request.POST)
        if form.is_valid():
            nuevo = Articulo()
            nuevo.categoria = form.cleaned_data['categoria']
            nuevo.codigo = form.cleaned_data['codigo']
            nuevo.color = form.cleaned_data['color']
            nuevo.talle = form.cleaned_data['talle']
            nuevo.descripcion = form.cleaned_data['descripcion']
            nuevo.stock = form.cleaned_data['stock']
            nuevo.ultima_compra = form.cleaned_data['ultima_compra']
            nuevo.precio_compra = form.cleaned_data['precio_compra']
            nuevo.precio_venta = form.cleaned_data['precio_venta']
            nuevo.unidad = Unidad.objects.get(id=1)
            nuevo.tipo = Tipo_articulo.objects.get(id=1)
            nuevo.evaluar_estado()
            if nuevo.estado is not None:
                if nuevo.estado.estado == 'Valuar':
                    mensaje = "Se guardo satifactoriamente. Falta valuar"
                if nuevo.estado.estado == 'Stock':
                    mensaje = "Se guardo satifactoriamente. Faltan detalles"
                if nuevo.estado.estado == 'Venta':
                    mensaje = "Se guardo satifactoriamente. Listo para venta"
                nuevo.save()
                form = Articulo_form()
            else:
                mensaje = "Revisar campos vacios"
            #form_precio = Prenda_precio_form()
        else:
            mensaje = "Revisar errores antes de continuar"
        ctx = {'form': form, 'mensaje': mensaje, 'pagina': pagina, 'orden': orden}
        return render(request,'mantenimientos/articulo/alta.html', ctx)
    else:
        form = Articulo_form()
        form.ultima_compra = datetime.datetime.now()
        ctx = {'form': form, 'pagina': pagina, 'orden': orden}
        return render(request,'mantenimientos/articulo/alta.html', ctx)

def modificacion(request, articulo_id):
    pagina = str(request.session['articulo_pagina'])
    orden = str(request.session['articulo_orden'])
    articulo_mod = Articulo.objects.get(id=articulo_id)
    datos_inicial = {'codigo': articulo_mod.codigo,
                     'categoria': articulo_mod.categoria,
                     'color': articulo_mod.color,
                     'talle': articulo_mod.talle,
                     'descripcion': articulo_mod.descripcion,
                     'stock': articulo_mod.stock,
                     'unidad': articulo_mod.unidad,
                     'ultima_compra': articulo_mod.ultima_compra,
                     'precio_compra': articulo_mod.precio_compra,
                     'precio_venta': articulo_mod.precio_venta,
                     'tipo_iva': articulo_mod.tipo_iva,
                     'tipo': articulo_mod.tipo,
                     'estado': articulo_mod.estado,
                     }
    if request.method == 'POST':
        form = Articulo_form(request.POST, instance=articulo_mod)
        if form.is_valid():
            articulo_mod.codigo = form.cleaned_data['codigo']
            articulo_mod.categoria = form.cleaned_data['categoria']
            articulo_mod.color = form.cleaned_data['color']
            articulo_mod.talle = form.cleaned_data['talle']
            articulo_mod.descripcion = form.cleaned_data['descripcion']
            articulo_mod.precio_compra = form.cleaned_data['precio_compra']
            articulo_mod.precio_venta = form.cleaned_data['precio_venta']
            articulo_mod.evaluar_estado()
            if articulo_mod.estado is not None:
                if articulo_mod.estado.estado == 'Valuar':
                    request.session['msjlista'] = str(articulo_mod.codigo)+" Se modifico satifactoriamente. Falta valuar"
                if articulo_mod.estado.estado == 'Stock':
                    request.session['msjlista'] = str(articulo_mod.codigo)+" Se modifico satifactoriamente. Faltan detalles"
                if articulo_mod.estado.estado == 'Venta':
                    request.session['msjlista'] = str(articulo_mod.codigo)+" Se modifico satifactoriamente. Listo para venta"
                articulo_mod.save()
                return HttpResponseRedirect('/articulo/pagina/' + pagina + '/orden/' + orden)
            else:
                mensaje = "Revisar campos vacios"
        else:
            mensaje = 'Corregir errores antes de continuar'
        ctx = {'form': form, 'pagina': pagina, 'orden': orden, 'mensaje': mensaje }
    if request.method == 'GET':
        form = Articulo_form(initial=datos_inicial)
        ctx = { 'form': form, 'pagina': pagina, 'orden': orden }
    return render(request, 'mantenimientos/articulo/modificacion.html', ctx)

def baja(request, articulo_id):
    pagina = str(request.session['articulo_pagina'])
    orden = str(request.session['articulo_orden'])
    articulo_baja = Articulo.objects.get(id=articulo_id)
    if request.method == 'POST':
        try:
            baja = Articulo()
            baja.id = articulo_id
            baja.delete()
            request.session['msjlista'] = 'Se elimino correctamente'
            return HttpResponseRedirect('/articulo/pagina/' + pagina + '/orden/' + orden)
        except ProtectedError:
            mensaje = 'No se pudo eliminar'
            ctx = {'articulo': articulo_baja, 'pagina': pagina, 'orden': orden,
                   'mensaje': mensaje}
    else:
        ctx = {'articulo': articulo_baja, 'pagina': pagina, 'orden': orden}
    return render(request, 'mantenimientos/articulo/baja.html', ctx)

def detalle(request, articulo_id):
    pagina = str(request.session['articulo_pagina'])
    orden = str(request.session['articulo_orden'])
    articulo = Articulo.objects.get(id=articulo_id)
    form = Articulo_form(instance=articulo)
    ctx = {'form': form, 'pagina': pagina, 'orden': orden}
    return render(request, 'mantenimientos/articulo/detalle.html', ctx)