import math import os from datetime import datetime from django.conf import settings from django.utils import timezone from rest_framework import serializers from module_iam.models import IAmPrincipal from ..models import ( BeverageRecord, Bowel, ChronicCondition, FoodIngredientRecord, FoodIngredintDataset, FoodRecord, Intolerance, MealRecord, MealSymptomRecord, Medication, Medicine, PastTreatment, PrincipalHealthData, Symptoms, SymptomTypeAfterMeal, SymptomTypeBeforeMeal, ) class FoodDatasetSerializer(serializers.ModelSerializer): class Meta: model = FoodIngredintDataset fields = ["id", "food_name"] class FoodIngredientDatasetSerializer(serializers.ModelSerializer): class Meta: model = FoodIngredintDataset fields = ['id', 'food_name', 'ingredients'] class IAmPrincipalSerializer(serializers.ModelSerializer): class Meta: model = IAmPrincipal fields = [ "profile_photo", "first_name", "date_of_birth", "gender", "phone_no", ] class PrincipalHealthDataSerializer(serializers.ModelSerializer): weight = serializers.DecimalField(max_digits=5, decimal_places=2, read_only=True) height = serializers.DecimalField(max_digits=6, decimal_places=2, read_only=True) class Meta: model = PrincipalHealthData fields = [ "ethenicity", "weight", "weight_unit", "height", "height_unit", "gastrointestinal_health", "exercise_frequency", "sleep_duration", "eat_frequency", ] class PrincipalAndHealthSerializer(serializers.ModelSerializer): ethenicity = serializers.CharField(read_only=True) weight = serializers.DecimalField(max_digits=5, decimal_places=2, read_only=True) weight_unit = serializers.CharField(max_length=10, read_only=True) height = serializers.DecimalField(max_digits=6, decimal_places=2, read_only=True) height_unit = serializers.CharField(max_length=10, read_only=True) gastrointestinal_health = serializers.CharField(read_only=True) exercise_frequency = serializers.CharField(read_only=True) sleep_duration = serializers.CharField(read_only=True) eat_frequency = serializers.CharField(read_only=True) profile_complete = serializers.IntegerField(read_only=True) class Meta: model = IAmPrincipal fields = [ "profile_photo", "first_name", "email", "date_of_birth", "gender", "phone_no", # "phone_verified", # "email_verified", "ethenicity", "weight", "weight_unit", "height", "height_unit", "gastrointestinal_health", "exercise_frequency", "sleep_duration", "eat_frequency", "profile_complete", ] def calculate_profile_completion(self, user): """ Calculates the profile completion percentage for a user based on the required fields. """ fields = [ "profile_photo", "first_name", "email", "date_of_birth", "gender", "phone_no", "ethenicity", "weight", "height", "gastrointestinal_health", "exercise_frequency", "sleep_duration", "eat_frequency", ] try: # Retrieve the user profile from the database profile = IAmPrincipal.objects.get(id=user) try: # Retrieve the user's health data from the database health_data = PrincipalHealthData.objects.get(principal=profile) except PrincipalHealthData.DoesNotExist: # If health data doesn't exist, set health_data to None health_data = None # Initialize a counter for completed fields completed_fields = sum( 1 for field in fields if ( # If the field is in the user profile and the field value is not None, not an empty string, and not an instance of datetime.date ( field in vars(profile) and vars(profile).get(field, "") and vars(profile).get(field) != datetime.date ) or # If health data exists, the field is in the user's health data, and the field value is not None, not an empty string, and not an instance of datetime.date ( health_data and field in vars(health_data) and vars(health_data).get(field, "") and vars(health_data).get(field) != datetime.date ) ) ) except IAmPrincipal.DoesNotExist: # If the user profile doesn't exist, return 0 return 0 # Calculate the total number of fields total_fields = len(fields) # Calculate the profile completion percentage completion_percentage = math.floor((completed_fields / total_fields) * 100) # Return the profile completion percentage return completion_percentage def get_image_url(self, obj, field_name, request): image_field = getattr(obj, field_name) if image_field: return request.build_absolute_uri(image_field.url) else: # Return the URL of the default image from the static path default_image_path = os.path.join( settings.STATIC_URL, "img/default_profile.jpg" ) return request.build_absolute_uri(default_image_path) def to_representation(self, instance): data = super().to_representation(instance) request = self.context.get("request") data["profile_photo"] = self.get_image_url(instance, "profile_photo", request) data["profile_complete"] = self.calculate_profile_completion(request.user.id) if ( hasattr(instance, "health_data_principal") and instance.health_data_principal ): health_data = instance.health_data_principal data["ethenicity"] = health_data.ethenicity data["weight"] = health_data.weight data["weight_unit"] = health_data.weight_unit data["height"] = health_data.height data["height_unit"] = health_data.height_unit data["gastrointestinal_health"] = health_data.gastrointestinal_health data["exercise_frequency"] = health_data.exercise_frequency data["sleep_duration"] = health_data.sleep_duration data["eat_frequency"] = health_data.eat_frequency else: # If health_data_principal doesn't exist or is empty, set empty strings for all attributes data["ethenicity"] = "" data["weight"] = 0.00 data["weight_unit"] = "kg" data["height"] = 0.00 data["height_unit"] = "cm" data["gastrointestinal_health"] = "" data["exercise_frequency"] = "" data["sleep_duration"] = "" data["eat_frequency"] = "" return data class IntoleranceSerializer(serializers.ModelSerializer): class Meta: model = Intolerance fields = ["id", "name", "duration"] class SymptomsSerializer(serializers.ModelSerializer): class Meta: model = Symptoms fields = ["id", "name", "duration"] class PastTreatmentSerializer(serializers.ModelSerializer): class Meta: model = PastTreatment fields = ["id", "name", "duration", "is_recurring", "treatment_frequency"] class ChronicConditionSerializer(serializers.ModelSerializer): class Meta: model = ChronicCondition fields = ["id", "name", "duration"] class FoodIngredientRecordSerializer(serializers.ModelSerializer): class Meta: model = FoodIngredientRecord fields = ["name", "from_dataset"] class FoodRecordSerializer(serializers.ModelSerializer): class Meta: model = FoodRecord fields = ["name", "quantity"] class BeverageRecordSerializer(serializers.ModelSerializer): class Meta: model = BeverageRecord fields = [ "beverage_type", "quantity", "quantity_measure", ] class MealRecordSerializer(serializers.ModelSerializer): food_records = FoodRecordSerializer(many=True) beverage_records = BeverageRecordSerializer(many=True) food_ingredient_records = FoodIngredientRecordSerializer(many=True) class Meta: model = MealRecord fields = [ "id", "date", "time", "meal_type", "food_records", "food_ingredient_records", "beverage_records", ] def create(self, validated_data): food_record_data = validated_data.pop("food_records", []) beverage_record_data = validated_data.pop("beverage_records", []) food_ingredient_record_data = validated_data.pop("food_ingredient_records", []) meal_record = self.Meta.model.objects.create(**validated_data) food_record_list = [] for data in food_record_data: obj, _ = FoodRecord.objects.get_or_create(**data) food_record_list.append(obj) food_ingredient_list = [] for data in food_ingredient_record_data: obj, _ = FoodIngredientRecord.objects.get_or_create(**data) food_ingredient_list.append(obj) beverage_record_list = [] for data in beverage_record_data: obj, _ = BeverageRecord.objects.get_or_create(**data) beverage_record_list.append(obj) meal_record.food_records.set(food_record_list) meal_record.food_ingredient_records.set(food_ingredient_list) meal_record.beverage_records.set(beverage_record_list) return meal_record def update(self, instance, validated_data): instance.date = validated_data.get("date", instance.date) instance.time = validated_data.get("time", instance.time) instance.meal_type = validated_data.get("meal_type", instance.meal_type) instance.save() food_record_data = validated_data.pop("food_records", []) beverage_record_data = validated_data.pop("beverage_records", []) food_ingredient_record_data = validated_data.pop("food_ingredient_records", []) food_record_list = [] for data in food_record_data: obj, _ = FoodRecord.objects.get_or_create(**data) food_record_list.append(obj) food_ingredient_list = [] for data in food_ingredient_record_data: obj, _ = FoodIngredientRecord.objects.get_or_create(**data) food_ingredient_list.append(obj) beverage_record_list = [] for data in beverage_record_data: obj, _ = BeverageRecord.objects.get_or_create(**data) beverage_record_list.append(obj) instance.food_records.set(food_record_list) instance.food_ingredient_records.set(food_ingredient_list) instance.beverage_records.set(beverage_record_list) instance.save() return instance class MedicineSerializer(serializers.ModelSerializer): class Meta: model = Medicine fields = ["name", "quantity", "type"] class MedicationSerializer(serializers.ModelSerializer): medicines = MedicineSerializer(many=True) class Meta: model = Medication fields = ["id", "date", "time", "medicines"] def create(self, validated_data): medicines_data = validated_data.pop("medicines") medication = self.Meta.model.objects.create(**validated_data) for medicine_data in medicines_data: medicine = Medicine.objects.create(**medicine_data) medication.medicines.add(medicine) return medication def update(self, instance, validated_data): instance.date = validated_data.get("date", instance.date) instance.time = validated_data.get("time", instance.time) instance.save() medicines_data = validated_data.get("medicines", []) instance.medicines.clear() for medicine_data in medicines_data: medicine = Medicine.objects.create(**medicine_data) instance.medicines.add(medicine) return instance class BowelSerializer(serializers.ModelSerializer): class Meta: model = Bowel fields = [ "id", "date", "time", "stool_type", "stool_name", "duration", "completeness_of_evacuation", "urgency", "smellness", "pain_level", "volume", "color", "excessive_flatulence", ] class MealRecordSymptomsSerializer(serializers.ModelSerializer): class Meta: model = MealRecord fields = [ "id", "date", "time", "meal_type", ] class SymptomTypeBeforeMealSerializer(serializers.ModelSerializer): class Meta: model = SymptomTypeBeforeMeal fields = ["name"] class SymptomTypeAfterMealSerializer(serializers.ModelSerializer): class Meta: model = SymptomTypeAfterMeal fields = ["name"] class MealSymptomRecordSerializer(serializers.ModelSerializer): symptoms_before_meal = SymptomTypeBeforeMealSerializer(many=True) symptoms_after_meal = SymptomTypeAfterMealSerializer(many=True) related_meal_id = serializers.PrimaryKeyRelatedField( queryset=MealRecord.objects.all(), write_only=True ) # Added field to accept meal ID related_meal = MealRecordSymptomsSerializer(read_only=True) class Meta: model = MealSymptomRecord fields = [ "id", "date", "time", "symptoms_description", "interval", "symptoms_before_meal", "symptoms_after_meal", "related_meal_id", "related_meal" ] def create(self, validated_data): before_meal_data = validated_data.pop("symptoms_before_meal") after_meal_data = validated_data.pop("symptoms_after_meal") related_meal_id = validated_data.pop("related_meal_id") meal_symptom_record = MealSymptomRecord.objects.create( related_meal=related_meal_id, **validated_data ) before_meal_list = [] for data in before_meal_data: obj, _ = SymptomTypeBeforeMeal.objects.get_or_create(**data) before_meal_list.append(obj) after_meal_list = [] for data in after_meal_data: obj, _ = SymptomTypeAfterMeal.objects.get_or_create(**data) after_meal_list.append(obj) meal_symptom_record.symptoms_before_meal.set(before_meal_list) meal_symptom_record.symptoms_after_meal.set(after_meal_list) return meal_symptom_record def update(self, instance, validated_data): instance.date = validated_data.get("date", instance.date) instance.time = validated_data.get("time", instance.time) instance.symptoms_description = validated_data.get( "symptoms_description", instance.symptoms_description ) instance.interval = validated_data.get("interval", instance.interval) related_meal_id = validated_data.pop("related_meal_id", None) # Extract meal ID if related_meal_id is not None: instance.related_meal = related_meal_id before_meal_data = validated_data.pop("symptoms_before_meal", []) after_meal_data = validated_data.pop("symptoms_after_meal", []) before_meal_instances = [] for data in before_meal_data: obj, _ = SymptomTypeBeforeMeal.objects.get_or_create(**data) before_meal_instances.append(obj) after_meal_instances = [] for data in after_meal_data: obj, _ = SymptomTypeAfterMeal.objects.get_or_create(**data) after_meal_instances.append(obj) instance.symptoms_before_meal.set(before_meal_instances) instance.symptoms_after_meal.set(after_meal_instances) instance.save() return instance