from django.db import models
+from django.forms import ValidationError
import markdown
from taggit.managers import TaggableManager
-class UnitType (models.Model):
- "Weight, length, count, time, etc."
- name = models.CharField(max_length=40)
-
- def __unicode__(self):
- return u'{0.name}'.format(self)
-
-class UnitSystem (models.Model):
- "SI, CGS, British Imperial, US, etc."
- name = models.CharField(max_length=40)
-
- def __unicode__(self):
- return u'{0.name}'.format(self)
+SI = 'SI (International System of Units)'
+US = 'US (United States of America customary units)'
class Unit (models.Model):
"Kilograms, pounds, liters, gallons, etc."
- abbrev = models.CharField('abbreviation', max_length=6)
+ TYPES = (
+ ('c', 'count'),
+ ('m', 'mass'),
+ ('v', 'volume'),
+ ('t', 'time'),
+ ('T', 'temperature'),
+ )
+ SYSTEMS = (
+ ('SI', SI),
+ ('US', US),
+ )
name = models.CharField(max_length=40)
- type = models.ForeignKey(UnitType)
- system = models.ForeignKey(UnitSystem)
- si_scale = models.DecimalField(max_digits=30, decimal_places=15)
- si_offset = models.DecimalField(max_digits=30, decimal_places=15)
+ abbrev = models.CharField('abbreviation', max_length=6)
+ type = models.CharField(max_length=1, choices=TYPES)
+ system = models.CharField(max_length=2, choices=SYSTEMS)
+ scale = models.DecimalField(
+ 'SI/X-equivalent ratio (e.g. 453.6 for lb (g), 3.78 for gal (L))',
+ max_digits=30, decimal_places=15)
+ offset = models.DecimalField(
+ 'X when SI-equivalent is zero (e.g. 32 for degrees Farenheit (C))',
+ max_digits=30, decimal_places=15)
def __unicode__(self):
return u'{0.abbrev}'.format(self)
+ def convert_from_si(self, value):
+ return value / self.scale + self.offset
+
+ def convert_to_si(self, value):
+ return (value - self.offset) * self.scale
+
+
class Amount (models.Model):
"1 kg, 2-3 lb., 0.5 (0.3-0.6) gal., etc."
- unit = models.ForeignKey(Unit)
- value = models.DecimalField(max_digits=10, decimal_places=4)
+ value = models.DecimalField(
+ max_digits=10, decimal_places=4, null=True, blank=True)
min_value = models.DecimalField(
'minimum value', max_digits=10, decimal_places=4,
null=True, blank=True)
max_value = models.DecimalField(
'maximum value', max_digits=10, decimal_places=4,
null=True, blank=True)
+ unit = models.ForeignKey(Unit, null=True, blank=True)
- def __unicode__(self):
- if self.min_value is None and self.max_value is None:
- value = self.value
- elif self.min_value is None:
- value = '{0.value}-{0.max_value}'.format(self)
- elif self.max_value is None:
- value = '{0.min_value}-{0.value}'.format(self)
+ class Meta:
+ abstract = True
+
+ def format_amount(self):
+ if self.unit is None:
+ for v in [self.value, self.min_value, self.max_value]:
+ if v is not None:
+ raise ValidationError(v)
+ return u'-'
+ if self.value is None:
+ if self.min_value is None:
+ if self.max_value is not None:
+ raise ValidationError('cannot only set max_value')
+ fmt = u'- {0.unit}'
+ else:
+ if self.max_value is None:
+ raise ValidationError('cannot only set min_value')
+ fmt = u'{0.min_value}-{0.max_value} {0.unit}'
else:
- value = '{0.value} ({0.min_value}-{0.max_value})'.format(self)
- return u'{0} {1.unit}'.format(value, self)
+ if self.min_value is None:
+ if self.max_value is None:
+ fmt = u'{0.value} {0.unit}'
+ else:
+ fmt = u'{0.value}-{0.max_value} {0.unit}'
+ else:
+ if self.max_value is None:
+ fmt = u'{0.min_value}-{0.value} {0.unit}'
+ else:
+ fmt = u'{0.value} ({0.min_value}-{0.max_value}) {0.unit}'
+ return fmt.format(self)
-class Recipe (models.Model):
- name = models.CharField(max_length=200)
+ def validate_amount(self):
+ if self.value is None:
+ if self.min_value is None and self.max_value is not None:
+ raise ValidationError('cannot only set max_value')
+ elif self.min_value is not None and self.max_value is None:
+ raise ValidationError('cannot only set min_value')
+ if self.value is not None and self.unit is None:
+ raise ValidationError('values must have units')
+
+
+class Directions (models.Model):
directions_markdown = models.TextField(
- 'directions', help_text='Markdown syntax')
+ 'directions', help_text='Markdown syntax', blank=True, null=True)
directions = models.TextField('directions as HTML', blank=True, null=True)
- # yield is a reserved word
- x_yield = models.OneToOneField(
- Amount, verbose_name='yield', db_column='yield', null=True, blank=True)
+
+ class Meta:
+ abstract = True
+
+ def save(self):
+ # https://code.djangoproject.com/wiki/UsingMarkup
+ if self.directions_markdown is None:
+ self.directions = None
+ else:
+ self.directions = markdown.markdown(self.directions_markdown)
+ super(Directions, self).save()
+
+
+class Recipe (Amount, Directions):
+ name = models.CharField(max_length=200)
author = models.CharField(max_length=200, null=True, blank=True)
source = models.CharField(max_length=200, null=True, blank=True)
url = models.URLField('URL', null=True, blank=True)
def __unicode__(self):
return u'{0.name}'.format(self)
- def save(self):
- # https://code.djangoproject.com/wiki/UsingMarkup
- self.directions = markdown.markdown(self.directions_markdown)
- super(Recipe, self).save()
-
-class IngredientBlock (models.Model):
+class IngredientBlock (Directions):
name = models.CharField(max_length=200)
- directions_markdown = models.TextField(
- 'directions', help_text='markdown syntax', blank=True, null=True)
- directions = models.TextField('directions as HTML', blank=True, null=True)
recipe = models.ForeignKey(Recipe)
class Meta:
def __unicode__(self):
return u'{0.name}'.format(self)
- def save(self):
- if self.directions_markdown:
- self.directions = markdown.markdown(self.directions_markdown)
- super(IngredientBlock, self).save()
-
-class Ingredient (models.Model):
+class Ingredient (Amount):
"1 kg, 2 lb., 3.4 L, 0.5 gal., etc."
- amount = models.OneToOneField(Amount, null=True, blank=True)
name = models.CharField(max_length=200)
note = models.CharField(max_length=200, null=True, blank=True)
block = models.ForeignKey(IngredientBlock)
def __unicode__(self):
- fmt = '{0.name}'
- if self.amount:
- fmt = '{0.amount} ' + fmt
+ fmt = '{i.name}'
+ args = {'i': self}
+ if self.unit:
+ fmt = '{amount} ' + fmt
+ args['amount'] = self.format_amount()
if self.note:
- fmt += u', {0.note}'
- return fmt.format(self)
+ fmt += u', {i.note}'
+ return fmt.format(**args)