<commit_before>from django.db import models
from django.db.models import Sum

from django.utils import timezone
from django.utils.translation import ugettext as _

from django.core.urlresolvers import reverse

from core import util
from core.util import first_day_of_month, last_day_of_month

from myauth.models import MyUser

class User(MyUser):
    class Meta:
        proxy = True

    def get_shoppings(self, community):
        return Shoppings.objects.filter(user=self, community=community)

class Community(models.Model):
    # TODO name should be unique for clarity?
    name = models.CharField(max_length=32)
    time = models.DateTimeField(default=timezone.now)
    members = models.ManyToManyField(User, related_name="members", blank=True)
    conf_sendmail = models.BooleanField(default=True)

    def get_bills(self):
        return Bill.objects.filter(community=self)

    def get_shoppings(self):
        return Shopping.objects.filter(community=self)

    def get_entries(self):
        return ShoppingListEntry.objects.filter(community=self, time_done=None)

    def get_messages(self):
        return ChatEntry.objects.filter(community=self)

    def get_absolute_url(self):
        return reverse('community', args=[self.id])

    def __str__(self):
        return self.name

class Bill(models.Model):
    community = models.ForeignKey(Community)
    start = models.DateField(default=first_day_of_month)
    end = models.DateField(default=last_day_of_month)
    is_special = models.BooleanField(default=False)
    is_closed = models.BooleanField(default=False)
    description = models.CharField(max_length=1024, null=True, blank=True)

    def get_shoppings(self):
        return Shopping.objects.filter(community=self.community, shopping_day__range=[self.start, self.end])

    def get_sum(self):
        return self.get_shoppings().aggregate(Sum('expenses'))['expenses__sum']

    def get_payers(self):
        return Payer.objects.filter(bill=self)

    def get_dues(self):
        payers = self.get_payers()
        num_payers = payers.__len__()
        total_expenses = self.get_sum()
        try:
            due_per_payer = util.round_up(total_expenses / num_payers)
        except:
            due_per_payer = 0
        dues = {}
        for payer in payers:
            expenses = payer.get_expenses(self)
            payer_due = util.round_up(payer.fraction * due_per_payer) - expenses
            dues[payer] = payer_due
        return dues


    def toggle_close(self):
        if not self.is_closed:
            # make out a bill / calculate dues
            # send mail
            pass
        self.is_closed = not self.is_closed
        self.save()

    def get_absolute_url(self):
        return reverse('bill', args=[self.community.id, self.id])

    def __str__(self):
        return str(self.start) + " - " + str(self.end)


class Payer(models.Model):
    user = models.ForeignKey(User)
    bill = models.ForeignKey(Bill, related_name="payers")
    fraction = models.DecimalField(max_digits=3, decimal_places=2)

    class Meta:
        unique_together = (('user', 'bill'),)

    def get_expenses(self, bill):
        shoppings = bill.get_shoppings().filter(user=self.user)
        user_expenses = shoppings.aggregate(Sum('expenses'))['expenses__sum']
        return user_expenses if user_expenses != None else 0


    def __str__(self):
        return _("Bill '%(bill)s': %(user)s with fraction %(fraction)s") % {'bill': self.bill, 'user': self.user, 'fraction': self.fraction}<commit_msg>implement check before save for conflicting bills<commit_after>from django.db import models, transaction
from django.db.models import Sum, Q

from django.utils import timezone
from django.utils.translation import ugettext as _

from django.core.urlresolvers import reverse

from core import util
from core.util import first_day_of_month, last_day_of_month

from myauth.models import MyUser

class User(MyUser):
    class Meta:
        proxy = True

    def get_shoppings(self, community):
        return Shoppings.objects.filter(user=self, community=community)

class Community(models.Model):
    # TODO name should be unique for clarity?
    name = models.CharField(max_length=32)
    time = models.DateTimeField(default=timezone.now)
    members = models.ManyToManyField(User, related_name="members", blank=True)
    conf_sendmail = models.BooleanField(default=True)

    def get_bills(self):
        return Bill.objects.filter(community=self)

    def get_shoppings(self):
        return Shopping.objects.filter(community=self)

    def get_entries(self):
        return ShoppingListEntry.objects.filter(community=self, time_done=None)

    def get_messages(self):
        return ChatEntry.objects.filter(community=self)

    def get_absolute_url(self):
        return reverse('community', args=[self.id])

    def __str__(self):
        return self.name

class Bill(models.Model):
    community = models.ForeignKey(Community)
    start = models.DateField(default=first_day_of_month)
    end = models.DateField(default=last_day_of_month)
    is_special = models.BooleanField(default=False)
    is_closed = models.BooleanField(default=False)
    description = models.CharField(max_length=1024, null=True, blank=True)

    def get_shoppings(self):
        return Shopping.objects.filter(community=self.community, shopping_day__range=[self.start, self.end])

    def get_sum(self):
        return self.get_shoppings().aggregate(Sum('expenses'))['expenses__sum']

    def get_payers(self):
        return Payer.objects.filter(bill=self)

    def get_dues(self):
        payers = self.get_payers()
        num_payers = payers.__len__()
        total_expenses = self.get_sum()
        try:
            due_per_payer = util.round_up(total_expenses / num_payers)
        except:
            due_per_payer = 0
        dues = {}
        for payer in payers:
            expenses = payer.get_expenses(self)
            payer_due = util.round_up(payer.fraction * due_per_payer) - expenses
            dues[payer] = payer_due
        return dues

    def toggle_close(self):
        if not self.is_closed:
            # make out a bill / calculate dues
            # send mail
            pass
        self.is_closed = not self.is_closed
        self.save()

    def get_conflicting_bills(self):
        community_bills = self.community.get_bills()
        conflicting_bills = community_bills.filter(
            Q(start__lte=self.start) | Q(start__lte=self.end)).filter(
            Q(end__gte=self.start)   | Q(end__gte=self.end)
        )
        # we need to filter out ourself (i.e. if we change a bill range)
        # figure out if we are an existing object
        if self.id:
            return conflicting_bills.exclude(id=self.id)
        else:
            return conflicting_bills

    @transaction.atomic
    def save(self, *args, **kwargs):
        if not self.get_conflicting_bills().exists():
            super(Bill, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('bill', args=[self.community.id, self.id])

    def __str__(self):
        return str(self.start) + " - " + str(self.end)


class Payer(models.Model):
    user = models.ForeignKey(User)
    bill = models.ForeignKey(Bill, related_name="payers")
    fraction = models.DecimalField(max_digits=3, decimal_places=2)

    class Meta:
        unique_together = (('user', 'bill'),)

    def get_expenses(self, bill):
        shoppings = bill.get_shoppings().filter(user=self.user)
        user_expenses = shoppings.aggregate(Sum('expenses'))['expenses__sum']
        return user_expenses if user_expenses != None else 0


    def __str__(self):
        return _("Bill '%(bill)s': %(user)s with fraction %(fraction)s") % {'bill': self.bill, 'user': self.user, 'fraction': self.fraction}
{'fraction_non_alphanumeric': 0.10493985154850269, 'fraction_numerical': 0.002559508574353724, 'mean_word_length': 3.1769107429182255, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2170329', 'n_tokens_mistral': 2486, 'n_tokens_neox': 2382, 'n_words': 504}