@@ -12,7 +12,7 @@ from module_project import constants, date_utils
|
||||
from module_project.service import OneSignalNotificationService
|
||||
from module_project.utils import ApiResponse
|
||||
|
||||
from ..models import (FoodIngredintDataset, Bowel, ChronicCondition, Intolerance, MealRecord,
|
||||
from ..models import (BeverageRecord, FoodIngredientRecord, FoodIngredintDataset, Bowel, ChronicCondition, FoodRecord, Intolerance, MealRecord,
|
||||
MealSymptomRecord, Medication, PastTreatment,
|
||||
PrincipalHealthData, Symptoms)
|
||||
from .serializers import (FoodDatasetSerializer, FoodIngredientDatasetSerializer, BowelSerializer, ChronicConditionSerializer,
|
||||
@@ -103,6 +103,7 @@ class DailyRecordAPIView(APIView):
|
||||
"id": record.id,
|
||||
"date": record.date,
|
||||
"time": time_obj.strftime("%I:%M %p"),
|
||||
"sort_time": time_obj,
|
||||
# Add other fields as needed
|
||||
}
|
||||
|
||||
@@ -184,7 +185,7 @@ class DailyRecordAPIView(APIView):
|
||||
)
|
||||
|
||||
# all_records_sorted = sorted(all_records, key=lambda x: x["time"], reverse=True)
|
||||
all_records_sorted = sorted(all_records, key=lambda x: x["time"], reverse=True)
|
||||
all_records_sorted = sorted(all_records, key=lambda x: x["sort_time"], reverse=True)
|
||||
|
||||
return ApiResponse.success(message=constants.SUCCESS, data=all_records_sorted)
|
||||
|
||||
@@ -762,18 +763,38 @@ class ReportAPIView(APIView):
|
||||
ingredient_counts = defaultdict(int)
|
||||
beverage_counts = defaultdict(int)
|
||||
|
||||
symptom_records = MealSymptomRecord.objects.filter(
|
||||
# symptom_records = MealSymptomRecord.objects.select_related("related_meal").filter(
|
||||
# principal=self.get_user(), date__range=(start_date, end_date)
|
||||
# )
|
||||
|
||||
# for symptom_record in symptom_records:
|
||||
# closest_meal = (
|
||||
# MealRecord.objects.filter(
|
||||
# principal=symptom_record.principal, date__lte=symptom_record.date
|
||||
# )
|
||||
# .order_by("-date", "-time")
|
||||
# .first()
|
||||
# )
|
||||
# if closest_meal:
|
||||
# for food_record in closest_meal.food_records.all():
|
||||
# food_counts[food_record.name] += 1
|
||||
# for ingredient_record in closest_meal.food_ingredient_records.all():
|
||||
# ingredient_counts[ingredient_record.name] += 1
|
||||
# for beverage_record in closest_meal.beverage_records.all():
|
||||
# beverage_counts[beverage_record.beverage_type] += 1
|
||||
|
||||
# Fetch symptom records with related meal records and related food/ingredient/beverage records
|
||||
symptom_records = MealSymptomRecord.objects.prefetch_related(
|
||||
Prefetch('related_meal__food_records', queryset=FoodRecord.objects.all()),
|
||||
Prefetch('related_meal__food_ingredient_records', queryset=FoodIngredientRecord.objects.all()),
|
||||
Prefetch('related_meal__beverage_records', queryset=BeverageRecord.objects.all())
|
||||
).filter(
|
||||
principal=self.get_user(), date__range=(start_date, end_date)
|
||||
)
|
||||
|
||||
# Loop through symptom records and count food, ingredient, and beverage occurrences for each related meal record
|
||||
for symptom_record in symptom_records:
|
||||
closest_meal = (
|
||||
MealRecord.objects.filter(
|
||||
principal=symptom_record.principal, date__lte=symptom_record.date
|
||||
)
|
||||
.order_by("-date", "-time")
|
||||
.first()
|
||||
)
|
||||
closest_meal = symptom_record.related_meal
|
||||
if closest_meal:
|
||||
for food_record in closest_meal.food_records.all():
|
||||
food_counts[food_record.name] += 1
|
||||
|
||||
@@ -20,7 +20,7 @@ from module_project.utils import JsonResponseUtil
|
||||
|
||||
from .forms import (ChronicConditionForm, IntoleranceForm, PastTreatmentForm,
|
||||
SymptomsForm, UploadFileForm)
|
||||
from .models import (Bowel, ChronicCondition, FoodIngredintDataset,
|
||||
from .models import (BeverageRecord, Bowel, ChronicCondition, FoodIngredientRecord, FoodIngredintDataset, FoodRecord,
|
||||
Intolerance, MealRecord, MealSymptomRecord, Medication,
|
||||
PastTreatment, Symptoms)
|
||||
|
||||
@@ -611,7 +611,6 @@ class ReportDataView(generic.View):
|
||||
model = MealRecord
|
||||
|
||||
def get_user(self, *args, **kwargs):
|
||||
print(f"user id is {self.kwargs.get('principal_id')}")
|
||||
user = IAmPrincipal.objects.filter(id=self.kwargs.get("principal_id")).first()
|
||||
print(f"user is {user}")
|
||||
return user
|
||||
@@ -641,18 +640,38 @@ class ReportDataView(generic.View):
|
||||
ingredient_counts = defaultdict(int)
|
||||
beverage_counts = defaultdict(int)
|
||||
|
||||
symptom_records = MealSymptomRecord.objects.filter(
|
||||
# symptom_records = MealSymptomRecord.objects.filter(
|
||||
# principal=self.get_user(), date__range=(start_date, end_date)
|
||||
# )
|
||||
|
||||
# for symptom_record in symptom_records:
|
||||
# closest_meal = (
|
||||
# MealRecord.objects.filter(
|
||||
# principal=symptom_record.principal, date__lte=symptom_record.date
|
||||
# )
|
||||
# .order_by("-date", "-time")
|
||||
# .first()
|
||||
# )
|
||||
# if closest_meal:
|
||||
# for food_record in closest_meal.food_records.all():
|
||||
# food_counts[food_record.name] += 1
|
||||
# for ingredient_record in closest_meal.food_ingredient_records.all():
|
||||
# ingredient_counts[ingredient_record.name] += 1
|
||||
# for beverage_record in closest_meal.beverage_records.all():
|
||||
# beverage_counts[beverage_record.beverage_type] += 1
|
||||
|
||||
# Fetch symptom records with related meal records and related food/ingredient/beverage records
|
||||
symptom_records = MealSymptomRecord.objects.prefetch_related(
|
||||
Prefetch('related_meal__food_records', queryset=FoodRecord.objects.all()),
|
||||
Prefetch('related_meal__food_ingredient_records', queryset=FoodIngredientRecord.objects.all()),
|
||||
Prefetch('related_meal__beverage_records', queryset=BeverageRecord.objects.all())
|
||||
).filter(
|
||||
principal=self.get_user(), date__range=(start_date, end_date)
|
||||
)
|
||||
|
||||
# Loop through symptom records and count food, ingredient, and beverage occurrences for each related meal record
|
||||
for symptom_record in symptom_records:
|
||||
closest_meal = (
|
||||
MealRecord.objects.filter(
|
||||
principal=symptom_record.principal, date__lte=symptom_record.date
|
||||
)
|
||||
.order_by("-date", "-time")
|
||||
.first()
|
||||
)
|
||||
closest_meal = symptom_record.related_meal
|
||||
if closest_meal:
|
||||
for food_record in closest_meal.food_records.all():
|
||||
food_counts[food_record.name] += 1
|
||||
|
||||
@@ -38,25 +38,17 @@ class FaqListJson(BaseDatatableView):
|
||||
model = Faqs
|
||||
columns = ["id", "question", "answer", "active"]
|
||||
order_columns = ["id", "question", "answer", "active"]
|
||||
FILTER_ICONTAINS = "icontains"
|
||||
|
||||
def get_filter_method(self):
|
||||
"""Returns preferred filter method"""
|
||||
return self.FILTER_ICONTAINS
|
||||
|
||||
def get_initial_queryset(self):
|
||||
deleted_flag = self.request.GET.get('deleted_flag', None)
|
||||
|
||||
return self.model.objects.filter(deleted=deleted_flag)
|
||||
|
||||
# def filter_queryset(self, qs):
|
||||
# # Implement your custom filtering logic here
|
||||
# print(f"request is {self.request.GET}")
|
||||
# search_value = self.request.GET.get("search[value]", None)
|
||||
# if search_value:
|
||||
# qs = qs.filter(
|
||||
# Q(id__icontains=search_value)
|
||||
# | Q(question__icontains=search_value)
|
||||
# | Q(answer__icontains=search_value)
|
||||
# )
|
||||
|
||||
# return qs
|
||||
|
||||
def ordering(self, qs):
|
||||
order = self.request.GET.get('order[0][dir]', None)
|
||||
if order:
|
||||
|
||||
@@ -60,14 +60,3 @@ def iam_constants_context(request):
|
||||
'RESOURCE_IAM_ROLE': RESOURCE_IAM_ROLE,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def resource_permissions(request):
|
||||
if request.user.is_authenticated:
|
||||
resource_permissions = IAmPrincipal.objects.filter(id=request.user.id).values_list('principal_resource__name', flat=True)
|
||||
else:
|
||||
resource_permissions = []
|
||||
|
||||
return {
|
||||
'resource_permissions': resource_permissions,
|
||||
}
|
||||
@@ -41,28 +41,16 @@ class NotificationListJsonView(BaseDatatableView):
|
||||
columns = ["id", "title", "message", "active", "timestamp"]
|
||||
order_columns = ["id", "title", "message", "active", "timestamp"]
|
||||
|
||||
FILTER_ICONTAINS = "icontains"
|
||||
|
||||
def get_filter_method(self):
|
||||
"""Returns preferred filter method"""
|
||||
return self.FILTER_ICONTAINS
|
||||
|
||||
def get_initial_queryset(self):
|
||||
deleted_flag = self.request.GET.get("deleted_flag", None)
|
||||
return self.model.objects.filter(deleted=deleted_flag)
|
||||
|
||||
# def render_column(self, row, column):
|
||||
# if column == "timestamp":
|
||||
# return date_utils.format_date_to_string(row.timestamp)
|
||||
# return super().render_column(row, column)
|
||||
|
||||
# def filter_queryset(self, qs):
|
||||
# # Implement your custom filtering logic here
|
||||
# print(f"request is {self.request.GET}")
|
||||
# search_value = self.request.GET.get("search[value]", None)
|
||||
# if search_value:
|
||||
# qs = qs.filter(
|
||||
# Q(id__icontains=search_value)
|
||||
# | Q(question__icontains=search_value)
|
||||
# | Q(answer__icontains=search_value)
|
||||
# )
|
||||
|
||||
# return qs
|
||||
|
||||
def ordering(self, qs):
|
||||
order = self.request.GET.get('order[0][dir]', None)
|
||||
if order:
|
||||
|
||||
22
openfoodfact.py
Normal file
22
openfoodfact.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import openfoodfacts
|
||||
import json
|
||||
|
||||
# User-Agent is mandatory
|
||||
api = openfoodfacts.API(user_agent="Digest/1.0")
|
||||
|
||||
# Search for pizza products
|
||||
data = api.product.text_search("Rice Noodles")
|
||||
|
||||
# Create filename (adjust as needed)
|
||||
filename = "pizza_products.json"
|
||||
|
||||
# Open the file in write mode (will create if non-existent)
|
||||
with open(filename, "w") as json_file:
|
||||
|
||||
# Convert data to JSON string (ensure proper indentation)
|
||||
json_string = json.dumps(data, indent=4)
|
||||
|
||||
# Write the JSON string to the file
|
||||
json_file.write(json_string)
|
||||
|
||||
print(f"Pizza product data saved to '{filename}'.")
|
||||
@@ -1,3 +1,4 @@
|
||||
annotated-types==0.6.0
|
||||
anyio==4.3.0
|
||||
asgiref==3.7.2
|
||||
certifi==2024.2.2
|
||||
@@ -9,6 +10,7 @@ cryptography==42.0.5
|
||||
defusedxml==0.7.1
|
||||
Django==5.0.2
|
||||
django-cors-headers==4.3.1
|
||||
django-crontab==0.7.1
|
||||
django-datatables-view==1.20.0
|
||||
django-debug-toolbar==4.3.0
|
||||
django-environ==0.11.2
|
||||
@@ -29,6 +31,7 @@ numpy==1.26.4
|
||||
oauthlib==3.2.2
|
||||
onesignal-python-api==2.0.2
|
||||
onesignal-sdk==2.0.0
|
||||
openfoodfacts==0.2.1
|
||||
openpyxl==3.1.2
|
||||
packaging==23.2
|
||||
pandas==2.2.1
|
||||
@@ -36,6 +39,8 @@ phonenumbers==8.13.30
|
||||
pillow==10.2.0
|
||||
pluggy==1.4.0
|
||||
pycparser==2.21
|
||||
pydantic==2.6.4
|
||||
pydantic_core==2.16.3
|
||||
PyJWT==2.8.0
|
||||
pytest==8.0.2
|
||||
python-dateutil==2.9.0.post0
|
||||
@@ -47,5 +52,6 @@ six==1.16.0
|
||||
sniffio==1.3.1
|
||||
sqlparse==0.4.4
|
||||
tqdm==4.66.2
|
||||
typing_extensions==4.11.0
|
||||
tzdata==2023.4
|
||||
urllib3==2.2.1
|
||||
|
||||
@@ -24,57 +24,6 @@
|
||||
<div class="col-xl-12 col-lg-12 col-md-12 layout-spacing">
|
||||
<div class="statbox widget box box-shadow">
|
||||
<div class="widget-content widget-content-area">
|
||||
<!-- <form class="section general-info">
|
||||
<div class="info">
|
||||
<h6 class="">Profile</h6>
|
||||
<div class="row">
|
||||
<div class="col-lg-11 mx-auto">
|
||||
<div class="row">
|
||||
<div class="col-xl-2 col-lg-12 col-md-4">
|
||||
<div class="profile-image">
|
||||
<div class="img-uploader-content">
|
||||
<input type="file" class="filepond"
|
||||
name="filepond" accept="image/png, image/jpeg"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-10 col-lg-12 col-md-8 mt-md-0 mt-4">
|
||||
<div class="form">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="fullName">First Name</label>
|
||||
<input type="text" class="form-control mb-3"
|
||||
id="fullName" placeholder="Full Name"
|
||||
value="Jimmy Turner">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="profession">Last Name</label>
|
||||
<input type="text" class="form-control mb-3"
|
||||
id="profession" placeholder="Designer"
|
||||
value="Web Developer">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 mt-1">
|
||||
<div class="form-group text-end">
|
||||
<button
|
||||
class="btn btn-secondary _effect--ripple waves-effect waves-light">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form> -->
|
||||
<form method="POST" enctype="multipart/form-data" id="profileEditForm">
|
||||
{% csrf_token %}
|
||||
{% include 'base_structure/includes/dynamic_template_form.html' with form=form %}
|
||||
@@ -112,111 +61,116 @@
|
||||
FilePondPluginImageExifOrientation,
|
||||
FilePondPluginFileValidateSize,
|
||||
// FilePondPluginImageEdit
|
||||
);
|
||||
);
|
||||
|
||||
// Select the file input and use
|
||||
// create() to turn it into a pond
|
||||
const inputElement = document.getElementById('id_profile_photo');
|
||||
const pond = FilePond.create(inputElement, {
|
||||
storeAsFile: true,
|
||||
dropOnPage: true
|
||||
});
|
||||
// Get the profile photo URL using Django template syntax
|
||||
const profilePhotoUrl = "{% if form.profile_photo.value %}{{ form.profile_photo.value.url }}{% endif %}";
|
||||
// Select the file input and use
|
||||
// create() to turn it into a pond
|
||||
const inputElement = document.getElementById('id_profile_photo');
|
||||
const pond = FilePond.create(inputElement, {
|
||||
storeAsFile: true,
|
||||
dropOnPage: true
|
||||
});
|
||||
// Get the profile photo URL using Django template syntax
|
||||
const profilePhotoUrl = "{% if form.profile_photo.value %}{{ form.profile_photo.value.url }}{% endif %}";
|
||||
|
||||
if (profilePhotoUrl) {
|
||||
// If the URL exists, add the profile photo to FilePond
|
||||
pond.addFile(profilePhotoUrl);
|
||||
}
|
||||
if (profilePhotoUrl) {
|
||||
// If the URL exists, add the profile photo to FilePond
|
||||
pond.addFile(profilePhotoUrl);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Add custom validation method to check for special characters
|
||||
$.validator.addMethod("noSpecialChars", function(value, element) {
|
||||
return /^[a-zA-Z\s]*$/.test(value); // Allow only letters and whitespace
|
||||
}, "Please enter only letters and spaces.");
|
||||
$(document).ready(function() {
|
||||
// Add custom validation method to check for special characters
|
||||
$.validator.addMethod("noSpecialChars", function(value, element) {
|
||||
return /^[a-zA-Z\s]*$/.test(value); // Allow only letters and whitespace
|
||||
}, "Please enter only letters and spaces.");
|
||||
|
||||
// Add custom validation method to check for starting with a letter
|
||||
$.validator.addMethod("startsWithLetter", function(value, element) {
|
||||
return /^[a-zA-Z]/.test(value); // Check if the value starts with a letter
|
||||
}, "Please start with a letter.");
|
||||
// Add custom validation method to check for starting with a letter
|
||||
$.validator.addMethod("startsWithLetter", function(value, element) {
|
||||
return /^[a-zA-Z]/.test(value); // Check if the value starts with a letter
|
||||
}, "Please start with a letter.");
|
||||
|
||||
// Initialize form validation
|
||||
$("#profileEditForm").validate({
|
||||
rules: {
|
||||
first_name: {
|
||||
required: true,
|
||||
minlength: 2,
|
||||
maxlength:15,
|
||||
noSpecialChars: true,
|
||||
startsWithLetter: true
|
||||
},
|
||||
last_name: {
|
||||
required: true,
|
||||
minlength: 2,
|
||||
maxlength: 15,
|
||||
noSpecialChars: true,
|
||||
startsWithLetter: true
|
||||
},
|
||||
date_of_birth: {
|
||||
required: true,
|
||||
date: true,
|
||||
dateISO: true, // Check for ISO date format (YYYY-MM-DD)
|
||||
pattern: /^\d{4}-\d{2}-\d{2}$/ // Custom pattern for YYYY-MM-DD format
|
||||
},
|
||||
gender: {
|
||||
required: true
|
||||
},
|
||||
phone_no: {
|
||||
required: true,
|
||||
digits: true,
|
||||
minlength: 10,
|
||||
maxlength: 10
|
||||
}
|
||||
// Initialize form validation
|
||||
$("#profileEditForm").validate({
|
||||
rules: {
|
||||
first_name: {
|
||||
required: true,
|
||||
minlength: 2,
|
||||
maxlength:15,
|
||||
noSpecialChars: true,
|
||||
startsWithLetter: true
|
||||
},
|
||||
messages: {
|
||||
first_name: {
|
||||
required: "Please enter your first name.",
|
||||
minlength: "First name must be at least 2 characters.",
|
||||
maxlength: "First name must not exceed 15 characters.",
|
||||
noSpecialChars: "Please enter only letters and spaces.",
|
||||
startsWithLetter: "First name must start with a letter."
|
||||
},
|
||||
last_name: {
|
||||
required: "Please enter your last name.",
|
||||
minlength: "Last name must be at least 2 characters.",
|
||||
maxlength: "First name must not exceed 15 characters.",
|
||||
noSpecialChars: "Please enter only letters and spaces.",
|
||||
startsWithLetter: "Last name must start with a letter."
|
||||
},
|
||||
date_of_birth: {
|
||||
required: "Please enter your date of birth.",
|
||||
date: "Please enter a valid date in the format YYYY-MM-DD.",
|
||||
dateISO: "Please enter a valid date in the format YYYY-MM-DD.",
|
||||
pattern: "Please enter a valid date in the format YYYY-MM-DD."
|
||||
},
|
||||
gender: {
|
||||
required: "Please select your gender."
|
||||
},
|
||||
phone_no: {
|
||||
required: "Please enter your phone number.",
|
||||
digits: "Please enter only digits.",
|
||||
minlength: "Phone number must be 10 digits long.",
|
||||
maxlength: "Phone number must be 10 digits long."
|
||||
}
|
||||
last_name: {
|
||||
required: true,
|
||||
minlength: 2,
|
||||
maxlength: 15,
|
||||
noSpecialChars: true,
|
||||
startsWithLetter: true
|
||||
},
|
||||
errorElement: 'div',
|
||||
errorPlacement: function(error, element) {
|
||||
error.addClass('invalid-feedback');
|
||||
$(element).closest('.form-group').append(error);
|
||||
date_of_birth: {
|
||||
required: true,
|
||||
date: true,
|
||||
dateISO: true, // Check for ISO date format (YYYY-MM-DD)
|
||||
pattern: /^\d{4}-\d{2}-\d{2}$/ // Custom pattern for YYYY-MM-DD format
|
||||
},
|
||||
highlight: function(element, errorClass, validClass) {
|
||||
$(element).addClass('is-invalid').removeClass('is-valid');
|
||||
gender: {
|
||||
required: true
|
||||
},
|
||||
unhighlight: function(element, errorClass, validClass) {
|
||||
$(element).removeClass('is-invalid').addClass('is-valid');
|
||||
phone_no: {
|
||||
required: true,
|
||||
digits: true,
|
||||
minlength: 10,
|
||||
maxlength: 10
|
||||
}
|
||||
});
|
||||
},
|
||||
messages: {
|
||||
first_name: {
|
||||
required: "Please enter your first name.",
|
||||
minlength: "First name must be at least 2 characters.",
|
||||
maxlength: "First name must not exceed 15 characters.",
|
||||
noSpecialChars: "Please enter only letters and spaces.",
|
||||
startsWithLetter: "First name must start with a letter."
|
||||
},
|
||||
last_name: {
|
||||
required: "Please enter your last name.",
|
||||
minlength: "Last name must be at least 2 characters.",
|
||||
maxlength: "First name must not exceed 15 characters.",
|
||||
noSpecialChars: "Please enter only letters and spaces.",
|
||||
startsWithLetter: "Last name must start with a letter."
|
||||
},
|
||||
date_of_birth: {
|
||||
required: "Please enter your date of birth.",
|
||||
date: "Please enter a valid date in the format YYYY-MM-DD.",
|
||||
dateISO: "Please enter a valid date in the format YYYY-MM-DD.",
|
||||
pattern: "Please enter a valid date in the format YYYY-MM-DD."
|
||||
},
|
||||
gender: {
|
||||
required: "Please select your gender."
|
||||
},
|
||||
phone_no: {
|
||||
required: "Please enter your phone number.",
|
||||
digits: "Please enter only digits.",
|
||||
minlength: "Phone number must be 10 digits long.",
|
||||
maxlength: "Phone number must be 10 digits long."
|
||||
}
|
||||
},
|
||||
errorElement: 'div',
|
||||
errorPlacement: function(error, element) {
|
||||
error.addClass('invalid-feedback');
|
||||
$(element).closest('.form-group').append(error);
|
||||
},
|
||||
highlight: function(element, errorClass, validClass) {
|
||||
$(element).addClass('is-invalid').removeClass('is-valid');
|
||||
},
|
||||
unhighlight: function(element, errorClass, validClass) {
|
||||
$(element).removeClass('is-invalid').addClass('is-valid');
|
||||
},
|
||||
submitHandler: function(form, event) {
|
||||
event.preventDefault(); // Prevent default form submission on validation failure
|
||||
// You can now perform additional actions before submitting the form (optional)
|
||||
form.submit(); // Submit the form if validation passes (optional)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user