diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8651ff628..56006b360 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,7 +38,7 @@ jobs:
./cookbook/static
./staticfiles
key: |
- ${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.node-version }}-collectstatic-${{ hashFiles('**/*.css', '**/*.js', 'vue/src/*') }}
+ ${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.node-version }}-collectstatic-${{ hashFiles('**/*.css', '**/*.js', 'vue3/src/*') }}
# Build Vue frontend & Dependencies
- name: Set up Node ${{ matrix.node-version }}
diff --git a/.gitignore b/.gitignore
index 24df2fd47..16ceed730 100644
--- a/.gitignore
+++ b/.gitignore
@@ -89,3 +89,5 @@ venv/
.idea/easy-i18n.xml
cookbook/static/vue3
vue3/node_modules
+cookbook/tests/other/docs/reports/tests/tests.html
+cookbook/tests/other/docs/reports/tests/pytest.xml
diff --git a/cookbook/helper/ingredient_parser.py b/cookbook/helper/ingredient_parser.py
index 66c94415c..a99efb421 100644
--- a/cookbook/helper/ingredient_parser.py
+++ b/cookbook/helper/ingredient_parser.py
@@ -211,44 +211,46 @@ class IngredientParser:
# three arguments if it already has a unit there can't be
# a fraction for the amount
if len(tokens) > 2:
+ never_unit_applied = False
if not self.ignore_rules:
tokens, never_unit_applied = self.automation.apply_never_unit_automation(tokens)
- if never_unit_applied:
- unit = tokens[1]
- food, note = self.parse_food(tokens[2:])
- else:
- try:
- if unit is not None:
- # a unit is already found, no need to try the second argument for a fraction
- # probably not the best method to do it, but I didn't want to make an if check and paste the exact same thing in the else as already is in the except
- raise ValueError
- # try to parse second argument as amount and add that, in case of '2 1/2' or '2 ½'
- if tokens[1]:
- amount += self.parse_fraction(tokens[1])
- # assume that units can't end with a comma
- if len(tokens) > 3 and not tokens[2].endswith(','):
- # try to use third argument as unit and everything else as food, use everything as food if it fails
- try:
- food, note = self.parse_food(tokens[3:])
- unit = tokens[2]
- except ValueError:
- food, note = self.parse_food(tokens[2:])
- else:
+
+ if never_unit_applied:
+ unit = tokens[1]
+ food, note = self.parse_food(tokens[2:])
+ else:
+ try:
+ if unit is not None:
+ # a unit is already found, no need to try the second argument for a fraction
+ # probably not the best method to do it, but I didn't want to make an if check and paste the exact same thing in the else as already is in the except
+ raise ValueError
+ # try to parse second argument as amount and add that, in case of '2 1/2' or '2 ½'
+ if tokens[1]:
+ amount += self.parse_fraction(tokens[1])
+ # assume that units can't end with a comma
+ if len(tokens) > 3 and not tokens[2].endswith(','):
+ # try to use third argument as unit and everything else as food, use everything as food if it fails
+ try:
+ food, note = self.parse_food(tokens[3:])
+ unit = tokens[2]
+ except ValueError:
food, note = self.parse_food(tokens[2:])
- except ValueError:
- # assume that units can't end with a comma
- if not tokens[1].endswith(','):
- # try to use second argument as unit and everything else as food, use everything as food if it fails
- try:
- food, note = self.parse_food(tokens[2:])
- if unit is None:
- unit = tokens[1]
- else:
- note = tokens[1]
- except ValueError:
- food, note = self.parse_food(tokens[1:])
- else:
+ else:
+ food, note = self.parse_food(tokens[2:])
+ except ValueError:
+ # assume that units can't end with a comma
+ if not tokens[1].endswith(','):
+ # try to use second argument as unit and everything else as food, use everything as food if it fails
+ try:
+ food, note = self.parse_food(tokens[2:])
+ if unit is None:
+ unit = tokens[1]
+ else:
+ note = tokens[1]
+ except ValueError:
food, note = self.parse_food(tokens[1:])
+ else:
+ food, note = self.parse_food(tokens[1:])
else:
# only two arguments, first one is the amount
# which means this is the food
@@ -269,6 +271,7 @@ class IngredientParser:
if food and not self.ignore_rules:
food = self.automation.apply_food_automation(food)
+
if len(food) > Food._meta.get_field('name').max_length: # test if food name is to long
# try splitting it at a space and taking only the first arg
if len(food.split()) > 1 and len(food.split()[0]) < Food._meta.get_field('name').max_length:
diff --git a/cookbook/tests/other/docs/reports/tests/pytest.xml b/cookbook/tests/other/docs/reports/tests/pytest.xml
deleted file mode 100644
index 692255ba3..000000000
--- a/cookbook/tests/other/docs/reports/tests/pytest.xml
+++ /dev/null
@@ -1,157 +0,0 @@
-args = ()
-kwargs = {'request': <SubRequest 'space_1' for <Function test_never_unit_automation[arg0]>>}
-k = 'space_1__name'
-
- @functools.wraps(fixture_function)
- def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
- for k in set(kwargs.keys()) - function_args:
- del kwargs[k]
-> return fixture_function(*args, **kwargs)
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixturegen.py:88:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixturegen.py:49: in fn
- return function(*args, **kwargs)
- ^^^^^^^^^^^^^^^^^^^^^^^^^
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-request = <SubRequest 'space_1' for <Function test_never_unit_automation[arg0]>>
-factory_name = 'space_factory'
-
- def model_fixture(request: SubRequest, factory_name: str) -> object:
- """Model fixture implementation."""
- factoryboy_request: FactoryboyRequest = request.getfixturevalue("factoryboy_request")
-
- # Try to evaluate as much post-generation dependencies as possible
- factoryboy_request.evaluate(request)
-
- assert request.fixturename # NOTE: satisfy mypy
- fixture_name = request.fixturename
- prefix = "".join((fixture_name, SEPARATOR))
-
- factory_class: type[Factory[object]] = request.getfixturevalue(factory_name)
-
- # Create model fixture instance
-> NewFactory: type[Factory[object]] = cast(type[Factory[object]], type("Factory", (factory_class,), {}))
- ^^^^^^^^^^^^^^^
-E TypeError: type 'Factory' is not subscriptable
-
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixture.py:360: TypeErrorargs = ()
-kwargs = {'request': <SubRequest 'space_1' for <Function test_never_unit_automation[arg2]>>}
-k = 'space_1__name'
-
- @functools.wraps(fixture_function)
- def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
- for k in set(kwargs.keys()) - function_args:
- del kwargs[k]
-> return fixture_function(*args, **kwargs)
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixturegen.py:88:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixturegen.py:49: in fn
- return function(*args, **kwargs)
- ^^^^^^^^^^^^^^^^^^^^^^^^^
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-request = <SubRequest 'space_1' for <Function test_never_unit_automation[arg2]>>
-factory_name = 'space_factory'
-
- def model_fixture(request: SubRequest, factory_name: str) -> object:
- """Model fixture implementation."""
- factoryboy_request: FactoryboyRequest = request.getfixturevalue("factoryboy_request")
-
- # Try to evaluate as much post-generation dependencies as possible
- factoryboy_request.evaluate(request)
-
- assert request.fixturename # NOTE: satisfy mypy
- fixture_name = request.fixturename
- prefix = "".join((fixture_name, SEPARATOR))
-
- factory_class: type[Factory[object]] = request.getfixturevalue(factory_name)
-
- # Create model fixture instance
-> NewFactory: type[Factory[object]] = cast(type[Factory[object]], type("Factory", (factory_class,), {}))
- ^^^^^^^^^^^^^^^
-E TypeError: type 'Factory' is not subscriptable
-
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixture.py:360: TypeErrorargs = ()
-kwargs = {'request': <SubRequest 'space_1' for <Function test_never_unit_automation[arg3]>>}
-k = 'space_1__name'
-
- @functools.wraps(fixture_function)
- def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
- for k in set(kwargs.keys()) - function_args:
- del kwargs[k]
-> return fixture_function(*args, **kwargs)
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixturegen.py:88:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixturegen.py:49: in fn
- return function(*args, **kwargs)
- ^^^^^^^^^^^^^^^^^^^^^^^^^
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-request = <SubRequest 'space_1' for <Function test_never_unit_automation[arg3]>>
-factory_name = 'space_factory'
-
- def model_fixture(request: SubRequest, factory_name: str) -> object:
- """Model fixture implementation."""
- factoryboy_request: FactoryboyRequest = request.getfixturevalue("factoryboy_request")
-
- # Try to evaluate as much post-generation dependencies as possible
- factoryboy_request.evaluate(request)
-
- assert request.fixturename # NOTE: satisfy mypy
- fixture_name = request.fixturename
- prefix = "".join((fixture_name, SEPARATOR))
-
- factory_class: type[Factory[object]] = request.getfixturevalue(factory_name)
-
- # Create model fixture instance
-> NewFactory: type[Factory[object]] = cast(type[Factory[object]], type("Factory", (factory_class,), {}))
- ^^^^^^^^^^^^^^^
-E TypeError: type 'Factory' is not subscriptable
-
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixture.py:360: TypeErrorargs = ()
-kwargs = {'request': <SubRequest 'space_1' for <Function test_never_unit_automation[arg1]>>}
-k = 'space_1__name'
-
- @functools.wraps(fixture_function)
- def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
- for k in set(kwargs.keys()) - function_args:
- del kwargs[k]
-> return fixture_function(*args, **kwargs)
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixturegen.py:88:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixturegen.py:49: in fn
- return function(*args, **kwargs)
- ^^^^^^^^^^^^^^^^^^^^^^^^^
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-request = <SubRequest 'space_1' for <Function test_never_unit_automation[arg1]>>
-factory_name = 'space_factory'
-
- def model_fixture(request: SubRequest, factory_name: str) -> object:
- """Model fixture implementation."""
- factoryboy_request: FactoryboyRequest = request.getfixturevalue("factoryboy_request")
-
- # Try to evaluate as much post-generation dependencies as possible
- factoryboy_request.evaluate(request)
-
- assert request.fixturename # NOTE: satisfy mypy
- fixture_name = request.fixturename
- prefix = "".join((fixture_name, SEPARATOR))
-
- factory_class: type[Factory[object]] = request.getfixturevalue(factory_name)
-
- # Create model fixture instance
-> NewFactory: type[Factory[object]] = cast(type[Factory[object]], type("Factory", (factory_class,), {}))
- ^^^^^^^^^^^^^^^
-E TypeError: type 'Factory' is not subscriptable
-
-..\..\..\venv\Lib\site-packages\pytest_factoryboy\fixture.py:360: TypeError
\ No newline at end of file
diff --git a/cookbook/tests/other/docs/reports/tests/tests.html b/cookbook/tests/other/docs/reports/tests/tests.html
deleted file mode 100644
index 8da3718d1..000000000
--- a/cookbook/tests/other/docs/reports/tests/tests.html
+++ /dev/null
@@ -1,770 +0,0 @@
-
-
-
-
- tests.html
-
-
-
- tests.html
- Report generated on 17-Jul-2025 at 12:15:50 by pytest-html
- v4.1.1
-
-
-
-
-
- |
- |
-
-
-
-
-
- | No results found. Check the filters.
- |
-
-
-
-
-
-
-
-
-
-
-
-
Summary
-
-
-
0 test took 00:01:28.
-
(Un)check the boxes to filter the results.
-
-
-
-
-
-
-
-
-
-
-
-
- | Result |
- Test |
- Duration |
- Links |
-
-
-
-
-
-
\ No newline at end of file
diff --git a/cookbook/tests/other/test_ingredient_parser.py b/cookbook/tests/other/test_ingredient_parser.py
index 30c6eb6ab..c4cda057e 100644
--- a/cookbook/tests/other/test_ingredient_parser.py
+++ b/cookbook/tests/other/test_ingredient_parser.py
@@ -1,3 +1,4 @@
+import pytest
from django.contrib import auth
from django.test import RequestFactory
from django_scopes import scope
@@ -5,7 +6,11 @@ from django_scopes import scope
from cookbook.helper.ingredient_parser import IngredientParser
-def test_ingredient_parser(u1_s1):
+@pytest.mark.parametrize("arg", [
+ [True],
+ [False],
+])
+def test_ingredient_parser(arg, u1_s1):
expectations = {
"2¼ l Wasser": (2.25, "l", "Wasser", ""),
"3¼l Wasser": (3.25, "l", "Wasser", ""),
@@ -67,7 +72,8 @@ def test_ingredient_parser(u1_s1):
"1 Porreestange(n) , ca. 200 g": (1.0, None, 'Porreestange(n)', 'ca. 200 g'), # leading space before comma
# test for over long food entries to get properly split into the note field
"1 Lorem ipsum dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l Lorem ipsum dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l": (
- 1.0, 'Lorem', 'ipsum', 'dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l Lorem ipsum dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l'),
+ 1.0, 'Lorem', 'ipsum',
+ 'dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l Lorem ipsum dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l'),
"1 LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutl": (
1.0, None, 'LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingeli',
'LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutl'),
@@ -86,7 +92,7 @@ def test_ingredient_parser(u1_s1):
request = RequestFactory()
request.user = user
request.space = space
- ingredient_parser = IngredientParser(request, False, ignore_automations=True)
+ ingredient_parser = IngredientParser(request, False, ignore_automations=arg[0])
count = 0
with scope(space=space):
diff --git a/pytest.ini b/pytest.ini
index a3d26ec48..2755dc99d 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -4,4 +4,5 @@ testpaths = cookbook/tests
python_files = tests.py test_*.py *_tests.py
# uncomment to run coverage reports
addopts = -n auto --cov=. --cov-report=html:docs/reports/coverage --cov-report=xml:docs/reports/coverage/coverage.xml --junitxml=docs/reports/tests/pytest.xml --html=docs/reports/tests/tests.html
-# addopts = -n auto --junitxml=docs/reports/tests/pytest.xml --html=docs/reports/tests/tests.html
\ No newline at end of file
+# addopts = -n auto --junitxml=docs/reports/tests/pytest.xml --html=docs/reports/tests/tests.html
+asyncio_default_fixture_loop_scope = fixture
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index f0e7a21f5..ae629f162 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -55,11 +55,11 @@ litellm==1.64.1
# Development
pytest==8.4.1
pytest-django==4.11.0
-pytest-cov===6.0.0
-pytest-factoryboy==2.8.0
+pytest-cov===6.2.1
+pytest-factoryboy==2.8.1
pytest-html==4.1.1
-pytest-asyncio==0.25.3
-pytest-xdist==3.7.0
+pytest-asyncio==1.1.0
+pytest-xdist==3.8.0
autopep8==2.3.2
flake8==7.3.0
yapf==0.40.2