move transpose words to AutomationEngine
create tests for transpose words
This commit is contained in:
parent
39253cfd02
commit
b8317c2c29
@ -1,5 +1,6 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
from django.core.cache import caches
|
from django.core.cache import caches
|
||||||
from django.db.models import Q
|
|
||||||
from django.db.models.functions import Lower
|
from django.db.models.functions import Lower
|
||||||
|
|
||||||
from cookbook.models import Automation
|
from cookbook.models import Automation
|
||||||
@ -136,6 +137,38 @@ class AutomationEngine:
|
|||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
def apply_transpose_automation(self, string):
|
def apply_transpose_automation(self, string):
|
||||||
|
"""
|
||||||
|
If two words (param_1 & param_2) are detected in sequence, swap their position in the ingredient string
|
||||||
|
:param 1: first word to detect
|
||||||
|
:param 2: second word to detect
|
||||||
|
return: new ingredient string
|
||||||
|
"""
|
||||||
|
if self.use_cache and self.transpose_words is None:
|
||||||
|
self.transpose_words = {}
|
||||||
|
TRANSPOSE_WORDS_CACHE_KEY = f'automation_transpose_words_{self.request.space.pk}'
|
||||||
|
if c := caches['default'].get(TRANSPOSE_WORDS_CACHE_KEY, None):
|
||||||
|
self.transpose_words = c
|
||||||
|
caches['default'].touch(TRANSPOSE_WORDS_CACHE_KEY, 30)
|
||||||
|
else:
|
||||||
|
i = 0
|
||||||
|
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.TRANSPOSE_WORDS).only('param_1', 'param_2').order_by('order').all():
|
||||||
|
self.transpose_words[i] = [a.param_1.lower(), a.param_2.lower()]
|
||||||
|
i += 1
|
||||||
|
caches['default'].set(TRANSPOSE_WORDS_CACHE_KEY, self.transpose_words, 30)
|
||||||
|
else:
|
||||||
|
self.transpose_words = {}
|
||||||
|
|
||||||
|
tokens = [x.lower() for x in string.replace(',', ' ').split()]
|
||||||
|
if self.transpose_words:
|
||||||
|
for key, value in self.transpose_words.items():
|
||||||
|
if value[0] in tokens and value[1] in tokens:
|
||||||
|
string = re.sub(rf"\b({value[0]})\W*({value[1]})\b", r"\2 \1", string, flags=re.IGNORECASE)
|
||||||
|
else:
|
||||||
|
for rule in Automation.objects.filter(space=self.request.space, type=Automation.TRANSPOSE_WORDS, disabled=False) \
|
||||||
|
.annotate(param_1_lower=Lower('param_1'), param_2_lower=Lower('param_2')) \
|
||||||
|
.filter(param_1_lower__in=tokens, param_2_lower__in=tokens).order_by('order'):
|
||||||
|
if rule.param_1 in tokens and rule.param_2 in tokens:
|
||||||
|
string = re.sub(rf"\b({rule.param_1})\W*({rule.param_2})\b", r"\2 \1", string, flags=re.IGNORECASE)
|
||||||
return string
|
return string
|
||||||
|
|
||||||
def apply_regex_replace_automation(self, string):
|
def apply_regex_replace_automation(self, string):
|
||||||
|
@ -2,12 +2,8 @@ import re
|
|||||||
import string
|
import string
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
from django.core.cache import caches
|
|
||||||
from django.db.models import Q
|
|
||||||
from django.db.models.functions import Lower
|
|
||||||
|
|
||||||
from cookbook.helper.automation_helper import AutomationEngine
|
from cookbook.helper.automation_helper import AutomationEngine
|
||||||
from cookbook.models import Automation, Food, Ingredient, Unit
|
from cookbook.models import Food, Ingredient, Unit
|
||||||
|
|
||||||
|
|
||||||
class IngredientParser:
|
class IngredientParser:
|
||||||
@ -16,7 +12,7 @@ class IngredientParser:
|
|||||||
# food_aliases = {}
|
# food_aliases = {}
|
||||||
# unit_aliases = {}
|
# unit_aliases = {}
|
||||||
# never_unit = {}
|
# never_unit = {}
|
||||||
transpose_words = {}
|
# transpose_words = {}
|
||||||
automation = None
|
automation = None
|
||||||
|
|
||||||
def __init__(self, request, cache_mode=True, ignore_automations=False):
|
def __init__(self, request, cache_mode=True, ignore_automations=False):
|
||||||
@ -29,51 +25,51 @@ class IngredientParser:
|
|||||||
self.request = request
|
self.request = request
|
||||||
self.ignore_rules = ignore_automations
|
self.ignore_rules = ignore_automations
|
||||||
self.automation = AutomationEngine(self.request, use_cache=cache_mode)
|
self.automation = AutomationEngine(self.request, use_cache=cache_mode)
|
||||||
if cache_mode:
|
# if cache_mode:
|
||||||
# FOOD_CACHE_KEY = f'automation_food_alias_{self.request.space.pk}'
|
# FOOD_CACHE_KEY = f'automation_food_alias_{self.request.space.pk}'
|
||||||
# if c := caches['default'].get(FOOD_CACHE_KEY, None):
|
# if c := caches['default'].get(FOOD_CACHE_KEY, None):
|
||||||
# self.food_aliases = c
|
# self.food_aliases = c
|
||||||
# caches['default'].touch(FOOD_CACHE_KEY, 30)
|
# caches['default'].touch(FOOD_CACHE_KEY, 30)
|
||||||
# else:
|
# else:
|
||||||
# for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.FOOD_ALIAS).only('param_1', 'param_2').order_by('order').all():
|
# for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.FOOD_ALIAS).only('param_1', 'param_2').order_by('order').all():
|
||||||
# self.food_aliases[a.param_1.lower()] = a.param_2
|
# self.food_aliases[a.param_1.lower()] = a.param_2
|
||||||
# caches['default'].set(FOOD_CACHE_KEY, self.food_aliases, 30)
|
# caches['default'].set(FOOD_CACHE_KEY, self.food_aliases, 30)
|
||||||
|
|
||||||
# UNIT_CACHE_KEY = f'automation_unit_alias_{self.request.space.pk}'
|
# UNIT_CACHE_KEY = f'automation_unit_alias_{self.request.space.pk}'
|
||||||
# if c := caches['default'].get(UNIT_CACHE_KEY, None):
|
# if c := caches['default'].get(UNIT_CACHE_KEY, None):
|
||||||
# self.unit_aliases = c
|
# self.unit_aliases = c
|
||||||
# caches['default'].touch(UNIT_CACHE_KEY, 30)
|
# caches['default'].touch(UNIT_CACHE_KEY, 30)
|
||||||
# else:
|
# else:
|
||||||
# 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():
|
# 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.lower()] = a.param_2
|
# self.unit_aliases[a.param_1.lower()] = a.param_2
|
||||||
# caches['default'].set(UNIT_CACHE_KEY, self.unit_aliases, 30)
|
# caches['default'].set(UNIT_CACHE_KEY, self.unit_aliases, 30)
|
||||||
|
|
||||||
# TODO migrated to automation engine
|
# TODO migrated to automation engine
|
||||||
# NEVER_UNIT_CACHE_KEY = f'automation_never_unit_{self.request.space.pk}'
|
# NEVER_UNIT_CACHE_KEY = f'automation_never_unit_{self.request.space.pk}'
|
||||||
# if c := caches['default'].get(NEVER_UNIT_CACHE_KEY, None):
|
# if c := caches['default'].get(NEVER_UNIT_CACHE_KEY, None):
|
||||||
# self.never_unit = c
|
# self.never_unit = c
|
||||||
# caches['default'].touch(NEVER_UNIT_CACHE_KEY, 30)
|
# caches['default'].touch(NEVER_UNIT_CACHE_KEY, 30)
|
||||||
# else:
|
# 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():
|
# 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.lower()] = a.param_2
|
# self.never_unit[a.param_1.lower()] = a.param_2
|
||||||
# caches['default'].set(NEVER_UNIT_CACHE_KEY, self.never_unit, 30)
|
# caches['default'].set(NEVER_UNIT_CACHE_KEY, self.never_unit, 30)
|
||||||
|
|
||||||
# TODO migrated to automation engine
|
# TODO migrated to automation engine
|
||||||
TRANSPOSE_WORDS_CACHE_KEY = f'automation_transpose_words_{self.request.space.pk}'
|
# TRANSPOSE_WORDS_CACHE_KEY = f'automation_transpose_words_{self.request.space.pk}'
|
||||||
if c := caches['default'].get(TRANSPOSE_WORDS_CACHE_KEY, None):
|
# if c := caches['default'].get(TRANSPOSE_WORDS_CACHE_KEY, None):
|
||||||
self.transpose_words = c
|
# self.transpose_words = c
|
||||||
caches['default'].touch(TRANSPOSE_WORDS_CACHE_KEY, 30)
|
# caches['default'].touch(TRANSPOSE_WORDS_CACHE_KEY, 30)
|
||||||
else:
|
# else:
|
||||||
i = 0
|
# i = 0
|
||||||
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.TRANSPOSE_WORDS).only('param_1', 'param_2').order_by('order').all():
|
# for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.TRANSPOSE_WORDS).only('param_1', 'param_2').order_by('order').all():
|
||||||
self.transpose_words[i] = [a.param_1.lower(), a.param_2.lower()]
|
# self.transpose_words[i] = [a.param_1.lower(), a.param_2.lower()]
|
||||||
i += 1
|
# i += 1
|
||||||
caches['default'].set(TRANSPOSE_WORDS_CACHE_KEY, self.transpose_words, 30)
|
# caches['default'].set(TRANSPOSE_WORDS_CACHE_KEY, self.transpose_words, 30)
|
||||||
else:
|
# else:
|
||||||
# self.food_aliases = {}
|
# self.food_aliases = {}
|
||||||
# self.unit_aliases = {}
|
# self.unit_aliases = {}
|
||||||
# self.never_unit = {}
|
# self.never_unit = {}
|
||||||
self.transpose_words = {}
|
# self.transpose_words = {}
|
||||||
|
|
||||||
# def apply_food_automation(self, food):
|
# def apply_food_automation(self, food):
|
||||||
# """
|
# """
|
||||||
@ -277,33 +273,32 @@ class IngredientParser:
|
|||||||
|
|
||||||
# return tokens
|
# return tokens
|
||||||
|
|
||||||
# TODO migrated to automation engine
|
# def apply_transpose_words_automations(self, ingredient):
|
||||||
def apply_transpose_words_automations(self, ingredient):
|
# """
|
||||||
"""
|
# If two words (param_1 & param_2) are detected in sequence, swap their position in the ingredient string
|
||||||
If two words (param_1 & param_2) are detected in sequence, swap their position in the ingredient string
|
# :param 1: first word to detect
|
||||||
:param 1: first word to detect
|
# :param 2: second word to detect
|
||||||
:param 2: second word to detect
|
# return: new ingredient string
|
||||||
return: new ingredient string
|
# """
|
||||||
"""
|
|
||||||
|
|
||||||
if self.ignore_rules:
|
# if self.ignore_rules:
|
||||||
return ingredient
|
# return ingredient
|
||||||
|
|
||||||
else:
|
# else:
|
||||||
tokens = [x.lower() for x in ingredient.replace(',', ' ').split()]
|
# tokens = [x.lower() for x in ingredient.replace(',', ' ').split()]
|
||||||
if self.transpose_words:
|
# if self.transpose_words:
|
||||||
filtered_rules = {}
|
# filtered_rules = {}
|
||||||
for key, value in self.transpose_words.items():
|
# for key, value in self.transpose_words.items():
|
||||||
if value[0] in tokens and value[1] in tokens:
|
# if value[0] in tokens and value[1] in tokens:
|
||||||
filtered_rules[key] = value
|
# filtered_rules[key] = value
|
||||||
for k, v in filtered_rules.items():
|
# for k, v in filtered_rules.items():
|
||||||
ingredient = re.sub(rf"\b({v[0]})\W*({v[1]})\b", r"\2 \1", ingredient, flags=re.IGNORECASE)
|
# ingredient = re.sub(rf"\b({v[0]})\W*({v[1]})\b", r"\2 \1", ingredient, flags=re.IGNORECASE)
|
||||||
else:
|
# else:
|
||||||
for rule in Automation.objects.filter(space=self.request.space, type=Automation.TRANSPOSE_WORDS, disabled=False) \
|
# for rule in Automation.objects.filter(space=self.request.space, type=Automation.TRANSPOSE_WORDS, disabled=False) \
|
||||||
.annotate(param_1_lower=Lower('param_1'), param_2_lower=Lower('param_2')) \
|
# .annotate(param_1_lower=Lower('param_1'), param_2_lower=Lower('param_2')) \
|
||||||
.filter(Q(Q(param_1_lower__in=tokens) | Q(param_2_lower__in=tokens))).order_by('order'):
|
# .filter(Q(Q(param_1_lower__in=tokens) | Q(param_2_lower__in=tokens))).order_by('order'):
|
||||||
ingredient = re.sub(rf"\b({rule.param_1})\W*({rule.param_1})\b", r"\2 \1", ingredient, flags=re.IGNORECASE)
|
# ingredient = re.sub(rf"\b({rule.param_1})\W*({rule.param_1})\b", r"\2 \1", ingredient, flags=re.IGNORECASE)
|
||||||
return ingredient
|
# return ingredient
|
||||||
|
|
||||||
def parse(self, ingredient):
|
def parse(self, ingredient):
|
||||||
"""
|
"""
|
||||||
@ -345,8 +340,8 @@ class IngredientParser:
|
|||||||
if re.match('([0-9])+([A-z])+\\s', ingredient):
|
if re.match('([0-9])+([A-z])+\\s', ingredient):
|
||||||
ingredient = re.sub(r'(?<=([a-z])|\d)(?=(?(1)\d|[a-z]))', ' ', ingredient)
|
ingredient = re.sub(r'(?<=([a-z])|\d)(?=(?(1)\d|[a-z]))', ' ', ingredient)
|
||||||
|
|
||||||
# TODO migrated to automation engine
|
if not self.ignore_rules:
|
||||||
ingredient = self.apply_transpose_words_automations(ingredient)
|
ingredient = self.automation.apply_transpose_words_automations(ingredient)
|
||||||
|
|
||||||
tokens = ingredient.split() # split at each space into tokens
|
tokens = ingredient.split() # split at each space into tokens
|
||||||
if len(tokens) == 1:
|
if len(tokens) == 1:
|
||||||
@ -360,8 +355,8 @@ class IngredientParser:
|
|||||||
# three arguments if it already has a unit there can't be
|
# three arguments if it already has a unit there can't be
|
||||||
# a fraction for the amount
|
# a fraction for the amount
|
||||||
if len(tokens) > 2:
|
if len(tokens) > 2:
|
||||||
# TODO migrated to automation engine
|
if not self.ignore_rules:
|
||||||
tokens = self.automation.apply_never_unit_automation(tokens)
|
tokens = self.automation.apply_never_unit_automation(tokens)
|
||||||
try:
|
try:
|
||||||
if unit is not None:
|
if unit is not None:
|
||||||
# a unit is already found, no need to try the second argument for a fraction
|
# a unit is already found, no need to try the second argument for a fraction
|
||||||
|
@ -2,7 +2,6 @@ import re
|
|||||||
import traceback
|
import traceback
|
||||||
from html import unescape
|
from html import unescape
|
||||||
|
|
||||||
from django.core.cache import caches
|
|
||||||
from django.utils.dateparse import parse_duration
|
from django.utils.dateparse import parse_duration
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from isodate import parse_duration as iso_parse_duration
|
from isodate import parse_duration as iso_parse_duration
|
||||||
|
@ -6,16 +6,6 @@ from django_scopes import scope
|
|||||||
from cookbook.helper.automation_helper import AutomationEngine
|
from cookbook.helper.automation_helper import AutomationEngine
|
||||||
from cookbook.models import Automation
|
from cookbook.models import Automation
|
||||||
|
|
||||||
# TODO test case sensitive match, assert update value
|
|
||||||
# TODO test case insensitive match, assert update value
|
|
||||||
# TODO test no match, assert not update value
|
|
||||||
# TODO test accent insensitive match, assert not update value
|
|
||||||
|
|
||||||
|
|
||||||
# @pytest.fixture()
|
|
||||||
# def automation_food(space_1):
|
|
||||||
# return Keyword.objects.get_or_create(name='test_1', space=space_1)[0]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
@pytest.mark.parametrize("arg", [
|
||||||
['Match', True],
|
['Match', True],
|
||||||
@ -103,8 +93,24 @@ def test_never_unit_automation(u1_s1, arg):
|
|||||||
assert automation.apply_never_unit_automation(arg[0]) == arg[2]
|
assert automation.apply_never_unit_automation(arg[0]) == arg[2]
|
||||||
|
|
||||||
|
|
||||||
# def test_transpose_automation():
|
@pytest.mark.parametrize("arg", [
|
||||||
# assert True == True
|
['second first', 'first second'],
|
||||||
|
['longer string second first longer string', 'longer string first second longer string'],
|
||||||
|
['second fails first', 'second fails first'],
|
||||||
|
])
|
||||||
|
def test_transpose_automation(u1_s1, arg):
|
||||||
|
user = auth.get_user(u1_s1)
|
||||||
|
space = user.userspace_set.first().space
|
||||||
|
request = RequestFactory()
|
||||||
|
request.user = user
|
||||||
|
request.space = space
|
||||||
|
automation = AutomationEngine(request, False)
|
||||||
|
|
||||||
|
with scope(space=space):
|
||||||
|
Automation.objects.get_or_create(name='transpose words test', type=Automation.TRANSPOSE_WORDS, param_1='second', param_2='first', created_by=user, space=space)
|
||||||
|
assert automation.apply_transpose_automation(arg[0]) == arg[1]
|
||||||
|
|
||||||
|
assert True == True
|
||||||
# # for some reason this tests cant run due to some kind of encoding issue, needs to be fixed
|
# # for some reason this tests cant run due to some kind of encoding issue, needs to be fixed
|
||||||
# # def test_description_replace_automation(u1_s1, space_1):
|
# # def test_description_replace_automation(u1_s1, space_1):
|
||||||
# # if 'cookbook' in os.getcwd():
|
# # if 'cookbook' in os.getcwd():
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import pytest
|
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.test import RequestFactory
|
from django.test import RequestFactory
|
||||||
from django_scopes import scope
|
from django_scopes import scope
|
||||||
|
Loading…
Reference in New Issue
Block a user