first commit

This commit is contained in:
bobbyvish
2024-02-26 13:28:32 +05:30
commit 69dbc56374
1010 changed files with 136332 additions and 0 deletions

147
.gitignore vendored Normal file
View File

@@ -0,0 +1,147 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
media
logs
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
nifty11appvenv/
ENV/
env.bak/
venv.bak/
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
# cython_debug/
# Footer
# © 2022 GitHub, Inc.
# Footer navigation
# Terms
# Privacy
# Security
# Status
# Docs
# Contact GitHub
# Pricing
# # API
# Training
# Blog
# About

38
env.example Normal file
View File

@@ -0,0 +1,38 @@
# ENV_NAME options are Production, Staging and Development
ENV_NAME=
SECRET_KEY=
DJANGO_DEBUG=True
LOG_LEVEL=INFO
DB_DATABASE=niftyll
DB_HOST=127.0.0.1
DB_USERNAME=
DB_PASSWORD=
DB_PORT=3306
CELERY_BROKER_URL=redis://localhost:6379
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_HOST_USER=
EMAIL_HOST_PASSWORD=
EMAIL_USE_TLS=True
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
MY_TWILIO_NUMBER=
STRIPE_SECRET_KEY=
ALPHA_VANTAGE=
PAYTM_MER_ID=
PAYTM_MER_KEY=
PAYTM_WEBSITE=
PAYTM_IND_TYPE=
PAYTM_CHANL_ID_WEB=
PAYTM_CHANL_ID_APP=

22
manage.py Normal file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'module_project.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View File

3
module_activity/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,346 @@
from rest_framework import serializers
from django.utils import timezone
from datetime import datetime
from module_iam.models import IAmPrincipal
from ..models import (
PrincipalHealthData,
Intolerance,
Symptoms,
PastTreatment,
ChronicCondition,
Medicine,
Medication,
Bowel,
SymptomTypeAfterMeal,
SymptomTypeBeforeMeal,
MealSymptomRecord,
FoodIngredientRecord,
FoodRecord,
BeverageRecord,
MealRecord,
)
class IAmPrincipalSerializer(serializers.ModelSerializer):
class Meta:
model = IAmPrincipal
fields = [
# "profile_photo",
"first_name",
"date_of_birth",
"gender",
"phone_no",
]
class PrincipalHealthDataSerializer(serializers.ModelSerializer):
class Meta:
model = PrincipalHealthData
fields = [
"ethenicity",
"weight",
"height",
"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)
height = serializers.DecimalField(max_digits=6, decimal_places=2, 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)
class Meta:
model = IAmPrincipal
fields = [
"profile_photo",
"first_name",
"email",
"date_of_birth",
"gender",
"phone_no",
"phone_verified",
"email_verified",
"ethenicity",
"weight",
"height",
"gastrointestinal_health",
"exercise_frequency",
"sleep_duration",
"eat_frequency",
]
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)
return ""
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)
health_data = instance.health_data_principal
if health_data:
data['ethenicity'] = health_data.ethenicity
data['weight'] = health_data.weight
data['height'] = health_data.height
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
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"]
class ChronicConditionSerializer(serializers.ModelSerializer):
class Meta:
model = ChronicCondition
fields = ["id", "name", "duration"]
class FoodIngredientRecordSerializer(serializers.ModelSerializer):
class Meta:
model = FoodIngredientRecord
fields = ["name"]
class FoodRecordSerializer(serializers.ModelSerializer):
class Meta:
model = FoodRecord
fields = ["name", "quantity"]
class BeverageRecordSerializer(serializers.ModelSerializer):
class Meta:
model = BeverageRecord
fields = [
"beverage_type",
"glass_type",
"glass_count",
"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",
"duration",
"completeness_of_evacuation",
"urgency",
"smellness",
"pain_level",
"volume",
"color",
"excessive_flatulence",
]
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)
class Meta:
model = MealSymptomRecord
fields = [
"id",
"date",
"time",
"symptoms_description",
"interval",
"symptoms_before_meal",
"symptoms_after_meal",
]
def create(self, validated_data):
before_meal_data = validated_data.pop("symptoms_before_meal")
after_meal_data = validated_data.pop("symptoms_after_meal")
meal_symptom_record = MealSymptomRecord.objects.create(**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)
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

View File

@@ -0,0 +1,29 @@
from django.urls import path
from . import views
urlpatterns = [
path("profile/", views.ProfileAPIView.as_view()),
path("daily-records/", views.DailyRecordAPIView.as_view()),
path("intolerance/", views.IntoleranceListCreateAPIView.as_view()),
# path("intolerance/<int:pk>/", views.IntoleranceRetrieveUpdateDestroyAPIView.as_view()),
path("symptoms/", views.SymptomsListCreateAPIView.as_view()),
path("past-treatment/", views.PastTreatmentListCreateAPIView.as_view()),
path("chronic-condition/", views.ChronicConditionListCreateAPIView.as_view()),
path("medication/", views.MedicationAPIView.as_view()),
path("medication/<int:pk>/", views.MedicationAPIView.as_view()),
path("bowel/", views.BowelAPIView.as_view()),
path("bowel/<int:pk>/", views.BowelAPIView.as_view()),
path("meal-symptoms/", views.MealSymptomAPIView.as_view()),
path("meal-symptoms/<int:pk>/", views.MealSymptomAPIView.as_view()),
path("meal/", views.MealAPIView.as_view()),
path("meal/<int:pk>/", views.MealAPIView.as_view()),
]

View File

@@ -0,0 +1,617 @@
from datetime import datetime
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
from django.db.models import Prefetch
from module_project import constants
from module_project.utils import ApiResponse
from module_iam.models import IAmPrincipal
from ..models import (
PrincipalHealthData,
Intolerance,
Symptoms,
PastTreatment,
ChronicCondition,
Medication,
Bowel,
MealSymptomRecord,
MealRecord,
)
from .serializers import (
IntoleranceSerializer,
SymptomsSerializer,
PastTreatmentSerializer,
ChronicConditionSerializer,
MedicationSerializer,
BowelSerializer,
MealSymptomRecordSerializer,
MealRecordSerializer,
IAmPrincipalSerializer,
PrincipalHealthDataSerializer,
PrincipalAndHealthSerializer,
)
class ProfileAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
post_serializer_class = IAmPrincipalSerializer
get_serializer_class = PrincipalAndHealthSerializer
model = IAmPrincipal
def get(self, request):
try:
obj = self.model.objects.prefetch_related("health_data_principal").get(
pk=request.user.pk
)
serializer = self.get_serializer_class(obj, context={"request": request})
except self.model.DoesNotExist:
return ApiResponse.error(
status=status.HTTP_404_NOT_FOUND, message=constants.RECORD_NOT_FOUND
)
print(f"object data is {obj}")
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
def post(self, request):
data = request.data.copy()
# Separate principal and health data
principal_data = {}
health_data = {}
for key, value in data.items():
if key in self.post_serializer_class.Meta.fields:
principal_data[key] = value
else:
health_data[key] = value
principal_serializer = self.post_serializer_class(
instance=request.user, data=principal_data
)
health_serializer = PrincipalHealthDataSerializer(data=health_data)
if not principal_serializer.is_valid():
return ApiResponse.error(
message=constants.FAILURE, errors=principal_serializer.errors
)
if not health_serializer.is_valid():
return ApiResponse.error(
message=constants.FAILURE, errors=health_serializer.errors
)
try:
# with transaction.atomic(): # Ensure atomicity of database operations
principal_instance = principal_serializer.save()
# Check if health data already exists for the principal
health_data_instance, created = PrincipalHealthData.objects.get_or_create(
principal=principal_instance
)
health_serializer.update(health_data_instance, health_data)
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(message=constants.SUCCESS)
class DailyRecordAPIView(APIView):
def serialize_record(self, record):
time_obj = datetime.strptime(str(record.time), '%H:%M:%S')
return {
"id": record.id,
"date": record.date,
"time": time_obj.strftime('%I:%M %p'),
# Add other fields as needed
}
def get(self, request):
date = request.GET.get("date")
# date = datetime.now().date()
if not date:
return ApiResponse.error(message=constants.FAILURE, errors="Date parameter is missing")
try:
# Convert the date string to a datetime object
date_obj = datetime.strptime(date, "%Y-%m-%d").date()
except ValueError:
return ApiResponse.error(message=constants.FAILURE, errors="Invalid date format")
# Define prefetch related queries for filtering the record of paticular date of all related models
meal_records_prefetch = Prefetch(
"meal_principal",
queryset=MealRecord.objects.filter(date=date),
to_attr="filtered_meal_record",
)
medication_prefetch = Prefetch(
"medication_principal",
queryset=Medication.objects.filter(date=date),
to_attr="filtered_medication",
)
bowel_prefetch = Prefetch(
"bowel_principal",
queryset=Bowel.objects.filter(date=date),
to_attr="filtered_bowel",
)
meal_symptom_prefetch = Prefetch(
"meal_symptom_principal",
queryset=MealSymptomRecord.objects.filter(date=date),
to_attr="filtered_meal_symptom",
)
# Query the IAmPrincipal table of principal with prefetch_related to retrieve related records
principal = IAmPrincipal.objects.prefetch_related(
meal_records_prefetch,
medication_prefetch,
bowel_prefetch,
meal_symptom_prefetch,
).get(id=request.user.id)
serialized_meal_records = [
{"type": "Meal", **self.serialize_record(record)}
for record in principal.filtered_meal_record
]
serialized_medication = [
{"type": "Medication", **self.serialize_record(record)}
for record in principal.filtered_medication
]
serialized_bowel = [
{"type": "Bowel Movements", **self.serialize_record(record)}
for record in principal.filtered_bowel
]
serialized_symptom = [
{"type": "Symptom - Meal", **self.serialize_record(record)}
for record in principal.filtered_meal_symptom
]
all_records = (serialized_symptom + serialized_meal_records + serialized_medication + serialized_bowel)
# 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
)
return ApiResponse.success(
message=constants.SUCCESS, data=all_records_sorted
)
class IntoleranceListCreateAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = IntoleranceSerializer
model = Intolerance
def get(self, request):
obj = self.model.objects.filter(principal=request.user)
serializer = self.serializer_class(obj, many=True)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
def post(self, request):
print(f"request data for intolerance is {request.data}")
serializer = self.serializer_class(data=request.data, many=True)
if not serializer.is_valid():
return ApiResponse.error(
message=constants.FAILURE, errors=serializer.errors
)
try:
self.model.objects.filter(principal=request.user).delete()
instance = serializer.save(principal=request.user)
saved_data_serializer = self.serializer_class(instance, many=True)
return ApiResponse.success(
message=constants.SUCCESS, data=saved_data_serializer.data
)
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
class SymptomsListCreateAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = SymptomsSerializer
model = Symptoms
def get(self, request):
obj = self.model.objects.filter(principal=request.user)
serializer = self.serializer_class(obj, many=True)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
def post(self, request):
print(f"request data for Symptoms is {request.data}")
serializer = self.serializer_class(data=request.data, many=True)
if not serializer.is_valid():
return ApiResponse.error(
message=constants.FAILURE, errors=serializer.errors
)
try:
self.model.objects.filter(principal=request.user).delete()
instance = serializer.save(principal=request.user)
saved_data_serializer = self.serializer_class(instance, many=True)
return ApiResponse.success(
message=constants.SUCCESS, data=saved_data_serializer.data
)
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
class PastTreatmentListCreateAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = PastTreatmentSerializer
model = PastTreatment
def get(self, request):
obj = self.model.objects.filter(principal=request.user)
serializer = self.serializer_class(obj, many=True)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
def post(self, request):
print(f"request data for PastTreatment is {request.data}")
serializer = self.serializer_class(data=request.data, many=True)
if not serializer.is_valid():
return ApiResponse.error(
message=constants.FAILURE, errors=serializer.errors
)
try:
self.model.objects.filter(principal=request.user).delete()
instance = serializer.save(principal=request.user)
saved_data_serializer = self.serializer_class(instance, many=True)
return ApiResponse.success(
message=constants.SUCCESS, data=saved_data_serializer.data
)
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
class ChronicConditionListCreateAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = ChronicConditionSerializer
model = ChronicCondition
def get(self, request):
obj = self.model.objects.filter(principal=request.user)
serializer = self.serializer_class(obj, many=True)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
def post(self, request):
print(f"request data for PastTreatment is {request.data}")
serializer = self.serializer_class(data=request.data, many=True)
if not serializer.is_valid():
return ApiResponse.error(
message=constants.FAILURE, errors=serializer.errors
)
try:
self.model.objects.filter(principal=request.user).delete()
instance = serializer.save(principal=request.user)
saved_data_serializer = self.serializer_class(instance, many=True)
return ApiResponse.success(
message=constants.SUCCESS, data=saved_data_serializer.data
)
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
class MedicationAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = MedicationSerializer
model = Medication
def get_objects(self, pk):
try:
return self.model.objects.get(pk=pk)
except self.model.DoesNotExist:
return ApiResponse.error(
status=status.HTTP_404_NOT_FOUND, message=constants.RECORD_NOT_FOUND
)
def get(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
serializer = self.serializer_class(obj)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
return ApiResponse.error(
message=constants.FAILURE, errors=serializer.errors
)
try:
serializer.save(principal=request.user)
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
status=status.HTTP_201_CREATED,
message=constants.SUCCESS,
data=serializer.data,
)
def put(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
serializer = self.serializer_class(obj, data=request.data)
if not serializer.is_valid():
return ApiResponse.error(message=constants.FAILURE, errors=serializer.data)
try:
serializer.save()
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
status=status.HTTP_201_CREATED,
message=constants.SUCCESS,
data=serializer.data,
)
def delete(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
try:
obj.delete()
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
message=constants.RECORD_DELETED, status=status.HTTP_204_NO_CONTENT
)
class BowelAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = BowelSerializer
model = Bowel
def get_objects(self, pk):
try:
return self.model.objects.get(pk=pk)
except self.model.DoesNotExist:
return ApiResponse.error(
status=status.HTTP_404_NOT_FOUND, message=constants.RECORD_NOT_FOUND
)
def get(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
serializer = self.serializer_class(obj)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
return ApiResponse.error(
message=constants.FAILURE, errors=serializer.errors
)
try:
serializer.save(principal=request.user)
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
status=status.HTTP_201_CREATED,
message=constants.SUCCESS,
data=serializer.data,
)
def put(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
serializer = self.serializer_class(obj, data=request.data)
if not serializer.is_valid():
return ApiResponse.error(message=constants.FAILURE, errors=serializer.data)
try:
serializer.save()
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
status=status.HTTP_201_CREATED,
message=constants.SUCCESS,
data=serializer.data,
)
def delete(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
try:
obj.delete()
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
message=constants.RECORD_DELETED, status=status.HTTP_204_NO_CONTENT
)
class MealSymptomAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = MealSymptomRecordSerializer
model = MealSymptomRecord
def get_objects(self, pk):
try:
return self.model.objects.get(pk=pk)
except self.model.DoesNotExist:
return ApiResponse.error(
status=status.HTTP_404_NOT_FOUND, message=constants.RECORD_NOT_FOUND
)
def get(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
serializer = self.serializer_class(obj)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
return ApiResponse.error(
message=constants.FAILURE, errors=serializer.errors
)
try:
serializer.save(principal=request.user)
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
status=status.HTTP_201_CREATED,
message=constants.SUCCESS,
data=serializer.data,
)
def put(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
serializer = self.serializer_class(obj, data=request.data)
if not serializer.is_valid():
return ApiResponse.error(message=constants.FAILURE, errors=serializer.data)
try:
serializer.save()
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
status=status.HTTP_201_CREATED,
message=constants.SUCCESS,
data=serializer.data,
)
def delete(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
try:
obj.delete()
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
message=constants.RECORD_DELETED, status=status.HTTP_204_NO_CONTENT
)
class MealAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = MealRecordSerializer
model = MealRecord
def get_objects(self, pk):
try:
return self.model.objects.get(pk=pk)
except self.model.DoesNotExist:
return ApiResponse.error(
status=status.HTTP_404_NOT_FOUND, message=constants.RECORD_NOT_FOUND
)
def get(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
serializer = self.serializer_class(obj)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
return ApiResponse.error(
message=constants.FAILURE, errors=serializer.errors
)
try:
serializer.save(principal=request.user)
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
status=status.HTTP_201_CREATED,
message=constants.SUCCESS,
data=serializer.data,
)
def put(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
serializer = self.serializer_class(obj, data=request.data)
if not serializer.is_valid():
return ApiResponse.error(message=constants.FAILURE, errors=serializer.data)
try:
serializer.save()
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
status=status.HTTP_201_CREATED,
message=constants.SUCCESS,
data=serializer.data,
)
def delete(self, request, pk):
obj = self.get_objects(pk)
if isinstance(obj, Response):
return obj
try:
obj.delete()
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(
message=constants.RECORD_DELETED, status=status.HTTP_204_NO_CONTENT
)

6
module_activity/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ModuleActivityConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'module_activity'

View File

@@ -0,0 +1,333 @@
# Generated by Django 5.0.2 on 2024-02-16 17:40
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='BeverageRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('beverage_type', models.CharField(max_length=100)),
('glass_type', models.CharField(max_length=100)),
('glass_count', models.IntegerField()),
('quantity', models.IntegerField()),
('quantity_measure', models.CharField(max_length=100)),
],
options={
'db_table': 'beverage_record',
},
),
migrations.CreateModel(
name='FoodIngredientRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
],
options={
'db_table': 'food_ingredient_record',
},
),
migrations.CreateModel(
name='FoodRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('quantity', models.IntegerField()),
],
options={
'db_table': 'food_record',
},
),
migrations.CreateModel(
name='MealRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('meal_date', models.DateField()),
('meal_time', models.TimeField()),
],
options={
'db_table': 'meal_record',
},
),
migrations.CreateModel(
name='Medicine',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, max_length=255, null=True)),
('quantity', models.IntegerField(default=0)),
('type', models.CharField(blank=True, max_length=100, null=True)),
],
options={
'db_table': 'medicine',
},
),
migrations.CreateModel(
name='SymptomTypeAfterMeal',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
],
options={
'db_table': 'symptom_type_after_meal',
},
),
migrations.CreateModel(
name='SymptomTypeBeforeMeal',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
],
options={
'db_table': 'symptom_type_before_meal',
},
),
migrations.CreateModel(
name='Bowel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('bowel_date', models.DateField()),
('bowel_time', models.TimeField()),
('stool_type', models.CharField(blank=True, max_length=100, null=True)),
('duration', models.DurationField(blank=True, null=True)),
('completeness_of_evacuation', models.CharField(blank=True, max_length=100, null=True)),
('urgency', models.CharField(blank=True, max_length=100, null=True)),
('smellness', models.CharField(blank=True, max_length=100, null=True)),
('pain_level', models.CharField(blank=True, max_length=100, null=True)),
('volume', models.CharField(blank=True, max_length=100, null=True)),
('color', models.CharField(blank=True, max_length=100, null=True)),
('excessive_flatulence', models.BooleanField(default=False)),
('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bowel_principal', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'bowel',
},
),
migrations.CreateModel(
name='ChronicCondition',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('name', models.CharField(blank=True, max_length=255, null=True)),
('duration', models.CharField(blank=True, max_length=255, null=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modified', to=settings.AUTH_USER_MODEL)),
('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chronic_principal', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'chronic_condition',
},
),
migrations.CreateModel(
name='Intolerance',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('name', models.CharField(blank=True, max_length=255, null=True)),
('duration', models.CharField(blank=True, max_length=255, null=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modified', to=settings.AUTH_USER_MODEL)),
('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='intolerance_principal', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'intolerance',
},
),
migrations.CreateModel(
name='MealRecordBeverageRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('beverage_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='module_activity.beveragerecord')),
('meal_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='module_activity.mealrecord')),
],
options={
'db_table': 'meal_record_beverage_record',
},
),
migrations.AddField(
model_name='mealrecord',
name='beverage_records',
field=models.ManyToManyField(through='module_activity.MealRecordBeverageRecord', to='module_activity.beveragerecord'),
),
migrations.CreateModel(
name='MealRecordFoodIngredientRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('food_ingredient_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='module_activity.foodingredientrecord')),
('meal_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='module_activity.mealrecord')),
],
options={
'db_table': 'meal_record_food_ingredient_record',
},
),
migrations.AddField(
model_name='mealrecord',
name='food_ingredient_records',
field=models.ManyToManyField(through='module_activity.MealRecordFoodIngredientRecord', to='module_activity.foodingredientrecord'),
),
migrations.CreateModel(
name='MealRecordFoodRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('food_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='module_activity.foodrecord')),
('meal_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='module_activity.mealrecord')),
],
options={
'db_table': 'meal_record_food_record',
},
),
migrations.AddField(
model_name='mealrecord',
name='food_records',
field=models.ManyToManyField(through='module_activity.MealRecordFoodRecord', to='module_activity.foodrecord'),
),
migrations.CreateModel(
name='MealSymptomRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('symptoms_date', models.DateField()),
('symptoms_time', models.TimeField()),
('symptoms_description', models.TextField(blank=True, null=True)),
('interval', models.DurationField()),
('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meal_symptom_principal', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'meal_symptom_record',
},
),
migrations.CreateModel(
name='Medication',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField()),
('time', models.TimeField()),
('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='medication_principal', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'medication',
},
),
migrations.CreateModel(
name='MedicationMedicine',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('medication', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='medication_medicines', to='module_activity.medication')),
('medicine', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='medication_medicines', to='module_activity.medicine')),
],
options={
'db_table': 'medication_medicine',
},
),
migrations.AddField(
model_name='medication',
name='medicines',
field=models.ManyToManyField(related_name='medications', through='module_activity.MedicationMedicine', to='module_activity.medicine'),
),
migrations.CreateModel(
name='PastTreatment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('name', models.CharField(blank=True, max_length=255, null=True)),
('duration', models.DateField()),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modified', to=settings.AUTH_USER_MODEL)),
('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pasttreatment_principal', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'past_treatment',
},
),
migrations.CreateModel(
name='PrincipalHealthData',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('gastrointestinal_health', models.CharField(blank=True, help_text='Describe your gastrointestinal health (e.g., best, average, poor, etc.)', max_length=255, null=True, verbose_name='Gastrointestinal Health')),
('exercise_frequency', models.CharField(blank=True, help_text='Describe your exercise frequency (e.g., Less than equal to 3 days, 2 days, Greater than 3 days, etc.)', max_length=255, null=True, verbose_name='Exercise Frequency')),
('sleep_duration', models.CharField(blank=True, help_text='Enter your average sleep duration in hours per night.', max_length=255, null=True, verbose_name='Sleep Duration (hours)')),
('ethenicity', models.CharField(blank=True, help_text='Select your ethnicity.', max_length=255, null=True, verbose_name='Ethnicity')),
('weight', models.DecimalField(blank=True, decimal_places=2, help_text='Enter your weight in kilograms.', max_digits=5, null=True, verbose_name='Weight (kg)')),
('height', models.DecimalField(blank=True, decimal_places=2, help_text='Enter your height in centimeters.', max_digits=4, null=True, verbose_name='Height (cm)')),
('eat_frequency', models.CharField(blank=True, help_text='Describe your eating frequency (e.g., 3 meals per day, frequent snacking, etc.)', max_length=255, null=True, verbose_name='Eating Frequency')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modified', to=settings.AUTH_USER_MODEL)),
('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='health_data_principal', to=settings.AUTH_USER_MODEL, verbose_name='Principal')),
],
options={
'db_table': 'princpal_health_data',
},
),
migrations.CreateModel(
name='Symptoms',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('name', models.CharField(blank=True, max_length=255, null=True)),
('duration', models.CharField(blank=True, max_length=255, null=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modified', to=settings.AUTH_USER_MODEL)),
('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='symptoms_principal', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'symptoms',
},
),
migrations.CreateModel(
name='SymptomRecordAfterMeal',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('symptom_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='module_activity.mealsymptomrecord')),
('symptom_type_after_meal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='module_activity.symptomtypeaftermeal')),
],
options={
'db_table': 'symptom_record_after_meal',
},
),
migrations.AddField(
model_name='mealsymptomrecord',
name='symptoms_after_meal',
field=models.ManyToManyField(through='module_activity.SymptomRecordAfterMeal', to='module_activity.symptomtypeaftermeal'),
),
migrations.CreateModel(
name='SymptomRecordBeforeMeal',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('symptom_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='module_activity.mealsymptomrecord')),
('symptom_type_before_meal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='module_activity.symptomtypebeforemeal')),
],
options={
'db_table': 'symptom_record_before_meal',
},
),
migrations.AddField(
model_name='mealsymptomrecord',
name='symptoms_before_meal',
field=models.ManyToManyField(through='module_activity.SymptomRecordBeforeMeal', to='module_activity.symptomtypebeforemeal'),
),
]

View File

@@ -0,0 +1,22 @@
# Generated by Django 5.0.2 on 2024-02-16 17:42
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('module_activity', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='mealrecord',
name='principal',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='meal_principal', to=settings.AUTH_USER_MODEL),
preserve_default=False,
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0.2 on 2024-02-17 06:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('module_activity', '0002_mealrecord_principal'),
]
operations = [
migrations.AlterField(
model_name='principalhealthdata',
name='height',
field=models.DecimalField(blank=True, decimal_places=2, help_text='Enter your height in centimeters.', max_digits=6, null=True, verbose_name='Height (cm)'),
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 5.0.2 on 2024-02-17 07:12
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('module_activity', '0003_alter_principalhealthdata_height'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterField(
model_name='principalhealthdata',
name='principal',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='health_data_principal', to=settings.AUTH_USER_MODEL, verbose_name='Principal'),
),
]

View File

@@ -0,0 +1,43 @@
# Generated by Django 5.0.2 on 2024-02-17 14:44
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('module_activity', '0004_alter_principalhealthdata_principal'),
]
operations = [
migrations.RenameField(
model_name='bowel',
old_name='bowel_date',
new_name='date',
),
migrations.RenameField(
model_name='bowel',
old_name='bowel_time',
new_name='time',
),
migrations.RenameField(
model_name='mealrecord',
old_name='meal_date',
new_name='date',
),
migrations.RenameField(
model_name='mealrecord',
old_name='meal_time',
new_name='time',
),
migrations.RenameField(
model_name='mealsymptomrecord',
old_name='symptoms_date',
new_name='date',
),
migrations.RenameField(
model_name='mealsymptomrecord',
old_name='symptoms_time',
new_name='time',
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0.2 on 2024-02-21 13:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('module_activity', '0005_rename_bowel_date_bowel_date_and_more'),
]
operations = [
migrations.AddField(
model_name='mealrecord',
name='meal_type',
field=models.CharField(blank=True, max_length=100, null=True),
),
]

View File

299
module_activity/models.py Normal file
View File

@@ -0,0 +1,299 @@
from django.db import models
from module_iam.models import BaseModel, IAmPrincipal
# Create your models here.
class PrincipalHealthData(BaseModel):
principal = models.OneToOneField(
IAmPrincipal,
on_delete=models.CASCADE,
related_name="health_data_principal",
verbose_name="Principal",
db_index=True,
)
# Gastrointestinal health (choices if applicable)
gastrointestinal_health = models.CharField(
max_length=255,
blank=True,
null=True,
verbose_name="Gastrointestinal Health",
help_text="Describe your gastrointestinal health (e.g., best, average, poor, etc.)",
)
# Exercise frequency (choices if applicable)
exercise_frequency = models.CharField(
max_length=255,
blank=True,
null=True,
verbose_name="Exercise Frequency",
help_text="Describe your exercise frequency (e.g., Less than equal to 3 days, 2 days, Greater than 3 days, etc.)",
)
sleep_duration = models.CharField(
max_length=255,
blank=True,
null=True,
verbose_name="Sleep Duration (hours)",
help_text="Enter your average sleep duration in hours per night.",
)
# Ethnicity (choices if applicable)
ethenicity = models.CharField(
max_length=255,
blank=True,
null=True,
verbose_name="Ethnicity",
help_text="Select your ethnicity.",
)
weight = models.DecimalField(
max_digits=5,
decimal_places=2,
blank=True,
null=True,
verbose_name="Weight (kg)",
help_text="Enter your weight in kilograms.",
)
height = models.DecimalField(
max_digits=6,
decimal_places=2,
blank=True,
null=True,
verbose_name="Height (cm)",
help_text="Enter your height in centimeters.",
)
# Eat frequency (choices if applicable)
eat_frequency = models.CharField(
max_length=255,
blank=True,
null=True,
verbose_name="Eating Frequency",
help_text="Describe your eating frequency (e.g., 3 meals per day, frequent snacking, etc.)",
)
class Meta:
db_table = "princpal_health_data"
def __str__(self):
return f"Health Data for {self.principal}"
class Intolerance(BaseModel):
principal = models.ForeignKey(
IAmPrincipal,
on_delete=models.CASCADE,
related_name="intolerance_principal",
db_index=True,
)
name = models.CharField(max_length=255, blank=True, null=True)
duration = models.CharField(max_length=255, blank=True, null=True)
class Meta:
db_table = "intolerance"
def __str__(self):
return f"intolerance of {self.principal}"
class Symptoms(BaseModel):
principal = models.ForeignKey(
IAmPrincipal,
on_delete=models.CASCADE,
related_name="symptoms_principal",
db_index=True,
)
name = models.CharField(max_length=255, blank=True, null=True)
duration = models.CharField(max_length=255, blank=True, null=True)
class Meta:
db_table = "symptoms"
def __str__(self):
return f"symptoms of {self.principal}"
class PastTreatment(BaseModel):
principal = models.ForeignKey(
IAmPrincipal,
on_delete=models.CASCADE,
related_name="pasttreatment_principal",
db_index=True,
)
name = models.CharField(max_length=255, blank=True, null=True)
duration = models.DateField()
class Meta:
db_table = "past_treatment"
def __str__(self):
return f"past treatment of {self.principal}"
class ChronicCondition(BaseModel):
principal = models.ForeignKey(
IAmPrincipal,
on_delete=models.CASCADE,
related_name="chronic_principal",
db_index=True,
)
name = models.CharField(max_length=255, blank=True, null=True)
duration = models.CharField(max_length=255, blank=True, null=True)
class Meta:
db_table = "chronic_condition"
def __str__(self):
return f"chronic condition of {self.principal}"
class FoodIngredientRecord(models.Model):
name = models.CharField(max_length=100)
class Meta:
db_table = "food_ingredient_record"
class FoodRecord(models.Model):
name = models.CharField(max_length=100)
quantity = models.IntegerField()
class Meta:
db_table = "food_record"
class BeverageRecord(models.Model):
beverage_type = models.CharField(max_length=100)
glass_type = models.CharField(max_length=100)
glass_count = models.IntegerField()
quantity = models.IntegerField()
quantity_measure = models.CharField(max_length=100)
class Meta:
db_table = "beverage_record"
class MealRecord(models.Model):
principal = models.ForeignKey(
IAmPrincipal, related_name="meal_principal", on_delete=models.CASCADE
)
date = models.DateField()
time = models.TimeField()
meal_type = models.CharField(max_length=100, blank=True, null=True)
food_records = models.ManyToManyField(FoodRecord, through='MealRecordFoodRecord')
beverage_records = models.ManyToManyField(BeverageRecord, through='MealRecordBeverageRecord')
food_ingredient_records = models.ManyToManyField(FoodIngredientRecord, through='MealRecordFoodIngredientRecord')
class Meta:
db_table = "meal_record"
class MealRecordFoodRecord(models.Model):
meal_record = models.ForeignKey(MealRecord, on_delete=models.CASCADE)
food_record = models.ForeignKey(FoodRecord, on_delete=models.CASCADE)
class Meta:
db_table = "meal_record_food_record"
class MealRecordBeverageRecord(models.Model):
meal_record = models.ForeignKey(MealRecord, on_delete=models.CASCADE)
beverage_record = models.ForeignKey(BeverageRecord, on_delete=models.CASCADE)
class Meta:
db_table = "meal_record_beverage_record"
class MealRecordFoodIngredientRecord(models.Model):
meal_record = models.ForeignKey(MealRecord, on_delete=models.CASCADE)
food_ingredient_record = models.ForeignKey(FoodIngredientRecord, on_delete=models.CASCADE)
class Meta:
db_table = "meal_record_food_ingredient_record"
class Medicine(models.Model):
name = models.CharField(max_length=255, blank=True, null=True)
quantity = models.IntegerField(default=0)
type = models.CharField(max_length=100, blank=True, null=True)
class Meta:
db_table = "medicine"
def __str__(self):
return f"{self.name} Medicine"
class Medication(models.Model):
principal = models.ForeignKey(
IAmPrincipal, related_name="medication_principal", on_delete=models.CASCADE
)
date = models.DateField()
time = models.TimeField()
medicines = models.ManyToManyField(
Medicine,
through="MedicationMedicine",
related_name="medications"
)
class Meta:
db_table = "medication"
class MedicationMedicine(models.Model):
medication = models.ForeignKey(Medication, related_name="medication_medicines", on_delete=models.CASCADE)
medicine = models.ForeignKey(Medicine, related_name="medication_medicines", on_delete=models.CASCADE)
class Meta:
db_table = "medication_medicine"
class Bowel(models.Model):
principal = models.ForeignKey(
IAmPrincipal, related_name="bowel_principal", on_delete=models.CASCADE
)
date = models.DateField()
time = models.TimeField()
stool_type = models.CharField(max_length=100, blank=True, null=True)
duration = models.DurationField(blank=True, null=True)
completeness_of_evacuation = models.CharField(max_length=100, blank=True, null=True)
urgency = models.CharField(max_length=100, blank=True, null=True)
smellness = models.CharField(max_length=100, blank=True, null=True)
pain_level = models.CharField(max_length=100, blank=True, null=True)
volume = models.CharField(max_length=100, blank=True, null=True)
color = models.CharField(max_length=100, blank=True, null=True)
excessive_flatulence = models.BooleanField(default=False)
class Meta:
db_table = "bowel"
class SymptomTypeBeforeMeal(models.Model):
name = models.CharField(max_length=100)
class Meta:
db_table = "symptom_type_before_meal"
class SymptomTypeAfterMeal(models.Model):
name = models.CharField(max_length=100)
class Meta:
db_table = "symptom_type_after_meal"
class MealSymptomRecord(models.Model):
principal = models.ForeignKey(IAmPrincipal, related_name="meal_symptom_principal", on_delete=models.CASCADE)
date = models.DateField()
time = models.TimeField()
symptoms_description = models.TextField(blank=True, null=True)
interval = models.DurationField()
symptoms_before_meal = models.ManyToManyField(SymptomTypeBeforeMeal, through='SymptomRecordBeforeMeal')
symptoms_after_meal = models.ManyToManyField(SymptomTypeAfterMeal, through='SymptomRecordAfterMeal')
class Meta:
db_table = "meal_symptom_record"
class SymptomRecordBeforeMeal(models.Model):
symptom_record = models.ForeignKey(MealSymptomRecord, on_delete=models.CASCADE)
symptom_type_before_meal = models.ForeignKey(SymptomTypeBeforeMeal, on_delete=models.CASCADE)
class Meta:
db_table = "symptom_record_before_meal"
class SymptomRecordAfterMeal(models.Model):
symptom_record = models.ForeignKey(MealSymptomRecord, on_delete=models.CASCADE)
symptom_type_after_meal = models.ForeignKey(SymptomTypeAfterMeal, on_delete=models.CASCADE)
class Meta:
db_table = "symptom_record_after_meal"

3
module_activity/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

31
module_activity/urls.py Normal file
View File

@@ -0,0 +1,31 @@
from django.urls import path
from . import views
app_name = "module_activity"
urlpatterns = [
path('intolerance/<int:principal_id>/', views.IntoleranceView.as_view(), name='intolerance'),
path('intolerance/list/<int:principal_id>/', views.IntoleranceListJson.as_view(), name='intolerance_list'),
path('intolerance/action/', views.IntoleranceActionView.as_view(), name='intolerance_action'),
path('symptoms/<int:principal_id>/', views.SymptomsView.as_view(), name='symptoms'),
path('symptoms/list/<int:principal_id>/', views.SymptomsListJson.as_view(), name='symptoms_list'),
path('symptoms/action/', views.SymptomsActionView.as_view(), name='symptoms_action'),
path('past_treatment/<int:principal_id>/', views.PastTreatmentView.as_view(), name='past_treatment'),
path('past_treatment/list/<int:principal_id>/', views.PastTreatmentListJson.as_view(), name='past_treatment_list'),
path('past_treatment/action/', views.PastTreatmentActionView.as_view(), name='past_treatment_action'),
path('chronic_condition/<int:principal_id>/', views.ChronicConditionView.as_view(), name='chronic_condition'),
path('chronic_condition/list/<int:principal_id>/', views.ChronicConditionListJson.as_view(), name='chronic_condition_list'),
path('chronic_condition/action/', views.ChronicConditionActionView.as_view(), name='chronic_condition_action'),
path('user_activity/<int:principal_id>/', views.UserActivityRecordView.as_view(), name='activity_list'),
path('meal_detail/<int:pk>/', views.MealDetialView.as_view(), name='meal_detail'),
path('medication_detail/<int:pk>/', views.MedicationDetailView.as_view(), name='medication_detail'),
path('bowel_detail/<int:pk>/', views.BowelDetailView.as_view(), name='bowel_detail'),
path('meal_symptom_detail/<int:pk>/', views.MealSymptomDetailView.as_view(), name='meal_symptom_detail'),
]

247
module_activity/views.py Normal file
View File

@@ -0,0 +1,247 @@
import logging
from datetime import datetime
from django.shortcuts import get_object_or_404, render, redirect
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.views import generic
from django.db.models import Q, Prefetch
from .models import Intolerance, Symptoms, ChronicCondition, PastTreatment, MealRecord, Bowel, MealSymptomRecord, Medication
from django_datatables_view.base_datatable_view import BaseDatatableView
from module_iam.models import IAmPrincipal
from module_project import constants
from module_project.utils import JsonResponseUtil
from django.http import JsonResponse
logger = logging.getLogger(__name__)
class BaseView(generic.TemplateView):
page_name = None
resource = None
action = None
template_name = None
model = Intolerance
context_objext_name = "obj"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
context["principal_id"] = self.kwargs.get('principal_id')
return context
class BaseListJson(BaseDatatableView):
model = Intolerance
columns = ["id", "name", "duration", "active", "deleted"]
order_columns = ["id", "name", "duration", "active", "deleted"]
def get_initial_queryset(self):
principal_id = self.kwargs.get('principal_id')
deleted_flag = self.request.GET.get('deleted_flag', None)
if deleted_flag == 'true':
# Show only deleted records
return self.model.objects.filter(principal=principal_id, deleted=True)
else:
# Show all records except deleted ones
return self.model.objects.filter(principal=principal_id, deleted=False)
def filter_queryset(self, qs):
search_value = self.request.GET.get("search[value]", None)
if search_value:
qs = qs.filter(
Q(name__icontains=search_value) |
Q(duration__icontains=search_value)
)
return qs
class BaseActionView(generic.View):
model = Intolerance
def post(self, request, *args, **kwargs):
action = request.POST.get('action') # 'archive', 'active', or 'unarchive'
ids = request.POST.getlist('ids[]') # List of user IDs to perform action on
active = request.POST.get('active')
print(f"arhive action {action} and id is {ids} and active data is {active}")
if action == 'archive':
# Update 'deleted' field to True for the selected users
self.model.objects.filter(id__in=ids).update(deleted=True, active=False)
message = 'Record archived successfully.'
elif action == 'active':
# Update 'active' field to True for the selected users
self.model.objects.filter(id__in=ids).update(active=active.capitalize())
message = 'Record activated successfully.'
elif action == 'unarchive':
# Update 'deleted' field to False for the selected users
self.model.objects.filter(id__in=ids).update(deleted=False)
message = 'Record unarchived successfully.'
else:
return JsonResponseUtil.error(message="Invalid Action")
return JsonResponseUtil.success(message=message)
class IntoleranceView(BaseView):
model = Intolerance
template_name = "module_activity/intolerance_list.html"
class IntoleranceListJson(BaseListJson):
model = Intolerance
class IntoleranceActionView(BaseActionView):
model = Intolerance
class SymptomsView(BaseView):
model = Symptoms
template_name = "module_activity/symptoms_list.html"
class SymptomsListJson(BaseListJson):
model = Symptoms
class SymptomsActionView(BaseActionView):
model = Symptoms
class PastTreatmentView(BaseView):
model = PastTreatment
template_name = "module_activity/past_treatment_list.html"
class PastTreatmentListJson(BaseListJson):
model = PastTreatment
class PastTreatmentActionView(BaseActionView):
model = PastTreatment
class ChronicConditionView(BaseView):
model = ChronicCondition
template_name = "module_activity/chronic_conditon_list.html"
class ChronicConditionListJson(BaseListJson):
model = ChronicCondition
class ChronicConditionActionView(BaseActionView):
model = ChronicCondition
class UserActivityRecordView(generic.View):
def serialize_record(self, record):
time_obj = datetime.strptime(str(record.time), '%H:%M:%S')
return {
"id": record.id,
"date": record.date,
"time": time_obj.strftime('%I:%M %p'),
}
def get(self, request, *args, **kwargs):
try:
principal_id = self.kwargs.get('principal_id')
date = request.GET.get("date")
print(f"principal_id is {principal_id} data is {date} and type is {type(date)}")
if not date:
return JsonResponseUtil.error(message="Date parameter is missing")
try:
date_obj = datetime.strptime(date, "%Y-%m-%d").date()
except ValueError:
return JsonResponseUtil.error(message="Invalid date format")
# Retrieve data from different models
meal_records = MealRecord.objects.filter(principal=principal_id, date=date_obj)
medication_records = Medication.objects.filter(principal=principal_id, date=date_obj)
bowel_records = Bowel.objects.filter(principal=principal_id, date=date_obj)
meal_symptom_records = MealSymptomRecord.objects.filter(principal=principal_id, date=date_obj)
print(f"==================meal record {meal_records}")
# Prepare combined results
data = []
for record in meal_records:
data.append({"type": "Meal", **self.serialize_record(record)})
for record in medication_records:
data.append({"type": "Medication", **self.serialize_record(record)})
for record in bowel_records:
data.append({"type": "Bowel", **self.serialize_record(record)})
for record in meal_symptom_records:
data.append({"type": "Symptom", **self.serialize_record(record)})
all_records_sorted = sorted(data, key=lambda x: x["time"], reverse=True)
response_data = {
"recordsTotal": len(all_records_sorted),
"recordsFiltered": len(all_records_sorted),
"data": all_records_sorted,
}
return JsonResponse(response_data)
except Exception as e:
return JsonResponseUtil.error(message="Something went wrong", errors=str(e))
class MealDetialView(generic.TemplateView):
template_name = "module_activity/meal_detail.html"
model = MealRecord
def get_record(self):
id = self.kwargs.get('pk')
meal_record = get_object_or_404(
self.model.objects.prefetch_related(
'food_records', 'beverage_records', 'food_ingredient_records'
),
id=id
)
return meal_record
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['obj'] = self.get_record()
return context
class MedicationDetailView(generic.TemplateView):
template_name = "module_activity/medication_detail.html"
model = Medication
def get_record(self):
id = self.kwargs.get('pk')
obj = get_object_or_404(
self.model.objects.prefetch_related('medicines'),
id=id
)
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['obj'] = self.get_record()
return context
class BowelDetailView(generic.TemplateView):
template_name = "module_activity/bowel_detail.html"
model = Bowel
def get_record(self):
id = self.kwargs.get('pk')
obj = get_object_or_404(self.model, id=id)
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['obj'] = self.get_record()
return context
class MealSymptomDetailView(generic.TemplateView):
template_name = "module_activity/meal_symptom_details.html"
model = MealSymptomRecord
def get_record(self):
pk = self.kwargs.get('pk')
obj = get_object_or_404(
MealSymptomRecord.objects.prefetch_related('symptoms_before_meal', 'symptoms_after_meal'),
id=pk
)
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['obj'] = self.get_record()
return context

0
module_auth/__init__.py Normal file
View File

3
module_auth/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,103 @@
from django.contrib.auth.hashers import make_password
from rest_framework import serializers
from module_iam.models import IAmPrincipal
from module_project import constants
from django.contrib.auth import authenticate
# class BasePasswordSerializer(serializers.Serializer):
# confirm_password = serializers.CharField(write_only=True, required=True)
# def validate(self, attrs):
# password = attrs.get("password")
# confirm_password = attrs.pop("confirm_password")
# if password != confirm_password:
# raise serializers.ValidationError({'password': constants.PASSWORD_NOT_MATCH})
# return super().validate(attrs)
# def update(self, instance, validate_data):
# new_password = validate_data.get("password")
# if new_password:
# instance.password = make_password(new_password)
# instance.save()
# return instance
class RegistrationSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=True)
confirm_password = serializers.CharField(write_only=True, required=True)
class Meta:
model = IAmPrincipal
fields = [
"first_name",
"email",
"phone_no",
"password",
"confirm_password",
"player_id",
]
def validate(self, attrs):
email = attrs.get("email")
phone_no = attrs.get("phone_no")
password = attrs.get("password")
confirm_password = attrs.get("confirm_password")
if password != confirm_password:
raise serializers.ValidationError({'password': constants.PASSWORD_NOT_MATCH})
obj = self.Meta.model.objects.filter(email=email).first()
if obj:
raise serializers.ValidationError({"email": constants.EMAIL_EXISTS})
return attrs
def create(self, validated_data):
del validated_data['confirm_password']
validated_data["username"] = validated_data["email"]
validated_data["password"] = make_password(validated_data["password"])
principal = self.Meta.model.objects.create(**validated_data)
return principal
def update(self, instance, validated_data):
# update prinicpal instance fiedls based on the validation data
instance.first_name = validated_data.get("first_name", instance.first_name)
instance.email = validated_data.get("email", instance.email)
instance.username = validated_data.get("email", instance.email)
instance.save()
return instance
class LoginSerializer(serializers.Serializer):
email = serializers.EmailField(write_only=True, required=True)
password = serializers.CharField(write_only=True, required=True)
player_id = serializers.CharField(write_only=True, required=True)
class OtpVerificationSerializer(serializers.Serializer):
email = serializers.EmailField(write_only=True, required=True)
otp = serializers.IntegerField(write_only=True, required=True)
class PasswordResetSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=True)
confirm_password = serializers.CharField(write_only=True, required=True)
class Meta:
model = IAmPrincipal
fields = [
"password",
"confirm_password"
]
def validate(self, attrs):
password = attrs.get("password")
confirm_password = attrs.get("confirm_password")
if password != confirm_password:
raise serializers.ValidationError({'password': constants.PASSWORD_NOT_MATCH})
return super().validate(attrs)
def update(self, instance, validate_data):
new_password = validate_data.get("password")
if new_password:
instance.password = make_password(new_password)
instance.save()
return instance

16
module_auth/api/urls.py Normal file
View File

@@ -0,0 +1,16 @@
from django.urls import path
from . import views
from rest_framework_simplejwt.views import TokenRefreshView
urlpatterns = [
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path("signup/", views.RegistrationView.as_view()),
path("login/", views.LoginView.as_view()),
path("request-otp/", views.OtpRequestView.as_view()),
path("verify-otp/", views.OTPVerificationView.as_view()),
path("forget-password/", views.ForgetPasswordView.as_view()),
# path("profile/", views.Profile)
]

183
module_auth/api/utils.py Normal file
View File

@@ -0,0 +1,183 @@
from typing import Optional
from module_project import constants
from module_project.utils import ApiResponse
from module_iam.models import IAmPrincipal, IAmPrincipalOtp
from rest_framework_simplejwt.tokens import RefreshToken
from django.core.exceptions import ValidationError
import logging
logger = logging.getLogger(__name__)
def generate_token_and_user_data(principal):
"""
Generate a token and user data based on an 'IAmPrincipal' object.
Args:
principal (IAmPrincipal): The user object.
Returns:
dict: A dictionary containing token data and user information.
"""
refresh = RefreshToken.for_user(principal)
data = {
"access": str(refresh.access_token),
"refresh": str(refresh),
"first_name": principal.first_name,
"phone_no": str(principal.phone_no),
"complete": principal.register_complete,
}
return data
class AuthService:
"""
Provides authentication services for IAmPrincipal users.
"""
def __init__(self, principal_model, otp_model=None):
self.principal_model = principal_model
self.otp_model = otp_model
def authenticate(self, principal_id: str, otp: Optional[str] = None, password: Optional[str] = None) -> None:
"""
Authenticates a principal using OTP and/or password.
Raises:
AuthenticationError: If authentication fails.
"""
try:
principal = self.get_principal(principal_id)
self.validate_principal_status(principal)
self.validate_credentials_provided(otp, password)
if otp:
self.authenticate_with_otp(principal, otp)
else:
self.authenticate_with_password(principal, password)
except ValidationError as e:
raise e
except Exception as e:
logger.error(f"Authentication failed for principal {principal_id}: {str(e)}")
raise ValidationError("Authentication failed") from e
def get_principal(self, principal_id: str) -> IAmPrincipal:
"""Retrieves a principal instance by ID."""
try:
return self.principal_model.objects.get(pk=principal_id)
except self.principal_model.DoesNotExist:
raise ValidationError("Invalid principal ID")
def get_principal_by_email(self, email: str):
"""Retrieves a principal instance by email."""
try:
return self.principal_model.objects.get(email=email)
except self.principal_model.DoesNotExist:
raise ValidationError(constants.EMAIL_NOT_REGISTERED)
def validate_principal_status(self, principal: IAmPrincipal) -> None:
"""Validates that the principal is active."""
if not principal.is_active:
raise ValidationError(constants.ACCOUNT_DEACTIVATED)
def validate_credentials_provided(self, otp: Optional[str], password: Optional[str]) -> None:
"""Ensures that at least one of OTP or password is provided."""
if otp is None and password is None:
raise ValidationError(constants.OTP_OR_PASSWORD_REQUIRED)
def authenticate_with_otp(self, principal: IAmPrincipal, otp: str) -> None:
"""Authenticates using OTP."""
if self.otp_model is None:
raise ValidationError("OTP authentication is not supported")
otp_instance = self.otp_model.objects.filter(principal=principal, otp_code=otp).last()
if not otp_instance:
raise ValidationError(constants.OTP_INVALID)
if otp_instance.is_expired():
raise ValidationError(constants.OTP_EXPIRED)
otp_instance.is_used = True
otp_instance.save()
def authenticate_with_password(self, principal: IAmPrincipal, password: str) -> None:
"""Authenticates using password."""
if not principal.check_password(password):
raise ValidationError(constants.INVALID_PASSWORD)
def authticate_with_otp_and_passsword(principal: IAmPrincipal, otp=None, password=None):
"""
Authenticate a principal using OTP and/or Password.
Parameters:
- principal (User): The principal object to authenticate.
- otp (str, optional): One-Time Password (OTP). Default is None.
- password (str, optional): User's password. Default is None.
Returns:
None: Successful authentication.
Response: Error response if authentication fails.
Example:
```
principal = User.objects.get(phone_no='8987546598')
otp = request.data.get("otp")
password = request.data.get("password")
result = authenticate_with_otp_and_password(principal, otp, password)
if isinstance(result, Response):
return result # Authentication failed, return error response
else:
# Authentication successful, proceed with authorized actions.
```
"""
if not principal.is_active:
return ApiResponse.error(
message=constants.ACCOUNT_DEACTIVATED, errors=constants.ACCOUNT_DEACTIVATED
)
# Ensure that either OTP or password is provided
if otp is None and password is None:
return ApiResponse.error(
message=constants.OTP_OR_PASSWORD_REQUIRED, errors=constants.OTP_OR_PASSWORD_REQUIRED
)
if otp:
otp_instance = IAmPrincipalOtp.objects.filter(
principal=principal, otp_code=otp
).last()
if not otp_instance:
return ApiResponse.error(
message=constants.OTP_INVALID, errors=constants.OTP_INVALID
)
if otp_instance.is_expired():
return ApiResponse.error(
message=constants.OTP_EXPIRED, errors=constants.OTP_EXPIRED
)
otp_instance.is_used = True
otp_instance.save()
elif password:
print(password)
if not principal.check_password(password):
return ApiResponse.error(
message=constants.INVALID_PASSWORD, errors=constants.INVALID_PASSWORD
)
print("after passsowrd", password)
return None
def get_principal_by_email(email: str):
try:
principal = IAmPrincipal.objects.get(email=email)
return principal
except IAmPrincipal.DoesNotExist:
error_response = {
"message": constants.EMAIL_NOT_REGISTERED,
"errors": constants.EMAIL_NOT_REGISTERED,
}
return ApiResponse.error(**error_response)

217
module_auth/api/views.py Normal file
View File

@@ -0,0 +1,217 @@
import datetime
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
from module_project import constants
from module_project.service import SMSService, EmailService
from module_project.utils import ApiResponse
from .utils import AuthService
from module_iam.models import IAmPrincipal, IAmPrincipalOtp
from .serializers import RegistrationSerializer, LoginSerializer, OtpVerificationSerializer, PasswordResetSerializer
from django.conf import settings
from rest_framework.response import Response
from .utils import (
generate_token_and_user_data, get_principal_by_email, authticate_with_otp_and_passsword
)
class RegistrationView(APIView):
authentication_classes = []
permission_classes = []
model = IAmPrincipal
serializer_class = RegistrationSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
print(f"request data is {request.data}")
if not serializer.is_valid():
error_response = {
"status": status.HTTP_403_FORBIDDEN,
"message": constants.REGISTRATION_FAIL,
"errors": serializer.errors,
}
return ApiResponse.error(**error_response)
try:
instance = serializer.save()
principal = instance
token_data = generate_token_and_user_data(principal)
except Exception as e:
return ApiResponse.error(
status=status.HTTP_403_FORBIDDEN, message=str(e), errors=str(e)
)
return ApiResponse.success(message=constants.REGISTRATION_SUCCESS, data=token_data)
class LoginView(APIView):
authentication_classes = []
permission_classes = []
model = IAmPrincipal
serializer_class = LoginSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
error_response = {
"status": status.HTTP_403_FORBIDDEN,
"message": constants.LOGIN_FAIL,
"errors": serializer.errors,
}
return ApiResponse.error(**error_response)
email = request.data.get("email")
otp = request.data.get("otp")
password = request.data.get("password")
player_id = request.data.get("player_id")
principal = get_principal_by_email(email=email)
if isinstance(principal, Response):
return principal
validation_result = authticate_with_otp_and_passsword(
principal, otp=otp, password=password
)
print("pasword instance ", validation_result)
if isinstance(validation_result, Response):
print("Errror reponse")
return validation_result # Return the error response if validation fails
# auth_service = AuthService(principal_model=IAmPrincipal)
# try:
# principal = self.model.objects.get(email=email)
# except Exception as e:
# error_response = {
# "status": status.HTTP_403_FORBIDDEN,
# "message": constants.INVALID_EMAIL_PASSWORD,
# "errors": constants.INVALID_EMAIL_PASSWORD,
# }
# return ApiResponse.error(**error_response)
# try:
# auth_service.authenticate(principal_id=principal.id, password=password)
# except Exception as e:
# error_response = {
# "status": status.HTTP_403_FORBIDDEN,
# "message": e,
# "errors": e,
# }
# return ApiResponse.error(**error_response)
try:
principal.player_id = player_id
principal.last_login = datetime.datetime.now()
principal.save()
except Exception as e:
error_response = {
"status": status.HTTP_500_INTERNAL_SERVER_ERROR,
"message": constants.INTERNAL_SERVER_ERROR,
"errors": str(e),
}
return ApiResponse.error(**error_response)
token_data = generate_token_and_user_data(principal)
return ApiResponse.success(message=constants.LOGIN_SUCCESS, data=token_data)
class OtpRequestView(APIView):
authentication_classes = []
permission_classes = []
def post(self, request):
if "email" not in request.data:
return ApiResponse.error(message=constants.EMAIL_REQUIRED, errors=constants.EMAIL_REQUIRED)
print(f"email auth username: {settings.EMAIL_HOST_USER}")
email = request.data.get("email")
principal = get_principal_by_email(email=email)
if isinstance(principal, Response):
return principal
try:
# auth_service = AuthService(IAmPrincipal)
# principal = auth_service.get_principal_by_email(request.data.get("email"))
otp_code = SMSService().create_otp(principal=principal, otp_purpose="Forget password")
except Exception as e:
return ApiResponse.error(message=str(e), errors=str(e))
email_service = EmailService(
subject="Forget Password",
to=principal.email,
from_email=settings.EMAIL_HOST_USER
)
# Send the email
try:
email_service.load_template("module_auth/email_template.html", context={"code": otp_code} )
email_service.send()
except Exception as e:
return ApiResponse.error(message=f"Error sending email: {str(e)}", errors=str(e))
return ApiResponse.success(message=constants.SUCCESS)
class OTPVerificationView(APIView):
authentication_classes = []
permission_classes = []
serializer_class = OtpVerificationSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
error_response = {
"status": status.HTTP_403_FORBIDDEN,
"message": constants.VALIDATION_ERROR,
"errors": serializer.errors,
}
return ApiResponse.error(**error_response)
email = serializer.validated_data.get("email")
otp = serializer.validated_data.get("otp")
principal = get_principal_by_email(email=email)
if isinstance(principal, Response):
return principal
validation_result = authticate_with_otp_and_passsword(
principal, otp=otp
)
print("pasword instance ", validation_result)
if isinstance(validation_result, Response):
print("Errror reponse")
return validation_result # Return the error response if validation fails
token_data = generate_token_and_user_data(principal)
return ApiResponse.success(message=constants.SUCCESS, data=token_data)
class ForgetPasswordView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = PasswordResetSerializer
def post(self, request):
serializer = self.serializer_class(request.user, data=request.data)
if not serializer.is_valid():
error_response = {
"status": status.HTTP_403_FORBIDDEN,
"message": constants.VALIDATION_ERROR,
"errors": serializer.errors,
}
return ApiResponse.error(**error_response)
try:
serializer.save()
except Exception as e:
return ApiResponse.error(message=str(e), errors=str(e))
return ApiResponse.success(message=constants.SUCCESS)

6
module_auth/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ModuleAuthConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'module_auth'

15
module_auth/forms.py Normal file
View File

@@ -0,0 +1,15 @@
from django import forms
from django.core import validators
from module_project import constants
class LoginForm(forms.Form):
email = forms.EmailField(
max_length=254,
widget=forms.TextInput(attrs={"autofocus": True}),
label="Email",
)
password = forms.CharField(
label="Password",
strip=False,
widget=forms.PasswordInput()
)

View File

3
module_auth/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
module_auth/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

17
module_auth/urls.py Normal file
View File

@@ -0,0 +1,17 @@
from django.urls import path
from . import views
app_name = "module_auth"
urlpatterns = [
path('login/', views.AdminLoginView.as_view(), name="login"),
path('logout/', views.AdminLogoutView.as_view(), name="logout"),
path('password-reset/', views.CustomPasswordResetView.as_view(), name='password_reset'),
path('password-reset/done/', views.CustomPasswordResetDoneView.as_view(), name='password_reset_done'),
path('password-reset-confirm/<uidb64>/<token>/', views.CustomPasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('password-reset-complete/', views.CustomPasswordResetCompleteView.as_view(), name='password_reset_complete'),
path('users/', views.UserDashView.as_view(), name='users'),
path('users/list/', views.UserListJson.as_view(), name='users_list'),
path('user/view/<int:id>/', views.UserRecordView.as_view(), name='user_view'),
]

203
module_auth/views.py Normal file
View File

@@ -0,0 +1,203 @@
import logging
from django.db.models import Q, Prefetch
from django.contrib import messages
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.views import LogoutView
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import (
LoginView,
PasswordResetCompleteView,
PasswordResetConfirmView,
PasswordResetDoneView,
PasswordResetView,
)
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy
from django.views import generic
from .forms import LoginForm
from module_iam.models import IAmPrincipal
from module_activity.models import PrincipalHealthData, Intolerance, Symptoms, PastTreatment, ChronicCondition
from django_datatables_view.base_datatable_view import BaseDatatableView
from module_project import constants
logger = logging.getLogger(__name__)
class AdminLoginView(generic.View):
template_name = "module_auth/login.html"
form_class = LoginForm
success_url = reverse_lazy("module_iam:dashboard")
error_url = reverse_lazy("module_auth:login")
success_message = constants.LOGIN_SUCCESS
error_message = constants.INVALID_EMAIL_PASSWORD
def get(self, request):
form = self.form_class()
return render(request, self.template_name, context={"form": form})
def post(self, request):
form = self.form_class(data=request.POST)
context = {"form": form}
if not form.is_valid():
messages.error(request, constants.INVALID_EMAIL_PASSWORD)
return render(request, self.template_name, context=context)
email = form.cleaned_data['email']
password = form.cleaned_data['password']
user = authenticate(request, email=email, password=password)
if user is None:
messages.error(request, constants.INVALID_EMAIL_PASSWORD)
return render(request, self.template_name, context=context)
login(request, user)
logging.info(f"User {user.email} logged in.")
return redirect(self.success_url)
class AdminLogoutView(LogoutView):
next_page = reverse_lazy("module_auth:login")
class CustomPasswordResetView(PasswordResetView):
form_class = PasswordResetForm
template_name = "module_auth/password_reset_form.html"
email_template_name = "module_auth/password_reset_email_template.html"
success_url = reverse_lazy("module_auth:password_reset_done")
class CustomPasswordResetDoneView(PasswordResetDoneView):
template_name = "module_auth/password_reset_done.html"
class UserDashView(LoginRequiredMixin, generic.TemplateView):
page_name = None
resource = None
action = None
template_name = "module_auth/users_list.html"
model = IAmPrincipal
context_objext_name = "obj"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
class UserListJson(BaseDatatableView):
model = IAmPrincipal
columns = ["id", "first_name", "email", "phone_no", "date_of_birth", "is_active"]
order_columns = ["id", "first_name", "email", "phone_no", "date_of_birth", "is_active"]
def filter_queryset(self, qs):
print(f"request is {self.request.GET}")
search_value = self.request.GET.get("search[value]", None)
if search_value:
# print(f"isdiget {search_value.isdigit()}")
# if search_value.isdigit():
# qs = qs.filter(Q(id=search_value))
qs = qs.filter(
Q(id__icontains=search_value)
| Q(first_name__icontains=search_value)
| Q(email__icontains=search_value)
| Q(date_of_birth__icontains=search_value)
| Q(phone_no__icontains=search_value)
)
for column in self.columns:
search_value = self.request.GET.get(f'columns[{self.columns.index(column)}][search][value]', None)
if search_value:
qs = qs.filter(**{f"{column}__icontains": search_value})
return qs
class UserRecordView(LoginRequiredMixin, generic.View):
page_name = None
resource = None
action = None
model = IAmPrincipal
template_name = "module_auth/user_view.html"
def get(self, request, id):
# Retrieve the IAmPrincipal instance
principal_instance = get_object_or_404(IAmPrincipal, id=id)
# Prefetch related Intolerance objects for the principal
intolerance_prefetch = Prefetch(
"intolerance_principal",
queryset=Intolerance.objects.filter(principal=principal_instance),
to_attr="intolerance_data",
)
symptom_prefetch = Prefetch(
"symptoms_principal",
queryset=Symptoms.objects.filter(principal=principal_instance),
to_attr="symptoms_data",
)
pasttreatment_prefetch = Prefetch(
"pasttreatment_principal",
queryset=PastTreatment.objects.filter(principal=principal_instance),
to_attr="pasttreatment_data",
)
chronic_prefetch = Prefetch(
"chronic_principal",
queryset=ChronicCondition.objects.filter(principal=principal_instance),
to_attr="chronic_data",
)
# Now fetch the principal instance with prefetches
obj = IAmPrincipal.objects.prefetch_related(
intolerance_prefetch,
symptom_prefetch,
pasttreatment_prefetch,
chronic_prefetch
).get(id=id)
print(f"prefetch datatas")
for data in obj.chronic_data:
print(f"data is {data.name, data.duration}")
# Render the template with the principal instance and related data
return render(request, self.template_name, {'obj': obj})
class CustomPasswordResetConfirmView(PasswordResetConfirmView):
template_name = "module_auth/password_reset_confirm.html"
success_url = reverse_lazy("module_auth:password_reset_complete")
class CustomPasswordResetCompleteView(PasswordResetCompleteView):
template_name = "module_auth/password_reset_complete.html"

0
module_cms/__init__.py Normal file
View File

6
module_cms/admin.py Normal file
View File

@@ -0,0 +1,6 @@
from django.contrib import admin
from .models import Faqs, Organization
# Register your models here.
admin.site.register(Faqs)
admin.site.register(Organization)

View File

@@ -0,0 +1,40 @@
from rest_framework import serializers
from taggit.models import Tag
from module_cms.models import Faqs, Organization
class FaqSerializer(serializers.ModelSerializer):
class Meta:
model = Faqs
fields = ["id", "question", "answer"]
class FaqListSerializer(serializers.ModelSerializer):
class Meta:
model = Faqs
fields = "__all__"
class OrganizationSerializer(serializers.ModelSerializer):
about_us = serializers.CharField(source='about_us.html', read_only=True)
terms_condition = serializers.CharField(source='terms_condition.html', read_only=True)
terms_condition_user = serializers.CharField(source='terms_condition_user.html', read_only=True)
terms_condition_merchant = serializers.CharField(source='terms_condition_merchant.html', read_only=True)
privacy_policy = serializers.CharField(source='privacy_policy.html', read_only=True)
privacy_policy_user = serializers.CharField(source='privacy_policy_user.html', read_only=True)
privacy_policy_merchant = serializers.CharField(source='privacy_policy_merchant.html', read_only=True)
subscription_agreement = serializers.CharField(source='subscription_agreement.html', read_only=True)
license_agreement_user = serializers.CharField(source='license_agreement_user.html', read_only=True)
license_agreement_merchant = serializers.CharField(source='license_agreement_merchant.html', read_only=True)
class Meta:
model = Organization
fields = [
"about_us",
"terms_condition",
"terms_condition_user",
"terms_condition_merchant",
"privacy_policy",
"privacy_policy_user",
"privacy_policy_merchant",
"subscription_agreement",
"license_agreement_user",
"license_agreement_merchant",
]

7
module_cms/api/urls.py Normal file
View File

@@ -0,0 +1,7 @@
from django.urls import path
from . import views
urlpatterns = [
path("faq/", views.FaqListAPIView.as_view()),
path("organization/", views.OrganizationAPIView.as_view())
]

30
module_cms/api/views.py Normal file
View File

@@ -0,0 +1,30 @@
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
from module_project import constants
from module_project.utils import ApiResponse
from .serializers import FaqSerializer, OrganizationSerializer
from ..models import Faqs, Organization
class FaqListAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = FaqSerializer
model = Faqs
def get(self, request):
queryset = self.model.objects.filter(active=True)
serializer = self.serializer_class(queryset, many=True)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
class OrganizationAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = OrganizationSerializer
model = Organization
def get(self, request):
queryset = self.model.objects.filter(active=True).last()
serializer = self.serializer_class(queryset)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)

6
module_cms/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ModuleCmsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'module_cms'

83
module_cms/forms.py Normal file
View File

@@ -0,0 +1,83 @@
from django import forms
from django.core.exceptions import ValidationError
from django.core import validators
from .models import (
Organization,
FaqCategory,
Faqs,
)
from module_project import constants
class OrganizationForm(forms.ModelForm):
class Meta:
model = Organization
fields = [
"title",
"contact_us_email",
"instagram_handle",
"facebook_handle",
"linkedin_handle",
"logo_image",
"favicon_image",
"website_url",
]
labels = {
"title": "Organization Title",
"contact_us_email": "Contact Email",
"instagram_handle": "Instagram URL",
"facebook_handle": "Facebook URL",
"linkedin_handle": "LinkedIn URL",
"logo_image": "Organization Logo",
"favicon_image": "Favicon",
"website_url": "Website URL",
}
class AboutUsForm(forms.ModelForm):
class Meta:
model = Organization
fields = ["about_us"]
labels = {"about_us": "Enter information about your organization:"}
class TermsAndConditionForm(forms.ModelForm):
class Meta:
model = Organization
fields = ["terms_condition"]
labels = {"terms_condition": "Enter Terms and Conditions:"}
class PrivacyPolicyForm(forms.ModelForm):
class Meta:
model = Organization
fields = ["privacy_policy"]
labels = {"privacy_policy": "Enter Privacy Police:"}
class FaqCategoryFrom(forms.ModelForm):
class Meta:
model = FaqCategory
fields = ["name"]
labels = {"name": "Category name"}
class FaqsForm(forms.ModelForm):
class Meta:
model = Faqs
fields = [
# "faq_category",
"question",
"answer",
"active",
]
# labels = {"faq_category": "Category"}
def __init__(self, *args, **kwargs):
instance = kwargs.get("instance")
super().__init__(*args, **kwargs)
# Fetch the choices for the faq_category field from the database
# self.fields["faq_category"].queryset = FaqCategory.objects.all()
if instance is None:
# This is an add operation, exclude the 'active' field
self.fields.pop("active")

View File

@@ -0,0 +1,81 @@
# Generated by Django 5.0.2 on 2024-02-14 14:09
import django.db.models.deletion
import django_quill.fields
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='FaqCategory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=255)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modified', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'faq_category',
},
),
migrations.CreateModel(
name='Faqs',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('question', models.TextField(max_length=255)),
('answer', models.TextField(blank=True, null=True)),
('published', models.BooleanField(default=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('faq_category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='faqs_category', to='module_cms.faqcategory')),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modified', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'faq',
},
),
migrations.CreateModel(
name='Organization',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('title', models.CharField(max_length=255)),
('contact_us_email', models.EmailField(blank=True, max_length=254, null=True, unique=True)),
('instagram_handle', models.URLField(blank=True, null=True)),
('facebook_handle', models.URLField(blank=True, null=True)),
('linkedin_handle', models.URLField(blank=True, null=True)),
('logo_image', models.ImageField(blank=True, null=True, upload_to='organization/logo')),
('favicon_image', models.ImageField(blank=True, null=True, upload_to='organization/favicon')),
('website_url', models.URLField(blank=True, null=True)),
('about_us', django_quill.fields.QuillField()),
('terms_condition', django_quill.fields.QuillField()),
('privacy_policy', django_quill.fields.QuillField()),
('subscription_agreement', django_quill.fields.QuillField()),
('license_agreement', django_quill.fields.QuillField()),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modified', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'organization',
},
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 5.0.2 on 2024-02-14 14:11
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('module_cms', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='faqs',
name='published',
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.0.2 on 2024-02-14 14:13
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('module_cms', '0002_remove_faqs_published'),
]
operations = [
migrations.AlterField(
model_name='faqs',
name='faq_category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='faqs_category', to='module_cms.faqcategory'),
),
]

View File

51
module_cms/models.py Normal file
View File

@@ -0,0 +1,51 @@
from django.db import models
from module_iam.models import BaseModel, IAmPrincipal
from taggit.managers import TaggableManager
from django_quill.fields import QuillField
# Create your models here.
class Organization(BaseModel):
title = models.CharField(max_length=255)
contact_us_email = models.EmailField(unique=True, blank=True, null=True)
instagram_handle = models.URLField(blank=True, null=True)
facebook_handle = models.URLField(blank=True, null=True)
linkedin_handle = models.URLField(blank=True, null=True)
logo_image = models.ImageField(blank=True, null=True, upload_to="organization/logo")
favicon_image = models.ImageField(
blank=True, null=True, upload_to="organization/favicon"
)
website_url = models.URLField(blank=True, null=True)
about_us = QuillField()
terms_condition = QuillField()
privacy_policy = QuillField()
subscription_agreement = QuillField()
license_agreement = QuillField()
class Meta:
db_table = "organization"
def __str__(self):
return self.title
class FaqCategory(BaseModel):
name = models.CharField(max_length=255)
class Meta:
db_table = "faq_category"
def __str__(self):
return self.name
class Faqs(BaseModel):
faq_category = models.ForeignKey(
FaqCategory, related_name="faqs_category", blank=True, null=True, on_delete=models.SET_NULL
)
question = models.TextField(max_length=255)
answer = models.TextField(blank=True, null=True)
class Meta:
db_table = "faq"
def __str__(self):
return self.question

3
module_cms/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

19
module_cms/urls.py Normal file
View File

@@ -0,0 +1,19 @@
from django.urls import path
from . import views
app_name = "module_cms"
urlpatterns = [
path('faq/', views.FaqView.as_view(), name="faq"),
path('faq/list/', views.FaqListJson.as_view(), name="faq_list"),
path('faq/add/', views.FaqCreateOrUpdateView.as_view(), name='faq_add'),
path('about-us/', views.AboutUsView.as_view(), name='about_us'),
path('about-us/edit/', views.AboutUsCreateOrUpdateView.as_view(), name='about_us_add'),
path('terms-condition/', views.TermsConditionView.as_view(), name='terms_and_condition'),
path('terms-condition/edit/', views.TermsConditionCreateOrUpdateView.as_view(), name='terms_and_condition_edit'),
path('privacy-policy/', views.PrivacyPolicyView.as_view(), name='privacy_policy'),
path('privacy-policy/edit/', views.PrivacyPolicyCreateOrUpdateView.as_view(), name='privacy_policy_edit'),
]

331
module_cms/views.py Normal file
View File

@@ -0,0 +1,331 @@
import logging
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy
from django.views import generic
from module_iam.models import IAmPrincipal
from .forms import AboutUsForm, TermsAndConditionForm, FaqCategoryFrom, PrivacyPolicyForm
from .models import Faqs, Organization
from .api.serializers import FaqListSerializer
from module_project.mixins import DatatablesMixin
from django_datatables_view.base_datatable_view import BaseDatatableView
from module_project import constants
logger = logging.getLogger(__name__)
class FaqView(LoginRequiredMixin, generic.TemplateView):
page_name = None
resource = None
action = None
template_name = "module_cms/faq.html"
model = Faqs
context_objext_name = "obj"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
# class FaqDatatableView(DatatablesMixin, LoginRequiredMixin, generic.View):
# model = Faqs
# def get_queryset(self):
# return self.model.objects.filter(deleted=False)
# def get(self, request):
# (
# draw,
# start,
# length,
# order_columns,
# order_directions,
# search_value,
# ) = self.get_datatables_params(request)
# queryset = self.get_queryset()
# page_obj, total_count, filtered_count = self.get_pagination(
# queryset, start, length
# )
# serializer = FaqListSerializer(
# page_obj.object_list, many=True
# )
# response = self.prepare_datatables_response(
# draw, total_count, filtered_count, serializer.data
# )
# return response
class FaqListJson(BaseDatatableView):
model = Faqs
columns = ["id", "question", "answer", "active", "deleted"]
order_columns = ["id", "question", "answer", "active", "deleted"]
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)
)
for column in self.columns:
search_value = self.request.GET.get(f'columns[{self.columns.index(column)}][search][value]', None)
if search_value:
qs = qs.filter(**{f"{column}__icontains": search_value})
return qs
class FaqCreateOrUpdateView(generic.View):
pass
class AboutUsView(LoginRequiredMixin, generic.DetailView):
page_name = None
template_name = "module_cms/about_us_view.html"
model = Organization
context_object_name = "organization"
def get_object(self, queryset=None):
return self.model.objects.only("about_us").first()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
class AboutUsCreateOrUpdateView(LoginRequiredMixin, generic.View):
# Set the page_name and resource
page_name = None
resource = None
# Initialize the action as ACTION_CREATE (can change based on logic)
action = None # Default action
template_name = "module_cms/about_us_add.html"
model = Organization
form_class = AboutUsForm
success_url = reverse_lazy("module_cms:about_us")
error_message = "An error occurred while saving the data."
# Determine the success message dynamically based on whether it's an update or create
def get_success_message(self):
self.success_message = (
constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED
)
return self.success_message
# Get the object (if exists) based on URL parameter 'pk'
def get_object(self):
return self.model.objects.only("about_us").first()
# Add page_name and operation to the context
def get_context_data(self, **kwargs):
context = {
"page_name": self.page_name,
"operation": "Add" if not self.object else "Edit",
}
context.update(kwargs) # Include any additional context data passed to the view
return context
def get(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = None
form = self.form_class(instance=self.object)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = None
form = self.form_class(request.POST, instance=self.object)
if not form.is_valid():
print(form.errors)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
form.save()
messages.success(self.request, self.get_success_message())
return redirect(self.success_url)
class TermsConditionView(LoginRequiredMixin, generic.DetailView):
page_name = None
resource = None
action = None
template_name = "module_cms/terms_and_condition_view.html"
model = Organization
context_object_name = "organization"
def get_object(self, queryset=None):
return self.model.objects.only("terms_condition").first()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
class TermsConditionCreateOrUpdateView(LoginRequiredMixin, generic.View):
# Set the page_name and resource
page_name = None
resource = None
# Initialize the action as ACTION_CREATE (can change based on logic)
action = None # Default action
template_name = "module_cms/terms_and_condition_edit.html"
model = Organization
form_class = TermsAndConditionForm
success_url = reverse_lazy("module_cms:terms_and_condition")
error_message = "An error occurred while saving the data."
# Determine the success message dynamically based on whether it's an update or create
def get_success_message(self):
self.success_message = (
constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED
)
return self.success_message
# Get the object (if exists) based on URL parameter 'pk'
def get_object(self):
return self.model.objects.only("terms_condition").first()
# Add page_name and operation to the context
def get_context_data(self, **kwargs):
context = {
"page_name": self.page_name,
"operation": "Add" if not self.object else "Edit",
}
context.update(kwargs) # Include any additional context data passed to the view
return context
def get(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
# if self.object is not None:
# self.action = resource_action.ACTION_UPDATE
form = self.form_class(instance=self.object)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
# if self.object is not None:
# self.action = resource_action.ACTION_UPDATE
form = self.form_class(request.POST, instance=self.object)
if not form.is_valid():
print(form.errors)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
form.save()
messages.success(self.request, self.get_success_message())
return redirect(self.success_url)
class PrivacyPolicyView(LoginRequiredMixin, generic.DetailView):
page_name = None
resource = None
action = None
template_name = "module_cms/privacy_policy_view.html"
model = Organization
context_object_name = "organization"
def get_object(self, queryset=None):
return self.model.objects.only("privacy_policy").first()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
return context
class PrivacyPolicyCreateOrUpdateView(LoginRequiredMixin, generic.View):
# Set the page_name and resource
page_name = None
resource = None
# Initialize the action as ACTION_CREATE (can change based on logic)
action = None # Default action
template_name = "module_cms/privacy_policy_edit.html"
model = Organization
form_class = PrivacyPolicyForm
success_url = reverse_lazy("module_cms:privacy_policy")
error_message = "An error occurred while saving the data."
# Determine the success message dynamically based on whether it's an update or create
def get_success_message(self):
self.success_message = (
constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED
)
return self.success_message
# Get the object (if exists) based on URL parameter 'pk'
def get_object(self):
return self.model.objects.only("privacy_policy").first()
# Add page_name and operation to the context
def get_context_data(self, **kwargs):
context = {
"page_name": self.page_name,
"operation": "Add" if not self.object else "Edit",
}
context.update(kwargs) # Include any additional context data passed to the view
return context
def get(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = None
form = self.form_class(instance=self.object)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
# If an object is found, change action to ACTION_UPDATE
if self.object is not None:
self.action = None
form = self.form_class(request.POST, instance=self.object)
if not form.is_valid():
print(form.errors)
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
form.save()
messages.success(self.request, self.get_success_message())
return redirect(self.success_url)

0
module_iam/__init__.py Normal file
View File

18
module_iam/admin.py Normal file
View File

@@ -0,0 +1,18 @@
from django.contrib import admin
from . import models
# Register your models here.
admin.site.register(models.IAmPrincipal)
admin.site.register(models.IAmPrincipalType)
admin.site.register(models.IAmPrincipalSource)
admin.site.register(models.IAmPrincipalGroup)
admin.site.register(models.IAmAppResource)
admin.site.register(models.IAmRole)
admin.site.register(models.IAmAppAction)
admin.site.register(models.IAmPrincipalGroupLink)
admin.site.register(models.IAmPrincipalOtp)
admin.site.register(models.IAmPrincipalBiometric)
admin.site.register(models.IAmAppResourceActionLink)
admin.site.register(models.IAmPricipalGroupRoleLink)
admin.site.register(models.IAmRoleAppResourceActionLink)

6
module_iam/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ModuleIamConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'module_iam'

View File

@@ -0,0 +1,291 @@
# Generated by Django 5.0.2 on 2024-02-11 19:08
import django.contrib.auth.validators
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='IAmAppAction',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('label', models.CharField(blank=True, max_length=255, null=True)),
('slug', models.SlugField(blank=True, max_length=255, null=True)),
('sort_order', models.IntegerField(blank=True, null=True)),
('small_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('large_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_by', models.SmallIntegerField(blank=True, null=True)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_by', models.SmallIntegerField(blank=True, null=True)),
('modified_on', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'iam_app_action',
},
),
migrations.CreateModel(
name='IAmAppResource',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('label', models.CharField(blank=True, max_length=255, null=True)),
('slug', models.SlugField(blank=True, max_length=255, null=True)),
('sort_order', models.IntegerField(blank=True, null=True)),
('small_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('large_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_by', models.SmallIntegerField(blank=True, null=True)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_by', models.SmallIntegerField(blank=True, null=True)),
('modified_on', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'iam_app_resource',
},
),
migrations.CreateModel(
name='IAmPrincipalGroup',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('label', models.CharField(blank=True, max_length=255, null=True)),
('slug', models.SlugField(blank=True, max_length=255, null=True)),
('sort_order', models.IntegerField(blank=True, null=True)),
('small_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('large_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_by', models.SmallIntegerField(blank=True, null=True)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_by', models.SmallIntegerField(blank=True, null=True)),
('modified_on', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'iam_principal_group',
},
),
migrations.CreateModel(
name='IAmPrincipalSource',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('label', models.CharField(blank=True, max_length=255, null=True)),
('slug', models.SlugField(blank=True, max_length=255, null=True)),
('sort_order', models.IntegerField(blank=True, null=True)),
('small_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('large_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_by', models.SmallIntegerField(blank=True, null=True)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_by', models.SmallIntegerField(blank=True, null=True)),
('modified_on', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'iam_principal_source',
},
),
migrations.CreateModel(
name='IAmPrincipalType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('label', models.CharField(blank=True, max_length=255, null=True)),
('slug', models.SlugField(blank=True, max_length=255, null=True)),
('sort_order', models.IntegerField(blank=True, null=True)),
('small_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('large_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_by', models.SmallIntegerField(blank=True, null=True)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_by', models.SmallIntegerField(blank=True, null=True)),
('modified_on', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'iam_principal_type',
},
),
migrations.CreateModel(
name='IAmRole',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('label', models.CharField(blank=True, max_length=255, null=True)),
('slug', models.SlugField(blank=True, max_length=255, null=True)),
('sort_order', models.IntegerField(blank=True, null=True)),
('small_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('large_image_url', models.ImageField(blank=True, null=True, upload_to='')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_by', models.SmallIntegerField(blank=True, null=True)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_by', models.SmallIntegerField(blank=True, null=True)),
('modified_on', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'iam_role',
},
),
migrations.CreateModel(
name='IAmPrincipal',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('email', models.EmailField(max_length=254, unique=True)),
('gender', models.CharField(blank=True, max_length=5, null=True)),
('date_of_birth', models.DateField(blank=True, null=True)),
('phone_no', models.IntegerField()),
('address_line1', models.TextField(blank=True, null=True)),
('address_line2', models.TextField(blank=True, null=True)),
('city', models.CharField(blank=True, max_length=100, null=True)),
('state', models.CharField(blank=True, max_length=100, null=True)),
('country', models.CharField(blank=True, max_length=100, null=True)),
('post_code', models.CharField(blank=True, max_length=100, null=True)),
('profile_photo', models.ImageField(blank=True, null=True, upload_to='profile')),
('phone_verified', models.BooleanField(default=False)),
('email_verified', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('deleted', models.BooleanField(default=False)),
('register_complete', models.BooleanField(default=False)),
('player_id', models.CharField(blank=True, help_text='OneSignal player id for push notification', max_length=255, null=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='creations', to=settings.AUTH_USER_MODEL)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='modifications', to=settings.AUTH_USER_MODEL)),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'db_table': 'iam_principal',
},
),
migrations.CreateModel(
name='IAmAppResourceActionLink',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('app_action', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='resource_action_link_app_action', to='module_iam.iamappaction')),
('app_resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='resource_action_link_app_resource', to='module_iam.iamappresource')),
],
options={
'db_table': 'iam_app_resource_action_link',
},
),
migrations.AddField(
model_name='iamappresource',
name='action',
field=models.ManyToManyField(related_name='app_resource_action', through='module_iam.IAmAppResourceActionLink', to='module_iam.iamappaction'),
),
migrations.CreateModel(
name='IAmPrincipalBiometric',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('biometric_type', models.CharField(max_length=100)),
('biometric_data', models.CharField(max_length=255)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modified', to=settings.AUTH_USER_MODEL)),
('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='principal_biometric', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'iam_principal_biometric',
},
),
migrations.CreateModel(
name='IAmPricipalGroupRoleLink',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('principal_group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='role_link_principal_group', to='module_iam.iamprincipalgroup')),
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='role_link_role', to='module_iam.iamrole')),
],
options={
'db_table': 'iam_principal_group_role_link',
},
),
migrations.CreateModel(
name='IAmPrincipalGroupLink',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='principal_group_link_principal', to=settings.AUTH_USER_MODEL)),
('principal_group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='principal_group_link_group', to='module_iam.iamprincipalgroup')),
],
options={
'db_table': 'iam_principal_principal_group_link',
},
),
migrations.AddField(
model_name='iamprincipal',
name='principal_group',
field=models.ManyToManyField(related_name='principal_groups', through='module_iam.IAmPrincipalGroupLink', to='module_iam.iamprincipalgroup'),
),
migrations.CreateModel(
name='IAmPrincipalOtp',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('otp_code', models.CharField(max_length=4)),
('otp_purpose', models.CharField(blank=True, max_length=50, null=True)),
('valid_till', models.DateTimeField()),
('is_used', models.BooleanField(default=False)),
('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='principal_otp', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'iam_principal_otp',
},
),
migrations.AddField(
model_name='iamprincipal',
name='principal_source',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='principals_source', to='module_iam.iamprincipalsource'),
),
migrations.AddField(
model_name='iamprincipal',
name='principal_type',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='principals_type', to='module_iam.iamprincipaltype'),
),
migrations.AddField(
model_name='iamprincipalgroup',
name='role',
field=models.ManyToManyField(related_name='principal_group_role', through='module_iam.IAmPricipalGroupRoleLink', to='module_iam.iamrole'),
),
migrations.CreateModel(
name='IAmRoleAppResourceActionLink',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('app_resource_action', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='role_app_resource_action_link_app_resource_action', to='module_iam.iamappresourceactionlink')),
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='role_app_resource_action_link_role', to='module_iam.iamrole')),
],
options={
'db_table': 'iam_role_app_resource_action_link',
},
),
migrations.AddField(
model_name='iamrole',
name='app_resource_action',
field=models.ManyToManyField(related_name='role_app_resource_action', through='module_iam.IAmRoleAppResourceActionLink', to='module_iam.iamappresourceactionlink'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0.2 on 2024-02-12 11:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('module_iam', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='iamprincipal',
name='phone_no',
field=models.CharField(blank=True, max_length=15, null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0.2 on 2024-02-23 13:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('module_iam', '0002_alter_iamprincipal_phone_no'),
]
operations = [
migrations.AlterField(
model_name='iamprincipal',
name='gender',
field=models.CharField(blank=True, max_length=6, null=True),
),
]

View File

369
module_iam/models.py Normal file
View File

@@ -0,0 +1,369 @@
from collections.abc import Iterable
import datetime
import random
import string
# from manage_wallets.models import Wallet, Transaction, TransactionStatus, TransactionType
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.db import models
from django.utils import timezone
from django.utils.text import slugify
# from phonenumber_field.modelfields import PhoneNumberField
from module_project.utils import RandomGenerator
from .resource_action import PRINCIPAL_TYPE_USER, PRINCIPAL_TYPE_ADMIN
# from .utils import UserContext
from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator
class BaseModel(models.Model):
active = models.BooleanField(default=True)
deleted = models.BooleanField(default=False)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name="%(class)s_created",
on_delete=models.CASCADE,
blank=True,
null=True,
)
created_on = models.DateTimeField(auto_now_add=True)
modified_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name="%(class)s_modified",
on_delete=models.CASCADE,
blank=True,
null=True,
)
modified_on = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
@classmethod
def get_deleted(cls):
return cls.objects.filter(deleted=True)
@classmethod
def get_all_except_deleted(cls):
return cls.objects.filter(deleted=False)
@classmethod
def mark_deleted(cls, pk):
try:
obj = cls.objects.get(pk=pk)
obj.active = False
obj.deleted = True
obj.save()
return obj
except cls.DoesNotExist:
return None
def delete(self, *args, **kwargs):
self.active = False # Set active to False when deleting
self.deleted = True
self.save()
def save(self, *args, **kwargs):
if self.deleted:
self.active = False # Ensure active is False if record is marked as deleted
super().save(*args, **kwargs)
class MasterModel(models.Model):
name = models.CharField(max_length=255)
label = models.CharField(max_length=255, null=True, blank=True)
slug = models.SlugField(max_length=255, null=True, blank=True)
sort_order = models.IntegerField(blank=True, null=True)
small_image_url = models.ImageField(blank=True, null=True)
large_image_url = models.ImageField(blank=True, null=True)
active = models.BooleanField(default=True)
deleted = models.BooleanField(default=False)
created_by = models.SmallIntegerField(blank=True, null=True)
created_on = models.DateTimeField(auto_now_add=True)
modified_by = models.SmallIntegerField(blank=True, null=True)
modified_on = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
def __str__(self):
return f"{self.name}"
def save(self, *args, **kwargs):
# Generate a slug from the name field
self.slug = slugify(self.name)
return super().save(*args, **kwargs)
class IAmPrincipalType(MasterModel):
class Meta:
db_table = "iam_principal_type"
@classmethod
def get_principal_type(cls, type):
return cls.objects.filter(name=type).first()
class IAmPrincipalSource(MasterModel):
class Meta:
db_table = "iam_principal_source"
class IAmAppAction(MasterModel):
class Meta:
db_table = "iam_app_action"
class IAmAppResource(MasterModel):
action = models.ManyToManyField(
IAmAppAction,
through="IAmAppResourceActionLink",
related_name="app_resource_action",
)
class Meta:
db_table = "iam_app_resource"
class IAmRoleAppResourceActionLinkManager(models.Manager):
def generate_app_resource_action_data(self):
"""
Generate a dictionary mapping resource names to associated actions.
Returns:
dict: A dictionary with resource names as keys and nested dictionaries
where action IDs are keys and action names are values.
Example:
{
"res1": {1: "a1", 2: "a2"},
"res2": {3: "a1", 4: "a2"}
}
"""
app_resource_action = self.select_related("app_resource", "app_action").all()
resource_action_link = {}
for item in app_resource_action:
resource = item.app_resource.name
action = item.app_action.name
id = item.id
if resource in resource_action_link:
resource_action_link[resource][id] = action
else:
resource_action_link[resource] = {id: action}
# print(resource_action_link)
return resource_action_link
class IAmAppResourceActionLink(models.Model):
app_resource = models.ForeignKey(
IAmAppResource,
related_name="resource_action_link_app_resource",
on_delete=models.CASCADE,
)
app_action = models.ForeignKey(
IAmAppAction,
related_name="resource_action_link_app_action",
on_delete=models.CASCADE,
)
objects = IAmRoleAppResourceActionLinkManager()
class Meta:
db_table = "iam_app_resource_action_link"
def __str__(self):
return f"{self.app_resource.name}: {self.app_action.name}"
class IAmRole(MasterModel):
app_resource_action = models.ManyToManyField(
IAmAppResourceActionLink,
through="IAmRoleAppResourceActionLink",
related_name="role_app_resource_action",
)
class Meta:
db_table = "iam_role"
class IAmRoleAppResourceActionLink(models.Model):
role = models.ForeignKey(
IAmRole,
related_name="role_app_resource_action_link_role",
on_delete=models.CASCADE,
)
app_resource_action = models.ForeignKey(
IAmAppResourceActionLink,
related_name="role_app_resource_action_link_app_resource_action",
on_delete=models.CASCADE,
)
class Meta:
db_table = "iam_role_app_resource_action_link"
class IAmPrincipalGroup(MasterModel):
role = models.ManyToManyField(
IAmRole, through="IAmPricipalGroupRoleLink", related_name="principal_group_role"
)
class Meta:
db_table = "iam_principal_group"
class IAmPricipalGroupRoleLink(models.Model):
principal_group = models.ForeignKey(
IAmPrincipalGroup,
related_name="role_link_principal_group",
on_delete=models.CASCADE,
)
role = models.ForeignKey(
IAmRole, related_name="role_link_role", on_delete=models.CASCADE
)
class Meta:
db_table = "iam_principal_group_role_link"
class IAmPrincipalManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError("The Email field must be set")
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault("username", email)
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
extra_fields.setdefault("phone_no", "+919978895465")
extra_fields.setdefault("gender", "M")
extra_fields.setdefault("date_of_birth", timezone.now())
extra_fields.setdefault("created_by", None)
extra_fields.setdefault("created_on", timezone.now())
extra_fields.setdefault("modified_by", None)
extra_fields.setdefault("modified_on", timezone.now())
return self.create_user(email, password, **extra_fields)
class IAmPrincipal(AbstractUser):
principal_type = models.ForeignKey(
IAmPrincipalType,
related_name="principals_type",
null=True,
on_delete=models.PROTECT,
)
principal_source = models.ForeignKey(
IAmPrincipalSource,
related_name="principals_source",
on_delete=models.CASCADE,
null=True,
)
email = models.EmailField(unique=True)
gender = models.CharField(max_length=6, blank=True, null=True)
date_of_birth = models.DateField(blank=True, null=True)
# phone_no = PhoneNumberField()
phone_no = models.CharField(max_length=15, blank=True, null=True)
address_line1 = models.TextField(blank=True, null=True)
address_line2 = models.TextField(blank=True, null=True)
city = models.CharField(max_length=100, blank=True, null=True)
state = models.CharField(max_length=100, blank=True, null=True)
country = models.CharField(max_length=100, blank=True, null=True)
post_code = models.CharField(max_length=100, blank=True, null=True)
profile_photo = models.ImageField(upload_to="profile", blank=True, null=True)
phone_verified = models.BooleanField(default=False)
email_verified = models.BooleanField(default=False)
created_by = models.ForeignKey(
"self",
null=True,
blank=True,
related_name="creations",
on_delete=models.SET_NULL,
)
created_on = models.DateTimeField(auto_now_add=True)
modified_by = models.ForeignKey(
"self",
null=True,
blank=True,
related_name="modifications",
on_delete=models.SET_NULL,
)
modified_on = models.DateTimeField(auto_now=True)
deleted = models.BooleanField(default=False)
principal_group = models.ManyToManyField(
IAmPrincipalGroup,
through="IAmPrincipalGroupLink",
related_name="principal_groups",
)
register_complete = models.BooleanField(default=False)
player_id = models.CharField(max_length=255, null=True, blank=True, help_text="OneSignal player id for push notification")
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
objects = IAmPrincipalManager()
class Meta:
db_table = "iam_principal"
def __str__(self):
return f"{self.email}"
class IAmPrincipalGroupLink(models.Model):
principal = models.ForeignKey(
IAmPrincipal,
related_name="principal_group_link_principal",
on_delete=models.CASCADE,
)
principal_group = models.ForeignKey(
IAmPrincipalGroup,
related_name="principal_group_link_group",
on_delete=models.CASCADE,
)
class Meta:
db_table = "iam_principal_principal_group_link"
class IAmPrincipalOtp(models.Model):
principal = models.ForeignKey(
IAmPrincipal, related_name="principal_otp", on_delete=models.CASCADE
)
otp_code = models.CharField(max_length=4)
otp_purpose = models.CharField(max_length=50, null=True, blank=True)
valid_till = models.DateTimeField()
is_used = models.BooleanField(default=False)
class Meta:
db_table = "iam_principal_otp"
def __str__(self):
return f"{self.principal.phone_no}:{self.otp_code} : {self.otp_purpose}"
def save(self, *args, **kwargs):
if not self.pk:
self.otp_code = RandomGenerator.random_otp()
self.valid_till = timezone.now() + timezone.timedelta(
minutes=settings.OTP_EXPIRE_TIME
)
super(IAmPrincipalOtp, self).save(*args, **kwargs)
def is_expired(self):
return timezone.now() >= self.valid_till
class IAmPrincipalBiometric(BaseModel):
principal = models.ForeignKey(
IAmPrincipal, related_name="principal_biometric", on_delete=models.CASCADE
)
biometric_type = models.CharField(max_length=100)
biometric_data = models.CharField(max_length=255)
class Meta:
db_table = "iam_principal_biometric"
def __str__(self):
return f"{self.principal.first_name}:{self.biometric_type}"

View File

@@ -0,0 +1,31 @@
PRINCIPAL_TYPE_USER = "user"
PRINCIPAL_TYPE_ADMIN = "admin"
ACTION_CREATE = "create"
ACTION_READ = "read"
ACTION_UPDATE = "update"
ACTION_DELETE = "delete"
RESOURCE_MANAGE_DASHBOARD = "manage_dashboard"
RESOURCE_MANAGE_IAM = "manage_iam"
RESOURCE_MANAGE_CUSTOMER = "manage_customer"
RESOURCE_MANAGE_WALLET = "manage_wallet"
RESOURCE_MANAGE_PAYMENT = "manage_payment"
RESOURCE_MANAGE_GAMES = "manage_games"
RESOURCE_MANAGE_CONTACT_US = "manage_contact_us"
RESOURCE_MANAGE_TICKET = "manage_ticket"
RESOURCE_MANAGE_CMS = "manage_cms"
RESOURCE_MANAGE_REPORTS = "manage_reports"
RESOURCE_MANAGE_COUPON = "manage_coupon"
RESOURCE_MANAGE_FEEDBACK = "manage_feedback"
RESOURCE_MANAGE_STOCK = "manage_stock"
# These constants are used solely for managing the active and inactive state of pages
# and should not be considered as resources in the typical sense.
# They are used for page management purposes only.
RESOURCE_IAM_PRINCIPAL = "iam_principal"
RESOURCE_IAM_PRINCIPAL_GROUP = "iam_principal_group"
RESOURCE_IAM_GROUP = "iam_group"
RESOURCE_IAM_ROLE = "iam_role"

3
module_iam/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

8
module_iam/urls.py Normal file
View File

@@ -0,0 +1,8 @@
from django.urls import path
from . import views
app_name = "module_iam"
urlpatterns = [
path('dashboard/', views.DashboardView.as_view(), name="dashboard")
]

7
module_iam/views.py Normal file
View File

@@ -0,0 +1,7 @@
from django.shortcuts import render
from django.views import generic
# Create your views here.
class DashboardView(generic.TemplateView):
template_name = "base_structure/layout/dashboard.html"

View File

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ModuleNotificationConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'module_notification'

View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

View File

16
module_project/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for module_project project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'module_project.settings')
application = get_asgi_application()

View File

@@ -0,0 +1,72 @@
VALIDATION_ERROR = "Validation Error"
# CRUD Related Constants
SUCCESS = "Operation successful."
FAILURE = "Operation failed."
RECORD_CREATED = "Record created successfully."
RECORD_UPDATED = "Record updated successfully."
RECORD_NOT_FOUND = "Record not found."
RECORD_DELETED = "Record deleted successfully."
ERROR_OCCURR = "An error occurred: {}"
SOMETHING_WRONG = "Something went wrong"
DATA_SAVED = "Data saved successfully."
DATA_UPDATED = "Data updated successfully."
DATA_DELETED = "Data deleted successfully."
DATA_IMPORT_SUCCESS = "Data import successful."
DATA_EXPORT_SUCCESS = "Data export successful."
DATA_INTEGRITY_ERROR = "Data integrity error. Please contact support."
INTERNAL_SERVER_ERROR = "Internal server error"
# File Related Constants
FILE_NOT_FOUND = "The requested file was not found."
FILE_UPLOAD_ERROR = "An error occurred while uploading files."
FILE_UPLOAD_SUCCESS = "Files uploaded successfully."
# Registration and Authentication Related Constants
REGISTRATION_INCOMPLETE = "Registration incomplete."
REGISTRATION_SUCCESS = "Registration successful."
REGISTRATION_FAIL = "Registration failed."
LOGIN_FAIL = "Login failed."
LOGIN_REQUIRED = "Login required to perform this action."
LOGIN_SUCCESS = "Login successful."
LOGOUT_SUCCESS = "Logout successful."
SESSION_EXPIRED = "Your session has expired. Please log in again."
ACCOUNT_DEACTIVATED = "Your account is inactive. Please contact support."
EMAIL_EXISTS = "This email address is already in use. Please use a different email."
INVALID_EMAIL_PASSWORD = "Invalid email or password."
INVALID_PASSWORD = "Invalid password."
PASSWORD_NOT_MATCH = "Password do not match"
INVALID_OPERATION = "Invalid operation requested."
PASSWORD_RESET_SUCCESS = "Password reset successful. You can now log in with your new password."
EMAIL_VERIFICATION_SUCCESS = "Email verification successful. You can now log in."
PASSWORD_CHANGE_SUCCESS = "Password change successful. Your password has been updated."
PASSWORD_CHANGE_FAILURE = "Password change failed. Please try again later."
# Mobile OTP Related Constants
PHONE_NUMBER_EXISTS = "This phone number is already in use."
PHONE_NUMBER_NOT_REGISTERED = "This phone number is not registered."
PHONE_NUMBER_NOT_FOUND = "Phone number not found."
PHONE_FIELD_IS_REQUIRED = "Phone field is required."
PHONE_NUMBER_INVALID = 'Invalid phone number.'
PHONE_NUMBER_VERIFICATION_SUCCESS = "Phone number verification successful."
PHONE_NUMBER_VERIFICATION_FAILED = "Phone number verification failed."
OTP_INVALID = "Invalid OTP."
OTP_VERIFIED = "OTP verified successful"
OTP_SENT = "OTP sent successfully."
OTP_FIELD_IS_REQUIRED = "OTP is required."
OTP_OR_PASSWORD_REQUIRED = "OTP or Password is required"
OTP_EXPIRED = "OTP has expired."
# Email Related Constants
EMAIL_NOT_REGISTERED = "Email is not registered"
EMAIL_REQUIRED = "Email field is required"
EMAIL_SENT = "Email sent successfully."
EMAIL_NOT_SENT = "Email could not be sent. Please try again later."
# Payment and Transaction Related Constants
PAYMENT_SUCCESS = "Payment successful. Thank you for your purchase!"
PAYMENT_FAILED = "Payment failed. Please check your payment details and try again."
TRANSACTION_PENDING = "Your transaction is currently pending processing."
WITHDRAWAL_FAILED = "Withdraw failed. Please check your wallet details."
WITHDRAWAL_SUCCESS = "Withdraw successful. Happy Earnings!"

View File

@@ -0,0 +1,64 @@
from datetime import datetime, timedelta
# Format date
def format_date_to_string(date, format='%d %b, %Y'):
return date.strftime(format)
# Format date or time
def format_datetime_to_sting(dt, format='%d %b, %Y %H:%M:%S'):
return dt.strftime(format)
# Get current date
def get_current_date():
return datetime.now()
# Get current time
def get_current_time():
return datetime.now().time()
# Get current date in a specific timezone
from pytz import timezone
def get_current_date_in_timezone(timezone_str='UTC'):
tz = timezone(timezone_str)
return datetime.now(tz)
# Convert string to datetime
def string_to_date(date_string, format='%Y-%m-%d'):
return datetime.strptime(date_string, format)
# Convert string to datetime
def string_to_datetime(datetime_string, format='%Y-%m-%d %H:%M:%S'):
return datetime.strptime(datetime_string, format)
# Get difference between two dates
def get_date_difference(date1, date2):
return abs(date2 - date1)
# Get difference between two datetimes
def get_datetime_difference(datetime1, datetime2):
return abs(datetime2 - datetime1)
# Add days to a given date
def add_days_to_date(date, days):
return date + timedelta(days=days)
# Subtract days from a given date
def subtract_days_from_date(date, days):
return date - timedelta(days=days)
# Check if a year is a leap year
def is_leap_year(year):
return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
# Get the last day of a given month and year
def last_day_of_month(year, month):
if month == 12:
year += 1
month = 1
else:
month += 1
return datetime(year, month, 1) - timedelta(days=1)
# Check if a date is within a specific range
def is_date_within_range(date, start_date, end_date):
return start_date <= date <= end_date

86
module_project/mixins.py Normal file
View File

@@ -0,0 +1,86 @@
from django.db.models import Q
from django.http.response import JsonResponse
from django.core.paginator import Paginator
class DatatablesMixin:
"""
Mixin for handling Datatables parameters, filtering, sorting, pagination,
and response preparation.
"""
filter_fields = []
search_fields = []
def get_datatables_params(self, request):
"""
Extracts Datatables parameters from the request.
"""
draw = request.GET.get('draw', 1)
start = request.GET.get("start", 0)
length = request.GET.get("length", 10)
order_columns = request.GET.getlist('order[][column]')
order_directions = request.GET.getlist('order[][dir]')
search_value = request.GET.get("search[value]", "")
return draw, start, length, order_columns, order_directions, search_value
def get_filters(self, request):
"""
Extracts and applies filters based on request parameters.
"""
filters = {}
for field in self.filter_fields:
value = request.GET.get(field)
if value:
filters[field] = value
return filters
def get_sorting(self, request, order_columns, order_directions):
"""
Constructs the sorting order based on request parameters.
"""
sorting = []
for i in range(len(order_columns)):
column_index = int(order_columns[i])
order = order_directions[i]
field = self.model._meta.get_field(column_index).name
sorting.append('-' + field if order == 'desc' else field)
return sorting
def get_search_filters(self, request, search_value, search_fields):
"""
Generates Q objects for global and individual column search.
"""
search_filters = Q()
if search_value:
for field in search_fields:
search_filters |= Q(**{f'{field}__icontains': search_value})
if 'columns' in request.GET:
search_filters = Q()
for i, column_index in enumerate(request.GET.getlist('order[][column]')):
if request.GET.get(f'columns[{column_index}][search][value]'):
field = self.model._meta.get_field(int(column_index)).name
search_filters |= Q(**{f'{field}__icontains': request.GET.get(f'columns[{column_index}][search][value]')})
return search_filters
def get_pagination(self, queryset, start, length):
"""
Performs pagination based on the requested page and page size.
"""
paginator = Paginator(queryset, length)
page_number = (int(start) // int(length)) + 1
page_obj = paginator.page(page_number)
total_count = paginator.count
filtered_count = len(page_obj.object_list)
return page_obj, total_count, filtered_count
def prepare_datatables_response(self, draw, total_count, filtered_count, data):
"""
Prepares the JSON response for Datatables.
"""
return JsonResponse({
"draw": draw,
"recordsTotal": total_count,
"recordsFiltered": filtered_count,
"data": data
})

255
module_project/service.py Normal file
View File

@@ -0,0 +1,255 @@
from django.conf import settings
from django.core.files.uploadedfile import UploadedFile
from django.core.mail import EmailMessage
from django.utils.html import strip_tags
from django.template.loader import render_to_string
from django.shortcuts import get_object_or_404
from smtplib import SMTPException
from module_iam.models import IAmPrincipal, IAmPrincipalOtp, IAmPrincipalType
from .utils import RandomGenerator
# from twilio.rest import Client
from django.db.models import Q
import phonenumbers
from decimal import Decimal
from django.db.models import F
from django.db import transaction
from datetime import timedelta, time, datetime
from django.utils import timezone
# from onesignal_sdk.client import Client as OneSignalClient
import logging
logger = logging.getLogger(__name__)
class EmailService:
email = None
body = None
subject = None
to = None
from_email = None
content_subtype = "html"
def __init__(self, subject=None, to=None, from_email=None):
self.subject = subject
self.to = (to,)
self.from_email = from_email
def set_to(self, to):
self.to = to
def set_subject(self, subject):
self.subject = subject
def set_from_email(self, from_email):
self.from_email = from_email
def set_text_body(self, body):
self.body = strip_tags(body)
def set_html_body(self, html_body):
self.body = html_body
def load_template(self, path=None, context={}):
if path is None:
raise Exception("Email temaplate path is not provided.")
self.content_subtype = "html"
html_body = render_to_string(path, context=context)
self.body = html_body
def attach(self, file_path):
self.email.attach_file(file_path)
def send(self):
try:
self.email = EmailMessage(
subject=self.subject,
body=self.body,
to=self.to,
from_email=self.from_email,
)
self.email.content_subtype = self.content_subtype
self.email.send()
except SMTPException as e:
logger.error(str(e))
class SMSError(Exception):
def __init__(self, message, payload=None):
self.message = message
self.payload = payload
def __str__(self):
return str(self.message)
class SMSService:
# def send(self, to: list, text: str):
# """
# Sends text sms to the given user(s).
# Parameters:
# to (list): list of phone numbers
# text (str): a text message.
# Return:
# True or False
# """
# # if settings.TESTING_ENV:
# # logger.info(f"TESTING ENV SMS LOG : {text}")
# # return True
# account_sid = settings.TWILIO_ACCOUNT_SID
# auth_token = settings.TWILIO_AUTH_TOKEN
# my_twilio_number = settings.MY_TWILIO_NUMBER
# client = Client(account_sid, auth_token)
# try:
# for number in to:
# logger.info("SENDING SMS TO " + str(number))
# message = client.messages.create(
# from_=my_twilio_number, body=text, to=number
# )
# except Exception as e:
# logger.error(str(e))
# raise SMSError(message=str(e))
def create_otp(self, principal: IAmPrincipal, otp_purpose: str):
otp = IAmPrincipalOtp.objects.create(
principal=principal, otp_purpose=otp_purpose
)
otp.save()
return otp.otp_code
def send_otp(self, principal: IAmPrincipal, otp_purpose: str):
"""
Sends otp to the given user.
Parameters:
user (User): User object
otp_purpose (str) : a text that describe otp purpose
Return:
True or False
"""
if not isinstance(principal, IAmPrincipal):
raise Exception(
f"parameter 'principal' required type of User object, Given {type(principal)} type object"
)
otp_code = self.create_otp(principal=principal, otp_purpose=otp_purpose)
# below working will change as it is temporary purpose
body = f"Your Nifty11 OTP is {otp_code}."
print(body)
phone_numbers = []
try:
parsed_number = phonenumbers.parse(str(principal.phone_no), None)
if phonenumbers.is_valid_number(parsed_number):
formatted_number = phonenumbers.format_number(
parsed_number, phonenumbers.PhoneNumberFormat.E164
)
phone_numbers.append(formatted_number)
else:
raise ValueError("Invalid phone number")
except Exception as e:
logger.warning(f"{e}")
raise ValueError("Invalid phone number")
if not phone_numbers:
raise ValueError("Invalid phone number")
print(f"phone number {type(phone_numbers)} {phone_numbers}")
# self.send(phone_numbers, body)
return otp_code
# class OneSignalNotificationService:
# """
# Class for sending notifications using the OneSignal API.
# Provides a convenient way to create and send notifications to OneSignal users,
# with features like targeting specific devices or segments, customizing notification content,
# and handling errors gracefully.
# **Parameters:**
# - **app_id** (str): Your OneSignal App ID.
# - **rest_api_key** (str): Your OneSignal REST API Key.
# - **user_auth_key** (str): Your OneSignal User Auth Key.
# **Keyword Arguments:**
# This method accepts additional keyword arguments (`**kwargs`) to customize the notification
# further, including:
# - `url` (str): URL to open when the notification is clicked.
# - `data` (dict): Custom data to be sent with the notification.
# - `buttons` (list): List of action buttons to display within the notification.
# - `send_after` (str): Timestamp for scheduling the notification.
# - `delayed_option` (dict): Option for delayed delivery (Android-specific).
# - `android_channel_id` (str): Channel ID for Android notifications.
# - `ios_sound` (str): Sound to play for iOS notifications.
# - `ios_badgeType` (str): Badge type for iOS notifications.
# - `ios_badgeCount` (int): Badge count for iOS notifications.
# - `ios_thread_id` (str): Thread ID to group notifications in iOS.
# - `android_background_layout` (str): Layout for background notifications on Android.
# - `android_group` (str): Group notification on Android.
# - `android_group_message` (str): Summary for grouped notifications on Android.
# - `android_group_summary` (str): Summary for grouped notifications on Android.
# - `android_led_color` (str): LED color for Android notifications.
# - `android_accent_color` (str): Accent color for Android notifications.
# - `android_visibility` (str): Visibility settings for Android notifications.
# **Example usage:**
# notification = OneSignalNotificationService()
# response = notification.send_notification(
# headings="Welcome",
# message="Thanks for signing up!",
# player_tokens=["PLAYER_TOKEN1", "PLAYER_TOKEN2"],
# url="https://yourwebsite.com/welcome",
# data={"user_id": 123},
# )
# """
# def __init__(self):
# self.config = OneSignalClient(
# app_id=settings.ONESIGNAL_APP_ID,
# rest_api_key=settings.ONESIGNAL_REST_API_KEY,
# user_auth_key=settings.ONESIGNAL_USER_AUTH_KEY
# )
# # Set up logging
# self.logger = logging.getLogger(__name__)
# def send_notification(self, headings, message, player_tokens=None, **kwargs):
# notification_obj = {
# "headings": {"en": headings},
# "contents": {"en": message},
# **kwargs
# }
# if player_tokens:
# notification_obj["include_player_ids"] = player_tokens
# try:
# response = self.config.send_notification(notification_obj)
# self.logger.info(f"Notification send successfully : {response}")
# return response
# except Exception as e:
# self.logger.error(f"OneSignal error {e}")
# raise Exception("Generic OneSignal error: {}".format(e))

View File

@@ -0,0 +1,23 @@
import environ
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
env = environ.Env()
READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=True)
if READ_DOT_ENV_FILE:
# OS environment variables take precedence over variables from .env
env.read_env(str(BASE_DIR / ".env"))
# Access the "ENV_NAME" environment variable using env
env_name = env("ENV_NAME")
if env_name == 'Production':
from .production import * # noqa
elif env_name == 'Staging':
from .staging import * # noqa
elif env_name == 'Development':
from .development import * # noqa
else:
raise ValueError("Invalid or missing ENV_NAME environment variable")

View File

@@ -0,0 +1,276 @@
"""
Django settings for module_project project.
Generated by 'django-admin startproject' using Django 4.2.5.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
import environ
from pathlib import Path
from django.contrib.messages import constants as messages
import colorlog
import datetime
BASE_DIR = Path(__file__).resolve().parent.parent.parent
env = environ.Env()
READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=True)
if READ_DOT_ENV_FILE:
# OS environment variables take precedence over variables from .env
env.read_env(str(BASE_DIR / ".env"))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-#7rdu=fr58ba9_!n3$l5pm!xs8l%6%8xt@vb8$&o@hqhd@rtd%'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
DJANGO_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
LOCAL_APPS = [
"module_iam",
"module_auth",
"module_activity",
"module_cms",
"module_support",
]
THIRD_PARTY_APPS = [
"corsheaders",
"widget_tweaks",
"rest_framework_simplejwt",
"taggit",
"django_quill",
]
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'module_project.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR.joinpath("templates")],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'module_project.wsgi.application'
# WSGI_APPLICATION = 'module_project.asgi.application'
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "digest_db",
"HOST": env.str("DB_HOST"),
"USER": env.str("DB_USERNAME"),
"PASSWORD": env.str("DB_PASSWORD"),
"PORT": env.str("DB_PORT"),
}
}
# DATABASES["default"]["ATOMIC_REQUESTS"] = True
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
SHORT_DATETIME_FORMAT = "d-m-Y H:i:s"
SHORT_DATE_FORMAT = "d-m-Y"
TIME_FORMAT = "H:i p"
# otp expire time limit
OTP_EXPIRE_TIME = 10 # mins
APPEND_SLASH = True
LOGIN_REDIRECT_URL = "/iam/dashboard/"
LOGIN_URL = "/auth/login/"
LOGOUT_REDIRECT_URL = "/auth/login/"
# https://docs.djangoproject.com/en/4.2/topics/auth/customizing/#substituting-a-custom-user-model
AUTH_USER_MODEL = "module_iam.IAmPrincipal"
# https://docs.djangoproject.com/en/4.2/topics/auth/customizing/
AUTHENTICATION_BACKENDS = [
# 'accounts.backend.EmailBackend',
"django.contrib.auth.backends.ModelBackend",
]
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# rest framework permission and authentication settings
# https://www.django-rest-framework.org/api-guide/permissions/#setting-the-permission-policy
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
}
MESSAGE_TAGS = {
messages.DEBUG: "alert-info",
messages.INFO: "alert-info",
messages.SUCCESS: "alert-success",
messages.WARNING: "alert-warning",
messages.ERROR: "alert-danger",
}
# smtp email settings
# https://docs.djangoproject.com/en/4.2/topics/email/#smtp-backend
EMAIL_BACKEND = env.str(
"EMAIL_BACKEND", default="django.core.mail.backends.smtp.EmailBackend"
)
EMAIL_HOST = env.str("EMAIL_HOST")
EMAIL_HOST_USER = env.str("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = env.str("EMAIL_HOST_PASSWORD")
EMAIL_PORT = env.str("EMAIL_PORT")
EMAIL_USE_TLS = True
# LOGGING
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/4.2/topics/logging/#logging
# https://docs.djangoproject.com/en/dev/ref/settings/#logging
# See https://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"()": colorlog.ColoredFormatter,
"format": "%(cyan)s%(asctime)s%(reset)s | %(red)s[%(levelname)8s]%(reset)s | [ %(yellow)s%(name)s.%(module)s:%(white)s%(lineno)d%(reset)s - %(green)s%(funcName)10s()%(reset)s ] --> %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
"log_colors": {
"DEBUG": "white",
"INFO": "green",
"WARNING": "yellow",
"ERROR": "red",
"CRITICAL": "bold_red",
},
"secondary_log_colors": {},
"style": "%",
}
},
"handlers": {
"console": {
"level": "DEBUG",
"class": "logging.StreamHandler",
"formatter": "verbose",
}
},
"root": {"level": "INFO", "handlers": ["console"]},
}
# jwt configuration
# https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html#settings
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": datetime.timedelta(days=10),
"REFRESH_TOKEN_LIFETIME": datetime.timedelta(days=15),
"ROTATE_REFRESH_TOKENS": False,
"BLACKLIST_AFTER_ROTATION": False,
"UPDATE_LAST_LOGIN": False,
"ALGORITHM": "HS256",
"SIGNING_KEY": SECRET_KEY,
"VERIFYING_KEY": "",
"AUDIENCE": None,
"ISSUER": None,
"JSON_ENCODER": None,
"JWK_URL": None,
"LEEWAY": 0,
"AUTH_HEADER_TYPES": ("Bearer",),
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
"USER_ID_FIELD": "id",
"USER_ID_CLAIM": "user_id",
"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
"TOKEN_TYPE_CLAIM": "token_type",
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
"JTI_CLAIM": "jti",
}

View File

@@ -0,0 +1,46 @@
from .base import * # noqa
import os
DEBUG = True
ALLOWED_HOSTS = [
"*",
]
# CORS
# CORS_ALLOWED_ORIGINS = [
# # "http://127.0.0.1:3000",
# ]
# CORS_ORIGIN_ALLOW_ALL = True
# CORS_ORIGIN_WHITELIST = ("http://localhost:3000",)
if DEBUG:
MIDDLEWARE += [
"debug_toolbar.middleware.DebugToolbarMiddleware",
]
INSTALLED_APPS += [
"debug_toolbar",
"django_extensions",
]
INTERNAL_IPS = [
"127.0.0.1",
]
BASE_DOMAIN = ""
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
# STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATIC_URL = "/static/"
STATICFILES_DIRS = [BASE_DIR.joinpath("static")]

View File

@@ -0,0 +1,87 @@
from .base import * # noqa
from .base import env, BASE_DIR
import os
import colorlog
from logging.handlers import TimedRotatingFileHandler
DEBUG = False
ALLOWED_HOSTS = []
# CORS_ALLOWED_ORIGINS = [
# # "http://127.0.0.1:3000",
# ]
# CORS_ORIGIN_ALLOW_ALL = True
# CORS_ORIGIN_WHITELIST = ("",)
LOGGING_DIR = os.path.join(
BASE_DIR, "logs"
) # Define the directory where log files will be stored
# Ensure the directory exists; create it if it doesn't
if not os.path.exists(LOGGING_DIR):
os.makedirs(LOGGING_DIR)
LOGGING_LEVEL = env.str(
"LOG_LEVEL", "INFO"
) # Set your desired log level (e.g., DEBUG, INFO, WARNING, ERROR)
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"()": colorlog.ColoredFormatter,
"format": "%(cyan)s%(asctime)s%(reset)s | %(red)s[%(levelname)8s]%(reset)s | [ %(yellow)s%(name)s.%(module)s:%(white)s%(lineno)d%(reset)s - %(green)s%(funcName)10s()%(reset)s ] --> %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
"log_colors": {
"DEBUG": "white",
"INFO": "green",
"WARNING": "yellow",
"ERROR": "red",
"CRITICAL": "bold_red",
},
"secondary_log_colors": {},
"style": "%",
}
},
"handlers": {
"logfile": {
"level": LOGGING_LEVEL,
"class": "logging.handlers.RotatingFileHandler",
"filename": os.path.join(LOGGING_DIR, "errors.log"),
'maxBytes': 5242880, # 5*1024*1024 bytes (5MB)
"backupCount": 10, # Number of log files to keep (15 days' worth of logs)
"formatter": "verbose",
},
},
"loggers": {
"django": {
"handlers": ["logfile"],
"level": LOGGING_LEVEL,
"propagate": False,
},
},
}
BASE_DOMAIN = ""
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
# STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATIC_URL = "/static/"
STATICFILES_DIRS = [BASE_DIR.joinpath("static")]

View File

@@ -0,0 +1,85 @@
from .base import * # noqa
from .base import env, BASE_DIR
import os
import colorlog
from logging.handlers import TimedRotatingFileHandler
DEBUG = False
ALLOWED_HOSTS = []
# CORS_ALLOWED_ORIGINS = [
# "http://127.0.0.1:3000",
# ]
# CORS_ORIGIN_ALLOW_ALL = True
# CORS_ORIGIN_WHITELIST = ("http://localhost:3000",)
LOGGING_DIR = os.path.join(
BASE_DIR, "logs"
) # Define the directory where log files will be stored
# Ensure the directory exists; create it if it doesn't
if not os.path.exists(LOGGING_DIR):
os.makedirs(LOGGING_DIR)
LOGGING_LEVEL = env.str(
"LOG_LEVEL", "INFO"
) # Set your desired log level (e.g., DEBUG, INFO, WARNING, ERROR) in the env file
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"()": colorlog.ColoredFormatter,
"format": "%(cyan)s%(asctime)s%(reset)s | %(red)s[%(levelname)8s]%(reset)s | [ %(yellow)s%(name)s.%(module)s:%(white)s%(lineno)d%(reset)s - %(green)s%(funcName)10s()%(reset)s ] --> %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
"log_colors": {
"DEBUG": "white",
"INFO": "green",
"WARNING": "yellow",
"ERROR": "red",
"CRITICAL": "bold_red",
},
"secondary_log_colors": {},
"style": "%",
}
},
"handlers": {
"logfile": {
"level": LOGGING_LEVEL,
"class": "logging.handlers.RotatingFileHandler",
"filename": os.path.join(LOGGING_DIR, "errors.log"),
'maxBytes': 5242880, # 5*1024*1024 bytes (5MB)
"backupCount": 10, # Number of log files to keep (15 days' worth of logs)
"formatter": "verbose",
},
},
"loggers": {
"django": {
"handlers": ["logfile"],
"level": LOGGING_LEVEL,
"propagate": False,
},
},
}
BASE_DOMAIN = ""
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
# STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATIC_URL = "/static/"
STATICFILES_DIRS = [BASE_DIR.joinpath("static")]

View File

@@ -0,0 +1,123 @@
"""
Django settings for module_project project.
Generated by 'django-admin startproject' using Django 4.2.5.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-#7rdu=fr58ba9_!n3$l5pm!xs8l%6%8xt@vb8$&o@hqhd@rtd%'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'module_project.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'module_project.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

44
module_project/urls.py Normal file
View File

@@ -0,0 +1,44 @@
"""
URL configuration for module_project project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('iam/', include('module_iam.urls')),
path('auth/', include('module_auth.urls')),
path('api/auth/', include('module_auth.api.urls')),
path('cms/', include('module_cms.urls')),
path('api/cms/', include('module_cms.api.urls')),
# path('support/', include('module_support.urls')),
path('api/support/', include('module_support.api.urls')),
path('activity/', include("module_activity.urls")),
path('api/activity/', include("module_activity.api.urls")),
]
if settings.DEBUG:
import debug_toolbar
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += [path('__debug__/', include(debug_toolbar.urls))]

77
module_project/utils.py Normal file
View File

@@ -0,0 +1,77 @@
from rest_framework.response import Response
from rest_framework.exceptions import NotFound
from django.http import JsonResponse
from . import constants
from rest_framework import status
import string
import random
import logging
import os
import stat
class GroupWriteRotatingFileHandler(logging.handlers.RotatingFileHandler):
def doRollover(self):
"""
Overrides base class method to make the new log file group writable.
"""
# Rotate the file first.
logging.handlers.RotatingFileHandler.doRollover(self)
# Add group write to the current permissions.
currMode = os.stat(self.baseFilename).st_mode
os.chmod(self.baseFilename, currMode | stat.S_IWGRP)
class ApiResponse:
@staticmethod
def success(message, data=None, status=status.HTTP_200_OK):
response_data = {"success": True, "status": status, "message": message}
if data is not None:
response_data["data"] = data
return Response(response_data, status=status)
@staticmethod
def error(message, errors=None, status=status.HTTP_403_FORBIDDEN):
response_data = {"success": False, "status": status, "message": message}
if errors is not None:
response_data["errors"] = errors
return Response(response_data, status=status)
# @staticmethod
# def validation_error(errors, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY):
# return ApiResponse.error("Validation error", errors, status_code)
class JsonResponseUtil:
@staticmethod
def success(message, data=None, status_code=200):
response_data = {"success": True, "status": status_code, "message": message}
if data is not None:
response_data["data"] = data
return JsonResponse(response_data, status=status_code)
@staticmethod
def error(message, errors=None, status_code=403):
response_data = {"success": False, "status": status_code, "message": message}
if errors is not None:
response_data["errors"] = errors
return JsonResponse(response_data, status=status_code)
class RandomGenerator:
@staticmethod
def number(start, end):
# import random
return random.randint(start, end)
@staticmethod
def password_reset_code():
return RandomGenerator.number(10000000, 99999999)
def random_otp():
return RandomGenerator.number(1000, 9999)
@staticmethod
def random_alphnum(length):
return "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(length)
)

16
module_project/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for module_project project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'module_project.settings')
application = get_wsgi_application()

View File

3
module_report/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
module_report/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ModuleReportConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'module_report'

View File

3
module_report/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
module_report/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
module_report/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

View File

3
module_support/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,13 @@
from rest_framework import serializers
from module_support.models import ContactUs, Feedback
class ContactUsSerializer(serializers.ModelSerializer):
class Meta:
model = ContactUs
fields = ["email_address", "subject", "message"]
class FeedbackSerializer(serializers.ModelSerializer):
class Meta:
model = Feedback
fields = ["feedback_reaction", "comment"]

View File

@@ -0,0 +1,7 @@
from django.urls import path
from . import views
urlpatterns = [
path("contact-us/", views.ContactusAPIView.as_view()),
path("feedback/", views.FeedbackAPIView.as_view()),
]

View File

@@ -0,0 +1,41 @@
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
from module_project import constants
from module_project.utils import ApiResponse
from .serializers import ContactUsSerializer, FeedbackSerializer
from ..models import ContactUs, Feedback
class ContactusAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = ContactUsSerializer
model = ContactUs
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
return ApiResponse.error(message=constants.FAILURE, errors=serializer.errors)
try:
serializer.save()
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
class FeedbackAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = FeedbackSerializer
model = Feedback
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
return ApiResponse.error(message=constants.FAILURE, errors=serializer.errors)
try:
serializer.save(principal=request.user)
except Exception as e:
return ApiResponse.error(message=constants.FAILURE, errors=str(e))
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)

6
module_support/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ModuleSupportConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'module_support'

View File

@@ -0,0 +1,38 @@
# Generated by Django 5.0.2 on 2024-02-14 14:51
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='ContactUs',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('name', models.CharField(blank=True, max_length=100, null=True)),
('email_address', models.EmailField(max_length=254)),
('mobile_number', models.CharField(blank=True, max_length=15, null=True)),
('subject', models.CharField(max_length=200)),
('message', models.TextField()),
('reply', models.TextField(blank=True, null=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modified', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'contact_us',
},
),
]

View File

@@ -0,0 +1,35 @@
# Generated by Django 5.0.2 on 2024-02-15 05:54
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('module_support', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Feedback',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active', models.BooleanField(default=True)),
('deleted', models.BooleanField(default=False)),
('created_on', models.DateTimeField(auto_now_add=True)),
('modified_on', models.DateTimeField(auto_now=True)),
('email', models.EmailField(blank=True, help_text='Email address of the feedback provider', max_length=254, null=True)),
('comment', models.TextField(help_text='Feedback comment')),
('feedback_reaction', models.CharField(blank=True, help_text='Reaction associated with the feedback', max_length=20, null=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modified', to=settings.AUTH_USER_MODEL)),
('principal', models.ForeignKey(blank=True, help_text='User associated with this feedback', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='feedbacks', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'feedback',
},
),
]

View File

40
module_support/models.py Normal file
View File

@@ -0,0 +1,40 @@
from django.db import models
from module_iam.models import BaseModel, IAmPrincipal
# Create your models here.
class ContactUs(BaseModel):
name = models.CharField(max_length=100, blank=True, null=True)
email_address = models.EmailField()
mobile_number = models.CharField(max_length=15, blank=True, null=True)
subject = models.CharField(max_length=200)
message = models.TextField()
reply = models.TextField(blank=True, null=True)
class Meta:
db_table = "contact_us"
class Feedback(BaseModel):
principal = models.ForeignKey(
IAmPrincipal,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="feedbacks",
help_text="User associated with this feedback",
)
email = models.EmailField(null=True, blank=True, help_text="Email address of the feedback provider")
comment = models.TextField(help_text="Feedback comment")
feedback_reaction = models.CharField(
max_length=20,
null=True,
blank=True,
help_text="Reaction associated with the feedback",
)
class Meta:
db_table = "feedback"
def __str__(self):
return f"Author: {self.principal}, Comment: {self.comment}"

3
module_support/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

Some files were not shown because too many files have changed in this diff Show More