salomax/livremarketplace

View on GitHub
app/sale/models.py

Summary

Maintainability
F
3 days
Test Coverage
#!/usr/bin/env python
# coding: utf-8
#
# Copyright 2016, Marcos Salomão.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import logging
import datetime
from itertools import groupby

from app import user
from app import util

from app.product import models as productModel
from app.customer import models as customer
from app.marketplace import models as marketplace
from app.purchase.models import get_query_purchase
from app.exceptions import NotFoundEntityException

from google.appengine.ext import ndb
from google.appengine.api import search as search_api


__author__ = "Marcos Salomão"
__email__ = "salomao.marcos@gmail.com"
__copyright__ = "Copyright 2016, Marcos Salomão"
__license__ = "Apache 2.0"


class SaleModel(ndb.Model):
    """ Sale model.
    """

    customer = ndb.KeyProperty(
        kind='CustomerModel', indexed=True, required=True)

    product = ndb.KeyProperty(
        kind='ProductModel', indexed=True, required=True)

    quantity = ndb.IntegerProperty(required=True, default=1)

    sale_date = ndb.DateTimeProperty(
        required=True, default=datetime.datetime.today())

    amount = ndb.FloatProperty(required=True)

    # Marketplace fare
    fare = ndb.FloatProperty(required=False)

    net_total = ndb.FloatProperty(required=True)

    track_code = ndb.StringProperty(indexed=False)

    created_date = ndb.DateTimeProperty(auto_now_add=True)


def get(id):
    """ Get sale by id.
    """

    marketplaceModel = marketplace.get_marketplace()

    sale = ndb.Key('SaleModel', int(id), parent=marketplaceModel.key).get()

    if sale is None:
        raise NotFoundEntityException("messages.sale.notfound")

    return customer


def get_sales_query():
    """ get sales model query.
    """

    marketplaceModel = marketplace.get_marketplace()

    return SaleModel.query(ancestor=marketplaceModel.key)


def has_sales_by_product(productKey):
    """ Check if there is any sale with product key.
    """

    return get_query_purchase().filter(SaleModel.product
                                       == productKey).get() is not None


def has_sales_by_customer(customerKey):
    """ Check if there is any sale with customer key.
    """

    return get_sales_query().filter(SaleModel.customer
                                    == customerKey).get() is not None


def list():
    """ List all sales.
    """

    sales = get_sales_query().order(
        -SaleModel.sale_date).fetch()

    return sales


@ndb.transactional
def save(sale):
    """ Add or remove a sale.
    """

    # Get parent
    marketplaceModel = marketplace.get_marketplace()

    logging.debug("Get user marketplace")

    if sale.id is not None:

        # Create sale with id
        saleModel = SaleModel(id=int(sale.id),
                              parent=marketplaceModel.key)

    else:

        # Create sale with random unique id
        saleModel = SaleModel(parent=marketplaceModel.key)

    logging.debug("Sale model created")

    # Get product
    productModel = ndb.Key('ProductModel', int(sale.product.id),
                           parent=marketplaceModel.key)

    if productModel is None:
        raise NotFoundEntityException("messages.product.notfound")

    logging.debug("Get product child entity ok")

    # Get customer
    customerModel = ndb.Key('CustomerModel', int(sale.customer.id),
                            parent=marketplaceModel.key)

    if customerModel is None:
        raise NotFoundEntityException("messages.customer.notfound")

    logging.debug("Get customer child entity ok")

    # Set attributes
    saleModel.product = productModel
    saleModel.customer = customerModel
    saleModel.quantity = sale.quantity
    saleModel.sale_date = sale.sale_date
    saleModel.amount = sale.amount
    saleModel.fare = sale.fare
    saleModel.net_total = sale.net_total
    saleModel.track_code = sale.track_code

    # Persiste it
    saleModel.put()

    logging.debug("Sale %d registered successfully!",
                  saleModel.key.id())

    # Return it
    return saleModel


@ndb.transactional
def delete(id):
    """ Remove a sale.
    """

    marketplaceModel = marketplace.get_marketplace()

    sale = ndb.Key('SaleModel', int(id), parent=marketplaceModel.key).get()

    if sale is None:
        raise NotFoundEntityException("messages.sales.notfound")

    sale.key.delete()


def report_customers_by_products():
    """ List customers have ever bought at once.
        The result is grouped by product.
    """

    # List all sales
    sales = get_sales_query().fetch()

    # Create result variable
    result = []

    # Group by products
    data = sorted(sales, key=lambda t: t.product.key.id())
    for k, g in groupby(data, key=lambda t: t.product.key.id()):

        # Create variables
        customers = []
        product = None

        # Create group and get product
        for sale in g:
            # product must be setted one time
            # Avoiding overhead unecessary
            if product is None:
                product = sale.product.key.get()
            customers.append(sale.customer.key.get())

        # Create dict with key and value
        result.append({
            'product': product,
            'customers': customers
        })

    # Return
    return result


def report_products_by_customers():
    """ List products grouped by its customers.
    """

    # List all sales
    sales = get_sales_query().fetch()

    # Create result variable
    result = []

    # Group by customers
    data = sorted(sales, key=lambda t: t.customer.key.id())
    for k, g in groupby(data, key=lambda t: t.customer.key.id()):

        # Create variables
        products = []
        customer = None

        # Create group and get customer
        for sale in g:
            # customer must be setted one time
            # Avoiding overhead unecessary
            if customer is None:
                customer = sale.customer.key.get()
            products.append(sale.product.key.get())

        # Create dict with key and value
        result.append({
            'customer': customer,
            'products': products
        })

    # Return
    return result


def get_stats_by_products():
    """ Get sales statistics
    """

    # Get all sales
    sales = list()

    logging.debug("Listed %d sales to get its stats", len(sales))

    # Init result variable
    stats_sales_products = []

    # Group by product
    data = sorted(sales, key=lambda t: t.product.id())
    for k, g in groupby(data, key=lambda t: t.product.id()):

        # Create variables
        product = None
        sum_quantity = 0
        sum_net_profit = 0.0
        sum_weighted_net_profit = 0.0
        sum_unit_net_profit = 0.0
        index = 0

        # Create group and get product
        for sale in g:

            # product must be setted one time
            # Avoiding overhead unecessary
            if product is None:
                product = sale.product.get()

            # Sum quantities
            sum_quantity = sum_quantity + sale.quantity

            # Sum net profits
            sum_net_profit = sum_net_profit + sale.net_total

            # Sum unit net profit
            sum_unit_net_profit = sum_unit_net_profit + \
                (sale.net_total / float(sale.quantity))

            # Sum weighted net profit
            sum_weighted_net_profit = sum_weighted_net_profit + sale.net_total

            # index++
            index = index + 1

        # Calculate average net profit
        avg_net_profit = sum_unit_net_profit / index

        # Calculate weighted average net profit
        weighted_avg_net_profit = sum_weighted_net_profit / float(sum_quantity)

        # Create dict with key and value
        stats_sales_products.append({
            'product': product,
            'sum_quantity': sum_quantity,
            'sum_net_profit': sum_net_profit,
            'avg_net_profit': avg_net_profit,
            'weighted_avg_net_profit': weighted_avg_net_profit
        })

    logging.debug("Sales stasts grouped by products sucessfully")

    return stats_sales_products