improved property system to differentiate between unknown and zero

This commit is contained in:
vabene1111
2024-02-18 11:26:16 +01:00
parent 717d4d2346
commit 28479e96e9
10 changed files with 110 additions and 19 deletions

View File

@@ -182,6 +182,8 @@ class OpenDataImporter:
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 @@ class OpenDataImporter:
'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 @@ class OpenDataImporter:
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:

View File

@@ -46,7 +46,7 @@ class FoodPropertyHelper:
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 @@ class FoodPropertyHelper:
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

View File

@@ -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),
),
]

View File

@@ -846,7 +846,7 @@ class PropertyType(models.Model, PermissionModelMixin):
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

View File

@@ -563,7 +563,7 @@ class PropertyTypeSerializer(OpenDataModelMixin, WritableNestedModelSerializer,
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

View File

@@ -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(

View File

@@ -607,11 +607,21 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
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 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
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',))