Fakultas Ilmu Komputer UI

Commit 3d783406 authored by Kefas Satrio Bangkit Solidedantyo's avatar Kefas Satrio Bangkit Solidedantyo
Browse files

[REFACTOR] change ProgramRecommendationResponse values to unique IDs and...

[REFACTOR] change ProgramRecommendationResponse values to unique IDs and chenge recommendation_breakfast to breakfast_recommendation
parent 20bd456d
stages:
- test
- sonar-scanner
# - sonar-scanner
- deploy
UnitTest:
......@@ -17,25 +17,27 @@ UnitTest:
- echo "Starting linter..."
- sh linter.sh
- echo "Starting tests..."
- rm -f coverage.xml
- coverage erase
- DATABASE_URL=$TEST_DATABASE_URL coverage run --include="./dietela_quiz/*","./nutritionists/*","./dietela_program/*" manage.py test --keepdb
- DATABASE_URL=$TEST_DATABASE_URL coverage run --branch --source=dietela_quiz,nutritionists,dietela_program manage.py test --keepdb
- coverage xml -i
- coverage report -m
artifacts:
paths:
- coverage.xml
SonarScanner:
image:
name: addianto/sonar-scanner-cli:latest
entrypoint: [""]
stage: sonar-scanner
script:
- sonar-scanner
-Dsonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube
-Dsonar.login=$SONARQUBE_TOKEN
-Dsonar.branch.name=$CI_COMMIT_REF_NAME
-Dsonar.projectKey=$SONARQUBE_PROJECT_KEY
# SonarScanner:
# image:
# name: sonarsource/sonar-scanner-cli:4.6
# entrypoint: [""]
# stage: sonar-scanner
# script:
# - sonar-scanner
# -Dsonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube
# -Dsonar.login=$SONARQUBE_TOKEN
# -Dsonar.projectKey=$SONARQUBE_PROJECT_KEY
# -Dsonar.branch.name=$CI_COMMIT_REF_NAME
# -Dsonar.python.coverage.reportPaths=coverage.xml
DeploymentStaging:
image: ruby:2.4
......
......@@ -12,15 +12,15 @@ class PhysicalActivityResponse:
LEVEL5_ACTIVITY = "Wow! Sudah bagus banget nih, jangan lupa lakukan monitoring rutin terhadap komposisi tubuhmu. Jangan lupa juga untuk mencukupi kebutuhan cairan sebelum, saat, dan setelah olahraga. Konsultasikan kebutuhan gizi harianmu kepada dietela untuk pengaturan pola makan yang sesuai dengan aktivitas fisikmu."
class ProgramRecommendationResponse:
BABY_1 = "Body for Baby Program 1 Bulan"
BABY_3 = "Body for Baby Program 3 Bulan"
GOALS_1 = "Body Goals Program 1 Bulan"
GOALS_3 = "Body Goals Program 3 Bulan"
GOALS_6 = "Body Goals Program 6 Bulan"
BALANCED_1 = "Balanced Diet Program 1 Bulan"
BALANCED_3 = "Balanced Diet Program 3 Bulan"
BALANCED_6 = "Balanced Diet Program 6 Bulan"
TRIAL = "One week trial"
BABY_1 = "BABY_1"
BABY_3 = "BABY_3"
GOALS_1 = "GOALS_1"
GOALS_3 = "GOALS_3"
GOALS_6 = "GOALS_6"
BALANCED_1 = "BALANCED_1"
BALANCED_3 = "BALANCED_3"
BALANCED_6 = "BALANCED_6"
TRIAL = "TRIAL"
class CallToActionCopywritingResponse:
PRIORITY_1 = "Dari hasil mini asesmen kamu tadi, Dietela menyarankan kamu untuk menggunakan layanan ini untuk membantu (masalah yang mau diperbaiki/goals yang mau dicapai). Program dietela dirancang agar kamu bisa menemukan pola makan dan gaya hidup sehat yang cocok dengan gayamu."
......
......@@ -2,13 +2,13 @@ from constants.quiz_constants import *
from constants.model_choices import *
def body_mass_index(weight, height):
def get_body_mass_index(weight, height):
if (weight < 0) or (height < 0):
raise ValueError("Invalid input for body mass index!")
return weight / ((height / 100)**2)
def nutrition_status(body_mass_index):
def get_nutrition_status(body_mass_index):
if (body_mass_index < 18.50):
return BodyMassConstants.UNDERWEIGHT
if (body_mass_index <= 22.99):
......@@ -20,13 +20,13 @@ def nutrition_status(body_mass_index):
return BodyMassConstants.OBESITY2
def ideal_weight_range(height):
def get_ideal_weight_range(height):
if (height < 0):
raise ValueError("Invalid input for ideal weight range!")
return {'min': 19 * ((height / 100)**2), 'max': 23 * ((height / 100)**2)}
def basic_energy_needs(gender, weight, height, age):
def get_basic_energy_needs(gender, weight, height, age):
if (gender > 2) or (gender < 1) or (
weight < 0) or (height < 0) or (age < 0):
raise ValueError("Invalid input for basic energy needs!")
......@@ -36,7 +36,7 @@ def basic_energy_needs(gender, weight, height, age):
return (10 * weight) + (6.25 * height - 5 * age) - 161
def physical_activity_percentage(body_activity):
def get_physical_activity_percentage(body_activity):
if (body_activity > 5) or (body_activity < 1):
raise ValueError("Invalid choice number!")
physical_activity_percentage_switch = {
......@@ -49,7 +49,7 @@ def physical_activity_percentage(body_activity):
return physical_activity_percentage_switch.get(body_activity)
def daily_energy_needs(
def get_daily_energy_needs(
special_condition,
body_mass,
basic_energy_needs,
......@@ -79,16 +79,15 @@ def daily_energy_needs(
(physical_activity_percentage * basic_energy_needs)
def recommendation_breakfast(breakfast_frequency):
if (breakfast_frequency == 1):
def get_breakfast_recommendation(breakfast_type):
if (breakfast_type == 1):
return BreakfastReponse.NO_BREAKFAST
if (breakfast_frequency == 2):
if (breakfast_type == 2):
return BreakfastReponse.MED_BREAKFAST
if (breakfast_frequency == 3):
return BreakfastReponse.HI_BREAKFAST
return BreakfastReponse.HI_BREAKFAST
def physical_activity_recommendation(physical_activity_frequency):
def get_physical_activity_recommendation(physical_activity_frequency):
if (physical_activity_frequency == 1):
return PhysicalActivityResponse.LEVEL1_ACTIVITY
if (physical_activity_frequency == 2):
......@@ -97,19 +96,18 @@ def physical_activity_recommendation(physical_activity_frequency):
return PhysicalActivityResponse.LEVEL3_ACTIVITY
if (physical_activity_frequency == 4):
return PhysicalActivityResponse.LEVEL4_ACTIVITY
if (physical_activity_frequency == 5):
return PhysicalActivityResponse.LEVEL5_ACTIVITY
return PhysicalActivityResponse.LEVEL5_ACTIVITY
def energy_needed_per_dine(breakfast_intensity, daily_energy_needs):
def get_energy_needed_per_dine(breakfast_type, daily_energy_needs):
energy_dictionary = {}
if (0 < breakfast_intensity < 3):
if (0 < breakfast_type < 3):
energy_dictionary['breakfast'] = daily_energy_needs * 0.1
energy_dictionary['morning_snack'] = daily_energy_needs * 0.15
energy_dictionary['lunch'] = daily_energy_needs * 0.3
energy_dictionary['afternoon_snack'] = daily_energy_needs * 0.15
energy_dictionary['dinner'] = daily_energy_needs * 0.3
elif (breakfast_intensity == 3):
elif (breakfast_type == 3):
energy_dictionary['breakfast'] = daily_energy_needs * 0.2
energy_dictionary['morning_snack'] = daily_energy_needs * 0.1
energy_dictionary['lunch'] = daily_energy_needs * 0.3
......@@ -120,8 +118,8 @@ def energy_needed_per_dine(breakfast_intensity, daily_energy_needs):
return energy_dictionary
def program_recommendation(condition, target, disease):
score = condition + target + disease
def get_program_recommendation(current_condition, problem_to_solve, health_problem):
score = current_condition + problem_to_solve + sum(health_problem)
programs_dictionary = {}
if (22 <= score <= 30) or (70 <= score <= 79):
......@@ -151,51 +149,51 @@ def program_recommendation(condition, target, disease):
return programs_dictionary
def calculate_specific_nutrition_needs(
def get_calculate_specific_nutrition_needs(
percentage, daily_energy_needs, denominator):
return ((percentage / 100) * daily_energy_needs) / denominator
def protein_needs(
def get_protein_needs(
special_condition_choice,
body_mass_index,
daily_energy_needs):
client_nutrition_status = nutrition_status(body_mass_index)
client_nutrition_status = get_nutrition_status(body_mass_index)
if (
special_condition_choice != 1) or (
client_nutrition_status == BodyMassConstants.UNDERWEIGHT) or (
client_nutrition_status == BodyMassConstants.NORMAL) or (
client_nutrition_status == BodyMassConstants.PREOBESITY):
return calculate_specific_nutrition_needs(13, daily_energy_needs, 4)
return calculate_specific_nutrition_needs(18, daily_energy_needs, 4)
return get_calculate_specific_nutrition_needs(13, daily_energy_needs, 4)
return get_calculate_specific_nutrition_needs(18, daily_energy_needs, 4)
def fat_needs(special_condition_choice, body_mass_index, daily_energy_needs):
client_nutrition_status = nutrition_status(body_mass_index)
def get_fat_needs(special_condition_choice, body_mass_index, daily_energy_needs):
client_nutrition_status = get_nutrition_status(body_mass_index)
if (
special_condition_choice != 1) or (
client_nutrition_status == BodyMassConstants.UNDERWEIGHT) or (
client_nutrition_status == BodyMassConstants.NORMAL) or (
client_nutrition_status == BodyMassConstants.PREOBESITY):
return calculate_specific_nutrition_needs(30, daily_energy_needs, 9)
return calculate_specific_nutrition_needs(27, daily_energy_needs, 9)
return get_calculate_specific_nutrition_needs(30, daily_energy_needs, 9)
return get_calculate_specific_nutrition_needs(27, daily_energy_needs, 9)
def carbohydrate_needs(
def get_carbohydrate_needs(
special_condition_choice,
body_mass_index,
daily_energy_needs):
client_nutrition_status = nutrition_status(body_mass_index)
client_nutrition_status = get_nutrition_status(body_mass_index)
if (
special_condition_choice != 1) or (
client_nutrition_status == BodyMassConstants.UNDERWEIGHT) or (
client_nutrition_status == BodyMassConstants.NORMAL) or (
client_nutrition_status == BodyMassConstants.PREOBESITY):
return calculate_specific_nutrition_needs(57, daily_energy_needs, 4)
return calculate_specific_nutrition_needs(55, daily_energy_needs, 4)
return get_calculate_specific_nutrition_needs(57, daily_energy_needs, 4)
return get_calculate_specific_nutrition_needs(55, daily_energy_needs, 4)
def fiber_needs(body_mass_index):
def get_fiber_needs(body_mass_index):
return 25
......@@ -203,15 +201,15 @@ def get_food_consumed_score(choice):
return choice - 1
def get_total_vegetable_and_fruit_score(vegetable_choice, fruit_choice):
if (vegetable_choice < 1) or (vegetable_choice > 5):
def get_total_vegetable_and_fruit_score(vegetables_in_one_day, fruits_in_one_day):
if (vegetables_in_one_day < 1) or (vegetables_in_one_day > 5):
raise ValueError("Invalid vegetable choice number!")
if (fruit_choice < 1) or (fruit_choice > 5):
if (fruits_in_one_day < 1) or (fruits_in_one_day > 5):
raise ValueError("Invalid fruit choice number!")
vegetable_score = get_food_consumed_score(vegetable_choice)
fruit_score = get_food_consumed_score(fruit_choice)
vegetable_score = get_food_consumed_score(vegetables_in_one_day)
fruit_score = get_food_consumed_score(fruits_in_one_day)
return vegetable_score + fruit_score
......@@ -277,29 +275,29 @@ def get_sugar_salt_fat_diet_recommendation(sugar_salt_fat_problem_response):
return SugarSaltFatDietRecommendation.EXCESSIVE
def get_large_meal_diet_recommendation(large_meal_choice):
if (large_meal_choice < 1) or (large_meal_choice > 4):
def get_large_meal_diet_recommendation(large_meal_in_one_day):
if (large_meal_in_one_day < 1) or (large_meal_in_one_day > 4):
raise ValueError("Invalid large meal choice number!")
if (large_meal_choice == 1):
if (large_meal_in_one_day == 1):
return LargeMealDietRecommendation.ONCE_A_DAY
if (large_meal_choice == 2):
if (large_meal_in_one_day == 2):
return LargeMealDietRecommendation.TWICE_A_DAY
if (large_meal_choice == 3):
if (large_meal_in_one_day == 3):
return LargeMealDietRecommendation.THRICE_A_DAY
return LargeMealDietRecommendation.MORE_THAN_THRICE_A_DAY
def get_snacks_diet_recommendation(snacks_choice):
if (snacks_choice < 1) or (snacks_choice > 5):
def get_snacks_diet_recommendation(snacks_in_one_day):
if (snacks_in_one_day < 1) or (snacks_in_one_day > 5):
raise ValueError("Invalid snacks choice number!")
if (snacks_choice == 1):
if (snacks_in_one_day == 1):
return SnacksDietRecommendation.NO_SNACK
if (snacks_choice == 2):
if (snacks_in_one_day == 2):
return SnacksDietRecommendation.ONCE_A_DAY
if (snacks_choice == 3):
if (snacks_in_one_day == 3):
return SnacksDietRecommendation.TWICE_A_DAY
if (snacks_choice == 4):
if (snacks_in_one_day == 4):
return SnacksDietRecommendation.THRICE_A_DAY
return SnacksDietRecommendation.MORE_THAN_THRICE_A_DAY
# Generated by Django 3.1 on 2021-03-28 12:55
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dietela_quiz', '0002_auto_20210323_2144'),
]
operations = [
migrations.CreateModel(
name='QuizResult',
fields=[
('diet_profile', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='dietela_quiz.dietprofile')),
('age', models.PositiveIntegerField()),
('weight', models.PositiveIntegerField()),
('height', models.PositiveIntegerField()),
('gender', models.IntegerField(choices=[(1, 'Laki-laki'), (2, 'Perempuan')])),
('body_mass_index', models.PositiveIntegerField()),
('nutrition_status', models.CharField(max_length=100)),
('ideal_weight_range', models.JSONField()),
('daily_energy_needs', models.FloatField()),
('daily_nutrition_needs', models.JSONField()),
('vegetable_and_fruit_sufficiency', models.CharField(max_length=100)),
('vegetable_and_fruit_diet_recommendation', models.TextField()),
('sugar_salt_fat_problem', models.CharField(max_length=100)),
('sugar_salt_fat_diet_recommendation', models.TextField()),
('large_meal_diet_recommendation', models.TextField()),
('snacks_diet_recommendation', models.TextField()),
('breakfast_recommendation', models.TextField()),
('energy_needed_per_dine', models.JSONField()),
('physical_activity_recommendation', models.TextField()),
('program_recommendation', models.JSONField()),
],
),
]
# Generated by Django 3.1 on 2021-03-29 07:43
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dietela_quiz', '0003_quizresult'),
]
operations = [
migrations.AlterField(
model_name='quizresult',
name='diet_profile',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='quiz_result', serialize=False, to='dietela_quiz.dietprofile'),
),
]
......@@ -14,6 +14,7 @@ from constants.model_choices import (
HealthProblem
)
from multiselectfield import MultiSelectField
from .formulas import *
class DietProfile(models.Model):
......@@ -54,3 +55,34 @@ class DietProfile(models.Model):
def __str__(self):
return f"{self.name} - {self.email}"
class QuizResult(models.Model):
diet_profile = models.OneToOneField(
DietProfile,
on_delete=models.CASCADE,
primary_key=True,
related_name="quiz_result"
)
age = models.PositiveIntegerField()
weight = models.PositiveIntegerField()
height = models.PositiveIntegerField()
gender = models.IntegerField(choices=Gender.choices)
body_mass_index = models.PositiveIntegerField()
nutrition_status = models.CharField(max_length=100)
ideal_weight_range = models.JSONField()
daily_energy_needs = models.FloatField()
daily_nutrition_needs = models.JSONField()
vegetable_and_fruit_sufficiency = models.CharField(max_length=100)
vegetable_and_fruit_diet_recommendation = models.TextField()
sugar_salt_fat_problem = models.CharField(max_length=100)
sugar_salt_fat_diet_recommendation = models.TextField()
large_meal_diet_recommendation = models.TextField()
snacks_diet_recommendation = models.TextField()
breakfast_recommendation = models.TextField()
energy_needed_per_dine = models.JSONField()
physical_activity_recommendation = models.TextField()
program_recommendation = models.JSONField()
def __str__(self):
return str(self.diet_profile)
from rest_framework import fields, serializers
from constants.model_choices import HealthProblem
from .models import DietProfile
from .models import DietProfile, QuizResult
from .utilities import create_or_update_quiz_result
class DietProfileSerializer(serializers.ModelSerializer):
......@@ -10,3 +11,10 @@ class DietProfileSerializer(serializers.ModelSerializer):
class Meta:
model = DietProfile
fields = "__all__"
class QuizResultSerializer(serializers.ModelSerializer):
class Meta:
model = QuizResult
fields = "__all__"
This diff is collapsed.
from .formulas import *
from .models import (
QuizResult
)
def create_or_update_quiz_result(profile):
age = profile.age
weight = profile.weight
height = profile.height
gender = profile.gender
body_mass_index = get_body_mass_index(profile.weight,profile.height)
nutrition_status = get_nutrition_status(body_mass_index)
ideal_weight_range = get_ideal_weight_range(profile.height)
basic_energy_needs = get_basic_energy_needs(profile.gender,profile.weight,profile.height,profile.age)
physical_activity_percentage = get_physical_activity_percentage(profile.body_activity)
daily_energy_needs = get_daily_energy_needs(profile.special_condition,nutrition_status,basic_energy_needs,physical_activity_percentage)
protein = get_protein_needs(profile.special_condition,body_mass_index,daily_energy_needs)
fat = get_fat_needs(profile.special_condition,body_mass_index,daily_energy_needs)
carbohydrate = get_carbohydrate_needs(profile.special_condition,body_mass_index,daily_energy_needs)
fiber = get_fiber_needs(body_mass_index)
daily_nutrition_needs = {'protein_needs': protein, 'fat_needs': fat, 'carbohydrate_needs': carbohydrate, 'fiber_needs': fiber,}
total_vegetable_and_fruit_score = get_total_vegetable_and_fruit_score(profile.vegetables_in_one_day,profile.fruits_in_one_day)
vegetable_and_fruit_sufficiency = get_vegetable_and_fruit_sufficiency_response(total_vegetable_and_fruit_score)
vegetable_and_fruit_diet_recommendation = get_vegetable_and_fruit_diet_recommendation(vegetable_and_fruit_sufficiency)
total_sugar_salt_fat_score = get_total_sugar_salt_fat_score(profile.fried_food_in_one_day,profile.sweet_snacks_in_one_day,profile.sweet_drinks_in_one_day,profile.packaged_food_in_one_day)
sugar_salt_fat_problem = get_sugar_salt_fat_problem_response(total_sugar_salt_fat_score)
sugar_salt_fat_diet_recommendation = get_sugar_salt_fat_diet_recommendation(sugar_salt_fat_problem)
large_meal_diet_recommendation = get_large_meal_diet_recommendation(profile.large_meal_in_one_day)
snacks_diet_recommendation = get_snacks_diet_recommendation(profile.snacks_in_one_day)
breakfast_recommendation = get_breakfast_recommendation(profile.breakfast_type)
energy_needed_per_dine = get_energy_needed_per_dine(profile.breakfast_type,daily_energy_needs)
physical_activity_recommendation = get_physical_activity_recommendation(profile.body_activity)
program_recommendation = get_program_recommendation(profile.current_condition,profile.problem_to_solve,profile.health_problem)
quiz_result = QuizResult.objects.update_or_create(diet_profile=profile,age=age,weight=weight,height=height,gender=gender,
body_mass_index=body_mass_index,nutrition_status=nutrition_status,ideal_weight_range=ideal_weight_range,
daily_energy_needs=daily_energy_needs,daily_nutrition_needs=daily_nutrition_needs,
vegetable_and_fruit_sufficiency=vegetable_and_fruit_sufficiency,vegetable_and_fruit_diet_recommendation=vegetable_and_fruit_diet_recommendation,
sugar_salt_fat_problem=sugar_salt_fat_problem,sugar_salt_fat_diet_recommendation=sugar_salt_fat_diet_recommendation,
large_meal_diet_recommendation=large_meal_diet_recommendation,snacks_diet_recommendation=snacks_diet_recommendation,
breakfast_recommendation=breakfast_recommendation,energy_needed_per_dine=energy_needed_per_dine,physical_activity_recommendation=physical_activity_recommendation,
program_recommendation=program_recommendation)
return quiz_result[0]
from django.shortcuts import render
from rest_framework import viewsets
from .serializers import DietProfileSerializer
from .models import DietProfile
# Create your views here.
from rest_framework import viewsets, status
from .serializers import DietProfileSerializer, QuizResultSerializer
from .models import DietProfile, QuizResult
from .utilities import create_or_update_quiz_result
from rest_framework.response import Response
class DietProfileViewSet(viewsets.ModelViewSet):
serializer_class = DietProfileSerializer
queryset = DietProfile.objects.all()
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
instance = self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
quiz_result = QuizResult.objects.get(diet_profile=instance)
response = serializer.data
response['quiz_result'] = QuizResultSerializer(quiz_result).data
return Response(response, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
instance = serializer.save()
create_or_update_quiz_result(instance)
return instance
[coverage:run]
omit =
*/apps.py
# SonarScanner properties file
## Server
sonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube
sonar.login=$SONARQUBE_TOKEN
## Project configuration
sonar.projectKey=$SONARQUBE_PROJECT_KEY
## Path to sources
sonar.sources=.
sonar.exclusions=**/migrations/**, **/__init__.py, **/admin.py, **/apps.py, dietela_backend/**, constants/**, **/tests.py, manage.py
......@@ -11,5 +18,8 @@ sonar.test.inclusions=**/tests.py
## Source encoding
sonar.sourceEncoding=UTF-8
## Branch analysis
sonar.branch.name=$CI_COMMIT_REF_NAME
## Coverage Report
sonar.python.coverage.reportPaths=coverage.xml
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment