Files @ 4ce72186b67c
Branch filter:

Location: django-shaker/djangoshaker/models.py

branko
Migrated the styling to use Bootstrap. Fixed the display of recipe ingredient to show decimals.
from django.db import models
from django.db.models import F

import datetime

class Measure(models.Model):
    """
    Models a measure. A measure is anything which can be used to represent some
    amount of ingredient, for example - pinch, litre, pound, ounce.

    Some measures are also convertible to others through the MeasureConversion
    module.

    Fields:

    name - Name of measure. E.g. 'litre'.

    marking - A short marking used for the measure. E.g. 'l'.

    description - Measure description.
    """

    name = models.CharField(max_length = 64, unique = True)
    marking = models.CharField(max_length = 24, unique = True)
    description = models.TextField(blank = True)

    def __unicode__(self):
        """
        Returns unicode representation of a measure.
        """

        return "%s (%s)" % (self.name, self.marking)

class Ingredient(models.Model):
    """
    Models an ingredient of a recipe. Ingredient is not bound to a particular
    brand.

    Fields:

    name - Ingredient name. E.g. 'white rum', 'orange juice'.

    description - Ingredient description.
    """

    name = models.CharField(max_length = 64, unique = True)
    description = models.TextField(blank = True)

    def __unicode__(self):
        """
        Returns a unicode representation of an ingredient.
        """

        return self.name

class Brand(models.Model):
    """
    Models a brand of some ingredient. The brand can be either specific and
    actually corresponding to some particular company, or generic, like tap
    water or milk.

    Fields:

    name - Name of a brand. E.g. 'Milton Milk'.

    description - Brand description.

    ingredient - Foreign key to an ingredient that this brand provides.

    created - Date when the brand was created in the database.

    modified - Last date of change of the brand.
    """

    name = models.CharField(max_length = 100, unique = True)
    description = models.TextField(blank = True)
    ingredient = models.ForeignKey(Ingredient)
    created = models.DateTimeField(editable = False)
    modified = models.DateTimeField(editable = False)

    def save(self, *args, **kwargs):
        """
        Overrides the default save operation in order to properly take care of
        the created and modified fields (they should be set to same value on
        initial creation of an object).
        """

        cur_date = datetime.datetime.today()

        if not self.id:
            self.created = cur_date

        self.modified = cur_date

        super(Brand, self).save(*args, **kwargs)

    def __unicode__(self):
        """
        Returns a unicode representation of a brand.
        """

        return "%s (%s)" % (self.name, self.ingredient.name)


    @staticmethod
    def latest_additions(count=5):
        """
        Returns a queryset of last added brands.

        Arguments:

        count - Number of last added entries to return. Default is 5.
        """

        return Brand.objects.order_by('-created')[:count]

    @staticmethod
    def latest_changes(count=5):
        """
        Returns a queryset of last modified brands.

        Arguments:

        count - Number of last modified entries to return. Default is 5.
        """

        return Brand.objects.filter(modified__gt=F('created')).order_by('-modified')[:count]
    
class MeasureConversion(models.Model):
    """
    Models conversion ratios between different measures. Allows the user to
    select related measures when presenting recipes.

    Fields:

    from_measure - Foreign key to a measure from which the conversion is made.

    to_measure - Foreign key to a measure to which the conversion is made.

    ratio - A number by which the from_measure is multiplied with in order to
    get the amount in to_measure.

    @TODO: Implement some checks that conversions aren't added in both (a, b)
    and (b, a).
    """

    from_measure = models.ForeignKey(Measure, related_name = 'conversion_from')
    to_measure = models.ForeignKey(Measure, related_name = 'conversion_to')
    ratio = models.FloatField()

    def __unicode__(self):
        """
        Returns a unicode representation of measure conversion.
        """

        return "1 %s amounts to %d %s" % (self.from_measure.marking, self.ratio, self.to_measure.marking)

class Recipe(models.Model):
    """
    Models basic recipe information. The recipe ingredients are kept in a
    separate model.

    Fields:

    name - Name of a recipe.

    description - Description of a recipe. This is not instructions. Those are
    kept in a separate field.

    instructions - Instructions for recipe preparation.

    created - Date when the recipe was created in the database.

    modified - Last date of change of the recipe.

    published - Keeps track whether the recipe has been published or
    not. Unpublished recipes are not shown in any list, and their modification
    time is kept same as the creatio one.

    ingredients = models.ManyToMany(Ingredient, through = "RecipeIngredients")
    """

    name = models.CharField(max_length = 128, unique = True)
    description = models.TextField(blank = True)
    instructions = models.TextField()
    created = models.DateTimeField(editable = False)
    modified = models.DateTimeField(editable = False)
    published = models.BooleanField(default = False, editable = False)

    def __init__(self, *args, **kwargs):
        """
        Overriden constructor that allows the instance to remember its original
        value for the published field.
        """

        super(Recipe, self).__init__(*args, **kwargs)
        self.__original_published = self.published

    def __unicode__(self):
        """
        Returns a unicode representation of a recipe.

        @TODO: See what the preferred output might be for this one. Currently
        listing all ingredients.
        """

        result = self.name + "("
        for ing in self.recipeingredient_set.all():
            result += str(ing) + ";"
        result += ")"
        return result

    def publish(self):
        """
        Publishes the recipe.
        """

        self.published = True
        self.save()

    def save(self, *args, **kwargs):
        """
        Overrides the default save operation in order to properly take care of
        the created and modified fields (they should be set to same value on
        initial creation of an object).

        It also takes care of updating the modified data _only_ while the recipe
        is published.
        """

        cur_date = datetime.datetime.today()

        if not self.id:
            self.created = cur_date
            self.modified = cur_date
        elif self.published and self.__original_published:
            self.modified = cur_date
        elif not self.published and self.__original_published:
            raise Exception("Unpublishing is not permitted.")

        super(Recipe, self).save(*args, **kwargs)

    def is_published(self):
        """
        Returns whether the recipe was published or not.
        """

        return self.published

    published.short_description = "Published?"

    @staticmethod
    def latest_additions(count=5):
        """
        Returns a queryset of last added recipes.

        Arguments:

        count - Number of last added recipes to return. Default is 5.
        """

        return Recipe.objects.filter(published = True).order_by('-created')[:count]

    @staticmethod
    def latest_changes(count=5):
        """
        Returns a queryset of last modified recipes.

        Arguments:

        count - Number of last modified entries to return. Default is 5.
        """

        return Recipe.objects.filter(modified__gt=F('created'), published = True).order_by('-modified')[:count]

class RecipeIngredient(models.Model):
    """
    Models the recipe ingredients. Connects the actualy recipe to a number of
    different ingredients.

    Fields:

    recipe - Foreign key to which an ingredient is assigned.

    ingredient - Foreign key to ingredient being used in a recipe.

    amount - Quantity of ingredient required for the recipe.
    """

    recipe = models.ForeignKey(Recipe)
    ingredient = models.ForeignKey(Ingredient)
    amount = models.FloatField()
    measure = models.ForeignKey(Measure)

    def __unicode__(self):
        """
        Returns a unicode representation of an ingredient for the recipe
        (stating which ingredient, how much of it etc).
        """

        return "%g %s of %s" % (self.amount, self.measure.marking, self.ingredient)

    def save(self, *args, **kwargs):
        """
        Overrides the default save method to update the modification time of a
        parent recipe (if applicable).
        """

        if self.recipe.published:
            self.recipe.save()

        super(RecipeIngredient, self).save(*args, **kwargs)