Merge pull request #28 from WDI-Ideas/development

Development
This commit is contained in:
BOBBY VISHWAKARMA
2024-04-15 15:00:45 +05:30
committed by GitHub
8 changed files with 199 additions and 208 deletions

View File

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

View File

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

View File

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

View File

@@ -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,
}

View File

@@ -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
View 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}'.")

View File

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

View File

@@ -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 %}