diff options
Diffstat (limited to 'web_interface')
| -rw-r--r-- | web_interface/Makefile | 3 | ||||
| -rw-r--r-- | web_interface/drinks/admin.py | 7 | ||||
| -rw-r--r-- | web_interface/drinks/migrations/0001_initial_squashed_0006_alter_drink_author_squashed_0007_alter_ingredient_drink_alter_ingredient_unit.py | 83 | ||||
| -rw-r--r-- | web_interface/drinks/models.py | 89 | ||||
| -rw-r--r-- | web_interface/drinks/urls.py | 3 | ||||
| -rw-r--r-- | web_interface/drinks_machine/settings.py | 6 | ||||
| -rw-r--r-- | web_interface/drinks_machine/urls.py | 5 |
7 files changed, 176 insertions, 20 deletions
diff --git a/web_interface/Makefile b/web_interface/Makefile index b5104fa..e5f4d7e 100644 --- a/web_interface/Makefile +++ b/web_interface/Makefile @@ -12,5 +12,8 @@ install: migrate: uv run manage.py migrate +user: + uv run manage.py createsuperuser + run_dev: uv run manage.py runserver diff --git a/web_interface/drinks/admin.py b/web_interface/drinks/admin.py index 8c38f3f..34e6e3f 100644 --- a/web_interface/drinks/admin.py +++ b/web_interface/drinks/admin.py @@ -1,3 +1,10 @@ from django.contrib import admin +from . import models # Register your models here. + +admin.site.register(models.Author) +admin.site.register(models.Bottle) +admin.site.register(models.Drink) +admin.site.register(models.Ingredient) +admin.site.register(models.Unit) diff --git a/web_interface/drinks/migrations/0001_initial_squashed_0006_alter_drink_author_squashed_0007_alter_ingredient_drink_alter_ingredient_unit.py b/web_interface/drinks/migrations/0001_initial_squashed_0006_alter_drink_author_squashed_0007_alter_ingredient_drink_alter_ingredient_unit.py new file mode 100644 index 0000000..bb3d845 --- /dev/null +++ b/web_interface/drinks/migrations/0001_initial_squashed_0006_alter_drink_author_squashed_0007_alter_ingredient_drink_alter_ingredient_unit.py @@ -0,0 +1,83 @@ +# Generated by Django 6.0 on 2025-12-07 11:29 + +import colorfield.fields +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + replaces = [('drinks', '0001_initial_squashed_0006_alter_drink_author'), ('drinks', '0007_alter_ingredient_drink_alter_ingredient_unit')] + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Author', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('first_name', models.CharField(help_text='First name of the author of a drink', verbose_name='First name')), + ('last_name', models.CharField(help_text='Last name of the author of a drink', verbose_name='Last name')), + ], + ), + migrations.CreateModel( + name='Bottle', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Name of the type bottle. e.g. Coca Cola, *Specific Whiskey*', verbose_name='Bottle')), + ('description', models.TextField(blank=True, help_text='Free text field that will be shown to the users', verbose_name='Description')), + ('picture', models.ImageField(blank=True, help_text='A picture for the overview', upload_to='', verbose_name='Picture')), + ('color', colorfield.fields.ColorField(blank=True, default='', help_text='Color of the drink, used in the UI', image_field='picture', max_length=25, samples=None, verbose_name='Color')), + ], + ), + migrations.CreateModel( + name='Unit', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('abbreviation', models.CharField(help_text='E.g: ml.', verbose_name='Abbreviation')), + ('name', models.CharField(blank=True, help_text='E.g: ml.', verbose_name='Name')), + ('pump_time', models.FloatField(blank=True, help_text='Amount of time to run the pump for to dispence 1 of these units', verbose_name='Pump time')), + ('valve_time', models.FloatField(blank=True, help_text='Amount of time to run the pump for to dispence 1 of these units', verbose_name='Pump time')), + ], + ), + migrations.CreateModel( + name='Drink', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Name of the drink, for displaying in overview', verbose_name='Name')), + ('description', models.TextField(blank=True, help_text='Free text field that will be shown to the users', verbose_name='Description')), + ('picture', models.ImageField(blank=True, help_text='A picture for the overview', upload_to='', verbose_name='Picture')), + ('author', models.ForeignKey(blank=True, help_text='Author of the drink', on_delete=django.db.models.deletion.CASCADE, to='drinks.author', verbose_name='Author')), + ], + ), + migrations.CreateModel( + name='Ingredient', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.IntegerField(help_text='In conjunction with unit it is an absolute amount', verbose_name='Amount')), + ('bottle', models.ForeignKey(help_text="This ingredient's bottle", on_delete=django.db.models.deletion.CASCADE, to='drinks.bottle', verbose_name='Bottle')), + ('unit', models.ForeignKey(default=1, help_text='The unit that the ammount is messured in, e.g. ml.', on_delete=django.db.models.deletion.CASCADE, to='drinks.unit', verbose_name='Unit')), + ('drink', models.ForeignKey(default=1, help_text='The drink this ingredient is part of', on_delete=django.db.models.deletion.CASCADE, to='drinks.drink', verbose_name='Drink')), + ], + ), + migrations.AddConstraint( + model_name='unit', + constraint=models.CheckConstraint(condition=models.Q(('pump_time__isnull', False), ('valve_time__isnull', False), _connector='OR'), name='one_of_valve_time_or_pump_time_must_be_filled'), + ), + migrations.AlterField( + model_name='drink', + name='author', + field=models.ForeignKey(blank=True, help_text='Author of the drink', null=True, on_delete=django.db.models.deletion.CASCADE, to='drinks.author', verbose_name='Author'), + ), + migrations.AlterField( + model_name='ingredient', + name='drink', + field=models.ForeignKey(help_text='The drink this ingredient is part of', on_delete=django.db.models.deletion.CASCADE, to='drinks.drink', verbose_name='Drink'), + ), + migrations.AlterField( + model_name='ingredient', + name='unit', + field=models.ForeignKey(help_text='The unit that the ammount is messured in, e.g. ml.', on_delete=django.db.models.deletion.CASCADE, to='drinks.unit', verbose_name='Unit'), + ), + ] diff --git a/web_interface/drinks/models.py b/web_interface/drinks/models.py index f1889bd..bb4b48a 100644 --- a/web_interface/drinks/models.py +++ b/web_interface/drinks/models.py @@ -1,5 +1,27 @@ from django.db import models +from django.db.models import Q + from colorfield.fields import ColorField +from django.utils.html import conditional_escape + + +class Author(models.Model): + first_name = models.CharField( + verbose_name="First name", + help_text="First name of the author of a drink", + ) + + last_name = models.CharField( + verbose_name="Last name", + help_text="Last name of the author of a drink", + ) + + @property + def initials(self) -> str: + return f"{self.first_name[0].upper()}.{self.last_name[0].upper()}." + + def __str__(self): + return self.initials class Drink(models.Model): @@ -11,35 +33,26 @@ class Drink(models.Model): description = models.TextField( verbose_name="Description", help_text="Free text field that will be shown to the users", + blank=True, ) picture = models.ImageField( verbose_name="Picture", help_text="A picture for the overview", + blank=True, ) author = models.ForeignKey( verbose_name="Author", help_text="Author of the drink", to=Author, - on_delete=models.CASCADE - ) - - -class Author(models.Model): - first_name = models.CharField( - verbose_name="First name", - help_text="First name of the author of a drink", - ) - - last_name = models.CharField( - verbose_name="Last name", - help_text="Last name of the author of a drink", + on_delete=models.CASCADE, + blank=True, + null=True, ) - @property - def initials(self) -> str: - return f"{self.first_name[0].upper()}.{self.last_name[0].upper()}." + def __str__(self): + return self.name class Bottle(models.Model): @@ -51,39 +64,60 @@ class Bottle(models.Model): description = models.TextField( verbose_name="Description", help_text="Free text field that will be shown to the users", + blank=True, ) picture = models.ImageField( verbose_name="Picture", help_text="A picture for the overview", + blank=True, ) color = ColorField( verbose_name="Color", help_text="Color of the drink, used in the UI", + image_field="picture", # Auto-populate the color, calculated from the top-left pixel + blank=True, ) + def __str__(self): + return self.name + class Unit(models.Model): abbreviation = models.CharField( - verbose_name="Bottle", + verbose_name="Abbreviation", help_text="E.g: ml.", ) name = models.CharField( - verbose_name="Bottle", + verbose_name="Name", help_text="E.g: ml.", + blank=True, ) pump_time = models.FloatField( verbose_name="Pump time", help_text="Amount of time to run the pump for to dispence 1 of these units", + blank=True, ) valve_time = models.FloatField( verbose_name="Pump time", help_text="Amount of time to run the pump for to dispence 1 of these units", + blank=True, ) + class Meta: + constraints = [ + models.CheckConstraint( + condition=Q(pump_time__isnull=False) | Q(valve_time__isnull=False), + name='one_of_valve_time_or_pump_time_must_be_filled' + ), + ] + + def __str__(self): + return self.abbreviation + class Ingredient(models.Model): bottle = models.ForeignKey( @@ -93,8 +127,25 @@ class Ingredient(models.Model): on_delete=models.CASCADE ) + drink = models.ForeignKey( + verbose_name="Drink", + help_text="The drink this ingredient is part of", + to=Drink, + on_delete=models.CASCADE + ) + amount = models.IntegerField( verbose_name="Amount", - help_text=="In conjunction with unit it is an absolute amount", + help_text="In conjunction with unit it is an absolute amount", ) + unit = models.ForeignKey( + verbose_name="Unit", + help_text="The unit that the ammount is messured in, e.g. ml.", + to=Unit, + on_delete=models.CASCADE, + ) + + def __str__(self) -> str: + return f"{self.drink} {self.amount}{self.unit} {self.bottle}" + diff --git a/web_interface/drinks/urls.py b/web_interface/drinks/urls.py index 5119061..33d413b 100644 --- a/web_interface/drinks/urls.py +++ b/web_interface/drinks/urls.py @@ -1,7 +1,10 @@ from django.urls import path +from django.conf.urls.static import static +from django.conf import settings from . import views urlpatterns = [ path("", views.index, name="index"), ] + diff --git a/web_interface/drinks_machine/settings.py b/web_interface/drinks_machine/settings.py index a851434..cd6ac69 100644 --- a/web_interface/drinks_machine/settings.py +++ b/web_interface/drinks_machine/settings.py @@ -11,10 +11,14 @@ https://docs.djangoproject.com/en/6.0/ref/settings/ """ from pathlib import Path +import os # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') +MEDIA_URL = '/media/' + # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/ @@ -37,6 +41,8 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'drinks', + 'colorfield' ] MIDDLEWARE = [ diff --git a/web_interface/drinks_machine/urls.py b/web_interface/drinks_machine/urls.py index e76ad91..5424ace 100644 --- a/web_interface/drinks_machine/urls.py +++ b/web_interface/drinks_machine/urls.py @@ -17,9 +17,12 @@ Including another URLconf from django.contrib import admin from django.urls import path, include from django.views.generic import RedirectView +from django.conf.urls.static import static +from django.conf import settings urlpatterns = [ path('admin/', admin.site.urls), path("drinks/", include("drinks.urls")), path('', RedirectView.as_view(url='/drinks/', permanent=False), name='redirect'), -] +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + |
