Home > Software engineering >  Python | Sum values when data is repeated on other list
Python | Sum values when data is repeated on other list

Time:01-19

So, this is something a little crazy, but I hope it has a solution. I am creating a web application to check sales on a publishing house. Up until now I got everything good: I created a model for products (books), for sales... Everything OK. I am using charts.js to display data. Here is the code for the view:

def top_sales_view (request, *args, **kwargs):
    labels = [] #list for charts.js
    sold_copies = [] #list for charts.js
    billed = [] #list for charts.js
    books_all = Product.objects.all() #get all data from books
    for book in books_all:
        book_data=Sale.objects.filter(book = book.id ) #get all sales per book
        quantity_list = [] #set list of each quantity per sale
        income_list=[] #set list of each income per sale
        for data in book_data:
            quantity = data.quantity
            income = float(data.final_price)
            quantity_list.append(quantity) #list all quantity sales per book
            copies=sum(quantity_list) #sum all quantity sales per book
            income_list.append(income) #list all income sales per book
            billing=sum(income_list) #sum all income sales per book
        book ={
            'id': book.id,
            'title':book.title,
            'cost_center': book.cost_center,
            'data' : {
                'copies': copies,
                'billing' : billing
            }
        }
#------------- PARA CHARTS.JS -------------
        if book['cost_center'] not in labels: #chech if cost_center is not on the list
            labels.append(book['cost_center']) #if it isn't, add it
        if book['cost_center'] in labels:
            sold_copies.append(book['data']['copies'])
            print (sold_copies)


    
    my_context = {
        'labels': labels,
        'copies': sold_copies,
        'billing': billed,
    }
    return render (request, "top-ventas.html", my_context)

The deal is that, there are two separate books, because one is the second edition of the first one, so they account it as separate books, but they have the same cost center value (as all income goes to cover the same "project", let's say). My question is: is there an effecient way to get book['data']['copies'] and book['data']['billing'], check if its correspondent cost center is in the labels list, and add/sum it so, even though they are different books they sum for the chart? I am not sure if I am making myself clear.

Edit: Added Models.

Sales

class Sale (models.Model):
    def __str__(self):
        if self.electronic_bill == True:
            return "FE"   str(self.bill)
        else:
            return str(self.bill)
    bill            = models.IntegerField()
    electronic_bill = models.BooleanField(default=True, blank=True, null=True,)
    type_options    =(
        ("L", "librería"),
        ("D", "directo"),
        ("F", "feria"),
        ("V", "venta de derechos"),
        ("A", "apoyos y becas"),
        ("E", "charlas y eventos"),
        ("w", "ventas web"),
        )
    type            = models.CharField(max_length=120, null=True, choices=type_options)
    client          = models.CharField(max_length=120, null=True, blank=True)
    date            = models.DateField(null=True, blank=True, )
    book            = models.ForeignKey (Product, on_delete=models.CASCADE, null=True, blank=True)
    cost_center     = models.CharField(max_length=120, null=True, choices=Product.cost_center_options)
    quantity        = models.IntegerField(blank=True,null=True)
    unit_price      = models.IntegerField (blank=True,null=True)
    discount        = models.IntegerField(blank=True,null=True)
    final_price     = models.FloatField(blank=True,null=True)

Product:

class Product (models.Model):
    def __str__(self):
        return self.title
    isbn                = models.IntegerField(blank=True, null=True)
    title               = models.CharField(max_length=200, blank=True, null=True)
    cost_center_options =(
        ("01T001", "El secreto del alma número diez"),
        ("01T002", "Un lápiz labial para una momia"),
        ("01T003", "Sobre el horror"),
        ("01T004", "El tambor encantado"),
        ("01T005", "Historia de una vieja geisha"),
        ("01T006", "El hombre que dejó de respirar"),
        ("01IL001", "No tengo ni puta idea de arte"),
        ("01IL002", "Del mismo modo en el sentido contrario"),
        ("01IL003", "La cólera en los tiempos del amor"),
        ("01IL004", "Título pendiente"),
        ("01IL005", "Cuentos para el desconfinamiento"),
        ("02S007", "Daruma"),
        )
    cost_center         = models.CharField(max_length=120, null=True, choices=cost_center_options)
    authors             = models.ManyToManyField(to="Author", related_name="autor", blank=True)
    illustrators        = models.ManyToManyField(to="Author", related_name="ilustrador", blank=True)
    translators         = models.ManyToManyField(to="Author", related_name="traductor", blank=True)
    price               = models.FloatField(blank=True, null=True)

CodePudding user response:

For your chart.js to work I noticed that you only need:

labels = []  # cost_center list of the books that have been saled
sold_copies = []  # quantity of copies of those books
billing = []  # how much money did those books sell

So then, you can use Django's ORM to retrieve the results on a cleaner and faster way:

from django.db.models import Sum


top_sales_qs = Product.objects.annotate(
    bill=Sum('sale__bill'), copies=Sum('sale__quantity')
).order_by('-bill').values_list('cost_center', 'copies', 'bill') 

To group them by center_cost value, you can do:

labels_dict = {}
for label, copies, bill in top_sales_qs:
    current_val = labels_dict.get(label)
    if current_val is None:
        current_val = [label, 0, 0]
    current_label, current_copies, current_bill = current_val
    new_copies = current_copies   copies
    new_bill = current_bill   bill
    labels_dict[label] = [label, new_copies, new_bill]

Then to get the lists for the chart.js

labels, sold_copies, billing = list(zip(*labels_dict.values()))
  •  Tags:  
  • Related