From 28479e96e90595577b7abe4cb65b36414d1e35c3 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sun, 18 Feb 2024 11:26:16 +0100 Subject: [PATCH] improved property system to differentiate between unknown and zero --- cookbook/helper/open_data_importer.py | 5 +- cookbook/helper/property_helper.py | 4 +- .../0212_alter_property_property_amount.py | 18 +++++ cookbook/models.py | 2 +- cookbook/serializer.py | 2 +- cookbook/tests/other/test_food_property.py | 2 +- cookbook/views/api.py | 21 +++++- .../PropertyEditorView/PropertyEditorView.vue | 67 ++++++++++++++++--- vue/src/components/PropertyViewComponent.vue | 7 +- vue/src/locales/en.json | 1 + 10 files changed, 110 insertions(+), 19 deletions(-) create mode 100644 cookbook/migrations/0212_alter_property_property_amount.py diff --git a/cookbook/helper/open_data_importer.py b/cookbook/helper/open_data_importer.py index a2e5061490..5fbb6ce129 100644 --- a/cookbook/helper/open_data_importer.py +++ b/cookbook/helper/open_data_importer.py @@ -182,6 +182,8 @@ def import_food(self): self._update_slug_cache(PropertyType, 'property') self._update_slug_cache(SupermarketCategory, 'category') + unit_g = Unit.objects.filter(space=self.request.space, base_unit__iexact='g').first() + existing_data = {} for obj in Food.objects.filter(space=self.request.space, open_data_slug__isnull=False).values('pk', 'name', 'open_data_slug'): existing_data[obj['open_data_slug']] = obj @@ -197,6 +199,7 @@ def import_food(self): 'supermarket_category_id': self.slug_id_cache['category'][self.data[datatype][k]['store_category']], 'fdc_id': re.sub(r'\D', '', self.data[datatype][k]['fdc_id']) if self.data[datatype][k]['fdc_id'] != '' else None, 'open_data_slug': k, + 'properties_food_unit': unit_g, 'space': self.request.space.id, } @@ -210,7 +213,7 @@ def import_food(self): total_count = 0 if self.update_existing and len(update_list) > 0: - Food.objects.bulk_update(update_list, ['name', 'plural_name', 'preferred_unit_id', 'preferred_shopping_unit_id', 'supermarket_category_id', 'fdc_id', 'open_data_slug', ]) + Food.objects.bulk_update(update_list, ['name', 'plural_name', 'properties_food_unit', 'supermarket_category_id', 'fdc_id', 'open_data_slug', ]) total_count += len(update_list) if len(create_list) > 0: diff --git a/cookbook/helper/property_helper.py b/cookbook/helper/property_helper.py index 04521c6582..29d74fffcb 100644 --- a/cookbook/helper/property_helper.py +++ b/cookbook/helper/property_helper.py @@ -46,7 +46,7 @@ def calculate_recipe_properties(self, recipe): for pt in property_types: found_property = False if i.food.properties_food_amount == 0 or i.food.properties_food_unit is None: - computed_properties[pt.id]['food_values'][i.food.id] = {'id': i.food.id, 'food': i.food.name, 'value': 0} + computed_properties[pt.id]['food_values'][i.food.id] = {'id': i.food.id, 'food': i.food.name, 'value': None} computed_properties[pt.id]['missing_value'] = i.food.properties_food_unit is None else: for p in i.food.properties.all(): @@ -59,7 +59,7 @@ def calculate_recipe_properties(self, recipe): computed_properties[p.property_type.id]['food_values'], c.food.id, (c.amount / i.food.properties_food_amount) * p.property_amount, c.food) if not found_property: computed_properties[pt.id]['missing_value'] = True - computed_properties[pt.id]['food_values'][i.food.id] = {'id': i.food.id, 'food': i.food.name, 'value': 0} + computed_properties[pt.id]['food_values'][i.food.id] = {'id': i.food.id, 'food': i.food.name, 'value': None} return computed_properties diff --git a/cookbook/migrations/0212_alter_property_property_amount.py b/cookbook/migrations/0212_alter_property_property_amount.py new file mode 100644 index 0000000000..2ec04c47d2 --- /dev/null +++ b/cookbook/migrations/0212_alter_property_property_amount.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2024-02-18 07:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0211_recipebook_order'), + ] + + operations = [ + migrations.AlterField( + model_name='property', + name='property_amount', + field=models.DecimalField(decimal_places=4, default=None, max_digits=32, null=True), + ), + ] diff --git a/cookbook/models.py b/cookbook/models.py index cb129e0c06..21c70c249f 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -846,7 +846,7 @@ class Meta: class Property(models.Model, PermissionModelMixin): - property_amount = models.DecimalField(default=0, decimal_places=4, max_digits=32) + property_amount = models.DecimalField(default=None, null=True, decimal_places=4, max_digits=32) property_type = models.ForeignKey(PropertyType, on_delete=models.PROTECT) import_food_id = models.IntegerField(null=True, blank=True) # field to hold food id when importing properties from the open data project diff --git a/cookbook/serializer.py b/cookbook/serializer.py index 873880d61d..8446e3fb5f 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -563,7 +563,7 @@ class Meta: class PropertySerializer(UniqueFieldsMixin, WritableNestedModelSerializer): property_type = PropertyTypeSerializer() - property_amount = CustomDecimalField() + property_amount = CustomDecimalField(allow_null=True) def create(self, validated_data): validated_data['space'] = self.context['request'].space diff --git a/cookbook/tests/other/test_food_property.py b/cookbook/tests/other/test_food_property.py index 534dcaf3ff..caeceae3f1 100644 --- a/cookbook/tests/other/test_food_property.py +++ b/cookbook/tests/other/test_food_property.py @@ -67,7 +67,7 @@ def test_food_property(space_1, space_2, u1_s1): assert property_values[property_fat.id]['name'] == property_fat.name assert abs(property_values[property_fat.id]['total_value']) < 0.0001 - assert abs(property_values[property_fat.id]['food_values'][food_1.id]['value']) < 0.0001 + assert property_values[property_fat.id]['food_values'][food_1.id]['value'] is None print('\n----------- TEST PROPERTY - PROPERTY CALCULATION UNIT CONVERSION ---------------') uc1 = UnitConversion.objects.create( diff --git a/cookbook/views/api.py b/cookbook/views/api.py index 6997377af6..eb2523f853 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -607,12 +607,22 @@ def fdc(self, request, pk): if properties with a fdc_id already exist they will be overridden, if existing properties don't have a fdc_id they won't be changed """ food = self.get_object() + if not food.fdc_id: + return JsonResponse({'msg': 'Food has no FDC ID associated.'}, status=400, + json_dumps_params={'indent': 4}) response = requests.get(f'https://api.nal.usda.gov/fdc/v1/food/{food.fdc_id}?api_key={FDC_API_KEY}') if response.status_code == 429: - return JsonResponse({'msg', 'API Key Rate Limit reached/exceeded, see https://api.data.gov/docs/rate-limits/ for more information. Configure your key in Tandoor using environment FDC_API_KEY variable.'}, status=429, + return JsonResponse({'msg': 'API Key Rate Limit reached/exceeded, see https://api.data.gov/docs/rate-limits/ for more information. Configure your key in Tandoor using environment FDC_API_KEY variable.'}, status=429, + json_dumps_params={'indent': 4}) + if response.status_code != 200: + return JsonResponse({'msg': f'Error while requesting FDC data using url https://api.nal.usda.gov/fdc/v1/food/{food.fdc_id}?api_key=****'}, status=response.status_code, json_dumps_params={'indent': 4}) + food.properties_food_amount = 100 + food.properties_food_unit = Unit.objects.get_or_create(base_unit__iexact='g', space=self.request.space, defaults={'name': 'g', 'base_unit': 'g', 'space': self.request.space})[0] + food.save() + try: data = json.loads(response.content) @@ -625,14 +635,23 @@ def fdc(self, request, pk): for pt in PropertyType.objects.filter(space=request.space, fdc_id__gte=0).all(): if pt.fdc_id: + property_found = False for fn in data['foodNutrients']: if fn['nutrient']['id'] == pt.fdc_id: + property_found = True food_property_list.append(Property( property_type_id=pt.id, property_amount=max(0, round(fn['amount'], 2)), # sometimes FDC might return negative values which make no sense, set to 0 import_food_id=food.id, space=self.request.space, )) + if not property_found: + food_property_list.append(Property( + property_type_id=pt.id, + property_amount=0, # if field not in FDC data the food does not have that property + import_food_id=food.id, + space=self.request.space, + )) Property.objects.bulk_create(food_property_list, ignore_conflicts=True, unique_fields=('space', 'import_food_id', 'property_type',)) diff --git a/vue/src/apps/PropertyEditorView/PropertyEditorView.vue b/vue/src/apps/PropertyEditorView/PropertyEditorView.vue index 076628c3bc..e889b8fec2 100644 --- a/vue/src/apps/PropertyEditorView/PropertyEditorView.vue +++ b/vue/src/apps/PropertyEditorView/PropertyEditorView.vue @@ -8,6 +8,7 @@

{{ recipe.name }}

{{ recipe.description }} +
@@ -17,7 +18,7 @@
- {{$t('FDC_Search')}} + {{ $t('FDC_Search') }} @@ -29,7 +30,7 @@ @@ -76,6 +82,40 @@ + + + + + {{ $t('Calculator') }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { let new_food_property = { property_type: pt, - property_amount: 0, + property_amount: null, } if (pt.id in existing_properties) { new_food_property = existing_properties[pt.id] @@ -217,6 +265,9 @@ export default { StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err) food.loading = false; }) + }, + copyCalculatedResult: function(){ + this.$copyText(this.calculator_to_amount) } }, } diff --git a/vue/src/components/PropertyViewComponent.vue b/vue/src/components/PropertyViewComponent.vue index 2c566e522c..2a92cdcc3e 100644 --- a/vue/src/components/PropertyViewComponent.vue +++ b/vue/src/components/PropertyViewComponent.vue @@ -41,10 +41,8 @@ @@ -117,6 +115,7 @@ export default { has_food_properties = true } } + console.log('has food propers', has_food_properties) return has_food_properties }, property_list: function () { diff --git a/vue/src/locales/en.json b/vue/src/locales/en.json index c6b9b0d057..1bf5da8206 100644 --- a/vue/src/locales/en.json +++ b/vue/src/locales/en.json @@ -83,6 +83,7 @@ "Open_Data_Import": "Open Data Import", "Properties_Food_Amount": "Properties Food Amount", "Properties_Food_Unit": "Properties Food Unit", + "Calculator": "Calculator", "FDC_ID": "FDC ID", "FDC_Search": "FDC Search", "FDC_ID_help": "FDC database ID",
{{ pt.name }} ({{ pt.unit }})
- {{ pt.order}} + {{ pt.order }} FDC FDC
@@ -48,7 +49,7 @@ - + @@ -67,7 +68,12 @@
- + +
- - - - + +