diff --git a/food_planner/forms.py b/food_planner/forms.py index 999de37..bbff98c 100644 --- a/food_planner/forms.py +++ b/food_planner/forms.py @@ -41,4 +41,5 @@ class UserForm(forms.ModelForm): class ProfileForm(forms.ModelForm): class Meta: model = Profile - + fields = ['daily_fat_demand', 'daily_carbohydrate_demand', + 'daily_sugar_demand', 'daily_roughage_demand', 'daily_protein_demand'] diff --git a/food_planner/migrations/0001_initial.py b/food_planner/migrations/0001_initial.py new file mode 100644 index 0000000..4067adf --- /dev/null +++ b/food_planner/migrations/0001_initial.py @@ -0,0 +1,145 @@ +# Generated by Django 3.0.3 on 2020-02-19 11:35 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Ingredient', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='Name')), + ], + ), + migrations.CreateModel( + name='KitchenUtility', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='Name')), + ], + ), + migrations.CreateModel( + name='Pantry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + migrations.CreateModel( + name='Recipe', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='Name')), + ('image', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Image')), + ('experience_level', models.CharField(choices=[('NOV', 'Novice'), ('INT', 'Intermediate'), ('ADV', 'Advanced'), ('EXP', 'Expert')], max_length=255, verbose_name='Required cooking experience')), + ('fat', models.PositiveIntegerField(help_text='How much fat does one portion contain?', verbose_name='Fat')), + ('carbohydrates', models.PositiveIntegerField(help_text='How much carbohydrates does one portion contain?', verbose_name='Carbohydrates')), + ('sugar', models.PositiveIntegerField(help_text='How much sugar does one portion contain?', verbose_name='Sugar')), + ('roughage', models.PositiveIntegerField(help_text='How much roughage does one portion contain?', verbose_name='Roughage')), + ('protein', models.PositiveIntegerField(help_text='How much protein does one portion contain?', verbose_name='Protein')), + ('author', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Vendor', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50, verbose_name='Vendor')), + ], + ), + migrations.CreateModel( + name='UsedKitchenUtilities', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.PositiveIntegerField(default=1, help_text='How often is this kitchen utility used?', verbose_name='Amount')), + ('kitchen_utility', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='food_planner.KitchenUtility')), + ('recipe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='food_planner.Recipe')), + ], + ), + migrations.CreateModel( + name='UsedIngredients', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.PositiveIntegerField(help_text='Please specify the used amount of the ingredient', verbose_name='Amount')), + ('unit', models.CharField(choices=[('KG', 'kg'), ('G', 'g'), ('MG', 'mg'), ('L', 'l'), ('ML', 'ml'), ('TEA', 'teaspoon'), ('TBL', 'tablespoon'), ('PINCH', 'pinch'), ('PIECE', 'piece')], max_length=10, verbose_name='Unit for amount')), + ('ingredient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='food_planner.Ingredient')), + ('recipe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='food_planner.Recipe')), + ], + ), + migrations.CreateModel( + name='StoredIngredients', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.PositiveIntegerField(help_text='Please specify the stored amount of the ingredient', verbose_name='Amount')), + ('unit', models.CharField(choices=[('KG', 'kg'), ('G', 'g'), ('MG', 'mg'), ('L', 'l'), ('ML', 'ml'), ('TEA', 'teaspoon'), ('TBL', 'tablespoon'), ('PINCH', 'pinch'), ('PIECE', 'piece')], max_length=10, verbose_name='Unit for amount')), + ('ingredient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='food_planner.Ingredient')), + ('pantry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='food_planner.Pantry')), + ], + ), + migrations.AddField( + model_name='recipe', + name='ingredients', + field=models.ManyToManyField(through='food_planner.UsedIngredients', to='food_planner.Ingredient'), + ), + migrations.AddField( + model_name='recipe', + name='kitchen_utilities', + field=models.ManyToManyField(through='food_planner.UsedKitchenUtilities', to='food_planner.KitchenUtility'), + ), + migrations.CreateModel( + name='Profile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('daily_fat_demand', models.PositiveIntegerField(default=117, help_text='At most this amount of fat is needed per day', verbose_name='Daily fat demand')), + ('daily_carbohydrate_demand', models.PositiveIntegerField(default=150, help_text='At most this amount of carbohydrates is needed per day', verbose_name='Daily carbohydrate demand')), + ('daily_sugar_demand', models.PositiveIntegerField(default=25, help_text='At most this amount of sugar is needed per day', verbose_name='Daily sugar demand')), + ('daily_roughage_demand', models.PositiveIntegerField(default=30, help_text='At least this amount of roughage should be eaten per day', verbose_name='Daily roughage demand')), + ('daily_protein_demand', models.PositiveIntegerField(default=90, help_text='At most this amount of protein should be eaten per day', verbose_name='Daily protein demand')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Product', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='Product')), + ('amount', models.PositiveIntegerField(help_text='Please specify the provided amount of the ingredient', verbose_name='Amount')), + ('unit', models.CharField(choices=[('KG', 'kg'), ('G', 'g'), ('MG', 'mg'), ('L', 'l'), ('ML', 'ml'), ('TEA', 'teaspoon'), ('TBL', 'tablespoon'), ('PINCH', 'pinch'), ('PIECE', 'piece')], max_length=10, verbose_name='Unit for amount')), + ('price', models.DecimalField(blank=True, decimal_places=2, default=0.0, max_digits=4, verbose_name='Price')), + ('ingredient', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='food_planner.Ingredient')), + ('vendor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='food_planner.Vendor')), + ], + ), + migrations.AddField( + model_name='pantry', + name='ingredients', + field=models.ManyToManyField(through='food_planner.StoredIngredients', to='food_planner.Ingredient'), + ), + migrations.AddField( + model_name='pantry', + name='owner', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.CreateModel( + name='RecipeStep', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('image', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Image')), + ('description', models.TextField(verbose_name='Description')), + ('number_of_step', models.PositiveIntegerField(verbose_name='Number of step')), + ('recipe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='food_planner.Recipe')), + ], + options={ + 'unique_together': {('recipe', 'number_of_step')}, + }, + ), + ] diff --git a/food_planner/models.py b/food_planner/models.py index a764041..e5d8aa0 100644 --- a/food_planner/models.py +++ b/food_planner/models.py @@ -1,11 +1,152 @@ # coding=utf-8 from django.contrib.auth.models import User from django.db import models +from django.utils.text import format_lazy +from django.utils.translation import gettext_lazy as _ + + +MEASUREMENT_UNITS = [('KG', 'kg'), + ('G', 'g'), + ('MG', 'mg'), + ('L', 'l'), + ('ML', 'ml'), + ('TEA', _('teaspoon')), + ('TBL', _('tablespoon')), + ('PINCH', _('pinch')), + ('PIECE', _('piece'))] class Profile(models.Model): user = models.OneToOneField(User, models.CASCADE) + daily_fat_demand = models.PositiveIntegerField(_('Daily fat demand'), + help_text=_('At most this amount of fat is needed per day'), + default=117) + daily_carbohydrate_demand = models.PositiveIntegerField( + _('Daily carbohydrate demand'), + help_text=_('At most this amount of carbohydrates is needed per day'), + default=150) + daily_sugar_demand = models.PositiveIntegerField(_('Daily sugar demand'), + help_text=_('At most this amount of sugar is needed per day'), + default=25) + daily_roughage_demand = models.PositiveIntegerField( + _('Daily roughage demand'), + help_text=_('At least this amount of roughage should be eaten per day'), + default=30) + daily_protein_demand = models.PositiveIntegerField( + _('Daily protein demand'), + help_text=_('At most this amount of protein should be eaten per day'), + default=90) def __str__(self): return self.user.username + +class KitchenUtility(models.Model): + name = models.CharField(_('Name'), max_length=255) + + def __str__(self): + return self.name + + +class Ingredient(models.Model): + name = models.CharField(_('Name'), max_length=255) + + def __str__(self): + return self.name + + +class Recipe(models.Model): + author = models.ForeignKey(User, models.SET_NULL, null=True) + kitchen_utilities = models.ManyToManyField(KitchenUtility, + through='UsedKitchenUtilities') + ingredients = models.ManyToManyField(Ingredient, through='UsedIngredients') + + name = models.CharField(_('Name'), max_length=255) + image = models.ImageField(_('Image'), blank=True, null=True) + experience_level = models.CharField(_('Required cooking experience'), max_length=255, + choices=[ + ('NOV', _('Novice')), + ('INT', _('Intermediate')), + ('ADV', _('Advanced')), + ('EXP', _('Expert')) + ]) + + fat = models.PositiveIntegerField(_('Fat'), + help_text=_('How much fat does one portion contain?')) + carbohydrates = models.PositiveIntegerField(_('Carbohydrates'), + help_text=_('How much carbohydrates does one portion contain?')) + sugar = models.PositiveIntegerField(_('Sugar'), + help_text=_('How much sugar does one portion contain?')) + roughage = models.PositiveIntegerField(_('Roughage'), + help_text=_('How much roughage does one portion contain?')) + protein = models.PositiveIntegerField(_('Protein'), + help_text=_('How much protein does one portion contain?')) + + def __str__(self): + return self.name + + +class RecipeStep(models.Model): + recipe = models.ForeignKey(Recipe, models.CASCADE) + image = models.ImageField(_('Image'), blank=True, null=True) + description = models.TextField(_('Description')) + number_of_step = models.PositiveIntegerField(_('Number of step')) + + class Meta: + unique_together = ['recipe', 'number_of_step'] + + +class UsedKitchenUtilities(models.Model): + recipe = models.ForeignKey(Recipe, models.CASCADE) + kitchen_utility = models.ForeignKey(KitchenUtility, models.CASCADE) + amount = models.PositiveIntegerField(_('Amount'), + help_text=_('How often is this kitchen utility used?'), + default=1) + + +class UsedIngredients(models.Model): + recipe = models.ForeignKey(Recipe, models.CASCADE) + ingredient = models.ForeignKey(Ingredient, models.CASCADE) + amount = models.PositiveIntegerField(_('Amount'), + help_text=_('Please specify the used amount of the ingredient')) + unit = models.CharField(_('Unit for amount'), max_length=10, choices=MEASUREMENT_UNITS) + +class Vendor(models.Model): + name = models.CharField(_('Vendor'), max_length=50) + + def __str__(self): + return self.name + + +class Product(models.Model): + name = models.CharField(_('Product'), max_length=255) + vendor = models.ForeignKey(Vendor, models.CASCADE) + ingredient = models.ForeignKey(Ingredient, models.SET_NULL, null=True) + amount = models.PositiveIntegerField(_('Amount'), + help_text=_('Please specify the provided amount of the ingredient')) + unit = models.CharField(_('Unit for amount'), max_length=10, + choices=MEASUREMENT_UNITS) + price = models.DecimalField(_('Price'), decimal_places=2, max_digits=4, + blank=True, default=0.0) + + def __str__(self): + return self.name + + +class Pantry(models.Model): + owner = models.ForeignKey(User, models.CASCADE) + ingredients = models.ManyToManyField(Ingredient, through='StoredIngredients') + + def __str__(self): + return format_lazy('Pantry of {firstName} {lastName}', + firstName=self.owner.first_name, + lastName=self.owner.last_name) + + +class StoredIngredients(models.Model): + pantry = models.ForeignKey(Pantry, models.CASCADE) + ingredient = models.ForeignKey(Ingredient, models.CASCADE) + amount = models.PositiveIntegerField(_('Amount'), + help_text=_('Please specify the stored amount of the ingredient')) + unit = models.CharField(_('Unit for amount'), max_length=10, + choices=MEASUREMENT_UNITS) diff --git a/requirements.txt b/requirements.txt index d71c197..d34983f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ django==3.0.3 django-crispy-forms==1.8.1 django-modeltranslation==0.14.2 +Pillow==7.0.0