add NEVER_UNIT automation

This commit is contained in:
smilerz
2023-04-24 10:59:48 -05:00
parent 5d5eb45b5a
commit 8fa00972bd
6 changed files with 178 additions and 82 deletions

View File

@@ -4,7 +4,7 @@ import unicodedata
from django.core.cache import caches
from cookbook.models import Unit, Food, Automation, Ingredient
from cookbook.models import Automation, Food, Ingredient, Unit
class IngredientParser:
@@ -12,6 +12,7 @@ class IngredientParser:
ignore_rules = False
food_aliases = {}
unit_aliases = {}
never_unit = {}
def __init__(self, request, cache_mode, ignore_automations=False):
"""
@@ -40,9 +41,19 @@ class IngredientParser:
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.UNIT_ALIAS).only('param_1', 'param_2').order_by('order').all():
self.unit_aliases[a.param_1] = a.param_2
caches['default'].set(UNIT_CACHE_KEY, self.unit_aliases, 30)
NEVER_UNIT_CACHE_KEY = f'automation_never_unit_{self.request.space.pk}'
if c := caches['default'].get(NEVER_UNIT_CACHE_KEY, None):
self.never_unit = c
caches['default'].touch(NEVER_UNIT_CACHE_KEY, 30)
else:
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.NEVER_UNIT).only('param_1', 'param_2').order_by('order').all():
self.never_unit[a.param_1] = a.param_2
caches['default'].set(NEVER_UNIT_CACHE_KEY, self.never_unit, 30)
else:
self.food_aliases = {}
self.unit_aliases = {}
self.never_unit = {}
def apply_food_automation(self, food):
"""
@@ -205,6 +216,49 @@ class IngredientParser:
food, note = self.parse_food_with_comma(tokens)
return food, note
def apply_never_unit_automations(self, tokens):
"""
Moves a string that should never be treated as a unit to next token and optionally replaced with default unit
e.g. NEVER_UNIT: param1: egg, param2: None would modify ['1', 'egg', 'white'] to ['1', '', 'egg', 'white']
or NEVER_UNIT: param1: egg, param2: pcs would modify ['1', 'egg', 'yolk'] to ['1', 'pcs', 'egg', 'yolk']
:param1 string: string that should never be considered a unit, will be moved to token[2]
:param2 (optional) unit as string: will insert unit string into token[1]
:return: unit as string (possibly changed by automation)
"""
if self.ignore_rules:
return tokens
new_unit = None
alt_unit = self.apply_unit_automation(tokens[1])
never_unit = False
if self.never_unit:
try:
new_unit = self.never_unit[tokens[1]]
never_unit = True
except KeyError:
return tokens
else:
if automation := Automation.objects.filter(space=self.request.space, type=Automation.UNIT_ALIAS, param_1__in=[tokens[1], alt_unit], disabled=False).order_by('order').first():
new_unit = automation.param_2
never_unit = True
if never_unit:
tokens.insert(1, new_unit)
return tokens
def parse_tokens(self, tokens):
"""
parser that applies automations to unmodified tokens
"""
if self.ignore_rules:
return tokens
return self.apply_never_unit_automations(tokens)
def parse(self, ingredient):
"""
Main parsing function, takes an ingredient string (e.g. '1 l Water') and extracts amount, unit, food, ...
@@ -257,6 +311,7 @@ class IngredientParser:
# three arguments if it already has a unit there can't be
# a fraction for the amount
if len(tokens) > 2:
tokens = self.parse_tokens(tokens)
try:
if unit is not None:
# a unit is already found, no need to try the second argument for a fraction

View File

@@ -0,0 +1,23 @@
# Generated by Django 4.1.7 on 2023-04-24 15:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0188_space_no_sharing_limit'),
]
operations = [
migrations.AlterField(
model_name='automation',
name='type',
field=models.CharField(choices=[('FOOD_ALIAS', 'Food Alias'), ('UNIT_ALIAS', 'Unit Alias'), ('KEYWORD_ALIAS', 'Keyword Alias'), ('DESCRIPTION_REPLACE', 'Description Replace'), ('INSTRUCTION_REPLACE', 'Instruction Replace'), ('NEVER_UNIT', 'Never Unit')], max_length=128),
),
migrations.AlterField(
model_name='userpreference',
name='use_fractions',
field=models.BooleanField(default=True),
),
]

View File

@@ -5,7 +5,6 @@ import uuid
from datetime import date, timedelta
import oauth2_provider.models
from PIL import Image
from annoying.fields import AutoOneToOneField
from django.contrib import auth
from django.contrib.auth.models import Group, User
@@ -14,13 +13,14 @@ from django.contrib.postgres.search import SearchVectorField
from django.core.files.uploadedfile import InMemoryUploadedFile, UploadedFile
from django.core.validators import MinLengthValidator
from django.db import IntegrityError, models
from django.db.models import Index, ProtectedError, Q, Avg, Max
from django.db.models import Avg, Index, Max, ProtectedError, Q
from django.db.models.fields.related import ManyToManyField
from django.db.models.functions import Substr
from django.utils import timezone
from django.utils.translation import gettext as _
from django_prometheus.models import ExportModelOperationsMixin
from django_scopes import ScopedManager, scopes_disabled
from PIL import Image
from treebeard.mp_tree import MP_Node, MP_NodeManager
from recipes.settings import (COMMENT_PREF_DEFAULT, FRACTION_PREF_DEFAULT, KJ_PREF_DEFAULT,
@@ -1319,10 +1319,12 @@ class Automation(ExportModelOperationsMixin('automations'), models.Model, Permis
KEYWORD_ALIAS = 'KEYWORD_ALIAS'
DESCRIPTION_REPLACE = 'DESCRIPTION_REPLACE'
INSTRUCTION_REPLACE = 'INSTRUCTION_REPLACE'
NEVER_UNIT = 'NEVER_UNIT'
type = models.CharField(max_length=128,
choices=((FOOD_ALIAS, _('Food Alias')), (UNIT_ALIAS, _('Unit Alias')), (KEYWORD_ALIAS, _('Keyword Alias')),
(DESCRIPTION_REPLACE, _('Description Replace')), (INSTRUCTION_REPLACE, _('Instruction Replace')),))
(DESCRIPTION_REPLACE, _('Description Replace')), (INSTRUCTION_REPLACE, _('Instruction Replace')),
(NEVER_UNIT, _('Never Unit')),))
name = models.CharField(max_length=128, default='')
description = models.TextField(blank=True, null=True)