Project files
This commit is contained in:
0
receipeServer/receipe/__init__.py
Normal file
0
receipeServer/receipe/__init__.py
Normal file
24
receipeServer/receipe/admin.py
Normal file
24
receipeServer/receipe/admin.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from django.contrib import admin
|
||||
from .models import *
|
||||
from treebeard.admin import TreeAdmin
|
||||
from treebeard.forms import movenodeform_factory
|
||||
# Register your models here.
|
||||
|
||||
class MyAdmin(TreeAdmin):
|
||||
form = movenodeform_factory(Category)
|
||||
|
||||
admin.site.register(Purchase)
|
||||
admin.site.register(ReceipeImage)
|
||||
admin.site.register(PurchaseArticle)
|
||||
admin.site.register(Market)
|
||||
admin.site.register(Article)
|
||||
admin.site.register(ArticleMaps)
|
||||
admin.site.register(Ingredients)
|
||||
admin.site.register(IngredientsArticle)
|
||||
admin.site.register(Allergenes)
|
||||
admin.site.register(Labels)
|
||||
admin.site.register(Brand)
|
||||
admin.site.register(NutritionalValues)
|
||||
admin.site.register(Packaging)
|
||||
admin.site.register(PackagingArticle)
|
||||
admin.site.register(Category, MyAdmin)
|
||||
5
receipeServer/receipe/admin.py~
Normal file
5
receipeServer/receipe/admin.py~
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from .models import Purchase
|
||||
# Register your models here.
|
||||
|
||||
admin.site.register(Purchase)
|
||||
23
receipeServer/receipe/apiTest.py
Normal file
23
receipeServer/receipe/apiTest.py
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
import os
|
||||
import requests
|
||||
|
||||
#url = 'http://192.168.178.85:8000/api/postImage/'
|
||||
#url = 'http://127.0.0.1:8000/api/postImage/'
|
||||
|
||||
path_img = '/home/elena/Documents/Projects/ReceipeScanner/Server/testbild.jpg'
|
||||
'''
|
||||
with open(path_img, 'rb') as img:
|
||||
name_img = os.path.basename(path_img)
|
||||
files= {'uploadedFile': (name_img,img,'multipart/form-data',{'Expires': '0'}) }
|
||||
with requests.Session() as s:
|
||||
r = s.post(url,files=files)
|
||||
print(r.status_code)
|
||||
'''
|
||||
#url = 'http://192.168.178.85:8000/api/scanner/receipeComplete/'
|
||||
#url = 'http://192.168.178.85:8000/api/scanner/processAgain/'
|
||||
url = 'http://127.0.0.1:8000/api/scanner/processAgain/'
|
||||
|
||||
with requests.Session() as s:
|
||||
r = s.post(url,data={'filename':'receipe-upload-_oqp9us3.jpg'})
|
||||
print(r.status_code)
|
||||
6
receipeServer/receipe/apps.py
Normal file
6
receipeServer/receipe/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ReceipeConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'receipe'
|
||||
1102
receipeServer/receipe/categories.json
Normal file
1102
receipeServer/receipe/categories.json
Normal file
File diff suppressed because it is too large
Load Diff
41
receipeServer/receipe/classes.py
Normal file
41
receipeServer/receipe/classes.py
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sun May 1 13:27:28 2022
|
||||
|
||||
@author: elena
|
||||
"""
|
||||
|
||||
#Not in use atm
|
||||
class Market():
|
||||
def __init__(self):
|
||||
self.id = -1
|
||||
self.name = ''
|
||||
self.street = ''
|
||||
self.street_number = 0
|
||||
self.zip = 0
|
||||
self.city = ''
|
||||
self.phone = ''
|
||||
|
||||
class noDBArticle():
|
||||
def __init__(self):
|
||||
self.id = -1
|
||||
self.articleId = -1
|
||||
self.name = ''
|
||||
self.quantity = 0
|
||||
self.price = 0.0
|
||||
self.nameString = ''
|
||||
self.nameBBox = None
|
||||
self.priceString = ''
|
||||
self.priceBBox = None
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.id, self.name))
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, type(self)):
|
||||
return NotImplemented
|
||||
return self.id == other.id and self.name == other.name
|
||||
|
||||
|
||||
|
||||
432
receipeServer/receipe/consumer.py
Normal file
432
receipeServer/receipe/consumer.py
Normal file
@@ -0,0 +1,432 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sat Apr 30 13:13:23 2022
|
||||
|
||||
@author: elena
|
||||
"""
|
||||
import os
|
||||
import uuid
|
||||
import hashlib
|
||||
import datetime
|
||||
from filelock import FileLock
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
#from django.db.models import Q
|
||||
from django.db import transaction
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from .models import ReceipeImage, Article, Purchase, PurchaseArticle,ArticleMaps
|
||||
|
||||
from .loggers import LoggingMixin
|
||||
from .parser import ReceipeParser
|
||||
from .image_processing import crop_binarize, crop_binarize_scanner
|
||||
from .file_handling import create_source_path_directory
|
||||
|
||||
MESSAGE_RECEIPE_ALREADY_EXISTS = "receipe_already_exists"
|
||||
MESSAGE_FILE_NOT_FOUND = "file_not_found"
|
||||
MESSAGE_PRE_CONSUME_SCRIPT_NOT_FOUND = "pre_consume_script_not_found"
|
||||
MESSAGE_PRE_CONSUME_SCRIPT_ERROR = "pre_consume_script_error"
|
||||
MESSAGE_POST_CONSUME_SCRIPT_NOT_FOUND = "post_consume_script_not_found"
|
||||
MESSAGE_POST_CONSUME_SCRIPT_ERROR = "post_consume_script_error"
|
||||
MESSAGE_NEW_FILE = "new_file"
|
||||
MESSAGE_UNSUPPORTED_TYPE = "unsupported_type"
|
||||
MESSAGE_PARSING_RECEIPE = "parsing_receipe"
|
||||
MESSAGE_GENERATING_THUMBNAIL = "generating_thumbnail"
|
||||
MESSAGE_PARSE_DATE = "parse_date"
|
||||
MESSAGE_SAVE_RECEIPE = "save_receipe"
|
||||
MESSAGE_FINISHED = "finished"
|
||||
|
||||
class ConsumerError(Exception):
|
||||
pass
|
||||
|
||||
class Consumer(LoggingMixin):
|
||||
logging_name = "receipeServer.consumer"
|
||||
|
||||
def _send_progress(self, current_progress, max_progress, status,
|
||||
message=None, document_id=None):
|
||||
payload = {
|
||||
'filename': os.path.basename(self.filename) if self.filename else None, # NOQA: E501
|
||||
'task_id': self.task_id,
|
||||
'current_progress': current_progress,
|
||||
'max_progress': max_progress,
|
||||
'status': status,
|
||||
'message': message,
|
||||
'document_id': document_id
|
||||
}
|
||||
#async_to_sync(self.channel_layer.group_send)("status_updates",
|
||||
# {'type': 'status_update',
|
||||
# 'data': payload})
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.path = None
|
||||
self.filename = None
|
||||
self.task_id = None
|
||||
|
||||
#self.channel_layer = get_channel_layer()
|
||||
|
||||
def _fail(self, message, log_message=None, exc_info=None):
|
||||
self._send_progress(100, 100, 'FAILED', message)
|
||||
self.log("error", log_message or message, exc_info=exc_info)
|
||||
raise ConsumerError(f"{self.filename}: {log_message or message}")
|
||||
|
||||
def pre_check_file_exists(self):
|
||||
if not os.path.isfile(self.path):
|
||||
self._fail(
|
||||
MESSAGE_FILE_NOT_FOUND,
|
||||
f"Cannot consume {self.path}: File not found."
|
||||
)
|
||||
|
||||
def pre_check_directories(self):
|
||||
os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
|
||||
os.makedirs(settings.THUMBNAIL_DIR, exist_ok=True)
|
||||
os.makedirs(settings.ORIGINALS_DIR, exist_ok=True)
|
||||
os.makedirs(settings.ARCHIVE_DIR, exist_ok=True)
|
||||
|
||||
def pre_check_duplicate(self):
|
||||
with open(self.path, "rb") as f:
|
||||
checksum = hashlib.md5(f.read()).hexdigest()
|
||||
#if ReceipeImage.objects.filter(Q(checksum=checksum) | Q(archive_checksum=checksum)).exists(): # NOQA: E501
|
||||
if ReceipeImage.objects.filter(checksum=checksum).exists():
|
||||
if settings.CONSUMER_DELETE_DUPLICATES:
|
||||
os.unlink(self.path)
|
||||
self._fail(
|
||||
MESSAGE_RECEIPE_ALREADY_EXISTS,
|
||||
f"Not consuming {self.filename}: It is a duplicate."
|
||||
)
|
||||
|
||||
|
||||
def try_consume_file(self,
|
||||
path,
|
||||
applyBinarize=True,
|
||||
debug=False,
|
||||
task_id=None,
|
||||
scannerFile=False):
|
||||
"""
|
||||
Return the receipe object if it was successfully created.
|
||||
"""
|
||||
|
||||
self.path = path
|
||||
self.filename = os.path.basename(path)
|
||||
self.task_id = task_id or str(uuid.uuid4())
|
||||
|
||||
self._send_progress(0, 100, 'STARTING', MESSAGE_NEW_FILE)
|
||||
|
||||
# this is for grouping logging entries for this particular file
|
||||
# together.
|
||||
|
||||
self.renew_logging_group()
|
||||
|
||||
# Make sure that preconditions for consuming the file are met.
|
||||
|
||||
self.pre_check_file_exists()
|
||||
self.pre_check_directories()
|
||||
self.pre_check_duplicate()
|
||||
|
||||
self.log("info", f"Consuming {self.filename}")
|
||||
|
||||
# Determine the parser class.
|
||||
|
||||
|
||||
# Notify all listeners that we're going to do some work.
|
||||
|
||||
#document_consumption_started.send(
|
||||
# sender=self.__class__,
|
||||
# filename=self.path,
|
||||
# logging_group=self.logging_group
|
||||
#)
|
||||
|
||||
|
||||
def progress_callback(current_progress, max_progress):
|
||||
# recalculate progress to be within 20 and 80
|
||||
p = int((current_progress / max_progress) * 50 + 20)
|
||||
self._send_progress(p, 100, "WORKING")
|
||||
|
||||
# This doesn't parse the document yet, but gives us a parser.
|
||||
|
||||
print("info Create parser")
|
||||
|
||||
document_parser = ReceipeParser(self.logging_group, debug = debug)
|
||||
|
||||
self.log("debug", f"Parser: {type(document_parser).__name__}")
|
||||
|
||||
# Parse the document. This may take some time.
|
||||
|
||||
articles = None
|
||||
date = None
|
||||
market = None
|
||||
|
||||
#Crop and binarize image
|
||||
if applyBinarize:
|
||||
self.path_bin = self.path[:-4]+'_binarized_cropped.jpg'
|
||||
if scannerFile:
|
||||
crop_binarize_scanner(self.path, self.path_bin)
|
||||
else:
|
||||
crop_binarize(self.path, self.path_bin)
|
||||
self.filename_bin = os.path.basename(self.path_bin)
|
||||
else:
|
||||
self.path_bin = self.path
|
||||
self.filename_bin = os.path.basename(self.path)
|
||||
|
||||
self._send_progress(20, 100, 'WORKING', MESSAGE_PARSING_RECEIPE)
|
||||
self.log("debug", "Parsing {}...".format(self.filename))
|
||||
print("Start parsing...")
|
||||
if scannerFile:
|
||||
document_parser.parse(self.path_bin, self.filename_bin, source='scanner')
|
||||
else:
|
||||
document_parser.parse(self.path_bin, self.filename_bin, source='cam')
|
||||
print("... done")
|
||||
|
||||
self.log("debug", f"Generating thumbnail for {self.filename}...")
|
||||
self._send_progress(70, 100, 'WORKING',
|
||||
MESSAGE_GENERATING_THUMBNAIL)
|
||||
|
||||
articles = document_parser.get_articles()
|
||||
|
||||
date = document_parser.get_date()
|
||||
|
||||
market = document_parser.get_market()
|
||||
|
||||
total = document_parser.get_total()
|
||||
|
||||
if debug:
|
||||
#print(articles)
|
||||
print(date)
|
||||
print(market)
|
||||
print(total)
|
||||
|
||||
|
||||
self._send_progress(90, 100, 'WORKING',
|
||||
MESSAGE_PARSE_DATE)
|
||||
|
||||
#archive_path = document_parser.get_archive_path()
|
||||
|
||||
# Prepare the document classifier.
|
||||
self._send_progress(95, 100, 'WORKING', MESSAGE_SAVE_RECEIPE)
|
||||
# now that everything is done, we can start to store the document
|
||||
# in the system. This will be a transaction and reasonably fast.
|
||||
if not debug:
|
||||
try:
|
||||
with transaction.atomic():
|
||||
|
||||
# store the receipe.
|
||||
receipeImage = self._store(
|
||||
articles=articles,
|
||||
date=date,
|
||||
market=market,
|
||||
total=total
|
||||
)
|
||||
|
||||
# If we get here, it was successful. Proceed with post-consume
|
||||
# hooks. If they fail, nothing will get changed.
|
||||
|
||||
#document_consumption_finished.send(
|
||||
# sender=self.__class__,
|
||||
# document=document,
|
||||
# logging_group=self.logging_group,
|
||||
# classifier=classifier
|
||||
#)
|
||||
|
||||
# After everything is in the database, copy the files into
|
||||
# place. If this fails, we'll also rollback the transaction.
|
||||
with FileLock(settings.MEDIA_LOCK):
|
||||
create_source_path_directory(receipeImage.source_path)
|
||||
|
||||
self._write(self.path, receipeImage.source_path)
|
||||
self._write(self.path_bin, receipeImage.source_path_trashed)
|
||||
|
||||
# Delete the file only if it was successfully consumed
|
||||
self.log("debug", "Deleting file {}".format(self.path))
|
||||
os.unlink(self.path)
|
||||
self.log("debug", "Deleting file {}".format(self.path_bin))
|
||||
os.unlink(self.path_bin)
|
||||
|
||||
# https://github.com/jonaswinkler/paperless-ng/discussions/1037
|
||||
shadow_file = os.path.join(
|
||||
os.path.dirname(self.path),
|
||||
"._" + os.path.basename(self.path))
|
||||
|
||||
if os.path.isfile(shadow_file):
|
||||
self.log("debug", "Deleting file {}".format(shadow_file))
|
||||
os.unlink(shadow_file)
|
||||
|
||||
shadow_file = os.path.join(
|
||||
os.path.dirname(self.path_bin),
|
||||
"._" + os.path.basename(self.path_bin))
|
||||
|
||||
if os.path.isfile(shadow_file):
|
||||
self.log("debug", "Deleting file {}".format(shadow_file))
|
||||
os.unlink(shadow_file)
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
self._fail(
|
||||
str(e),
|
||||
f"The following error occured while consuming "
|
||||
f"{self.filename}: {e}",
|
||||
exc_info=True
|
||||
)
|
||||
finally:
|
||||
pass
|
||||
#document_parser.cleanup()
|
||||
|
||||
#self.run_post_consume_script(document)
|
||||
|
||||
self.log(
|
||||
"info",
|
||||
"Receipe {} consumption finished".format(receipeImage)
|
||||
)
|
||||
|
||||
self._send_progress(100, 100, 'SUCCESS', MESSAGE_FINISHED, receipeImage.id)
|
||||
|
||||
return receipeImage
|
||||
else:
|
||||
return None
|
||||
|
||||
def _write(self,source, target):
|
||||
with open(source, "rb") as read_file:
|
||||
with open(target, "wb") as write_file:
|
||||
write_file.write(read_file.read())
|
||||
|
||||
def _store(self, articles, date, market, total):
|
||||
|
||||
stats = os.stat(self.path)
|
||||
|
||||
self.log("debug", "Saving record to database")
|
||||
|
||||
created = date or timezone.make_aware(
|
||||
datetime.datetime.fromtimestamp(stats.st_mtime))
|
||||
|
||||
#Save market if it not allready exists
|
||||
market.save()
|
||||
|
||||
|
||||
dateName=date or datetime.datetime.now()
|
||||
try:
|
||||
self.filename = ('Receipe_'+
|
||||
str(uuid.uuid4())+
|
||||
'_'+
|
||||
dateName.strftime('%d-%m-%Y-%H-%M-%S')+
|
||||
'.jpg'
|
||||
)
|
||||
self.filename_bin=self.filename[:-4]+'thrased.jpg'
|
||||
except:
|
||||
print('Something is wrong with new filename')
|
||||
|
||||
#Save receipeImage to database
|
||||
with open(self.path, "rb") as f, open(self.path_bin, "rb") as fbin:
|
||||
receipeImage = ReceipeImage.objects.create(
|
||||
filename=self.filename,
|
||||
filename_trashed=self.filename_bin,
|
||||
checksum=hashlib.md5(f.read()).hexdigest(),
|
||||
thrashed_checksum=hashlib.md5(fbin.read()).hexdigest(),
|
||||
created=created,
|
||||
modified=created,
|
||||
)
|
||||
receipeImage.save()
|
||||
|
||||
|
||||
|
||||
#Create new purchase
|
||||
purchase = Purchase.objects.create(
|
||||
purchase_date=dateName,
|
||||
total_price=total,
|
||||
market=market,
|
||||
receipeImage=receipeImage
|
||||
)
|
||||
|
||||
purchase.save()
|
||||
|
||||
for element in articles[1]:
|
||||
if len(element.name) >= 50:
|
||||
element.name = element.name[0:49]
|
||||
if len(element.nameString) >= 50:
|
||||
element.nameString = element.nameString[0:49]
|
||||
|
||||
article = Article.objects.create(
|
||||
name=element.name
|
||||
)
|
||||
|
||||
article.save()
|
||||
|
||||
purchaseArticle = PurchaseArticle.objects.create(
|
||||
purchase_id=purchase,
|
||||
article_id=article,
|
||||
quantity=element.quantity,
|
||||
price=element.price,
|
||||
inSale=False
|
||||
)
|
||||
|
||||
purchaseArticle.save()
|
||||
|
||||
try:
|
||||
articleMaps = ArticleMaps.objects.create(
|
||||
article=article,
|
||||
receipeString=element.nameString,
|
||||
location_x=element.nameBBox.x,
|
||||
location_y=element.nameBBox.y,
|
||||
location_h=element.nameBBox.h,
|
||||
location_w=element.nameBBox.w,
|
||||
receipeImage=receipeImage
|
||||
)
|
||||
except AttributeError:
|
||||
articleMaps = ArticleMaps.objects.create(
|
||||
article=article,
|
||||
receipeString=element.nameString,
|
||||
location_x=0,
|
||||
location_y=0,
|
||||
location_h=0,
|
||||
location_w=0,
|
||||
receipeImage=receipeImage
|
||||
)
|
||||
|
||||
articleMaps.save()
|
||||
|
||||
for element in articles[0]:
|
||||
print(element)
|
||||
print(element.name)
|
||||
print(element.articleId.pk)
|
||||
article = Article.objects.get(
|
||||
pk=element.articleId.pk
|
||||
)
|
||||
|
||||
purchaseArticle = PurchaseArticle.objects.create(
|
||||
purchase_id=purchase,
|
||||
article_id=article,
|
||||
quantity=element.quantity,
|
||||
price=element.price,
|
||||
inSale=False
|
||||
)
|
||||
|
||||
purchaseArticle.save()
|
||||
|
||||
try:
|
||||
articleMaps = ArticleMaps.objects.create(
|
||||
article=article,
|
||||
receipeString=element.nameString,
|
||||
location_x=element.nameBBox.x,
|
||||
location_y=element.nameBBox.y,
|
||||
location_h=element.nameBBox.h,
|
||||
location_w=element.nameBBox.w,
|
||||
receipeImage=receipeImage
|
||||
)
|
||||
except AttributeError:
|
||||
articleMaps = ArticleMaps.objects.create(
|
||||
article=article,
|
||||
receipeString=element.nameString,
|
||||
location_x=0,
|
||||
location_y=0,
|
||||
location_h=0,
|
||||
location_w=0,
|
||||
receipeImage=receipeImage
|
||||
)
|
||||
|
||||
articleMaps.save()
|
||||
|
||||
return receipeImage
|
||||
|
||||
|
||||
16
receipeServer/receipe/file_handling.py
Normal file
16
receipeServer/receipe/file_handling.py
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sat Apr 30 13:50:33 2022
|
||||
|
||||
@author: elena
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def create_source_path_directory(source_path):
|
||||
os.makedirs(os.path.dirname(source_path), exist_ok=True)
|
||||
|
||||
34
receipeServer/receipe/hierarchicalData.py
Normal file
34
receipeServer/receipe/hierarchicalData.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from django.db.models.fields import Field
|
||||
from django.db.models.lookups import In
|
||||
from django.db.models import Lookup
|
||||
from itertools import accumulate
|
||||
|
||||
@Field.register_lookup
|
||||
class AncestorLevelsOf(In):
|
||||
'''Find ancestors based on a level/path string in format of ie: 1_1_123_4'''
|
||||
lookup_name = 'ancestorsof'
|
||||
|
||||
def get_prep_lookup(self):
|
||||
'''
|
||||
This function gets called before as_sql() and returns a value to be assigned as self.rhs.
|
||||
Split the rhs input string by "_", and returns list of all possible ancestor paths.
|
||||
'''
|
||||
levels = self.rhs.split("_")
|
||||
return list(accumulate(levels, func=lambda *i: "_".join(i)))
|
||||
|
||||
@Field.register_lookup
|
||||
class SiblingLevelsOf(Lookup):
|
||||
'''Find silbings based on a level/path string in format of ie: 1_1_123_4'''
|
||||
lookup_name = 'siblingsof'
|
||||
|
||||
def get_prep_lookup(self):
|
||||
'''Change the rhs to parent level'''
|
||||
nodes = self.rhs.split("_")
|
||||
return "^" + "_".join(nodes[:-1]) + "\_[^\_]+$"
|
||||
|
||||
def as_postgresql(self, compiler, connection):
|
||||
|
||||
lhs, lhs_params = self.process_lhs(compiler, connection)
|
||||
rhs, rhs_params = self.process_rhs(compiler, connection)
|
||||
params = lhs_params + rhs_params
|
||||
return f"{lhs} ~* {rhs}", params
|
||||
189
receipeServer/receipe/image_processing.py
Normal file
189
receipeServer/receipe/image_processing.py
Normal file
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sun May 1 09:37:53 2022
|
||||
|
||||
@author: elena
|
||||
"""
|
||||
|
||||
import cv2
|
||||
import doxapy
|
||||
import numpy as np
|
||||
import scipy.ndimage as inter
|
||||
|
||||
class BBox:
|
||||
def __init__(self, x, y, w, h):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.w = w
|
||||
self.h = h
|
||||
|
||||
def __repr__(self):
|
||||
return "x: {:d}, y: {:d}, w: {:d}, h: {:d}".format(self.x,self.y,self.w,self.h)
|
||||
|
||||
def crop_binarize(inputfile: str, outputfile: str) -> None:
|
||||
'''
|
||||
Load inputfile from disk, apply binarization & threasholding and save as outputfile. Works best with pictures from a smartphone
|
||||
|
||||
Args:
|
||||
inputfile (str): Filename of the colored picture with receipe
|
||||
outputfile (str): Filename of the binarized & cropped picture
|
||||
Returns:
|
||||
None
|
||||
'''
|
||||
img = cv2.imread(inputfile)
|
||||
|
||||
# Rotate image
|
||||
(H,W) = img.shape[:2]
|
||||
if H < W:
|
||||
img = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
|
||||
|
||||
(H,W) = img.shape[:2]
|
||||
|
||||
# Save a copy of the image before we do the transformations
|
||||
orig = img.copy()
|
||||
|
||||
|
||||
#%% Start the cropping procedure
|
||||
|
||||
##(1) convert to hsv-space, then split the channels
|
||||
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
||||
h,s,v = cv2.split(hsv)
|
||||
|
||||
##(2) threshold the S channel using adaptive method(`THRESH_OTSU`) or fixed thresh
|
||||
th, threshed = cv2.threshold(s, 35, 255, cv2.THRESH_BINARY_INV)
|
||||
#threshed = cv2.bitwise_not(threshed)
|
||||
|
||||
##(3) find all the external contours on the threshed S
|
||||
cnts = cv2.findContours(threshed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
|
||||
canvas = img.copy()
|
||||
|
||||
## (4) sort and choose the largest contour
|
||||
cnts = sorted(cnts, key = cv2.contourArea)
|
||||
cnt = cnts[-1]
|
||||
|
||||
## (5) approx the contour, so the get the corner points
|
||||
arclen = cv2.arcLength(cnt, True)
|
||||
approx = cv2.approxPolyDP(cnt, 0.02* arclen, True)
|
||||
cv2.drawContours(canvas, [approx], -1, (0, 0, 255), 1, cv2.LINE_AA)
|
||||
|
||||
xcoords = approx[:,:,0]
|
||||
ycoords = approx[:,:,1]
|
||||
|
||||
#rect = np.array([[max(xcoords),max(ycoords)],[min(xcoords),min(ycoords)],[min(xcoords),max(ycoords)],[max(xcoords),min(ycoords)]])
|
||||
#cv2.drawContours(canvas, [rect], -1, (0, 255, 0), 1, cv2.LINE_AA)
|
||||
|
||||
## (6) Crop the original picture
|
||||
crop = orig[int(min(ycoords)[0]):int(max(ycoords)[0]), int(min(xcoords)[0]):int(max(xcoords)[0])]
|
||||
border = 30
|
||||
(H,W) = crop.shape[:2]
|
||||
crop = crop[:,border:W-border]
|
||||
|
||||
#%% Start the binarization
|
||||
|
||||
## (1) Convert to gray
|
||||
crop = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
## (2) Performing gaussian blur with following adaptive thresholding
|
||||
crop = cv2.GaussianBlur(crop,(5,5),0)
|
||||
thresh1 = cv2.adaptiveThreshold(crop,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
|
||||
cv2.THRESH_BINARY,17,2)
|
||||
#thresh1 = cv2.adaptiveThreshold(crop,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
|
||||
# cv2.THRESH_BINARY,11,10)
|
||||
|
||||
|
||||
#%% Apply improvements for text
|
||||
|
||||
# Applying dilation on the threshold image to get better letters, without interruptions
|
||||
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1))
|
||||
erose = cv2.erode(cv2.bitwise_not(thresh1), rect_kernel, iterations = 1)
|
||||
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (4, 4))
|
||||
dilation = cv2.dilate(erose, rect_kernel, iterations = 1)
|
||||
out = cv2.bitwise_not(dilation)
|
||||
|
||||
##% Remove some artefacts from the previous steps
|
||||
# Mainly vertical lines
|
||||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,3))
|
||||
dilate = cv2.dilate(cv2.bitwise_not(out), kernel, iterations=3)
|
||||
edge = cv2.Canny(dilate, 100, 250)
|
||||
cnts = cv2.findContours(edge, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
|
||||
|
||||
color = cv2.cvtColor(dilate, cv2.COLOR_GRAY2BGR)
|
||||
|
||||
badboxes = []
|
||||
|
||||
for c in cnts:
|
||||
x,y,w,h = cv2.boundingRect(c)
|
||||
if h > 150:
|
||||
a = BBox(x,y,w,h)
|
||||
badboxes.append(a)
|
||||
cv2.drawContours(color, [c], -1, (0, 255, 0), 3, cv2.LINE_AA)
|
||||
cv2.rectangle(color,(x,y),(x+w,y+h),(255,0,0),3)
|
||||
|
||||
for element in badboxes:
|
||||
out[element.y:element.y+element.h,element.x:element.x+element.w] = 255
|
||||
|
||||
cv2.imwrite(outputfile,out)
|
||||
|
||||
|
||||
def correct_skew(image: np.array, delta:float = 0.1, limit: int = 2) -> (float, np.array):
|
||||
'''
|
||||
Here's an implementation of the Projection Profile Method algorithm for skew angle estimation.
|
||||
Various angle points are projected into an accumulator array where the skew angle can be defined
|
||||
as the angle of projection within a search interval that maximizes alignment. The idea is to
|
||||
rotate the image at various angles and generate a histogram of pixels for each iteration. To
|
||||
determine the skew angle, we compare the maximum difference between peaks and using this skew
|
||||
angle, rotate the image to correct the skew.
|
||||
|
||||
Args:
|
||||
image (np.array): image to apply skew corretion
|
||||
delta (float): increment streps for angle
|
||||
limit (int): maximal angle to test for
|
||||
Returns:
|
||||
best_angle (float): Calculated corretion angle
|
||||
corrected (np.array): corrected image
|
||||
'''
|
||||
def determine_score(arr, angle):
|
||||
data = inter.rotate(arr, angle, reshape=False, order=0)
|
||||
histogram = np.sum(data, axis=1, dtype=float)
|
||||
score = np.sum((histogram[1:] - histogram[:-1]) ** 2, dtype=float)
|
||||
return histogram, score
|
||||
|
||||
scores = []
|
||||
angles = np.arange(-limit, limit + delta, delta)
|
||||
for angle in angles:
|
||||
histogram, score = determine_score(image, angle)
|
||||
scores.append(score)
|
||||
|
||||
best_angle = angles[scores.index(max(scores))]
|
||||
|
||||
(h, w) = image.shape[:2]
|
||||
center = (w // 2, h // 2)
|
||||
M = cv2.getRotationMatrix2D(center, best_angle, 1.0)
|
||||
corrected = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, \
|
||||
borderMode=cv2.BORDER_REPLICATE)
|
||||
|
||||
return best_angle, corrected
|
||||
|
||||
def crop_binarize_scanner(inputfile: str, outputfile: str) -> None:
|
||||
'''
|
||||
Load inputfile from disk, apply binarization & threasholding and save as outputfile. Works best with pictures from the scanner
|
||||
|
||||
Args:
|
||||
inputfile (str): Filename of the colored picture with receipe
|
||||
outputfile (str): Filename of the binarized & cropped picture
|
||||
Returns:
|
||||
None
|
||||
'''
|
||||
img = cv2.imread(inputfile, cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
binary_image = np.empty(img.shape, img.dtype)
|
||||
sauvola = doxapy.Binarization(doxapy.Binarization.Algorithms.ISAUVOLA)
|
||||
sauvola.initialize(img)
|
||||
sauvola.to_binary(binary_image, {"window": 45, "k": 0.009})
|
||||
|
||||
angle, out = correct_skew(binary_image)
|
||||
print(angle)
|
||||
|
||||
cv2.imwrite(outputfile,out)
|
||||
111
receipeServer/receipe/linearalignment.py
Normal file
111
receipeServer/receipe/linearalignment.py
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Fri May 27 13:10:59 2022
|
||||
|
||||
@author: elena
|
||||
"""
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from .loggers import LoggingMixin
|
||||
from django.conf import settings
|
||||
|
||||
class LinearAlignment(LoggingMixin):
|
||||
logging_name = "receipeServer.alignment"
|
||||
|
||||
def __init__(self, logging_group, progress_callback=None):
|
||||
super().__init__()
|
||||
#self.logging_group = logging_group
|
||||
|
||||
self.FMatrix = []
|
||||
self.distance = 0
|
||||
|
||||
self.compareString = ''
|
||||
self.searchString = ''
|
||||
|
||||
#self.charIndex = {'A':0, 'B':1, 'C':2, 'D':3, 'E':4, 'F':5, 'G':6, 'H':7, 'I':8,
|
||||
# 'J': 9, 'K': 10, 'L':11, 'M':12, 'N':13, 'O':14, 'P':15, 'Q':16, 'R':17,
|
||||
# 'S': 18, 'T': 19, 'U':20, 'V':21, 'W':22, 'X':23, 'Y':24, 'Z':25,
|
||||
# '1':26, '2': 27, '3': 28, '4':29, '5':30, '6':31, '7':32,
|
||||
# '8':33, '9':34, '0':35, ':':36, '.': 37, '!':38, ' ':39, '%':40}
|
||||
|
||||
self.scoreMatrix = pd.read_csv(settings.BASE_DIR+'/receipe/score_matrix.csv',sep=';',index_col=0,header=0)
|
||||
self.scoreMatrix.fillna(0,inplace=True)
|
||||
|
||||
def setStrings(self, compareString, searchString):
|
||||
self.FMatrix = []
|
||||
self.compareString = compareString
|
||||
self.searchString = searchString
|
||||
|
||||
def scoring(self):
|
||||
#Zeilen
|
||||
compareStringSize = len(self.compareString)
|
||||
line = []
|
||||
|
||||
for idx in range(0,compareStringSize + 1):
|
||||
line.append(-self.distance*idx)
|
||||
|
||||
self.FMatrix.append(line)
|
||||
|
||||
#Spalten
|
||||
for idy in range(1,len(self.searchString) + 1) :
|
||||
for idx in range(0, compareStringSize):
|
||||
if idx == 0:
|
||||
line[idx] = -self.distance*idy
|
||||
else:
|
||||
line[idx] = 0
|
||||
|
||||
self.FMatrix.append(line)
|
||||
|
||||
|
||||
|
||||
for idy in range(1, len(self.FMatrix)):
|
||||
for idx in range(1, len(self.FMatrix[0])):
|
||||
elements = np.array([])
|
||||
|
||||
elements = np.append(elements,self.FMatrix[idy-1][idx-1] + self.scoreMatrix[self.searchString[idy-1]][self.compareString[idx-1]])
|
||||
elements = np.append(elements,self.FMatrix[idy][idx-1] - self.distance);
|
||||
elements = np.append(elements,self.FMatrix[idy-1][idx] - self.distance);
|
||||
|
||||
biggest = np.max(elements)
|
||||
self.FMatrix[idy][idx] = biggest
|
||||
|
||||
|
||||
return self.FMatrix[len(self.FMatrix)-1][len(self.FMatrix[0])-1] / np.max([len(self.compareString),len(self.searchString)]) / 10
|
||||
|
||||
def backTrace(self):
|
||||
if len(self.FMatrix) == 0:
|
||||
print('Run scoring before backTrace!')
|
||||
return 0
|
||||
alignmentA = "";
|
||||
alignmentB = "";
|
||||
|
||||
idx = len(self.compareString)
|
||||
idy = len(self.searchString)
|
||||
|
||||
while (idx > 0 or idy > 0) :
|
||||
if (idx > 0) and (idy > 0) and (self.FMatrix[idy][idx] == self.FMatrix[idy-1][idx-1] + self.scoreMatrix[self.searchString[idy-1]][self.compareString[idx-1]]):
|
||||
alignmentA = alignmentA + self.compareString[idx-1]
|
||||
alignmentB = alignmentB + self.searchString[idy-1]
|
||||
idx = idx - 1
|
||||
idy = idy - 1
|
||||
|
||||
elif (idx > 0) and (self.FMatrix[idy][idx-1] - self.distance) :
|
||||
alignmentA = alignmentA + self.compareString[idx-1]
|
||||
alignmentB = alignmentB + '-'
|
||||
idx = idx - 1
|
||||
|
||||
else:
|
||||
alignmentA = alignmentA + '-'
|
||||
alignmentB = alignmentB + self.searchString[idy-1]
|
||||
idy = idy - 1
|
||||
|
||||
|
||||
|
||||
alignmentA = alignmentA[::-1]
|
||||
alignmentB = alignmentB[::-1]
|
||||
|
||||
|
||||
return alignmentA,alignmentB
|
||||
|
||||
36
receipeServer/receipe/loggers.py
Normal file
36
receipeServer/receipe/loggers.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sat Apr 30 13:14:41 2022
|
||||
|
||||
@author: elena
|
||||
"""
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class LoggingMixin:
|
||||
|
||||
logging_group = None
|
||||
|
||||
logging_name = None
|
||||
|
||||
def renew_logging_group(self):
|
||||
self.logging_group = uuid.uuid4()
|
||||
|
||||
def log(self, level, message, **kwargs):
|
||||
if self.logging_name:
|
||||
logger = logging.getLogger(self.logging_name)
|
||||
else:
|
||||
name = ".".join([
|
||||
self.__class__.__module__,
|
||||
self.__class__.__name__
|
||||
])
|
||||
logger = logging.getLogger(name)
|
||||
|
||||
getattr(logger, level)(message, extra={
|
||||
"group": self.logging_group
|
||||
}, **kwargs)
|
||||
295
receipeServer/receipe/migrations/0001_initial.py
Normal file
295
receipeServer/receipe/migrations/0001_initial.py
Normal file
@@ -0,0 +1,295 @@
|
||||
# Generated by Django 4.0.3 on 2022-06-11 14:05
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import receipe.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Allergenes',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='allergene name')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Article',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='article name')),
|
||||
('EAN', receipe.models.CharNullField(blank=True, max_length=50, null=True, unique=True, verbose_name='EAN')),
|
||||
('offlink', models.URLField(blank=True, null=True, verbose_name='Open Food Fact link')),
|
||||
('quantityPerPackage', models.PositiveIntegerField(blank=True, null=True, verbose_name='quantity per packages')),
|
||||
('unit', receipe.models.CharNullField(blank=True, max_length=2, null=True, verbose_name='unit')),
|
||||
('novaGroup', models.PositiveIntegerField(blank=True, null=True, verbose_name='NOVA group')),
|
||||
('nutritionGrade', models.CharField(blank=True, max_length=1, null=True, verbose_name='nutri grade')),
|
||||
('MwSt', models.PositiveIntegerField(blank=True, null=True, verbose_name='taxes')),
|
||||
('created', models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='created')),
|
||||
('modified', models.DateTimeField(auto_now=True, db_index=True, verbose_name='modified')),
|
||||
('nutritionImage', models.FilePathField(default=None, editable=False, help_text='Nutrition image filename in storage', max_length=1024, null=True, unique=True, verbose_name='nutriton image filepath')),
|
||||
('ingredientsImage', models.FilePathField(default=None, editable=False, help_text='Ingredients image filename in storage', max_length=1024, null=True, unique=True, verbose_name='ingredients image filepath')),
|
||||
('frontImage', models.FilePathField(default=None, editable=False, help_text='Front image filename in storage', max_length=1024, null=True, unique=True, verbose_name='front image filepath')),
|
||||
('allergenes', models.ManyToManyField(blank=True, to='receipe.allergenes')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Brand',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='brand name')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EMBs',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='emb name')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Ingredients',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='ingredient name')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Labels',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='label name')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ManufacturingPlaces',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='manufacturing places name')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OriginIngredients',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='origin of igredients')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Packaging',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='packaging name')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ReceipeImage',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('checksum', models.CharField(editable=False, help_text='The checksum of the original image.', max_length=32, unique=True, verbose_name='checksum')),
|
||||
('thrashed_checksum', models.CharField(editable=False, help_text='The checksum of the thrashed image.', max_length=32, unique=True, verbose_name='threshed checksum')),
|
||||
('filename', models.FilePathField(default=None, editable=False, help_text='Current filename in storage', max_length=1024, null=True, unique=True, verbose_name='filename')),
|
||||
('filename_trashed', models.FilePathField(default=None, editable=False, help_text='Current filename of the modified picture in storage', max_length=1024, null=True, unique=True, verbose_name='filename trashed')),
|
||||
('created', models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='created')),
|
||||
('modified', models.DateTimeField(auto_now=True, db_index=True, verbose_name='modified')),
|
||||
('added', models.DateTimeField(db_index=True, default=django.utils.timezone.now, editable=False, verbose_name='added')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Traces',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='trace name')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='NutritionalValues',
|
||||
fields=[
|
||||
('article', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='receipe.article')),
|
||||
('energy', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='energy')),
|
||||
('energyUnit', models.CharField(max_length=2)),
|
||||
('fat', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='fat')),
|
||||
('fatUnit', models.CharField(max_length=2)),
|
||||
('saturatedFat', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='saturatedFat')),
|
||||
('saturatedFatUnit', models.CharField(max_length=2)),
|
||||
('carbohydrate', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='carbohydrate')),
|
||||
('carbohydrateUnit', models.CharField(max_length=2)),
|
||||
('sugars', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='sugars')),
|
||||
('sugarsUnit', models.CharField(max_length=2)),
|
||||
('dietaryFiber', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='dietaryFiber')),
|
||||
('dietaryFiberUnit', models.CharField(max_length=2)),
|
||||
('proteins', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='proteins')),
|
||||
('proteinsUnit', models.CharField(max_length=2)),
|
||||
('salt', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='salt')),
|
||||
('saltUnit', models.CharField(max_length=2)),
|
||||
('sodium', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='sodium')),
|
||||
('sodiumUnit', models.CharField(max_length=2)),
|
||||
('vitamin_a', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_a')),
|
||||
('vitamin_aUnit', models.CharField(max_length=2)),
|
||||
('vitamin_b1', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_b1')),
|
||||
('vitamin_b1mUnit', models.CharField(max_length=2)),
|
||||
('vitamin_b2', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_b2')),
|
||||
('vitamin_b2Unit', models.CharField(max_length=2)),
|
||||
('vitamin_b3', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_b3')),
|
||||
('vitamin_b3Unit', models.CharField(max_length=2)),
|
||||
('vitamin_b5', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_b5')),
|
||||
('vitamin_b5Unit', models.CharField(max_length=2)),
|
||||
('vitamin_b7', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_b7')),
|
||||
('vitamin_b7Unit', models.CharField(max_length=2)),
|
||||
('vitamin_b9', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_b9')),
|
||||
('vitamin_b9Unit', models.CharField(max_length=2)),
|
||||
('vitamin_b12', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_b12')),
|
||||
('vitamin_b12Unit', models.CharField(max_length=2)),
|
||||
('vitamin_c', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_c')),
|
||||
('vitamin_cUnit', models.CharField(max_length=2)),
|
||||
('vitamin_d', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_d')),
|
||||
('vitamin_dUnit', models.CharField(max_length=2)),
|
||||
('vitamin_e', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_e')),
|
||||
('vitamin_eUnit', models.CharField(max_length=2)),
|
||||
('vitamin_k', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='vitamin_k')),
|
||||
('vitamin_kUnit', models.CharField(max_length=2)),
|
||||
('potassium', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='potassium')),
|
||||
('potassiumUnit', models.CharField(max_length=2)),
|
||||
('calcium', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='calcium')),
|
||||
('calciumUnit', models.CharField(max_length=2)),
|
||||
('magnesium', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='magnesium')),
|
||||
('magnesiumUnit', models.CharField(max_length=2)),
|
||||
('iron', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='iron')),
|
||||
('ironUnit', models.CharField(max_length=2)),
|
||||
('zinc', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='zinc')),
|
||||
('zincUnit', models.CharField(max_length=2)),
|
||||
('sulfat', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='sulfat')),
|
||||
('sulfatUnit', models.CharField(max_length=2)),
|
||||
('chlorid', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='chlorid')),
|
||||
('chloridUnit', models.CharField(max_length=2)),
|
||||
('hydrogencarbonat', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='hydrogencarbonat')),
|
||||
('hydrogencarbonatUnit', models.CharField(max_length=2)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Purchase',
|
||||
fields=[
|
||||
('purchase_date', models.DateField(verbose_name='purchase date')),
|
||||
('payment_type', models.CharField(default='EC', max_length=10, verbose_name='payment type')),
|
||||
('total_price', models.DecimalField(decimal_places=2, default=0, max_digits=7, verbose_name='total price')),
|
||||
('created', models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='created')),
|
||||
('modified', models.DateTimeField(auto_now=True, db_index=True, verbose_name='modified')),
|
||||
('receipeImage', models.OneToOneField(blank=True, on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='receipe.receipeimage')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PackagingArticle',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('weight', models.PositiveIntegerField(verbose_name='weight')),
|
||||
('article_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='receipe.article', verbose_name='purchase id')),
|
||||
('packaging_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='receipe.packaging', verbose_name='article id')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Market',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='market name')),
|
||||
('street', models.CharField(max_length=50, verbose_name='street name')),
|
||||
('street_number', models.PositiveIntegerField(verbose_name='street number')),
|
||||
('zip_code', models.CharField(max_length=5, verbose_name='zip code')),
|
||||
('city', models.CharField(max_length=50, verbose_name='city')),
|
||||
('phone', receipe.models.CharNullField(blank=True, max_length=50, null=True, verbose_name='phone number')),
|
||||
('created', models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='created')),
|
||||
('modified', models.DateTimeField(auto_now=True, db_index=True, verbose_name='modified')),
|
||||
('articles', models.ManyToManyField(blank=True, to='receipe.article')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Category',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, unique=True, verbose_name='category name')),
|
||||
('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='receipe.category')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ArticleMaps',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('receipeString', models.CharField(max_length=50, verbose_name='receipe string')),
|
||||
('location_x', models.PositiveIntegerField(verbose_name='x coordiante in image')),
|
||||
('location_y', models.PositiveIntegerField(verbose_name='y coordiante in image')),
|
||||
('location_h', models.PositiveIntegerField(verbose_name='height in image')),
|
||||
('location_w', models.PositiveIntegerField(verbose_name='width in image')),
|
||||
('article', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='receipe.article', verbose_name='article id')),
|
||||
('receipeImage', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='receipe.receipeimage')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='brand',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='receipe.brand', verbose_name='brand id'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='category',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='receipe.category', verbose_name='category id'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='embs',
|
||||
field=models.ManyToManyField(blank=True, to='receipe.embs'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='ingredients',
|
||||
field=models.ManyToManyField(blank=True, to='receipe.ingredients'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='manufacturingPlaces',
|
||||
field=models.ManyToManyField(blank=True, to='receipe.manufacturingplaces'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='originIngredients',
|
||||
field=models.ManyToManyField(blank=True, to='receipe.originingredients'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='packaging',
|
||||
field=models.ManyToManyField(blank=True, through='receipe.PackagingArticle', to='receipe.packaging'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='traces',
|
||||
field=models.ManyToManyField(blank=True, to='receipe.traces'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PurchaseArticle',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('quantity', models.PositiveIntegerField(verbose_name='quantity')),
|
||||
('net_weight', models.DecimalField(decimal_places=3, max_digits=7, verbose_name='net weight')),
|
||||
('price', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='price')),
|
||||
('inSale', models.BooleanField(verbose_name='in sale')),
|
||||
('article_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='receipe.article', verbose_name='article id')),
|
||||
('purchase_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='receipe.purchase', verbose_name='purchase id')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='purchase',
|
||||
name='articles',
|
||||
field=models.ManyToManyField(through='receipe.PurchaseArticle', to='receipe.article'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='purchase',
|
||||
name='market',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='receipe.market', verbose_name='market id'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.0.3 on 2022-06-11 15:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='purchasearticle',
|
||||
name='inSale',
|
||||
field=models.BooleanField(default=False, verbose_name='in sale'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='purchasearticle',
|
||||
name='net_weight',
|
||||
field=models.DecimalField(blank=True, decimal_places=3, max_digits=7, null=True, verbose_name='net weight'),
|
||||
),
|
||||
]
|
||||
18
receipeServer/receipe/migrations/0003_article_labels.py
Normal file
18
receipeServer/receipe/migrations/0003_article_labels.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2022-06-18 13:42
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0002_alter_purchasearticle_insale_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='labels',
|
||||
field=models.ManyToManyField(blank=True, to='receipe.labels'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,58 @@
|
||||
# Generated by Django 4.0.3 on 2022-06-18 13:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0003_article_labels'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='allergenes',
|
||||
name='name',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='allergene name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='brand',
|
||||
name='name',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='brand name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='embs',
|
||||
name='name',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='emb name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ingredients',
|
||||
name='name',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='ingredient name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='labels',
|
||||
name='name',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='label name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='manufacturingplaces',
|
||||
name='name',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='manufacturing places name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='originingredients',
|
||||
name='name',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='origin of igredients'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='packaging',
|
||||
name='name',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='packaging name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='traces',
|
||||
name='name',
|
||||
field=models.CharField(max_length=50, unique=True, verbose_name='trace name'),
|
||||
),
|
||||
]
|
||||
18
receipeServer/receipe/migrations/0005_article_ecograde.py
Normal file
18
receipeServer/receipe/migrations/0005_article_ecograde.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2022-06-19 16:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0004_alter_allergenes_name_alter_brand_name_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='ecoGrade',
|
||||
field=models.CharField(blank=True, max_length=1, null=True, verbose_name='eco grade'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,303 @@
|
||||
# Generated by Django 4.0.3 on 2022-06-19 20:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0005_article_ecograde'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='calcium',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='calcium'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='calciumUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='carbohydrate',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='carbohydrate'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='carbohydrateUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='chlorid',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='chlorid'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='chloridUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='dietaryFiber',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='dietaryFiber'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='dietaryFiberUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='energy',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='energy'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='energyUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='fat',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='fat'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='fatUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='hydrogencarbonat',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='hydrogencarbonat'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='hydrogencarbonatUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='iron',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='iron'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='ironUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='magnesium',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='magnesium'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='magnesiumUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='potassium',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='potassium'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='potassiumUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='proteins',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='proteins'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='proteinsUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='salt',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='salt'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='saltUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='saturatedFat',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='saturatedFat'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='saturatedFatUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='sodium',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='sodium'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='sodiumUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='sugars',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='sugars'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='sugarsUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='sulfat',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='sulfat'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='sulfatUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_a',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_a'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_aUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b1',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_b1'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b12',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_b12'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b12Unit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b1mUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b2',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_b2'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b2Unit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b3',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_b3'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b3Unit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b5',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_b5'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b5Unit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b7',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_b7'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b7Unit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b9',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_b9'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_b9Unit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_c',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_c'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_cUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_d',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_d'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_dUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_e',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_e'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_eUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_k',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='vitamin_k'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='vitamin_kUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='zinc',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True, verbose_name='zinc'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='zincUnit',
|
||||
field=models.CharField(blank=True, max_length=2, null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2022-06-20 16:14
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0006_alter_nutritionalvalues_calcium_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='nutritionalvalues',
|
||||
old_name='vitamin_b1mUnit',
|
||||
new_name='vitamin_b1Unit',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,42 @@
|
||||
# Generated by Django 4.0.3 on 2022-07-09 12:54
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0007_rename_vitamin_b1munit_nutritionalvalues_vitamin_b1unit'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='category',
|
||||
name='parent',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='level',
|
||||
field=models.CharField(default='0', max_length=100),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='packagingarticle',
|
||||
name='article_id',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='receipe.article', verbose_name='article id'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='packagingarticle',
|
||||
name='packaging_id',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='receipe.packaging', verbose_name='packaging id'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='category',
|
||||
index=models.Index(fields=['name'], name='receipe_cat_name_e79a8c_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='category',
|
||||
index=models.Index(fields=['level'], name='receipe_cat_level_4234e7_idx'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2022-11-04 15:50
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0008_remove_category_parent_category_level_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='article',
|
||||
old_name='packaging',
|
||||
new_name='packagings',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2023-02-23 14:11
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0009_rename_packaging_article_packagings'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='energyUnit',
|
||||
field=models.CharField(blank=True, max_length=4, null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,47 @@
|
||||
# Generated by Django 4.0.3 on 2023-02-23 17:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0010_alter_nutritionalvalues_energyunit'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveIndex(
|
||||
model_name='category',
|
||||
name='receipe_cat_name_e79a8c_idx',
|
||||
),
|
||||
migrations.RemoveIndex(
|
||||
model_name='category',
|
||||
name='receipe_cat_level_4234e7_idx',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='category',
|
||||
name='level',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='depth',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='numchild',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='path',
|
||||
field=models.CharField(default='', max_length=255, unique=True),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='category',
|
||||
name='name',
|
||||
field=models.CharField(max_length=30),
|
||||
),
|
||||
]
|
||||
18
receipeServer/receipe/migrations/0012_alter_category_name.py
Normal file
18
receipeServer/receipe/migrations/0012_alter_category_name.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2023-02-23 17:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0011_remove_category_receipe_cat_name_e79a8c_idx_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='category',
|
||||
name='name',
|
||||
field=models.CharField(max_length=30, unique=True, verbose_name='category name'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 4.0.3 on 2023-02-23 18:16
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0012_alter_category_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='category',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Category',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
# Generated by Django 4.0.3 on 2023-02-23 18:22
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0013_remove_article_category_delete_category'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Category',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('path', models.CharField(max_length=255, unique=True)),
|
||||
('depth', models.PositiveIntegerField()),
|
||||
('numchild', models.PositiveIntegerField(default=0)),
|
||||
('name', models.CharField(max_length=30, unique=True, verbose_name='category name')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='category',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='receipe.category', verbose_name='category id'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2023-02-24 16:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0014_category_article_category'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
field=models.ImageField(default=None, help_text='Front image', null=True, unique=True, upload_to='frontImages', verbose_name='front image'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.0.3 on 2023-02-25 12:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0015_alter_article_frontimage'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='ingredientsImage',
|
||||
field=models.ImageField(default=None, help_text='Ingredients image', null=True, unique=True, upload_to='ingredientsImages', verbose_name='ingredients image filepath'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='nutritionImage',
|
||||
field=models.ImageField(default=None, help_text='Nutrition image', null=True, unique=True, upload_to='nutritionImages', verbose_name='nutriton image filepath'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 4.0.3 on 2023-02-25 17:56
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0016_alter_article_ingredientsimage_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='purchasearticle',
|
||||
name='article_id',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='receipe.article', verbose_name='article id'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='purchasearticle',
|
||||
name='purchase_id',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='receipe.purchase', verbose_name='purchase id'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-01 08:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0017_alter_purchasearticle_article_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Front image', null=True, unique=True, upload_to='frontImages', verbose_name='front image'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='ingredientsImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Ingredients image', null=True, unique=True, upload_to='ingredientsImages', verbose_name='ingredients image filepath'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='nutritionImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Nutrition image', null=True, unique=True, upload_to='nutritionImages', verbose_name='nutriton image filepath'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-01 08:10
|
||||
|
||||
from django.db import migrations
|
||||
import receipe.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0018_alter_article_frontimage_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
field=receipe.models.ImageNullField(blank=True, default=None, help_text='Front image', null=True, unique=True, upload_to='frontImages', verbose_name='front image'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-01 08:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0019_alter_article_frontimage'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Front image', null=True, unique=True, upload_to='frontImages', verbose_name='front image'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-01 08:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0020_alter_article_frontimage'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Front image', upload_to='frontImages', verbose_name='front image'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='ingredientsImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Ingredients image', upload_to='ingredientsImages', verbose_name='ingredients image filepath'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='nutritionImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Nutrition image', upload_to='nutritionImages', verbose_name='nutriton image filepath'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-01 08:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0021_alter_article_frontimage_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Front image', null=True, upload_to='frontImages', verbose_name='front image'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='ingredientsImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Ingredients image', null=True, upload_to='ingredientsImages', verbose_name='ingredients image filepath'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='nutritionImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Nutrition image', null=True, upload_to='nutritionImages', verbose_name='nutriton image filepath'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-01 08:52
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0022_alter_article_frontimage_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Front image', null=True, unique=True, upload_to='frontImages', verbose_name='front image'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='ingredientsImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Ingredients image', null=True, unique=True, upload_to='ingredientsImages', verbose_name='ingredients image filepath'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='nutritionImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Nutrition image', null=True, unique=True, upload_to='nutritionImages', verbose_name='nutriton image filepath'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-01 08:54
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0023_alter_article_frontimage_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='ingredientsImage',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='nutritionImage',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-01 08:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0024_remove_article_frontimage_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Front image', upload_to='frontImages', verbose_name='front image'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='ingredientsImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Ingredients image', upload_to='ingredientsImages', verbose_name='ingredients image filepath'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='nutritionImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Nutrition image', upload_to='nutritionImages', verbose_name='nutriton image filepath'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-01 08:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0025_article_frontimage_article_ingredientsimage_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Front image', null=True, upload_to='frontImages', verbose_name='front image'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='ingredientsImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Ingredients image', null=True, upload_to='ingredientsImages', verbose_name='ingredients image filepath'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='nutritionImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Nutrition image', null=True, upload_to='nutritionImages', verbose_name='nutriton image filepath'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-01 08:56
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0026_alter_article_frontimage_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='ingredientsImage',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='nutritionImage',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-01 08:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0027_remove_article_frontimage_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='frontImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Front image', null=True, upload_to='frontImages', verbose_name='front image'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='ingredientsImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Ingredients image', null=True, upload_to='ingredientsImages', verbose_name='ingredients image filepath'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='nutritionImage',
|
||||
field=models.ImageField(blank=True, default=None, help_text='Nutrition image', null=True, upload_to='nutritionImages', verbose_name='nutriton image filepath'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-02 15:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0028_article_frontimage_article_ingredientsimage_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='articlemaps',
|
||||
name='location_h',
|
||||
field=models.PositiveIntegerField(null=True, verbose_name='height in image'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='articlemaps',
|
||||
name='location_w',
|
||||
field=models.PositiveIntegerField(null=True, verbose_name='width in image'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='articlemaps',
|
||||
name='location_x',
|
||||
field=models.PositiveIntegerField(null=True, verbose_name='x coordiante in image'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='articlemaps',
|
||||
name='location_y',
|
||||
field=models.PositiveIntegerField(null=True, verbose_name='y coordiante in image'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-03 14:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0029_alter_articlemaps_location_h_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ingredients',
|
||||
name='allergenes',
|
||||
field=models.ManyToManyField(blank=True, to='receipe.allergenes'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ingredients',
|
||||
name='subIngredients',
|
||||
field=models.ManyToManyField(blank=True, to='receipe.ingredients'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ingredients',
|
||||
name='vegan',
|
||||
field=models.BooleanField(default=False, verbose_name='vegan?'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ingredients',
|
||||
name='vegetarian',
|
||||
field=models.BooleanField(default=False, verbose_name='vegetarian?'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-03 14:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0030_ingredients_allergenes_ingredients_subingredients_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ingredients',
|
||||
name='palmoilfree',
|
||||
field=models.BooleanField(default=False, verbose_name='Palm oil free?'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-03 14:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0031_ingredients_palmoilfree'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ingredients',
|
||||
name='palmoilfree',
|
||||
field=models.BooleanField(default=True, verbose_name='Palm oil free?'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-03 14:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0032_alter_ingredients_palmoilfree'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ingredients',
|
||||
name='additive',
|
||||
field=models.BooleanField(blank=True, default=False, verbose_name='Additiv?'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ingredients',
|
||||
name='eNumber',
|
||||
field=models.CharField(max_length=5, null=True, verbose_name='E number'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-03 14:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0033_ingredients_additive_ingredients_enumber'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ingredients',
|
||||
name='additive',
|
||||
field=models.BooleanField(default=False, verbose_name='Additiv?'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ingredients',
|
||||
name='eNumber',
|
||||
field=models.CharField(blank=True, max_length=5, null=True, verbose_name='E number'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,36 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-03 15:21
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0034_alter_ingredients_additive_alter_ingredients_enumber'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='IngredientsArticle',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('position', models.PositiveIntegerField(verbose_name='Position')),
|
||||
],
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='ingredients',
|
||||
field=models.ManyToManyField(blank=True, through='receipe.IngredientsArticle', to='receipe.ingredients'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ingredientsarticle',
|
||||
name='article',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='receipe.article', verbose_name='article'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ingredientsarticle',
|
||||
name='ingredient',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='receipe.ingredients', verbose_name='Ingredient'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-03 15:21
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0035_ingredientsarticle_alter_article_ingredients_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='ingredients',
|
||||
),
|
||||
]
|
||||
18
receipeServer/receipe/migrations/0037_article_ingredients.py
Normal file
18
receipeServer/receipe/migrations/0037_article_ingredients.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-03 15:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0036_remove_article_ingredients'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='ingredients',
|
||||
field=models.ManyToManyField(blank=True, through='receipe.IngredientsArticle', to='receipe.ingredients'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-03 15:25
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0037_article_ingredients'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='article',
|
||||
name='ingredients',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='IngredientsArticle',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,36 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-03 15:26
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0038_remove_article_ingredients_delete_ingredientsarticle'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='IngredientsArticle',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('position', models.PositiveIntegerField(verbose_name='Position')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='ingredients',
|
||||
field=models.ManyToManyField(blank=True, through='receipe.IngredientsArticle', to='receipe.ingredients'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ingredientsarticle',
|
||||
name='article',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='receipe.article', verbose_name='article'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ingredientsarticle',
|
||||
name='ingredient',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='receipe.ingredients', verbose_name='Ingredient'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,63 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-05 19:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0039_ingredientsarticle_article_ingredients_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ingredientsarticle',
|
||||
name='percent',
|
||||
field=models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='Percentage on total weight'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ingredients',
|
||||
name='eNumber',
|
||||
field=models.CharField(blank=True, max_length=7, null=True, verbose_name='E number'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='carbohydrateUnit',
|
||||
field=models.CharField(blank=True, default='g', max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='dietaryFiberUnit',
|
||||
field=models.CharField(blank=True, default='g', max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='energyUnit',
|
||||
field=models.CharField(blank=True, default='kJ', max_length=4, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='fatUnit',
|
||||
field=models.CharField(blank=True, default='g', max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='proteinsUnit',
|
||||
field=models.CharField(blank=True, default='g', max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='saltUnit',
|
||||
field=models.CharField(blank=True, default='g', max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='saturatedFatUnit',
|
||||
field=models.CharField(blank=True, default='g', max_length=2, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='nutritionalvalues',
|
||||
name='sugarsUnit',
|
||||
field=models.CharField(blank=True, default='g', max_length=2, null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-05 21:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0040_ingredientsarticle_percent_alter_ingredients_enumber_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='purchasearticle',
|
||||
name='price',
|
||||
field=models.DecimalField(decimal_places=2, default=0, max_digits=7, verbose_name='price'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-07 17:57
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0041_alter_purchasearticle_price'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='purchasearticle',
|
||||
name='article_id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='purchasearticle',
|
||||
name='article',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='receipe.article', verbose_name='article object'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-07 17:59
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0042_remove_purchasearticle_article_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='purchasearticle',
|
||||
name='article',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='purchasearticle',
|
||||
name='article_id',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='receipe.article', verbose_name='article id'),
|
||||
),
|
||||
]
|
||||
19
receipeServer/receipe/migrations/0044_alter_article_unit.py
Normal file
19
receipeServer/receipe/migrations/0044_alter_article_unit.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.0.3 on 2023-03-08 14:44
|
||||
|
||||
from django.db import migrations
|
||||
import receipe.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0043_remove_purchasearticle_article_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='article',
|
||||
name='unit',
|
||||
field=receipe.models.CharNullField(blank=True, max_length=4, null=True, verbose_name='unit'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.0.3 on 2023-04-09 16:42
|
||||
|
||||
from django.db import migrations
|
||||
import receipe.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0044_alter_article_unit'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ingredients',
|
||||
name='eNumber',
|
||||
field=receipe.models.CharNullField(blank=True, max_length=7, null=True, verbose_name='E number'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2023-04-16 14:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0045_alter_ingredients_enumber'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ingredientsarticle',
|
||||
name='percent',
|
||||
field=models.FloatField(blank=True, default=None, null=True, verbose_name='Percentage on total weight'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2023-04-16 14:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('receipe', '0046_alter_ingredientsarticle_percent'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ingredients',
|
||||
name='name',
|
||||
field=models.CharField(max_length=100, unique=True, verbose_name='ingredient name'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.2.1 on 2023-07-15 11:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0047_alter_ingredients_name"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="ingredients",
|
||||
name="name",
|
||||
field=models.CharField(max_length=100, verbose_name="ingredient name"),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Generated by Django 4.2.1 on 2023-07-17 16:04
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0048_alter_ingredients_name"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name="articlemaps",
|
||||
unique_together={("article", "receipeImage")},
|
||||
),
|
||||
]
|
||||
17
receipeServer/receipe/migrations/0050_alter_article_name.py
Normal file
17
receipeServer/receipe/migrations/0050_alter_article_name.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.2.1 on 2023-08-07 18:21
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0049_alter_articlemaps_unique_together"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="article",
|
||||
name="name",
|
||||
field=models.CharField(max_length=100, verbose_name="article name"),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.2.1 on 2023-08-20 11:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0050_alter_article_name"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="purchase",
|
||||
name="edit_finished",
|
||||
field=models.BooleanField(default=True, verbose_name="edit finished"),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.2.1 on 2023-08-20 11:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0051_purchase_edit_finished"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="purchase",
|
||||
name="edit_finished",
|
||||
field=models.BooleanField(default=False, verbose_name="edit finished"),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,97 @@
|
||||
# Generated by Django 4.2.1 on 2023-10-12 11:53
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0052_alter_purchase_edit_finished"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Nutrient",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
max_length=50, unique=True, verbose_name="nutrient name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"isMacroNutrient",
|
||||
models.BooleanField(
|
||||
default=False, verbose_name="Is macronutrient?"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="NutrientArticle",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"value",
|
||||
models.DecimalField(
|
||||
blank=True,
|
||||
decimal_places=2,
|
||||
max_digits=7,
|
||||
null=True,
|
||||
verbose_name="value",
|
||||
),
|
||||
),
|
||||
("unit", models.CharField(blank=True, max_length=4, null=True)),
|
||||
(
|
||||
"isEstimated",
|
||||
models.BooleanField(
|
||||
default=False, verbose_name="Is nutrient exstimated?"
|
||||
),
|
||||
),
|
||||
(
|
||||
"article",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="receipe.article",
|
||||
verbose_name="article",
|
||||
),
|
||||
),
|
||||
(
|
||||
"nutrient",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="receipe.nutrient",
|
||||
verbose_name="nutrient",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="article",
|
||||
name="nutrients",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, through="receipe.NutrientArticle", to="receipe.nutrient"
|
||||
),
|
||||
),
|
||||
]
|
||||
41
receipeServer/receipe/migrations/0054_nutrientalias.py
Normal file
41
receipeServer/receipe/migrations/0054_nutrientalias.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# Generated by Django 4.2.1 on 2023-10-12 12:11
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0053_nutrient_nutrientarticle_article_nutrients"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="NutrientAlias",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
max_length=50, unique=True, verbose_name="nutrient alias name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"nutrient",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="receipe.nutrient",
|
||||
verbose_name="Nutrient",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
84
receipeServer/receipe/migrations/0055_auto_20231012_1612.py
Normal file
84
receipeServer/receipe/migrations/0055_auto_20231012_1612.py
Normal file
@@ -0,0 +1,84 @@
|
||||
# Generated by Django 4.2.1 on 2023-10-12 16:12
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def transfer_nutrivals(apps, schema_editor):
|
||||
NutritionalValues = apps.get_model("receipe", "NutritionalValues")
|
||||
Article = apps.get_model("receipe", "Article")
|
||||
Nutrient = apps.get_model("receipe", "Nutrient")
|
||||
NutrientArticle = apps.get_model("receipe", "NutrientArticle")
|
||||
|
||||
for nutriVal in NutritionalValues.objects.filter(energy__isnull=False):
|
||||
keyVal = [['Energie','energy'],
|
||||
['Fett','fat'],
|
||||
['gesättigte Fettsäuren','saturatedFat'],
|
||||
['Kohlenhydrate','carbohydrate'],
|
||||
['Zucker','sugars'],
|
||||
['Ballaststoffe','dietaryFiber'],
|
||||
['Eiweiß','proteins'],
|
||||
['Salz','salt'],
|
||||
['Natrium','sodium'],
|
||||
['Vitamin A','vitamin_a'],
|
||||
['Vitamin B3','vitamin_b3'],
|
||||
['Vitamin B1','vitamin_b1'],
|
||||
['Vitamin B2','vitamin_b2'],
|
||||
['Vitamin B5','vitamin_b5'],
|
||||
#['Vitamin B6','vitamin_b6'],
|
||||
['Vitamin B7','vitamin_b7'],
|
||||
['Vitamin B9','vitamin_b9'],
|
||||
['Vitamin B12','vitamin_b12'],
|
||||
['Vitamin C','vitamin_c'],
|
||||
['Vitamin D','vitamin_d'],
|
||||
['Vitamin E','vitamin_e'],
|
||||
['Vitamin K','vitamin_k'],
|
||||
['Kalium','potassium'],
|
||||
['Kalzium','calcium'],
|
||||
['Magnesium','magnesium'],
|
||||
['Eisen','iron'],
|
||||
['Zink','zinc'],
|
||||
['Sulfat','sulfat'],
|
||||
['Chlorid','chlorid'],
|
||||
['Hydrogencarbonat','hydrogencarbonat']
|
||||
]
|
||||
|
||||
for element in keyVal:
|
||||
if eval('nutriVal.'+element[1]) is not None:
|
||||
if element[0] != 'Salz' or element[0] != 'Natrium':
|
||||
nutrient = Nutrient.objects.get(name=element[0])
|
||||
nutrienArticle = NutrientArticle(article=nutriVal.article,
|
||||
nutrient=nutrient,
|
||||
value=eval('nutriVal.'+element[1]),
|
||||
unit=eval('nutriVal.'+element[1]+'Unit'),
|
||||
isEstimated=False
|
||||
)
|
||||
nutrienArticle.save()
|
||||
|
||||
elif element[0] == 'Salz':
|
||||
nutrient = Nutrient.objects.get(name=element[0])
|
||||
nutrienArticle = NutrientArticle(article=nutriVal.article,
|
||||
nutrient=nutrient,
|
||||
value=eval('nutriVal.'+element[1]),
|
||||
unit=eval('nutriVal.'+element[1]+'Unit'),
|
||||
isEstimated=False
|
||||
)
|
||||
nutrienArticle.save()
|
||||
|
||||
nutrient = Nutrient.objects.get(name='Natrium')
|
||||
nutrienArticle = NutrientArticle(article=nutriVal.article,
|
||||
nutrient=nutrient,
|
||||
value=eval('nutriVal.'+element[1])*0.3937,
|
||||
unit=eval('nutriVal.'+element[1]+'Unit'),
|
||||
isEstimated=False
|
||||
)
|
||||
nutrienArticle.save()
|
||||
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0054_nutrientalias"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(transfer_nutrivals),
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Generated by Django 4.2.1 on 2023-10-13 14:12
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0055_auto_20231012_1612"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name="nutrientarticle",
|
||||
unique_together={("article", "nutrient")},
|
||||
),
|
||||
]
|
||||
32
receipeServer/receipe/migrations/0057_receipestring.py
Normal file
32
receipeServer/receipe/migrations/0057_receipestring.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# Generated by Django 4.2.1 on 2023-10-16 13:07
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0056_alter_nutrientarticle_unique_together"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ReceipeString",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"receipeString",
|
||||
models.CharField(
|
||||
max_length=50, unique=True, verbose_name="receipe string"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.1 on 2023-10-16 13:39
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0057_receipestring"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="articlemaps",
|
||||
name="receipeStringRef",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="receipe.receipestring",
|
||||
verbose_name="receipe string",
|
||||
),
|
||||
),
|
||||
]
|
||||
30
receipeServer/receipe/migrations/0059_auto_20231016_1341.py
Normal file
30
receipeServer/receipe/migrations/0059_auto_20231016_1341.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 4.2.1 on 2023-10-16 13:41
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def transfer_values(apps, schema_editor):
|
||||
ReceipeString = apps.get_model("receipe", "ReceipeString")
|
||||
ArticleMaps = apps.get_model("receipe", "ArticleMaps")
|
||||
|
||||
#Get all unique receipeStrings form ArticleMaps
|
||||
receipeStrings = ArticleMaps.objects.values('receipeString').distinct()
|
||||
|
||||
#Add all unique receipeStrings to ReceipeString
|
||||
for element in receipeStrings:
|
||||
receipeString = ReceipeString(receipeString=element['receipeString'])
|
||||
receipeString.save()
|
||||
|
||||
#For each receiepString in ArticleMaps, get the corresponding ReceipeString object and add it to ArticleMaps-receipeStringRef
|
||||
for element in ArticleMaps.objects.all():
|
||||
receipeString = ReceipeString.objects.get(receipeString=element.receipeString)
|
||||
element.receipeStringRef = receipeString
|
||||
element.save()
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0058_articlemaps_receipestringref"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(transfer_values),
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.2.1 on 2023-10-16 15:02
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0059_auto_20231016_1341"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="articlemaps",
|
||||
name="receipeString",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='articlemaps',
|
||||
old_name='receipeStringRef',
|
||||
new_name='receipeString',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.2.1 on 2023-10-16 15:57
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0060_remove_articlemaps_receipestringref_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name="receipestring",
|
||||
old_name="receipeString",
|
||||
new_name="name",
|
||||
),
|
||||
]
|
||||
11
receipeServer/receipe/migrations/0062_auto_20231019_1207.py
Normal file
11
receipeServer/receipe/migrations/0062_auto_20231019_1207.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# Generated by Django 4.2.1 on 2023-10-19 12:07
|
||||
|
||||
from django.db import migrations
|
||||
from django.contrib.postgres.operations import TrigramExtension
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("receipe", "0061_rename_receipestring_receipestring_name"),
|
||||
]
|
||||
|
||||
operations = [TrigramExtension()]
|
||||
0
receipeServer/receipe/migrations/__init__.py
Normal file
0
receipeServer/receipe/migrations/__init__.py
Normal file
633
receipeServer/receipe/models.py
Normal file
633
receipeServer/receipe/models.py
Normal file
@@ -0,0 +1,633 @@
|
||||
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)
|
||||
|
||||
743
receipeServer/receipe/parser.py
Normal file
743
receipeServer/receipe/parser.py
Normal file
@@ -0,0 +1,743 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sat Apr 30 22:47:15 2022
|
||||
|
||||
@author: elena
|
||||
"""
|
||||
|
||||
#import logging
|
||||
import cv2
|
||||
import re
|
||||
import os
|
||||
from copy import deepcopy
|
||||
import numpy as np
|
||||
import pytesseract
|
||||
from scipy import stats
|
||||
from ordered_set import OrderedSet
|
||||
|
||||
from .loggers import LoggingMixin
|
||||
from .image_processing import BBox
|
||||
from .classes import noDBArticle
|
||||
#from .models import ReceipeString, Market
|
||||
import receipe.models as models
|
||||
from .linearalignment import LinearAlignment
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
|
||||
from django.contrib.postgres.search import TrigramSimilarity
|
||||
|
||||
# This regular expression will try to find dates in the document at
|
||||
# hand and will match the following formats:
|
||||
# - XX.YY.ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
|
||||
# - XX/YY/ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
|
||||
# - XX-YY-ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
|
||||
# - ZZZZ.XX.YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
|
||||
# - ZZZZ/XX/YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
|
||||
# - ZZZZ-XX-YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
|
||||
# - XX. MONTH ZZZZ with XX being 1 or 2 and ZZZZ being 2 or 4 digits
|
||||
# - MONTH ZZZZ, with ZZZZ being 4 digits
|
||||
# - MONTH XX, ZZZZ with XX being 1 or 2 and ZZZZ being 4 digits
|
||||
|
||||
DATE_REGEX = re.compile(
|
||||
r'(\b|(?!=([_-])))([0-9]{1,2})[\.\/-]([0-9]{1,2})[\.\/-]([0-9]{4}|[0-9]{2})(\b|(?=([_-])))|' # NOQA: E501
|
||||
r'(\b|(?!=([_-])))([0-9]{4}|[0-9]{2})[\.\/-]([0-9]{1,2})[\.\/-]([0-9]{1,2})(\b|(?=([_-])))|' # NOQA: E501
|
||||
r'(\b|(?!=([_-])))([0-9]{1,2}[\. ]+[^ ]{3,9} ([0-9]{4}|[0-9]{2}))(\b|(?=([_-])))|' # NOQA: E501
|
||||
r'(\b|(?!=([_-])))([^\W\d_]{3,9} [0-9]{1,2}, ([0-9]{4}))(\b|(?=([_-])))|'
|
||||
r'(\b|(?!=([_-])))([^\W\d_]{3,9} [0-9]{4})(\b|(?=([_-])))'
|
||||
)
|
||||
|
||||
STREET_REGEX = re.compile(r'[A-Z][a-z]{1,} \d{1,3}')
|
||||
ZIP_REGEX = re.compile(r'\d{5} .*') #Valid for Germany
|
||||
PHONE_REGEX = re.compile(r'\d{4,}(\/|-| \/ | - )\d*')
|
||||
FLOAT_REGEX = re.compile(r'(|-|\+)\d*(,|\.)\d*')
|
||||
MULTIPLE_REGEX = re.compile(r'\d{1,}(x|X){1,}')
|
||||
|
||||
|
||||
class ReceipeParser(LoggingMixin):
|
||||
logging_name = "receipeServer.parsing"
|
||||
|
||||
def __init__(self, logging_group, debug=False, progress_callback=None):
|
||||
super().__init__()
|
||||
self.logging_group = logging_group
|
||||
os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
|
||||
|
||||
self.debug = debug
|
||||
self.archive_path = None
|
||||
self.date = None
|
||||
self.market = None
|
||||
self.articles = None
|
||||
self.total = None
|
||||
self.progress_callback = progress_callback
|
||||
self.knownArticles = OrderedSet([])
|
||||
self.unknownArticles = set([])
|
||||
|
||||
def getLine(self,boxList: list) -> list:
|
||||
"""Take a list of bounding boxes (with text) and combine them to (text) lines
|
||||
|
||||
Args:
|
||||
boxList (list): The list containing all bounding boxes
|
||||
|
||||
Returns:
|
||||
lines (list): a list of (text) lines (one bouning box per line)
|
||||
"""
|
||||
|
||||
lines = []
|
||||
idx = 0
|
||||
while idx < len(boxList):
|
||||
line = [boxList[idx]]
|
||||
|
||||
yref1 = boxList[idx].y
|
||||
yref2 = yref1 + boxList[idx].h
|
||||
idy = idx + 1
|
||||
#Iterate over all boxes
|
||||
while idy < len(boxList):
|
||||
yval1 = boxList[idy].y
|
||||
yval2 = boxList[idy].y + boxList[idy].h
|
||||
|
||||
#Calculate the length of overlapp between the two boxes
|
||||
l = np.abs(np.max([yval1,yref1]) - np.min([yval2,yref2]))
|
||||
|
||||
#If distance combared to box 1 and box 2 is more than 50% and there is some overlapp include the box
|
||||
if l/boxList[idx].h > 0.5 and l/boxList[idy].h > 0.5 and not (yval2 < yref1 or yval1 > yref2):
|
||||
line.append(boxList[idy])
|
||||
|
||||
idx = idx + 1
|
||||
idy = idy + 1
|
||||
else:
|
||||
idy = idy + 1
|
||||
|
||||
line.sort(key=lambda s: s.x)
|
||||
lines.append(line)
|
||||
idx = idx + 1
|
||||
|
||||
return lines
|
||||
|
||||
def grouper(self, iterable: list, interval: int = 2) -> list:
|
||||
"""Group iterable into lines, within interval height. I do not understand what the code does
|
||||
|
||||
Args:
|
||||
iterable (list): The list containing all (character) bounding boxes
|
||||
interval (int): Measure to decide if boxes should be included in line
|
||||
|
||||
Returns:
|
||||
group (generator): returns a generator for the lines
|
||||
"""
|
||||
prev = None
|
||||
group = []
|
||||
for item in iterable:
|
||||
if not prev or abs(item[1] - prev[1]) <= interval:
|
||||
group.append(item)
|
||||
else:
|
||||
yield group
|
||||
group = [item]
|
||||
prev = item
|
||||
if group:
|
||||
yield group
|
||||
|
||||
def doOCR(self,imgwidth: int, rois: list, lines: list, h: float, border: int = 10) -> list:
|
||||
"""For the given rois (image parts) apply OCR and return the corresponding text
|
||||
|
||||
Args:
|
||||
imgwidth (int): Width of the total image
|
||||
rois (list): List containing the image fragments
|
||||
lines (list): List with the bounding boxes
|
||||
h (float): Mean lineheigth
|
||||
border: Padding to apply around the bounding box
|
||||
|
||||
Returns:
|
||||
texts (list): a list of the text of the different lines
|
||||
"""
|
||||
texts = []
|
||||
|
||||
#Tesseract
|
||||
for idy in range(0,len(rois)):
|
||||
lineroi = rois[idy]
|
||||
linebox = lines[idy]
|
||||
|
||||
tmptext = []
|
||||
|
||||
for idx in range(len(lineroi)):
|
||||
lineimg = np.ones((h+2*border,imgwidth), np.uint8)*255
|
||||
lineimg[border:border+h,linebox[idx].x:linebox[idx].x+linebox[idx].w][:] = cv2.resize(lineroi[idx], (linebox[idx].w,h))
|
||||
tmptext.append(pytesseract.image_to_string(lineimg,lang='deu').strip())
|
||||
|
||||
if tmptext != '':
|
||||
texts.append(tmptext)
|
||||
|
||||
print(texts)
|
||||
return texts
|
||||
|
||||
def parse_date(self,text: str):
|
||||
"""
|
||||
Returns the date of the receipe.
|
||||
"""
|
||||
|
||||
def __parser(ds, date_order):
|
||||
"""
|
||||
Call dateparser.parse with a particular date ordering
|
||||
"""
|
||||
import dateparser
|
||||
|
||||
return dateparser.parse(
|
||||
ds,
|
||||
settings={
|
||||
"DATE_ORDER": date_order,
|
||||
"PREFER_DAY_OF_MONTH": "first",
|
||||
"RETURN_AS_TIMEZONE_AWARE":
|
||||
True
|
||||
}
|
||||
)
|
||||
|
||||
def __filter(date):
|
||||
if date and date.year > 1900 and \
|
||||
date <= timezone.now():
|
||||
return date
|
||||
return None
|
||||
|
||||
date = None
|
||||
|
||||
# Iterate through all regex matches in text and try to parse the date
|
||||
for m in re.finditer(DATE_REGEX, text):
|
||||
date_string = m.group(0)
|
||||
|
||||
try:
|
||||
date = __parser(date_string, settings.DATE_ORDER)
|
||||
except (TypeError, ValueError):
|
||||
# Skip all matches that do not parse to a proper date
|
||||
continue
|
||||
|
||||
date = __filter(date)
|
||||
if date is not None:
|
||||
break
|
||||
|
||||
return date
|
||||
|
||||
def parse_zip_city(self,text):
|
||||
zipCode = None
|
||||
city = None
|
||||
for m in re.findall(ZIP_REGEX, text):
|
||||
stringElements = m.split(' ')
|
||||
zipCode = stringElements[0]
|
||||
if len(stringElements) > 1:
|
||||
city = stringElements[1]
|
||||
return zipCode, city
|
||||
|
||||
def parse_street_streetNum(self,text):
|
||||
street = None
|
||||
streetNum = None
|
||||
for m in re.findall(STREET_REGEX, text):
|
||||
stringElements = m.split(' ')
|
||||
street = stringElements[0]
|
||||
if len(stringElements) > 1:
|
||||
streetNum = stringElements[1]
|
||||
return street, streetNum
|
||||
|
||||
def parse_phone(self,text):
|
||||
for m in re.finditer(PHONE_REGEX, text):
|
||||
return m.group(0)
|
||||
|
||||
def parse_float(self,text):
|
||||
for m in re.finditer(FLOAT_REGEX, text):
|
||||
value = m.group(0)
|
||||
value = value.replace(',','.')
|
||||
try:
|
||||
value = float(value)
|
||||
except ValueError:
|
||||
value = 0
|
||||
return value
|
||||
return 0
|
||||
|
||||
def extractMarket(self,lineText,allText):
|
||||
'''Extract market detail out of the quarter of lines (allow for some artefacts from logo)
|
||||
'''
|
||||
tmpMarket = models.Market()
|
||||
for line in lineText:
|
||||
if line[0] != '':
|
||||
tmpMarket.name = line[0]
|
||||
break
|
||||
|
||||
roi = ''
|
||||
try:
|
||||
for idx in range(0,int(len(lineText)/4)):
|
||||
roi = roi + lineText[idx][0] +'\n'
|
||||
except IndexError:
|
||||
roi = ''
|
||||
|
||||
try:
|
||||
tmpMarket.zipCode, tmpMarket.city = self.parse_zip_city(roi)
|
||||
except TypeError:
|
||||
try:
|
||||
tmpMarket.zipCode, tmpMarket.city = self.parse_zip_city(allText)
|
||||
except TypeError:
|
||||
tmpMarket.zipCode = ''
|
||||
tmpMarket.city = ''
|
||||
|
||||
try:
|
||||
tmpMarket.street, tmpMarket.street_number = self.parse_street_streetNum(roi)
|
||||
except TypeError:
|
||||
try:
|
||||
tmpMarket.street, tmpMarket.street_number = self.parse_street_streetNum(allText)
|
||||
except TypeError:
|
||||
tmpMarket.street = ''
|
||||
tmpMarket.street_number = 0
|
||||
|
||||
try:
|
||||
tmpMarket.phone = self.parse_phone(roi)
|
||||
except TypeError:
|
||||
try:
|
||||
tmpMarket.phone = self.parse_phone(allText)
|
||||
except TypeError:
|
||||
tmpMarket.phone = ''
|
||||
|
||||
#TODO: Try some fuzzy search
|
||||
#markets = Market.objects.filter( name=tmpMarket.name, street=tmpMarket.street)
|
||||
# Trigram search for name and street
|
||||
markets = models.Market.objects.annotate(similarity=TrigramSimilarity('name', tmpMarket.name)).filter(similarity__gt=0.3).order_by("-similarity")
|
||||
markets = markets.annotate(similarity=TrigramSimilarity('street', tmpMarket.street)).filter(similarity__gt=0.3).order_by("-similarity")
|
||||
|
||||
if len(markets) != 0:
|
||||
return markets[0]
|
||||
else:
|
||||
return tmpMarket
|
||||
|
||||
def progress(self, current_progress, max_progress):
|
||||
if self.progress_callback:
|
||||
self.progress_callback(current_progress, max_progress)
|
||||
|
||||
def fixString(self,oldString):
|
||||
newString = oldString.replace('Ä','A').replace('Ö','O').replace('Ü','U').replace('ä','a').replace('ö','o').replace('ü','u').replace('#','').replace('©','o').replace('“','').replace('*','').replace('‚',',')
|
||||
return newString
|
||||
|
||||
def get_date(self):
|
||||
return self.date
|
||||
|
||||
def get_market(self):
|
||||
return self.market
|
||||
|
||||
def get_articles(self):
|
||||
return [self.knownArticles, self.unknownArticles]
|
||||
|
||||
def get_total(self):
|
||||
return self.total
|
||||
|
||||
def lineSegmentationSimple(self, img: np.array) -> list:
|
||||
#%% Dilate in y direction to detect text lines
|
||||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (75,2))
|
||||
dilate = cv2.dilate(cv2.bitwise_not(img), kernel, iterations=1)
|
||||
|
||||
# Find contours and filter using aspect ratio
|
||||
# Remove non-text contours by filling in the contour
|
||||
edge = cv2.Canny(dilate, 100, 250)
|
||||
cnts = cv2.findContours(edge, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
|
||||
|
||||
xall = []
|
||||
yall = []
|
||||
wall = []
|
||||
hall = []
|
||||
|
||||
textBoxes = []
|
||||
|
||||
for c in cnts:
|
||||
x,y,w,h = cv2.boundingRect(c)
|
||||
xall.append(x)
|
||||
yall.append(y)
|
||||
wall.append(w)
|
||||
hall.append(h)
|
||||
|
||||
zscoreH = stats.zscore(hall)
|
||||
|
||||
for idx in range(0,len(xall)):
|
||||
#ar = w / float(h)
|
||||
#if hall[idx] > lowerHbound:# and hall[idx] < upperHbound:
|
||||
if zscoreH[idx] > 0.1 and zscoreH[idx] < 1.4 and hall[idx] < wall[idx]:
|
||||
tmp = BBox(xall[idx],yall[idx],wall[idx],hall[idx])
|
||||
textBoxes.append(tmp)
|
||||
#cv2.drawContours(color, [cnts[idx]], -1, (0, 255, 0), 3, cv2.LINE_AA)
|
||||
|
||||
|
||||
lines = self.getLine(textBoxes)
|
||||
lines.reverse()
|
||||
|
||||
return lines
|
||||
|
||||
def lineSegmentationMSER(self, img: np.array) -> list:
|
||||
mser = cv2.MSER_create()
|
||||
regions, bboxes = mser.detectRegions(img)
|
||||
|
||||
hulls = [cv2.convexHull(p.reshape(-1, 1, 2)) for p in regions]
|
||||
|
||||
bboxes_list = list()
|
||||
heights = list()
|
||||
|
||||
for hull in hulls:
|
||||
x, y, w, h = cv2.boundingRect(hull)
|
||||
bboxes_list.append([x, y, x + w, y + h]) # Create list of bounding boxes, with each bbox containing the left-top and right-bottom coordinates
|
||||
heights.append(h)
|
||||
|
||||
heights = sorted(heights) # Sort heights
|
||||
median_height = heights[int(len(heights) / 2)] / 3 # Find third of the median height
|
||||
#print(median_height)
|
||||
|
||||
bboxes_list = sorted(bboxes_list, key=lambda k: k[1]) # Sort the bounding boxes based on y1 coordinate ( y of the left-top coordinate )
|
||||
combined_bboxes = self.grouper(bboxes_list, median_height) # Group the bounding boxes
|
||||
|
||||
lines = []
|
||||
for group in combined_bboxes:
|
||||
x_min = min(group, key=lambda k: k[0])[0] # Find min of x1
|
||||
x_max = max(group, key=lambda k: k[2])[2] # Find max of x2
|
||||
y_min = min(group, key=lambda k: k[1])[1] # Find min of y1
|
||||
y_max = max(group, key=lambda k: k[3])[3] # Find max of y2
|
||||
if abs(y_min - y_max) < 3 * 3 * median_height and abs(y_min - y_max) > median_height and abs(x_min - x_max) > 100:
|
||||
#cv2.rectangle(img, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2)
|
||||
lines.append(BBox(x_min,y_min,x_max-x_min,y_max-y_min))
|
||||
|
||||
linesWithSplit = []
|
||||
for element in lines:
|
||||
edges = cv2.Canny(img[element.y:element.y+element.h,element.x:element.x+element.w],100,200)
|
||||
|
||||
#imgplot = plt.imshow(edges)
|
||||
#Calculate projection onto x axis
|
||||
proj_v = np.sum(edges,0)
|
||||
|
||||
#plt.plot(proj_v)
|
||||
#plt.show()
|
||||
|
||||
count = 0
|
||||
prev = 0
|
||||
indexend = 0
|
||||
zeroSequence = []
|
||||
gaplist = []
|
||||
|
||||
#Find all gaps (projected value of 0)
|
||||
for i in range(0,len(proj_v)):
|
||||
if proj_v[i] == 0:
|
||||
count += 1
|
||||
else:
|
||||
if count > prev:
|
||||
prev = count
|
||||
indexend = i
|
||||
|
||||
if count > 70:
|
||||
gaplist.append([prev,indexend-prev,indexend-1])
|
||||
#indexend = 0
|
||||
count = 0
|
||||
prev = 0
|
||||
indexend = 0
|
||||
|
||||
#print("The longest sequence of 0's is "+str(prev))
|
||||
#print("index start at: "+ str(indexend-prev))
|
||||
#print("index ends at: "+ str(indexend-1))
|
||||
|
||||
#Split the line at the borders of the gaps
|
||||
if len (gaplist) < 1:
|
||||
linesWithSplit.append([element])
|
||||
elif len (gaplist) == 1:
|
||||
tmpList = []
|
||||
tmpList.append( BBox(element.x,element.y,gaplist[0][1],element.h) )
|
||||
tmpList.append( BBox(element.x+gaplist[0][2],element.y,element.w-gaplist[0][2],element.h) )
|
||||
linesWithSplit.append(tmpList)
|
||||
else:
|
||||
tmpList = []
|
||||
for i in range(0,len(gaplist)):
|
||||
if i == 0:
|
||||
tmpList.append( BBox(element.x,element.y,gaplist[i][1],element.h) )
|
||||
elif i < len(gaplist):
|
||||
tmpList.append( BBox(element.x+gaplist[i-1][2],element.y,gaplist[i][1]-gaplist[i-1][2],element.h) )
|
||||
|
||||
tmpList.append( BBox(element.x+gaplist[-1][2],element.y,element.w-gaplist[-1][2],element.h) )
|
||||
|
||||
linesWithSplit.append(tmpList)
|
||||
|
||||
return linesWithSplit
|
||||
|
||||
def parse(self,path,inputfile,source='cam'):
|
||||
self.knownArticles = set([])
|
||||
self.unknownArticles = set([])
|
||||
|
||||
# Read image from which text needs to be extracted
|
||||
print(path)
|
||||
img = cv2.imread(path,cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
if source == 'cam':
|
||||
lines = self.lineSegmentationSimple(img)
|
||||
elif source == 'scanner':
|
||||
lines = self.lineSegmentationMSER(img)
|
||||
|
||||
rois = []
|
||||
hsum = 0
|
||||
numh = 0
|
||||
|
||||
for idx in range(0,len(lines)):
|
||||
line = lines[idx]
|
||||
|
||||
tmproi = []
|
||||
for element in line:
|
||||
hsum = hsum + element.h
|
||||
numh = numh + 1
|
||||
#cv2.rectangle(color,(element.x,element.y),(element.x+element.w,element.y+element.h),(255,0,0),3)
|
||||
tmproi.append(img[element.y:element.y+element.h,element.x:element.x+element.w])
|
||||
|
||||
|
||||
rois.append(tmproi)
|
||||
|
||||
hmean = hsum/numh
|
||||
h = int(hmean)
|
||||
|
||||
#newimg = np.ones(((h+border)*len(rois)+2*border,len(img[0,:])), np.uint8)*255
|
||||
|
||||
lineText = self.doOCR(len(img[0,:]), rois, lines, h)
|
||||
allText = pytesseract.image_to_string(img,lang='deu')
|
||||
|
||||
#%% Extract market
|
||||
self.market = self.extractMarket(lineText,allText)
|
||||
print(self.market.name)
|
||||
print(self.market.street)
|
||||
print(self.market.street_number)
|
||||
print(self.market.zip_code)
|
||||
print(self.market.city)
|
||||
print(self.market.phone)
|
||||
|
||||
#%% Extract date
|
||||
date = None
|
||||
|
||||
for element in lineText:
|
||||
date = self.parse_date(' '.join(element))
|
||||
if date != None:
|
||||
break
|
||||
|
||||
if date == None:
|
||||
#Try again with OCR of the whole receipe
|
||||
date = self.parse_date(allText)
|
||||
|
||||
self.date = date
|
||||
print(self.date)
|
||||
|
||||
|
||||
#%% Extract total
|
||||
self.total = 0
|
||||
for line in lineText:
|
||||
text = ' '.join(line)
|
||||
if 'EUR' in text or 'SUMME' in text or 'TOTAL' in text:
|
||||
value = self.parse_float(text)
|
||||
if value != 0:
|
||||
self.total = value
|
||||
break
|
||||
|
||||
# If we find no result, try again with the total text
|
||||
if self.total == 0:
|
||||
for line in allText.split('\n'):
|
||||
text = line
|
||||
if 'EUR' in text or 'SUMME' in text or 'TOTAL' in text:
|
||||
value = self.parse_float(text)
|
||||
if value != 0:
|
||||
self.total = value
|
||||
break
|
||||
|
||||
print(self.total)
|
||||
|
||||
#%% Extract articles
|
||||
self.articles = []
|
||||
newArticle = noDBArticle()
|
||||
newArticle.quantity = 0
|
||||
for idx in range(5,len(lineText)):
|
||||
print(lineText[idx])
|
||||
text = ' '.join(lineText[idx])
|
||||
|
||||
#The last import line of a receipe ends with the total or sum of prices, so afterwards we stop.
|
||||
if ('EUR' in text or 'SUMME' in text or 'TOTAL' in text) and len(self.articles) > 1:
|
||||
break
|
||||
elif ('EUR' in text or 'SUMME' in text or 'TOTAL' in text) and len(self.articles) == 0:
|
||||
continue
|
||||
|
||||
if newArticle.quantity <= 1:
|
||||
newArticle.quantity = 1
|
||||
|
||||
#Runs only if regex found.
|
||||
for m in re.finditer(MULTIPLE_REGEX, text.replace(' ','').replace('\t','')):
|
||||
string = m.group(0)
|
||||
for k in re.finditer(r'\d{1,}', string):
|
||||
newArticle.quantity = int(k.group(0))
|
||||
|
||||
#TODO: Extract price per unit from this line
|
||||
|
||||
#If multiple articles are found, there is no info on the article, so we skip the rest
|
||||
if newArticle.quantity > 1:
|
||||
continue
|
||||
|
||||
if len(lineText[idx]) > 1:
|
||||
newArticle.name = lineText[idx][0]
|
||||
newArticle.nameString = lineText[idx][0]
|
||||
newArticle.nameBBox = lines[idx][0]
|
||||
|
||||
#for element in lineText[idx]:
|
||||
for idy in range(0,len(lineText[idx])):
|
||||
try:
|
||||
newArticle.price = self.parse_float(lineText[idx][idy]) / newArticle.quantity
|
||||
except ZeroDivisionError:
|
||||
print(newArticle.quantity)
|
||||
newArticle.price = 0.01
|
||||
if newArticle.price != 0:
|
||||
newArticle.priceString = lineText[idx][idy]
|
||||
newArticle.priceBBox = lines[idx][idy]
|
||||
break
|
||||
|
||||
newArticle.name = self.fixString(newArticle.name)
|
||||
copyOfNewArticle = deepcopy(newArticle)
|
||||
self.articles.append(copyOfNewArticle)
|
||||
newArticle.name=''
|
||||
newArticle.quantity = 0
|
||||
|
||||
savedArticleMaps = models.ReceipeString.objects.all()
|
||||
|
||||
alignmendObject = LinearAlignment(self.logging_group)
|
||||
|
||||
for parsedArticle in self.articles:
|
||||
matches = models.Article.objects.annotate(similarity=TrigramSimilarity('name', parsedArticle.name)).filter(similarity__gt=0.3).order_by("-similarity")
|
||||
print(parsedArticle.name)
|
||||
print(matches)
|
||||
if len(matches) > 0:
|
||||
# We try to find out, if we already have added the same article
|
||||
tmpknownArticles = self.knownArticles.copy()
|
||||
sizeSet = len(tmpknownArticles)
|
||||
tmpknownArticles.add(matches[0])
|
||||
# If the size remain the same, then the article is alread in our list, so we have to increase the quantity by 1 and then add it
|
||||
if len(tmpknownArticles) == sizeSet:
|
||||
# Elements of a set are not accessible directly, so we have to iterate over all elements
|
||||
for element in self.knownArticles:
|
||||
if element.name == article.name and element.id == article.id:
|
||||
# For the comparision of two elements, the quantity does not count, so we remove it first
|
||||
self.knownArticles.remove(matches[0])
|
||||
article.quantity = article.quantity + 1
|
||||
break
|
||||
|
||||
|
||||
# Then we add same again, but with changed quantity.
|
||||
self.knownArticles.add(matches[0])
|
||||
|
||||
# Otherwise, we just add it
|
||||
else:
|
||||
self.knownArticles.add(matches[0])
|
||||
|
||||
else:
|
||||
tmpunknownArticles = self.unknownArticles.copy()
|
||||
sizeSet = len(tmpunknownArticles)
|
||||
tmpunknownArticles.add(parsedArticle)
|
||||
# If the size remain the same, then the article is alread in our list, so we have to increase the quantity by 1 and then add it
|
||||
if len(tmpunknownArticles) == sizeSet:
|
||||
# Elements of a set are not accessible directly, so we have to iterate over all elements
|
||||
for element in self.unknownArticles:
|
||||
if element.name == parsedArticle.name:
|
||||
parsedArticle.quantity = parsedArticle.quantity + 1
|
||||
break
|
||||
|
||||
# For the comparision of to elements, the quantity does not count, so we remove it first
|
||||
try:
|
||||
self.unknownArticles.remove(parsedArticle)
|
||||
except KeyError:
|
||||
pass
|
||||
# Then we add same again, but with changed quantity.
|
||||
self.unknownArticles.add(parsedArticle)
|
||||
|
||||
# Otherwise, we just add it
|
||||
else:
|
||||
self.unknownArticles.add(parsedArticle)
|
||||
|
||||
'''
|
||||
for parsedArticle in self.articles:
|
||||
possibleMatches = []
|
||||
possibleMatchesScore = np.array([])
|
||||
for articleMaps in savedArticleMaps:
|
||||
#print(parsedArticle.name)
|
||||
#print(articleMaps.receipeString)
|
||||
#TODO: Add lower case letter to alignment matrix
|
||||
alignmendObject.setStrings(parsedArticle.name.upper(), articleMaps.receipeString.upper())
|
||||
try:
|
||||
stringScore = alignmendObject.scoring()
|
||||
except KeyError:
|
||||
stringScore = 0
|
||||
|
||||
if stringScore > 0.75:
|
||||
print(parsedArticle.name.upper()+' '+articleMaps.receipeString.upper()+' Score: '+str(stringScore))
|
||||
possibleMatches.append([parsedArticle,articleMaps])
|
||||
possibleMatchesScore = np.append(possibleMatchesScore, stringScore)
|
||||
if stringScore == 1:
|
||||
break
|
||||
|
||||
|
||||
if len(possibleMatches) > 0:
|
||||
maxIdx = np.argmax(possibleMatchesScore)
|
||||
|
||||
# Take the article with best matching name
|
||||
# First entry of list is the parsedArticle, then we overwrite name and id with the known one from the DB
|
||||
article = possibleMatches[maxIdx][0]
|
||||
article.name = possibleMatches[maxIdx][1].receipeString
|
||||
article.id = possibleMatches[maxIdx][1].pk
|
||||
article.articleId = possibleMatches[maxIdx][1].article
|
||||
|
||||
# We try to find out, if we already have added the same article
|
||||
tmpknownArticles = self.knownArticles.copy()
|
||||
sizeSet = len(tmpknownArticles)
|
||||
tmpknownArticles.add(article)
|
||||
# If the size remain the same, then the article is alread in our list, so we have to increase the quantity by 1 and then add it
|
||||
if len(tmpknownArticles) == sizeSet:
|
||||
# Elements of a set are not accessible directly, so we have to iterate over all elements
|
||||
for element in self.knownArticles:
|
||||
if element.name == article.name and element.id == article.id:
|
||||
# For the comparision of two elements, the quantity does not count, so we remove it first
|
||||
self.knownArticles.remove(article)
|
||||
article.quantity = article.quantity + 1
|
||||
break
|
||||
|
||||
|
||||
# Then we add same again, but with changed quantity.
|
||||
self.knownArticles.add(article)
|
||||
|
||||
# Otherwise, we just add it
|
||||
else:
|
||||
self.knownArticles.add(article)
|
||||
|
||||
else:
|
||||
tmpunknownArticles = self.unknownArticles.copy()
|
||||
sizeSet = len(tmpunknownArticles)
|
||||
tmpunknownArticles.add(parsedArticle)
|
||||
# If the size remain the same, then the article is alread in our list, so we have to increase the quantity by 1 and then add it
|
||||
if len(tmpunknownArticles) == sizeSet:
|
||||
# Elements of a set are not accessible directly, so we have to iterate over all elements
|
||||
for element in self.unknownArticles:
|
||||
if element.name == parsedArticle.name:
|
||||
parsedArticle.quantity = parsedArticle.quantity + 1
|
||||
break
|
||||
|
||||
# For the comparision of to elements, the quantity does not count, so we remove it first
|
||||
try:
|
||||
self.unknownArticles.remove(parsedArticle)
|
||||
except KeyError:
|
||||
pass
|
||||
# Then we add same again, but with changed quantity.
|
||||
self.unknownArticles.add(parsedArticle)
|
||||
|
||||
# Otherwise, we just add it
|
||||
else:
|
||||
self.unknownArticles.add(parsedArticle)
|
||||
'''
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
45
receipeServer/receipe/score_matrix.csv
Normal file
45
receipeServer/receipe/score_matrix.csv
Normal file
@@ -0,0 +1,45 @@
|
||||
;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;W;X;Y;Z;1;2;3;4;5;6;7;8;9;0;,;.;!; ;%;-;{;}
|
||||
A;10;4;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
B;4;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8;;;;;;;;;;
|
||||
C;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
D;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
E;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
F;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
G;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
H;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
I;;;;;;;;;10;7;;;;;;;;;;;;;;;;;8;;;;;;;;;;;;8;;;;;
|
||||
J;;;;;;;;;7;10;;5;;;;;;;;;;;;;;;;;;;;;;;;;;;7;;;;;
|
||||
K;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
L;;;;;;;;;;5;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
M;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
N;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
O;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;8;;;;;;;;
|
||||
P;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
Q;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
R;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
S;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
T;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
U;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;;
|
||||
V;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;;
|
||||
W;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;;
|
||||
X;;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;;
|
||||
Y;;;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;;
|
||||
Z;;;;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;;;
|
||||
1;;;;;;;;;8;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;8;;;;;;
|
||||
2;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;;
|
||||
3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;;
|
||||
4;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;;
|
||||
5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;;
|
||||
6;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;;
|
||||
7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;;
|
||||
8;;8;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;;
|
||||
9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;;
|
||||
0;;;;;;;;;;;;;;8;;;;;;;;;;;;;;;;;;;;;;10;;;;;;;;
|
||||
,;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;8;;;;;;
|
||||
.;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8;10;;;;;;
|
||||
!;;;;;;;;;8;7;;;;;;;;;;;;;;;;;8;;;;;;;;;;;;10;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;;;;
|
||||
%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;;;
|
||||
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;;
|
||||
{;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10;
|
||||
};;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10
|
||||
|
837
receipeServer/receipe/serializers.py
Normal file
837
receipeServer/receipe/serializers.py
Normal file
@@ -0,0 +1,837 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sun Apr 24 14:32:15 2022
|
||||
|
||||
@author: elena
|
||||
"""
|
||||
|
||||
from django.db.utils import IntegrityError
|
||||
from rest_framework import serializers
|
||||
from .models import *
|
||||
|
||||
class MarketSerializer(serializers.ModelSerializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
|
||||
class Meta:
|
||||
model = Market
|
||||
fields = ('id','name', 'street', 'street_number', 'zip_code', 'city', 'phone','articles','created','modified')
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
class ReceipeImageSerializer(serializers.ModelSerializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
|
||||
class Meta:
|
||||
model = Market
|
||||
fields = ('id','pk')
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
class PurchaseSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
purchase_date = serializers.DateField()
|
||||
payment_type = serializers.CharField(max_length=20)
|
||||
total_price = serializers.DecimalField(max_digits=7,decimal_places=2)
|
||||
market = MarketSerializer()
|
||||
edit_finished = serializers.BooleanField()
|
||||
receipeImage = ReceipeImageSerializer()
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Purchase` instance, given the validated data.
|
||||
"""
|
||||
try:
|
||||
return Purchase.objects.create(**validated_data)
|
||||
except IntegrityError:
|
||||
return Purchase.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Purchase` instance, given the validated data.
|
||||
"""
|
||||
instance.purchase_date = validated_data.get('purchase_date', instance.purchase_date)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
|
||||
'''
|
||||
class PurchaseSerializer(serializers.ModelSerializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
|
||||
class Meta:
|
||||
model = Purchase
|
||||
fields = ('id','purchase_date', 'payment_type', 'total_price', 'articles', 'market', 'created','modified','receipeImage','edit_finished')
|
||||
depth = 0
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
'''
|
||||
class PurchaseSerializerDepth(serializers.ModelSerializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
|
||||
class Meta:
|
||||
model = Purchase
|
||||
fields = ('id','purchase_date', 'payment_type', 'total_price', 'articles', 'market', 'created','modified','receipeImage','edit_finished')
|
||||
depth = 1
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
class NutritionalValuesSerializer(serializers.ModelSerializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
|
||||
class Meta:
|
||||
model = NutritionalValues
|
||||
fields = '__all__'
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
|
||||
class ReceipeStringSerializer(serializers.ModelSerializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
|
||||
class Meta:
|
||||
model = ReceipeString
|
||||
fields = '__all__'
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
class ArticleMapSerzializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ArticleMaps
|
||||
fields = ('pk','receipeString','location_x','location_y','location_h','location_w')
|
||||
depth = 1
|
||||
|
||||
|
||||
class CategorySerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(max_length=30)
|
||||
path = serializers.CharField(max_length=255)
|
||||
depth = serializers.IntegerField()
|
||||
numchild = serializers.IntegerField()
|
||||
ancestorIds = serializers.SerializerMethodField()
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Category` instance, given the validated data.
|
||||
"""
|
||||
try:
|
||||
return Category.objects.create(**validated_data)
|
||||
except IntegrityError:
|
||||
return Category.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Category` instance, given the validated data.
|
||||
"""
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
# For visualisation we need the ids of all ancestors, in order to expand the tree. Therefore we fetch the data here
|
||||
def get_ancestorIds(self, obj):
|
||||
ancestors = obj.get_ancestors()
|
||||
ids = []
|
||||
for element in ancestors:
|
||||
ids.append(element.id)
|
||||
return ids
|
||||
|
||||
class AllergenesSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(max_length=50)
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Allergenes` instance, given the validated data.
|
||||
"""
|
||||
try:
|
||||
return Allergenes.objects.create(**validated_data)
|
||||
except IntegrityError:
|
||||
return Allergenes.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Allergenes` instance, given the validated data.
|
||||
"""
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
class TracesSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(max_length=50)
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Traces` instance, given the validated data.
|
||||
"""
|
||||
try:
|
||||
return Traces.objects.create(**validated_data)
|
||||
except IntegrityError:
|
||||
return Traces.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Traces` instance, given the validated data.
|
||||
"""
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
class RecursiveField(serializers.Serializer):
|
||||
def to_representation(self, value):
|
||||
serializer = self.parent.parent.__class__(value, context=self.context)
|
||||
return serializer.data
|
||||
|
||||
class IngredientsSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(max_length=100)
|
||||
allergenes = AllergenesSerializer(allow_null = True, many=True)
|
||||
vegan = serializers.BooleanField(allow_null = True)
|
||||
vegetarian = serializers.BooleanField(allow_null = True)
|
||||
palmoilfree = serializers.BooleanField(allow_null = True)
|
||||
additive = serializers.BooleanField(allow_null = True)
|
||||
eNumber = serializers.CharField(max_length=7, allow_null = True, allow_blank=True)
|
||||
subIngredients = RecursiveField(allow_null= True, many=True)#IngredientsSerializer(allow_null = True, many=True) #serializers.ListField()
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Ingredients` instance, given the validated data.
|
||||
"""
|
||||
print("Ingredients:Create")
|
||||
#print(validated_data)
|
||||
allergenes = validated_data.pop('allergenes')
|
||||
subIngredients = validated_data.pop('subIngredients')
|
||||
print(subIngredients)
|
||||
allergenes_list = []
|
||||
subIngredients_list = []
|
||||
for allergene in allergenes:
|
||||
allergenes_list.append(Allergenes.objects.get(name=allergene['name']))
|
||||
|
||||
for subIngredient in subIngredients:
|
||||
print(subIngredient)
|
||||
subIngredients_list.append(Ingredients.objects.get(name=subIngredient['name']))
|
||||
|
||||
print(allergenes_list)
|
||||
try:
|
||||
newObject = Ingredients.objects.create(**validated_data)
|
||||
newObject.allergenes.set(allergenes_list)
|
||||
newObject.subIngredients.set(subIngredients_list)
|
||||
newObject.save()
|
||||
print(newObject)
|
||||
return newObject
|
||||
except IntegrityError:
|
||||
return Ingredients.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Ingredients` instance, given the validated data.
|
||||
"""
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
# Validate if subIngredients are given, if not raise error. This is needed, because value is emtpy, therefore we have to access the inital data
|
||||
def validate_subIngredients(self, value):
|
||||
print("Ingredients:Validate subIngredients")
|
||||
print(value)
|
||||
print(self.context)
|
||||
#print(self.initial_data)
|
||||
#print(self.initial_data['subIngredients'])
|
||||
if not 'subIngredients' in self.context:
|
||||
self.context['subIngredients'] = []
|
||||
return self.context['subIngredients']
|
||||
else:
|
||||
return self.context['subIngredients']
|
||||
|
||||
#IngredientsSerializer.base_fields['subIngredients'] = IngredientsSerializer(allow_null = True, many=True)
|
||||
|
||||
class LabelsSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(max_length=50)
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Brand` instance, given the validated data.
|
||||
"""
|
||||
print("Labels:Create")
|
||||
try:
|
||||
return Labels.objects.create(**validated_data)
|
||||
except IntegrityError:
|
||||
return Labels.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Snippet` instance, given the validated data.
|
||||
"""
|
||||
print("Labels:Update")
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
|
||||
class PackagingSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(max_length=50)
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Packaging` instance, given the validated data.
|
||||
"""
|
||||
print("Packaging:Create")
|
||||
try:
|
||||
return Packaging.objects.create(**validated_data)
|
||||
except IntegrityError:
|
||||
return Packaging.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Packaging` instance, given the validated data.
|
||||
"""
|
||||
print("Labels:Update")
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
class EMBSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(max_length=50)
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `EMBs` instance, given the validated data.
|
||||
"""
|
||||
print("Brand:Create")
|
||||
try:
|
||||
return EMBs.objects.create(**validated_data)
|
||||
except IntegrityError:
|
||||
return EMBs.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `EMBs` instance, given the validated data.
|
||||
"""
|
||||
print("Brand:Update")
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
class ManufacturingPlacesSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(max_length=50)
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Brand` instance, given the validated data.
|
||||
"""
|
||||
print("Brand:Create")
|
||||
try:
|
||||
return ManufacturingPlaces.objects.create(**validated_data)
|
||||
except IntegrityError:
|
||||
return ManufacturingPlaces.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Snippet` instance, given the validated data.
|
||||
"""
|
||||
print("Brand:Update")
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
class OriginIngredientsSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(max_length=50)
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Brand` instance, given the validated data.
|
||||
"""
|
||||
try:
|
||||
return OriginIngredients.objects.create(**validated_data)
|
||||
except IntegrityError:
|
||||
return OriginIngredients.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Snippet` instance, given the validated data.
|
||||
"""
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
class BrandSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(max_length=50)
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Brand` instance, given the validated data.
|
||||
"""
|
||||
print("Brand:Create")
|
||||
try:
|
||||
return Brand.objects.create(**validated_data)
|
||||
except IntegrityError:
|
||||
return Brand.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Snippet` instance, given the validated data.
|
||||
"""
|
||||
print("Brand:Update")
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
#def validate_name(self, value):
|
||||
# print('Validate: name')
|
||||
# return value
|
||||
|
||||
#def validate(self, data):
|
||||
# print('Validate object: name')
|
||||
# return data
|
||||
|
||||
|
||||
|
||||
class ArticleSerializer(serializers.ModelSerializer):
|
||||
brand = BrandSerializer(allow_null = True)
|
||||
allergenes = AllergenesSerializer(allow_null = True, many=True)
|
||||
#ingredients = IngredientsSerializer(allow_null = True, many=True)
|
||||
traces = TracesSerializer(allow_null = True, many=True)
|
||||
labels = LabelsSerializer(allow_null = True, many=True)
|
||||
#packagings = PackagingArticleSerializer(allow_null = True, many=True)
|
||||
embs = EMBSerializer(allow_null = True, many=True)
|
||||
manufacturingPlaces = ManufacturingPlacesSerializer(allow_null = True, many=True)
|
||||
originIngredients = OriginIngredientsSerializer(allow_null = True, many=True)
|
||||
|
||||
nutritionImage = serializers.ImageField(read_only = True)
|
||||
ingredientsImage = serializers.ImageField(read_only = True)
|
||||
frontImage = serializers.ImageField(read_only = True)
|
||||
|
||||
category = CategorySerializer(allow_null = True)
|
||||
|
||||
class Meta:
|
||||
model = Article
|
||||
fields = ('pk','name','brand','EAN','offlink','category',
|
||||
'quantityPerPackage','unit','novaGroup','nutritionGrade','ecoGrade',
|
||||
'MwSt','allergenes','traces','embs','labels',
|
||||
'manufacturingPlaces','originIngredients',
|
||||
'created','modified',
|
||||
'nutritionImage','ingredientsImage', 'frontImage') # 'ingredients',
|
||||
depth = 1
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
print("update method")
|
||||
print(validated_data)
|
||||
article = self.fill_deeper_fields(instance, validated_data)
|
||||
|
||||
article.name = validated_data.pop('name')
|
||||
article.EAN = validated_data.pop('EAN')
|
||||
article.offlink = validated_data.pop('offlink')
|
||||
article.quantityPerPackage = validated_data.pop('quantityPerPackage')
|
||||
article.unit = validated_data.pop('unit')
|
||||
article.novaGroup = validated_data.pop('novaGroup')
|
||||
article.nutritionGrade = validated_data.pop('nutritionGrade')
|
||||
article.ecoGrade = validated_data.pop('ecoGrade')
|
||||
article.MwSt = validated_data.pop('MwSt')
|
||||
|
||||
#print("Article before save")
|
||||
#print(article.labels.get())
|
||||
article.save()
|
||||
|
||||
return article
|
||||
|
||||
|
||||
def create(self, validated_data):
|
||||
print('create method')
|
||||
|
||||
article = Article.objects.create(**validated_data)
|
||||
article = self.fill_deeper_fields(article, validated_data)
|
||||
|
||||
article.name = validated_data.pop('name')
|
||||
article.EAN = validated_data.pop('EAN')
|
||||
article.quantityPerPackage = validated_data.pop('quantityPerPackage')
|
||||
article.unit = validated_data.pop('unit')
|
||||
article.novaGroup = validated_data.pop('novaGroup')
|
||||
article.nutritionGrade = validated_data.pop('nutritionGrade')
|
||||
article.ecoGrade = validated_data.pop('ecoGrade')
|
||||
article.MwSt = validated_data.pop('MwSt')
|
||||
|
||||
return article
|
||||
|
||||
def deserialize_multi_field(self, data_list, model):
|
||||
elements = []
|
||||
for data in data_list:
|
||||
if data != None:
|
||||
#if model == 'ingredients':
|
||||
# serializer = IngredientsSerializer(
|
||||
# data=data
|
||||
# )
|
||||
if model == 'allergenes':
|
||||
serializer = AllergenesSerializer(
|
||||
data=data
|
||||
)
|
||||
elif model == 'traces':
|
||||
serializer = TracesSerializer(
|
||||
data=data
|
||||
)
|
||||
elif model == 'labels':
|
||||
serializer = LabelsSerializer(
|
||||
data=data
|
||||
)
|
||||
#elif model == 'packaging':
|
||||
# serializer = PackagingArticleSerializer(
|
||||
# data=data
|
||||
# )
|
||||
elif model == 'embs':
|
||||
serializer = EMBSerializer(
|
||||
data=data
|
||||
)
|
||||
elif model == 'manufacturingPlaces':
|
||||
serializer = ManufacturingPlacesSerializer(
|
||||
data=data
|
||||
)
|
||||
elif model == 'originIngredients':
|
||||
serializer = OriginIngredientsSerializer(
|
||||
data=data
|
||||
)
|
||||
|
||||
if serializer.is_valid():
|
||||
ingredients = serializer.save()
|
||||
elements.append(ingredients)
|
||||
else:
|
||||
elements.append(None)
|
||||
|
||||
else:
|
||||
elements.append(None)
|
||||
return elements
|
||||
|
||||
|
||||
def fill_deeper_fields(self, article, validated_data):
|
||||
print(validated_data)
|
||||
brand_data = validated_data.pop('brand')
|
||||
if brand_data != None:
|
||||
serializer = BrandSerializer(
|
||||
data=brand_data
|
||||
)
|
||||
if serializer.is_valid():
|
||||
brand = serializer.save()
|
||||
article.brand = brand
|
||||
else:
|
||||
article.brand = None
|
||||
else:
|
||||
article.brand = None
|
||||
|
||||
category_data = validated_data.pop('category')
|
||||
if category_data != None:
|
||||
serializer = CategorySerializer(
|
||||
data=category_data
|
||||
)
|
||||
if serializer.is_valid():
|
||||
category = serializer.save()
|
||||
article.category = category
|
||||
else:
|
||||
article.category = None
|
||||
else:
|
||||
article.category = None
|
||||
|
||||
#data = validated_data.pop('ingredients')
|
||||
#elements = self.deserialize_multi_field(data, 'ingredients')
|
||||
#article.ingredients.set(elements)
|
||||
|
||||
data = validated_data.pop('allergenes')
|
||||
elements = self.deserialize_multi_field(data, 'allergenes')
|
||||
article.allergenes.set(elements)
|
||||
|
||||
data = validated_data.pop('traces')
|
||||
elements = self.deserialize_multi_field(data, 'traces')
|
||||
article.traces.set(elements)
|
||||
|
||||
data = validated_data.pop('labels')
|
||||
elements = self.deserialize_multi_field(data, 'labels')
|
||||
article.labels.set(elements)
|
||||
|
||||
#data = validated_data.pop('packaging')
|
||||
#elements = self.deserialize_multi_field(data, 'packaging')
|
||||
#article.packaging.set(elements)
|
||||
|
||||
data = validated_data.pop('embs')
|
||||
elements = self.deserialize_multi_field(data, 'embs')
|
||||
article.embs.set(elements)
|
||||
|
||||
data = validated_data.pop('manufacturingPlaces')
|
||||
elements = self.deserialize_multi_field(data, 'manufacturingPlaces')
|
||||
article.manufacturingPlaces.set(elements)
|
||||
|
||||
data = validated_data.pop('originIngredients')
|
||||
elements = self.deserialize_multi_field(data, 'originIngredients')
|
||||
article.originIngredients.set(elements)
|
||||
|
||||
|
||||
return article
|
||||
|
||||
# Problem: article_id is not readonly, therefore the serializer checks in validation for existence of article in order to create it, we have to prevent this
|
||||
class PurchaseArticlesSerializer(serializers.ModelSerializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
article_id = ArticleSerializer(read_only=True)
|
||||
#articleSer = ArticlesSerializer(article)
|
||||
sum_price = serializers.SerializerMethodField()
|
||||
map = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = PurchaseArticle
|
||||
fields = ('id','quantity','net_weight','price','inSale','article_id','purchase_id','sum_price','map')
|
||||
#depth = 2
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def get_sum_price(self, obj):
|
||||
return int(obj.quantity) * float(obj.price)
|
||||
|
||||
def get_map(self, obj):
|
||||
purchaseImage = Purchase.objects.get(pk=obj.purchase_id).receipeImage
|
||||
#print(obj.article_id)
|
||||
articleMap = ArticleMaps.objects.get(article=obj.article_id, receipeImage=purchaseImage)
|
||||
print(articleMap)
|
||||
serializedMap = ArticleMapSerzializer(articleMap, many=False)
|
||||
return serializedMap.data
|
||||
|
||||
def create(self, validated_data):
|
||||
print('create method of PurchaseArticlesSerializer')
|
||||
|
||||
print(validated_data)
|
||||
|
||||
#article = Article.objects.get(name=validated_data['article_id']['name'],
|
||||
# brand=validated_data['article_id']['brand'],
|
||||
# EAN=validated_data['article_id']['EAN'],
|
||||
# category=validated_data['article_id']['category'])
|
||||
|
||||
articleArticle = PurchaseArticle.objects.create(purchase_id=validated_data.pop('purchase_id'),
|
||||
article_id=validated_data.pop('article_id'),
|
||||
quantity= validated_data.pop('quantity'),
|
||||
price = validated_data.pop('price'),
|
||||
inSale = validated_data.pop('inSale'))
|
||||
|
||||
try:
|
||||
articleArticle.net_weight = validated_data.pop('net_weight')
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return articleArticle
|
||||
|
||||
def to_internal_value(self, data):
|
||||
article_id = data.get('article_id')
|
||||
internal_data = super().to_internal_value(data)
|
||||
try:
|
||||
article = Article.objects.get(pk=article_id['pk'])
|
||||
except Article.DoesNotExist:
|
||||
raise ValidationError(
|
||||
{'article': ['Invalid article primary key']},
|
||||
code='invalid',
|
||||
)
|
||||
internal_data['article_id'] = article
|
||||
return internal_data
|
||||
|
||||
#Serializer for nutrient model
|
||||
class NutrientSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk')
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
name = serializers.CharField(max_length=50)
|
||||
isMacroNutrient = serializers.BooleanField()
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Nutrient` instance, given the validated data.
|
||||
"""
|
||||
#print("Nutrient:Create")
|
||||
try:
|
||||
return Nutrient.objects.create(**validated_data)
|
||||
except IntegrityError:
|
||||
return Nutrient.objects.get(name=validated_data['name'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Nutrient` instance, given the validated data.
|
||||
"""
|
||||
#print("Labels:Update")
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
#Serializer for NutrientArticle model
|
||||
class NutrientArticleSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk',read_only=False)
|
||||
pk = serializers.IntegerField(read_only=False)
|
||||
|
||||
article_id = serializers.IntegerField(source="article.pk")
|
||||
nutrient = NutrientSerializer()
|
||||
value = serializers.DecimalField(max_digits=7, decimal_places=2, allow_null = True)
|
||||
unit = serializers.CharField(max_length=4,allow_null = True)
|
||||
isEstimated = serializers.BooleanField(allow_null = True)
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `NutrientArticle` instance, given the validated data.
|
||||
"""
|
||||
#print("NutrientArticle:Create")
|
||||
#print(validated_data)
|
||||
#try:
|
||||
article = Article.objects.get(pk=validated_data.pop('article')['pk'])
|
||||
nutrient = Nutrient.objects.get(pk=validated_data.pop('nutrient')['pk'])
|
||||
return PackagingArticle.objects.create(article=article,
|
||||
nutrient=nutrient,
|
||||
value=validated_data.pop('value'),
|
||||
unit=validated_data.pop('unit'),
|
||||
isEstimated=validated_data.pop('isEstimated')
|
||||
)
|
||||
#except IntegrityError:
|
||||
# return PackagingArticle.objects.get(article_id=validated_data['article_id'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
#print('update method for articlepackaging')
|
||||
print(validated_data)
|
||||
print(instance)
|
||||
print(type(instance))
|
||||
name = validated_data.pop('nutrient', instance.nutrient)['name']
|
||||
nutrient = Nutrient.objects.get(name=name)
|
||||
instance.nutrient = nutrient
|
||||
instance.value = validated_data.pop('value',instance.value)
|
||||
instance.unit = validated_data.pop('unit',instance.unit)
|
||||
instance.isEstimated = validated_data.pop('isEstimated',instance.isEstimated)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
class PackagingArticleSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk',read_only=False)
|
||||
pk = serializers.IntegerField(read_only=False)
|
||||
#If we need the whole object:
|
||||
#article_id = ArticleSerializer()
|
||||
#otherwise, we just take the article primary key
|
||||
article_id = serializers.IntegerField(source="article_id.pk")
|
||||
packaging_id = PackagingSerializer()
|
||||
weight = serializers.IntegerField()
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `PackagingArticle` instance, given the validated data.
|
||||
"""
|
||||
print("PackagingArticle:Create")
|
||||
print(validated_data)
|
||||
#try:
|
||||
article = Article.objects.get(pk=validated_data.pop('article_id')['pk'])
|
||||
return PackagingArticle.objects.create(article_id=article, weight=validated_data.pop('weight'))
|
||||
#except IntegrityError:
|
||||
# return PackagingArticle.objects.get(article_id=validated_data['article_id'])
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
print('update method for articlepackaging')
|
||||
print(validated_data)
|
||||
#print(instance.packaging_id)
|
||||
name = validated_data.pop('packaging_id', instance.packaging_id)['name']
|
||||
packaging = Packaging.objects.get(name=name)
|
||||
instance.packaging_id = packaging
|
||||
instance.weight = validated_data.pop('weight',instance.weight)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
class IngredientsArticleSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField('pk',read_only=False)
|
||||
pk = serializers.IntegerField(read_only=False)
|
||||
#If we need the whole object:
|
||||
#article_id = ArticleSerializer()
|
||||
#otherwise, we just take the article primary key
|
||||
article = serializers.IntegerField(source="article.pk")
|
||||
ingredient = IngredientsSerializer()
|
||||
position = serializers.IntegerField()
|
||||
percent = serializers.FloatField(allow_null = True)
|
||||
|
||||
def pk(self, obj):
|
||||
return obj.pk
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `IngredientsArticle` instance, given the validated data.
|
||||
"""
|
||||
print("IngredientsArticle:Create")
|
||||
print(validated_data)
|
||||
#try:
|
||||
article = Article.objects.get(pk=validated_data.pop('article')['pk'])
|
||||
return IngredientsArticle.objects.create(article=article, position=validated_data.pop('position'))
|
||||
#except IntegrityError:
|
||||
# return PackagingArticle.objects.get(article_id=validated_data['article_id'])
|
||||
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
print('update method for articleingredient')
|
||||
print(validated_data)
|
||||
#print(instance.packaging_id)
|
||||
#ingredient_json = validated_data.pop('ingredient', instance.ingredient)
|
||||
ingredient = Ingredients.objects.get(id=self.initial_data['ingredient']['id'])
|
||||
instance.ingredient = ingredient
|
||||
instance.position = validated_data.pop('position',instance.position)
|
||||
instance.percent = validated_data.pop('percent',instance.percent)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
|
||||
|
||||
|
||||
34
receipeServer/receipe/tasks.py
Normal file
34
receipeServer/receipe/tasks.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sat Apr 30 13:05:54 2022
|
||||
|
||||
@author: elena
|
||||
"""
|
||||
|
||||
#from django.conf import settings
|
||||
|
||||
from receipe.consumer import Consumer, ConsumerError
|
||||
|
||||
def consume_file(path,
|
||||
applyBinarize=True,
|
||||
debug=False,
|
||||
task_id=None,
|
||||
scannerFile=False):
|
||||
|
||||
print("Start consuming from task")
|
||||
receipe = Consumer().try_consume_file(
|
||||
path,
|
||||
applyBinarize=applyBinarize,
|
||||
debug=debug,
|
||||
task_id=task_id,
|
||||
scannerFile=scannerFile
|
||||
)
|
||||
|
||||
if receipe:
|
||||
return "Success. New document id {} created".format(
|
||||
receipe.pk
|
||||
)
|
||||
else:
|
||||
raise ConsumerError("Unknown error: Returned document was null, but "
|
||||
"no error message was given.")
|
||||
3
receipeServer/receipe/tests.py
Normal file
3
receipeServer/receipe/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
1231
receipeServer/receipe/views.py
Normal file
1231
receipeServer/receipe/views.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user