TandoorRecipes/cookbook/models.py
2021-01-05 21:46:24 +01:00

386 lines
14 KiB
Python

import re
import uuid
from datetime import date, timedelta
from annoying.fields import AutoOneToOneField
from django.contrib import auth
from django.contrib.auth.models import User, Group
from django.core.validators import MinLengthValidator
from django.utils.translation import gettext as _
from django.db import models
from django_random_queryset import RandomManager
from recipes.settings import COMMENT_PREF_DEFAULT, FRACTION_PREF_DEFAULT
def get_user_name(self):
if not (name := f"{self.first_name} {self.last_name}") == " ":
return name
else:
return self.username
auth.models.User.add_to_class('get_user_name', get_user_name)
def get_model_name(model):
return ('_'.join(re.findall('[A-Z][^A-Z]*', model.__name__))).lower()
class Space(models.Model):
name = models.CharField(max_length=128, default='Default')
message = models.CharField(max_length=512, default='', blank=True)
class UserPreference(models.Model):
# Themes
BOOTSTRAP = 'BOOTSTRAP'
DARKLY = 'DARKLY'
FLATLY = 'FLATLY'
SUPERHERO = 'SUPERHERO'
THEMES = ((BOOTSTRAP, 'Bootstrap'), (DARKLY, 'Darkly'), (FLATLY, 'Flatly'), (SUPERHERO, 'Superhero'))
# Nav colors
PRIMARY = 'PRIMARY'
SECONDARY = 'SECONDARY'
SUCCESS = 'SUCCESS'
INFO = 'INFO'
WARNING = 'WARNING'
DANGER = 'DANGER'
LIGHT = 'LIGHT'
DARK = 'DARK'
COLORS = ((PRIMARY, 'Primary'), (SECONDARY, 'Secondary'), (SUCCESS, 'Success'), (INFO, 'Info'), (WARNING, 'Warning'), (DANGER, 'Danger'), (LIGHT, 'Light'), (DARK, 'Dark'))
# Default Page
SEARCH = 'SEARCH'
PLAN = 'PLAN'
BOOKS = 'BOOKS'
PAGES = ((SEARCH, _('Search')), (PLAN, _('Meal-Plan')), (BOOKS, _('Books')),)
# Search Style
SMALL = 'SMALL'
LARGE = 'LARGE'
SEARCH_STYLE = ((SMALL, _('Small')), (LARGE, _('Large')),)
user = AutoOneToOneField(User, on_delete=models.CASCADE, primary_key=True)
theme = models.CharField(choices=THEMES, max_length=128, default=FLATLY)
nav_color = models.CharField(choices=COLORS, max_length=128, default=PRIMARY)
default_unit = models.CharField(max_length=32, default='g')
use_fractions = models.BooleanField(default=FRACTION_PREF_DEFAULT)
default_page = models.CharField(choices=PAGES, max_length=64, default=SEARCH)
search_style = models.CharField(choices=SEARCH_STYLE, max_length=64, default=LARGE)
show_recent = models.BooleanField(default=True)
plan_share = models.ManyToManyField(User, blank=True, related_name='plan_share_default')
ingredient_decimals = models.IntegerField(default=2)
comments = models.BooleanField(default=COMMENT_PREF_DEFAULT)
shopping_auto_sync = models.IntegerField(default=5)
def __str__(self):
return str(self.user)
class Storage(models.Model):
DROPBOX = 'DB'
NEXTCLOUD = 'NEXTCLOUD'
STORAGE_TYPES = ((DROPBOX, 'Dropbox'), (NEXTCLOUD, 'Nextcloud'))
name = models.CharField(max_length=128)
method = models.CharField(choices=STORAGE_TYPES, max_length=128, default=DROPBOX)
username = models.CharField(max_length=128, blank=True, null=True)
password = models.CharField(max_length=128, blank=True, null=True)
token = models.CharField(max_length=512, blank=True, null=True)
url = models.URLField(blank=True, null=True)
created_by = models.ForeignKey(User, on_delete=models.PROTECT)
def __str__(self):
return self.name
class Sync(models.Model):
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
path = models.CharField(max_length=512, default="")
active = models.BooleanField(default=True)
last_checked = models.DateTimeField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.path
class SyncLog(models.Model):
sync = models.ForeignKey(Sync, on_delete=models.CASCADE)
status = models.CharField(max_length=32)
msg = models.TextField(default="")
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.created_at}:{self.sync} - {self.status}"
class Keyword(models.Model):
name = models.CharField(max_length=64, unique=True)
icon = models.CharField(max_length=16, blank=True, null=True)
description = models.TextField(default="", blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
if self.icon:
return f"{self.icon} {self.name}"
else:
return f"{self.name}"
class Unit(models.Model):
name = models.CharField(unique=True, max_length=128, validators=[MinLengthValidator(1)])
description = models.TextField(blank=True, null=True)
def __str__(self):
return self.name
class Food(models.Model):
name = models.CharField(unique=True, max_length=128)
recipe = models.ForeignKey('Recipe', null=True, blank=True, on_delete=models.SET_NULL)
def __str__(self):
return self.name
class Ingredient(models.Model):
food = models.ForeignKey(Food, on_delete=models.PROTECT, null=True, blank=True)
unit = models.ForeignKey(Unit, on_delete=models.PROTECT, null=True, blank=True)
amount = models.DecimalField(default=0, decimal_places=16, max_digits=32)
note = models.CharField(max_length=256, null=True, blank=True)
is_header = models.BooleanField(default=False)
no_amount = models.BooleanField(default=False)
order = models.IntegerField(default=0)
def __str__(self):
return str(self.amount) + ' ' + str(self.unit) + ' ' + str(self.food)
class Meta:
ordering = ['order', 'pk']
class Step(models.Model):
TEXT = 'TEXT'
TIME = 'TIME'
name = models.CharField(max_length=128, default='', blank=True)
type = models.CharField(choices=((TEXT, _('Text')), (TIME, _('Time')),), default=TEXT, max_length=16)
instruction = models.TextField(blank=True)
ingredients = models.ManyToManyField(Ingredient, blank=True)
time = models.IntegerField(default=0, blank=True)
order = models.IntegerField(default=0)
show_as_header = models.BooleanField(default=True)
def get_instruction_render(self):
from cookbook.helper.template_helper import render_instructions
return render_instructions(self)
class Meta:
ordering = ['order', 'pk']
class NutritionInformation(models.Model):
fats = models.DecimalField(default=0, decimal_places=16, max_digits=32)
carbohydrates = models.DecimalField(default=0, decimal_places=16, max_digits=32)
proteins = models.DecimalField(default=0, decimal_places=16, max_digits=32)
calories = models.DecimalField(default=0, decimal_places=16, max_digits=32)
source = models.CharField(max_length=512, default="", null=True, blank=True)
def __str__(self):
return f'Nutrition'
class Recipe(models.Model):
name = models.CharField(max_length=128)
servings = models.IntegerField(default=1)
image = models.ImageField(upload_to='recipes/', blank=True, null=True)
storage = models.ForeignKey(Storage, on_delete=models.PROTECT, blank=True, null=True)
file_uid = models.CharField(max_length=256, default="", blank=True)
file_path = models.CharField(max_length=512, default="", blank=True)
link = models.CharField(max_length=512, null=True, blank=True)
cors_link = models.CharField(max_length=1024, null=True, blank=True)
keywords = models.ManyToManyField(Keyword, blank=True)
steps = models.ManyToManyField(Step, blank=True)
working_time = models.IntegerField(default=0)
waiting_time = models.IntegerField(default=0)
internal = models.BooleanField(default=False)
nutrition = models.ForeignKey(NutritionInformation, blank=True, null=True, on_delete=models.CASCADE)
created_by = models.ForeignKey(User, on_delete=models.PROTECT)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = RandomManager()
def __str__(self):
return self.name
class Comment(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
text = models.TextField()
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.text
class RecipeImport(models.Model):
name = models.CharField(max_length=128)
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
file_uid = models.CharField(max_length=256, default="")
file_path = models.CharField(max_length=512, default="")
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class RecipeBook(models.Model):
name = models.CharField(max_length=128)
description = models.TextField(blank=True)
icon = models.CharField(max_length=16, blank=True, null=True)
shared = models.ManyToManyField(User, blank=True, related_name='shared_with')
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
class RecipeBookEntry(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
book = models.ForeignKey(RecipeBook, on_delete=models.CASCADE)
def __str__(self):
return self.recipe.name
class MealType(models.Model):
name = models.CharField(max_length=128)
order = models.IntegerField(default=0)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
class MealPlan(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, blank=True, null=True)
servings = models.DecimalField(default=1, max_digits=8, decimal_places=4)
title = models.CharField(max_length=64, blank=True, default='')
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
shared = models.ManyToManyField(User, blank=True, related_name='plan_share')
meal_type = models.ForeignKey(MealType, on_delete=models.CASCADE)
note = models.TextField(blank=True)
date = models.DateField()
def get_label(self):
if self.title:
return self.title
return str(self.recipe)
def get_meal_name(self):
return self.meal_type.name
def __str__(self):
return f'{self.get_label()} - {self.date} - {self.meal_type.name}'
class ShoppingListRecipe(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, null=True, blank=True)
servings = models.DecimalField(default=1, max_digits=8, decimal_places=4)
def __str__(self):
return f'Shopping list recipe {self.id} - {self.recipe}'
def get_owner(self):
try:
return self.shoppinglist_set.first().created_by
except AttributeError:
return None
class ShoppingListEntry(models.Model):
list_recipe = models.ForeignKey(ShoppingListRecipe, on_delete=models.CASCADE, null=True, blank=True)
food = models.ForeignKey(Food, on_delete=models.CASCADE)
unit = models.ForeignKey(Unit, on_delete=models.CASCADE, null=True, blank=True)
amount = models.DecimalField(default=0, decimal_places=16, max_digits=32)
order = models.IntegerField(default=0)
checked = models.BooleanField(default=False)
def __str__(self):
return f'Shopping list entry {self.id}'
def get_owner(self):
try:
return self.shoppinglist_set.first().created_by
except AttributeError:
return None
class ShoppingList(models.Model):
uuid = models.UUIDField(default=uuid.uuid4)
note = models.TextField(blank=True, null=True)
recipes = models.ManyToManyField(ShoppingListRecipe, blank=True)
entries = models.ManyToManyField(ShoppingListEntry, blank=True)
shared = models.ManyToManyField(User, blank=True, related_name='list_share')
finished = models.BooleanField(default=False)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'Shopping list {self.id}'
class ShareLink(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
uuid = models.UUIDField(default=uuid.uuid4)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.recipe} - {self.uuid}'
class InviteLink(models.Model):
uuid = models.UUIDField(default=uuid.uuid4)
username = models.CharField(blank=True, max_length=64)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
valid_until = models.DateField(default=date.today() + timedelta(days=14))
used_by = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='used_by')
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.uuid}'
class CookLog(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
rating = models.IntegerField(null=True)
servings = models.IntegerField(default=0)
def __str__(self):
return self.recipe.name
class ViewLog(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.recipe.name