from django.db import models from django.utils import timezone from django.conf import settings from django.db.models.signals import m2m_changed from django.dispatch import receiver from django.db.utils import IntegrityError from .hierarchicalData import AncestorLevelsOf, SiblingLevelsOf from treebeard.mp_tree import MP_Node import os # Create your models here. class CharNullField(models.CharField): """ Subclass of the CharField that allows empty strings to be stored as NULL. This class is used to set charfield be optional but unique when added. Set blank=True, null=True when declaring the field """ description = "CharField that stores NULL but returns ''." def get_prep_value(self, value): """ Catches value right before sending to db. """ if value == '': # If Django tries to save an empty string, send the db None (NULL). return None else: # Otherwise, just pass the value. return value def from_db_value(self, value, expression, connection): """ Gets value right out of the db and changes it if its ``None``. """ if value is None: return '' else: return value def to_python(self, value): """ Gets value right out of the db or an instance, and changes it if its ``None``. """ if isinstance(value, models.CharField): # If an instance, just return the instance. return value if value is None: # If db has NULL, convert it to ''. return '' return value # Otherwise, just return the value. class ImageNullField(models.ImageField): """ Subclass of the CharField that allows empty strings to be stored as NULL. This class is used to set charfield be optional but unique when added. Set blank=True, null=True when declaring the field """ description = "ImageField that stores NULL but returns ''." def get_prep_value(self, value): """ Catches value right before sending to db. """ if value == '': # If Django tries to save an empty string, send the db None (NULL). return None else: # Otherwise, just pass the value. return value def from_db_value(self, value, expression, connection): """ Gets value right out of the db and changes it if its ``None``. """ if value is None: return '' else: return value def to_python(self, value): """ Gets value right out of the db or an instance, and changes it if its ``None``. """ if isinstance(value, models.ImageField): # If an instance, just return the instance. return value if value is None: # If db has NULL, convert it to ''. return '' return value # Otherwise, just return the value. class ReceipeImage(models.Model): #RowId = models.IntegerField(primary_key=True) checksum = models.CharField( "checksum", max_length=32, editable=False, unique=True, help_text="The checksum of the original image." ) thrashed_checksum = models.CharField( "threshed checksum", max_length=32, editable=False, unique=True, help_text="The checksum of the thrashed image." ) filename = models.FilePathField( "filename", max_length=1024, editable=False, default=None, unique=True, null=True, help_text="Current filename in storage" ) filename_trashed = models.FilePathField( "filename trashed", max_length=1024, editable=False, default=None, unique=True, null=True, help_text="Current filename of the modified picture in storage" ) created = models.DateTimeField( "created", default=timezone.now, db_index=True) modified = models.DateTimeField( "modified", auto_now=True, editable=False, db_index=True) added = models.DateTimeField( "added", default=timezone.now, editable=False, db_index=True) @property def source_path(self): fname = str(self.filename) return os.path.join( settings.ORIGINALS_DIR, fname ) @property def source_path_trashed(self): fname = str(self.filename_trashed) return os.path.join( settings.ORIGINALS_DIR, fname ) @property def source_file(self): return open(self.source_path, "rb") @property def source_file_trashed(self): return open(self.source_path_trashed, "rb") class Allergenes(models.Model): #RowId = models.IntegerField(primary_key=True) name = models.CharField("allergene name", max_length=50, unique=True) def __str__(self): return 'PK: '+str(self.pk) + ' ' + self.name class Brand(models.Model): #RowId = models.IntegerField(primary_key=True) name = models.CharField("brand name", max_length=50, unique=True) def __str__(self): return 'PK: '+str(self.pk) + ' ' + self.name class Packaging(models.Model): #RowId = models.IntegerField(primary_key=True) name = models.CharField("packaging name", max_length=50, unique=True) def __str__(self): return 'PK: '+str(self.pk) + ' ' + self.name class Ingredients(models.Model): #RowId = models.IntegerField(primary_key=True) name = models.CharField("ingredient name", max_length=100) allergenes = models.ManyToManyField(Allergenes, blank=True) vegan = models.BooleanField("vegan?",default=False) vegetarian = models.BooleanField("vegetarian?",default=False) palmoilfree = models.BooleanField("Palm oil free?",default=True) additive = models.BooleanField("Additiv?",default=False) eNumber = CharNullField("E number", max_length=7,null=True, blank=True) subIngredients = models.ManyToManyField('self', blank=True, symmetrical=False) #class Meta: # unique_together = ('name', 'subIngredients',) def __unicode__(self): return "Ingredients: "+str(self.id)+" "+self.name def __str__(self): return 'PK: '+str(self.pk) + ' ' + self.name @receiver(m2m_changed, sender=Ingredients.subIngredients.through) def verify_uniqueness(sender, **kwargs): ingredient = kwargs.get('instance', None) action = kwargs.get('action', None) subIngredients = kwargs.get('pk_set', None) if action == 'pre_add': for subIngredient in subIngredients: if Ingredients.objects.filter(name=ingredient.name).filter(subIngredients=subIngredient): raise IntegrityError('Ingredient with name %s already exists for subIngredient %s' % (ingredient.name, Ingredients.objects.get(pk=subIngredient))) class Traces(models.Model): #RowId = models.IntegerField(primary_key=True) name = models.CharField("trace name", max_length=50, unique=True) class Labels(models.Model): #RowId = models.IntegerField(primary_key=True) name = models.CharField("label name", max_length=50, unique=True) class EMBs(models.Model): #RowId = models.IntegerField(primary_key=True) name = models.CharField("emb name", max_length=50, unique=True) class ManufacturingPlaces(models.Model): #RowId = models.IntegerField(primary_key=True) name = models.CharField("manufacturing places name", max_length=50, unique=True) class OriginIngredients(models.Model): #RowId = models.IntegerField(primary_key=True) name = models.CharField("origin of igredients", max_length=50, unique=True) class Category(MP_Node): name = models.CharField("category name",max_length=30,unique=True) node_order_by = ['name'] def __str__(self): return 'Category: {}'.format(self.name) class Nutrient(models.Model): name = models.CharField("nutrient name", max_length=50, unique=True) isMacroNutrient = models.BooleanField("Is macronutrient?", default=False) def __str__(self): return 'PK: '+str(self.pk) + ' ' + self.name class NutrientAlias(models.Model): nutrient = models.ForeignKey( Nutrient, on_delete=models.CASCADE, verbose_name="Nutrient" ) name = models.CharField("nutrient alias name", max_length=50, unique=True) def __str__(self): return 'PK: '+str(self.pk) + ' ' + self.name #------------------------------------------------------------------------------- class Article(models.Model): #RowId = models.IntegerField(primary_key=True) name = models.CharField("article name", max_length=100) brand = models.ForeignKey( Brand, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="brand id" ) EAN = CharNullField( "EAN", max_length=50, unique=True, blank=True, null=True ) offlink = models.URLField("Open Food Fact link", blank=True, null=True) category = models.ForeignKey( Category, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="category id" ) quantityPerPackage = models.PositiveIntegerField("quantity per packages", blank=True, null=True) unit = CharNullField("unit",max_length=4, blank=True, null=True) novaGroup = models.PositiveIntegerField("NOVA group", blank=True, null=True) nutritionGrade = models.CharField("nutri grade",max_length=1, blank=True, null=True) ecoGrade = models.CharField("eco grade",max_length=1, blank=True, null=True) MwSt = models.PositiveIntegerField("taxes", blank=True, null=True) allergenes = models.ManyToManyField(Allergenes, blank=True) ingredients = models.ManyToManyField(Ingredients, through='IngredientsArticle', blank=True) traces = models.ManyToManyField(Traces, blank=True) embs = models.ManyToManyField(EMBs, blank=True) manufacturingPlaces = models.ManyToManyField(ManufacturingPlaces, blank=True) originIngredients = models.ManyToManyField(OriginIngredients, blank=True) packagings = models.ManyToManyField(Packaging, through='PackagingArticle', blank=True) nutrients = models.ManyToManyField(Nutrient, through='NutrientArticle', blank=True) labels = models.ManyToManyField(Labels, blank=True) models.ForeignKey( Category, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="category id" ) created = models.DateTimeField( "created", default=timezone.now, db_index=True) modified = models.DateTimeField( "modified", auto_now=True, editable=False, db_index=True) nutritionImage = models.ImageField( "nutriton image filepath", default=None, #unique=True, blank=True, null=True, help_text="Nutrition image", upload_to='nutritionImages' ) ingredientsImage = models.ImageField( "ingredients image filepath", default=None, #unique=True, blank=True, null=True, help_text="Ingredients image", upload_to='ingredientsImages' ) frontImage = models.ImageField( "front image", default=None, #unique=True, null=True, blank=True, help_text="Front image", upload_to='frontImages' ) def __str__(self): return 'PK: '+str(self.pk) + ' ' + self.name #------------------------------------------------------------------------------- class IngredientsArticle(models.Model): ingredient = models.ForeignKey( Ingredients, blank=True, null=True, on_delete=models.CASCADE, verbose_name="Ingredient" ) article = models.ForeignKey( Article, blank=True, null=True, on_delete=models.CASCADE, verbose_name="article" ) position = models.PositiveIntegerField("Position") percent = models.FloatField( "Percentage on total weight", blank=True, null=True, default=None ) #------------------------------------------------------------------------------- class PackagingArticle(models.Model): packaging_id = models.ForeignKey( Packaging, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="packaging id" ) article_id = models.ForeignKey( Article, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="article id" ) weight = models.PositiveIntegerField("weight") #------------------------------------------------------------------------------- class NutrientArticle(models.Model): nutrient = models.ForeignKey( Nutrient, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="nutrient" ) article = models.ForeignKey( Article, blank=True, null=True, on_delete=models.CASCADE, verbose_name="article" ) value = models.DecimalField("value", max_digits=7, decimal_places=2, blank=True, null=True) unit = models.CharField(max_length=4, blank=True, null=True) isEstimated = models.BooleanField("Is nutrient exstimated?", default=False) class Meta: unique_together = ('article', 'nutrient',) # Deprectated, will be deleted later class NutritionalValues(models.Model): article = models.OneToOneField( Article, on_delete=models.CASCADE, primary_key=True, ) energy = models.DecimalField("energy",max_digits=7,decimal_places=2,blank=True, null=True) energyUnit = models.CharField(max_length=4,blank=True, null=True,default='kJ') fat = models.DecimalField("fat",max_digits=7,decimal_places=2,blank=True, null=True) fatUnit = models.CharField(max_length=2,blank=True, null=True,default='g') saturatedFat = models.DecimalField("saturatedFat",max_digits=7,decimal_places=2,blank=True, null=True) saturatedFatUnit = models.CharField(max_length=2,blank=True, null=True,default='g') carbohydrate = models.DecimalField("carbohydrate",max_digits=7,decimal_places=2,blank=True, null=True) carbohydrateUnit = models.CharField(max_length=2,blank=True, null=True,default='g') sugars = models.DecimalField("sugars",max_digits=7,decimal_places=2,blank=True, null=True) sugarsUnit = models.CharField(max_length=2,blank=True, null=True,default='g') dietaryFiber = models.DecimalField("dietaryFiber",max_digits=7,decimal_places=2,blank=True, null=True) dietaryFiberUnit = models.CharField(max_length=2,blank=True, null=True,default='g') proteins = models.DecimalField("proteins",max_digits=7,decimal_places=2,blank=True, null=True) proteinsUnit = models.CharField(max_length=2,blank=True, null=True,default='g') salt = models.DecimalField("salt",max_digits=7,decimal_places=2,blank=True, null=True) saltUnit = models.CharField(max_length=2,blank=True, null=True,default='g') sodium = models.DecimalField("sodium",max_digits=7,decimal_places=2,blank=True, null=True) sodiumUnit = models.CharField(max_length=2,blank=True, null=True) vitamin_a = models.DecimalField("vitamin_a",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_aUnit = models.CharField(max_length=2,blank=True, null=True) vitamin_b1 = models.DecimalField("vitamin_b1",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_b1Unit = models.CharField(max_length=2,blank=True, null=True) vitamin_b2 = models.DecimalField("vitamin_b2",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_b2Unit = models.CharField(max_length=2,blank=True, null=True) vitamin_b3 = models.DecimalField("vitamin_b3",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_b3Unit = models.CharField(max_length=2,blank=True, null=True) vitamin_b5 = models.DecimalField("vitamin_b5",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_b5Unit = models.CharField(max_length=2,blank=True, null=True) vitamin_b7 = models.DecimalField("vitamin_b7",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_b7Unit = models.CharField(max_length=2,blank=True, null=True) vitamin_b9 = models.DecimalField("vitamin_b9",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_b9Unit = models.CharField(max_length=2,blank=True, null=True) vitamin_b12 = models.DecimalField("vitamin_b12",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_b12Unit = models.CharField(max_length=2,blank=True, null=True) vitamin_c = models.DecimalField("vitamin_c",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_cUnit = models.CharField(max_length=2,blank=True, null=True) vitamin_d = models.DecimalField("vitamin_d",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_dUnit = models.CharField(max_length=2,blank=True, null=True) vitamin_e = models.DecimalField("vitamin_e",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_eUnit = models.CharField(max_length=2,blank=True, null=True) vitamin_k = models.DecimalField("vitamin_k",max_digits=7,decimal_places=2,blank=True, null=True) vitamin_kUnit = models.CharField(max_length=2,blank=True, null=True) potassium = models.DecimalField("potassium",max_digits=7,decimal_places=2,blank=True, null=True) potassiumUnit = models.CharField(max_length=2,blank=True, null=True) calcium = models.DecimalField("calcium",max_digits=7,decimal_places=2,blank=True, null=True) calciumUnit = models.CharField(max_length=2,blank=True, null=True) magnesium = models.DecimalField("magnesium",max_digits=7,decimal_places=2,blank=True, null=True) magnesiumUnit = models.CharField(max_length=2,blank=True, null=True) iron = models.DecimalField("iron",max_digits=7,decimal_places=2,blank=True, null=True) ironUnit = models.CharField(max_length=2,blank=True, null=True) zinc = models.DecimalField("zinc",max_digits=7,decimal_places=2,blank=True, null=True) zincUnit = models.CharField(max_length=2,blank=True, null=True) sulfat = models.DecimalField("sulfat",max_digits=7,decimal_places=2,blank=True, null=True) sulfatUnit = models.CharField(max_length=2,blank=True, null=True) chlorid = models.DecimalField("chlorid",max_digits=7,decimal_places=2,blank=True, null=True) chloridUnit = models.CharField(max_length=2,blank=True, null=True) hydrogencarbonat = models.DecimalField("hydrogencarbonat",max_digits=7,decimal_places=2,blank=True, null=True) hydrogencarbonatUnit = models.CharField(max_length=2,blank=True, null=True) class ReceipeString(models.Model): name = models.CharField("receipe string", max_length=50, unique=True) def __str__(self): return 'PK: '+str(self.pk) + ' ' + self.receipeString class ArticleMaps(models.Model): article = models.ForeignKey( Article, blank=True, null=True, on_delete=models.CASCADE, verbose_name="article id" ) #receipeString = models.CharField("receipe string", max_length=50) receipeString = models.ForeignKey( ReceipeString, on_delete=models.CASCADE, null=True, verbose_name="receipe string" ) location_x = models.PositiveIntegerField("x coordiante in image", null=True) location_y = models.PositiveIntegerField("y coordiante in image", null=True) location_h = models.PositiveIntegerField("height in image", null=True) location_w = models.PositiveIntegerField("width in image", null=True) receipeImage = models.ForeignKey(ReceipeImage, on_delete=models.CASCADE) class Meta: unique_together = ('article', 'receipeImage',) def __str__(self): return 'PK: '+str(self.pk) + ' ' + ' ' + self.receipeString.name class Market(models.Model): #RowId = models.AutoField(primary_key=True) name = models.CharField("market name", max_length=50) street = models.CharField("street name", max_length=50) street_number = models.PositiveIntegerField("street number") zip_code = models.CharField("zip code", max_length=5) city = models.CharField("city", max_length=50) phone = CharNullField("phone number",max_length=50, blank=True, null=True) articles = models.ManyToManyField(Article,blank=True) created = models.DateTimeField( "created", default=timezone.now, db_index=True) modified = models.DateTimeField( "modified", auto_now=True, editable=False, db_index=True) def __str__(self): return 'PK: '+str(self.pk) + ' ' + self.name + ' ' + self.street + ' ' + str(self.street_number) + ' ' + self.city class Purchase(models.Model): purchase_date = models.DateField("purchase date") payment_type = models.CharField("payment type",max_length=10,default='EC') total_price = models.DecimalField("total price",max_digits=7,decimal_places=2,default=0) articles = models.ManyToManyField(Article, through='PurchaseArticle') market = models.ForeignKey( Market, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="market id" ) created = models.DateTimeField( "created", default=timezone.now, db_index=True) modified = models.DateTimeField( "modified", auto_now=True, editable=False, db_index=True) receipeImage = models.OneToOneField( ReceipeImage, on_delete=models.CASCADE, primary_key=True, blank = True, ) edit_finished = models.BooleanField("edit finished", default=False) def __str__(self): return 'PK: '+str(self.pk) + ' Datum: ' + self.purchase_date.strftime("%m/%d/%Y") + ' Preis: ' +str(self.total_price) #------------------------------------------------------------------------------- class PurchaseArticle(models.Model): #shoud be renamed to article, but migrations will delete the current database article_id = models.ForeignKey( Article, blank=True, null=True, on_delete=models.CASCADE, verbose_name="article id" ) purchase_id = models.ForeignKey( Purchase, blank=True, null=True, on_delete=models.CASCADE, verbose_name="purchase id" ) quantity = models.PositiveIntegerField("quantity") net_weight = models.DecimalField("net weight",max_digits=7,decimal_places=3, blank=True, null=True) price = models.DecimalField("price",max_digits=7,decimal_places=2,default=0) inSale = models.BooleanField("in sale",default=False)