Refactor i am principal

This commit is contained in:
bobbyvish
2024-04-01 11:31:16 +05:30
parent 869a7ace06
commit db2fab79fa
25 changed files with 518 additions and 126 deletions

View File

@@ -153,6 +153,11 @@ 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"]
FILTER_ICONTAINS = "icontains"
def get_filter_method(self):
"""Returns preferred filter method"""
return self.FILTER_ICONTAINS
def get_initial_queryset(self):
deleted_flag = self.request.GET.get('deleted_flag', False)
@@ -166,9 +171,9 @@ class UserListJson(BaseDatatableView):
for column in self.columns:
print(f" columen index pattern {self.request.GET.get(f'columns[{self.columns.index(column)+2}][search][value]', None)}")
search_value = self.request.GET.get(f'columns[{self.columns.index(column)+1}][search][value]', None)
search_value = self.request.GET.get(f'columns[{self.columns.index(column)+2}][search][value]', None)
if search_value:
column_data = self.request.GET.get(f'columns[{self.columns.index(column)+1}][data]')
column_data = self.request.GET.get(f'columns[{self.columns.index(column)+2}][data]')
if column_data == "is_active":
qs = qs.filter(**{f"{column}": search_value})
else:

View File

@@ -6,8 +6,8 @@
"name": "create",
"label": "create",
"slug": "create",
"created_on": "2024-03-18T12:21:41.416386",
"modified_on": "2024-03-18T12:21:41.416386"
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813"
}
},
{
@@ -17,8 +17,8 @@
"name": "read",
"label": "read",
"slug": "read",
"created_on": "2024-03-18T12:21:41.416386",
"modified_on": "2024-03-18T12:21:41.416386"
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813"
}
},
{
@@ -28,8 +28,8 @@
"name": "update",
"label": "update",
"slug": "update",
"created_on": "2024-03-18T12:21:41.416386",
"modified_on": "2024-03-18T12:21:41.416386"
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813"
}
},
{
@@ -39,8 +39,8 @@
"name": "delete",
"label": "delete",
"slug": "delete",
"created_on": "2024-03-18T12:21:41.416386",
"modified_on": "2024-03-18T12:21:41.416386"
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813"
}
}
]

View File

@@ -6,8 +6,8 @@
"name": "app",
"label": "app",
"slug": "app",
"created_on": "2024-03-18T12:21:41.414515",
"modified_on": "2024-03-18T12:21:41.414515"
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813"
}
},
{
@@ -17,8 +17,8 @@
"name": "web",
"label": "web",
"slug": "web",
"created_on": "2024-03-18T12:21:41.414515",
"modified_on": "2024-03-18T12:21:41.414515"
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813"
}
},
{
@@ -28,8 +28,8 @@
"name": "google",
"label": "google",
"slug": "google",
"created_on": "2024-03-18T12:21:41.414515",
"modified_on": "2024-03-18T12:21:41.414515"
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813"
}
},
{
@@ -39,8 +39,8 @@
"name": "apple",
"label": "apple",
"slug": "apple",
"created_on": "2024-03-18T12:21:41.414515",
"modified_on": "2024-03-18T12:21:41.414515"
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813"
}
}
]

View File

@@ -6,8 +6,8 @@
"name": "admin",
"label": "admin",
"slug": "admin",
"created_on": "2024-03-18T12:21:41.407938",
"modified_on": "2024-03-18T12:21:41.407938"
"created_on": "2024-04-01T11:17:39.364808",
"modified_on": "2024-04-01T11:17:39.364808"
}
},
{
@@ -17,8 +17,8 @@
"name": "subadmin",
"label": "subadmin",
"slug": "subadmin",
"created_on": "2024-03-18T12:21:41.407938",
"modified_on": "2024-03-18T12:21:41.407938"
"created_on": "2024-04-01T11:17:39.364808",
"modified_on": "2024-04-01T11:17:39.364808"
}
},
{
@@ -28,8 +28,8 @@
"name": "user",
"label": "user",
"slug": "user",
"created_on": "2024-03-18T12:21:41.407938",
"modified_on": "2024-03-18T12:21:41.407938"
"created_on": "2024-04-01T11:17:39.364808",
"modified_on": "2024-04-01T11:17:39.364808"
}
}
]

View File

@@ -3,11 +3,11 @@
"model": "module_iam.iamappresource",
"pk": 1,
"fields": {
"name": "manage_dashboard",
"label": "manage_dashboard",
"slug": "manage_dashboard",
"created_on": "2024-03-18T12:21:41.417445",
"modified_on": "2024-03-18T12:21:41.417445",
"name": "manage_iam",
"label": "manage_iam",
"slug": "manage_iam",
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813",
"action": [
1,
2,
@@ -20,11 +20,11 @@
"model": "module_iam.iamappresource",
"pk": 2,
"fields": {
"name": "manage_iam",
"label": "manage_iam",
"slug": "manage_iam",
"created_on": "2024-03-18T12:21:41.417445",
"modified_on": "2024-03-18T12:21:41.417445",
"name": "manage_user",
"label": "manage_user",
"slug": "manage_user",
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813",
"action": [
1,
2,
@@ -37,11 +37,11 @@
"model": "module_iam.iamappresource",
"pk": 3,
"fields": {
"name": "manage_user",
"label": "manage_user",
"slug": "manage_user",
"created_on": "2024-03-18T12:21:41.417445",
"modified_on": "2024-03-18T12:21:41.417445",
"name": "manage_support",
"label": "manage_support",
"slug": "manage_support",
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813",
"action": [
1,
2,
@@ -54,11 +54,11 @@
"model": "module_iam.iamappresource",
"pk": 4,
"fields": {
"name": "manage_support",
"label": "manage_support",
"slug": "manage_support",
"created_on": "2024-03-18T12:21:41.417445",
"modified_on": "2024-03-18T12:21:41.417445",
"name": "manage_contact_us",
"label": "manage_contact_us",
"slug": "manage_contact_us",
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813",
"action": [
1,
2,
@@ -71,11 +71,11 @@
"model": "module_iam.iamappresource",
"pk": 5,
"fields": {
"name": "manage_contact_us",
"label": "manage_contact_us",
"slug": "manage_contact_us",
"created_on": "2024-03-18T12:21:41.417445",
"modified_on": "2024-03-18T12:21:41.417445",
"name": "manage_feedback",
"label": "manage_feedback",
"slug": "manage_feedback",
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813",
"action": [
1,
2,
@@ -88,11 +88,11 @@
"model": "module_iam.iamappresource",
"pk": 6,
"fields": {
"name": "manage_feedback",
"label": "manage_feedback",
"slug": "manage_feedback",
"created_on": "2024-03-18T12:21:41.417445",
"modified_on": "2024-03-18T12:21:41.417445",
"name": "manage_cms",
"label": "manage_cms",
"slug": "manage_cms",
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813",
"action": [
1,
2,
@@ -105,11 +105,11 @@
"model": "module_iam.iamappresource",
"pk": 7,
"fields": {
"name": "manage_cms",
"label": "manage_cms",
"slug": "manage_cms",
"created_on": "2024-03-18T12:21:41.417445",
"modified_on": "2024-03-18T12:21:41.417445",
"name": "manage_faqs",
"label": "manage_faqs",
"slug": "manage_faqs",
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813",
"action": [
1,
2,
@@ -122,11 +122,11 @@
"model": "module_iam.iamappresource",
"pk": 8,
"fields": {
"name": "manage_faqs",
"label": "manage_faqs",
"slug": "manage_faqs",
"created_on": "2024-03-18T12:21:41.417445",
"modified_on": "2024-03-18T12:21:41.417445",
"name": "manage_tc",
"label": "manage_tc",
"slug": "manage_tc",
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813",
"action": [
1,
2,
@@ -139,11 +139,11 @@
"model": "module_iam.iamappresource",
"pk": 9,
"fields": {
"name": "manage_tc",
"label": "manage_tc",
"slug": "manage_tc",
"created_on": "2024-03-18T12:21:41.417445",
"modified_on": "2024-03-18T12:21:41.417445",
"name": "manage_privacypolicy",
"label": "manage_privacypolicy",
"slug": "manage_privacypolicy",
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813",
"action": [
1,
2,
@@ -156,11 +156,11 @@
"model": "module_iam.iamappresource",
"pk": 10,
"fields": {
"name": "manage_privacypolicy",
"label": "manage_privacypolicy",
"slug": "manage_privacypolicy",
"created_on": "2024-03-18T12:21:41.417445",
"modified_on": "2024-03-18T12:21:41.417445",
"name": "manage_notification",
"label": "manage_notification",
"slug": "manage_notification",
"created_on": "2024-04-01T11:17:39.378813",
"modified_on": "2024-04-01T11:17:39.378813",
"action": [
1,
2,

View File

@@ -223,6 +223,43 @@ class ProfileEditForm(forms.ModelForm):
]
class IAmPrincipalResourceLinkForm(IAmPrincipalForm):
class Meta:
model = models.IAmPrincipal
fields = [
"principal_type",
"first_name",
"last_name",
"email",
"password",
"confirm_password",
"principal_resource",
]
principal_resource = forms.ModelMultipleChoiceField(
label="Module Permission",
queryset=models.IAmAppResource.objects.filter(active=True, deleted=False),
required=False,
widget=forms.widgets.SelectMultiple(
attrs={"class": "form_select js-example-basic-multiple"}
),
)
def save(self, commit=True):
# First, save the instance of the IAmPrincipal model as usual
principal = super().save(commit=False)
# If the principal_resource field has data
if self.cleaned_data['principal_resource']:
# Get the principal_resource data
principal_resource_data = self.cleaned_data['principal_resource']
# Update the many-to-many relationship
principal.principal_resource.set(principal_resource_data)
# Save the instance to the database
if commit:
principal.save()
class IAmPrincipalGroupLinkForm(IAmPrincipalForm):
class Meta:

View File

@@ -21,7 +21,8 @@ from .iam_constant import (
RESOURCE_MANAGE_T_C,
RESOURCE_MANAGE_CMS,
RESOURCE_MANAGE_PRIVACYPOLICY,
RESOURCE_MANAGE_SUPPORT
RESOURCE_MANAGE_SUPPORT,
RESOURCE_MANAGE_NOTIFICATION
)
class IAMPrincipalType:
@@ -122,7 +123,6 @@ class IAMActions:
return iam_action_fixture_data
class IAMResources:
DASHBOARD = RESOURCE_MANAGE_DASHBOARD
IAM = RESOURCE_MANAGE_IAM
USER = RESOURCE_MANAGE_USER
SUPPORT = RESOURCE_MANAGE_SUPPORT
@@ -132,9 +132,9 @@ class IAMResources:
FAQS = RESOURCE_MANAGE_FAQS
T_C = RESOURCE_MANAGE_T_C
PRIVACYPOLICY = RESOURCE_MANAGE_PRIVACYPOLICY
NOTIFICATION = RESOURCE_MANAGE_NOTIFICATION
resources = [
DASHBOARD,
IAM,
USER,
SUPPORT,
@@ -144,6 +144,7 @@ class IAMResources:
FAQS,
T_C,
PRIVACYPOLICY,
NOTIFICATION,
]
@staticmethod

View File

@@ -0,0 +1,31 @@
# Generated by Django 5.0.2 on 2024-03-31 09:38
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('module_iam', '0006_alter_appversion_version'),
]
operations = [
migrations.CreateModel(
name='IAmPrincipalResourceLink',
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_resource_link_principal', to=settings.AUTH_USER_MODEL)),
('principal_resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='principal_resource_link_resource', to='module_iam.iamappresource')),
],
options={
'db_table': 'iam_principal_resource_group_link',
},
),
migrations.AddField(
model_name='iamprincipal',
name='principal_resource',
field=models.ManyToManyField(related_name='principal_resources', through='module_iam.IAmPrincipalResourceLink', to='module_iam.iamappresource'),
),
]

View File

@@ -357,6 +357,11 @@ class IAmPrincipal(AbstractUser):
blank=True,
help_text="OneSignal player id for push notification",
)
principal_resource = models.ManyToManyField(
IAmAppResource,
through="IAmPrincipalResourceLink",
related_name="principal_resources",
)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
@@ -370,6 +375,21 @@ class IAmPrincipal(AbstractUser):
return f"{self.email}"
class IAmPrincipalResourceLink(models.Model):
principal = models.ForeignKey(
IAmPrincipal,
related_name="principal_resource_link_principal",
on_delete=models.CASCADE,
)
principal_resource = models.ForeignKey(
IAmAppResource,
related_name="principal_resource_link_resource",
on_delete=models.CASCADE,
)
class Meta:
db_table = "iam_principal_resource_group_link"
class IAmPrincipalGroupLink(models.Model):
principal = models.ForeignKey(
IAmPrincipal,

View File

@@ -13,6 +13,7 @@ urlpatterns = [
# path('principal/delete/<int:pk>', views.PrincipalDeleteView.as_view(), name="principal_delete"),
path('principal/archive/', views.PrincipalArchiveView.as_view(), name="principal_archive"),
path('principal/archive/list/', views.PrincipalArchiveListJsonView.as_view(), name="principal_archive_list"),
path('principal/resource/permission/edit/<int:pk>/', views.PrincipalResourcePermissionEditView.as_view(), name="principal_resource_permission_edit"),
path('principal/group/link/', views.PrincipalGroupLinkView.as_view(), name="principal_group_link"),
path('principal/group/link/list/admin/', views.PrincipalGroupLinkAdminListJsonView.as_view(), name="principal_group_link_list"),

View File

@@ -19,7 +19,7 @@ from module_project import constants
from module_project.mixins import ActionMixin, DatatablesMixin
from module_project.utils import JsonResponseUtil
from .forms import (CustomAuthenticationForm, IAmPrincipalForm,
from .forms import (CustomAuthenticationForm, IAmPrincipalForm,IAmPrincipalResourceLinkForm,
IAmPrincipalGroupLinkForm, IAmPrincipalGroupRoleLinkForm,
IAmPrincipalRoleAppResourceActionLinkForm, ProfileEditForm)
from .models import (IAmAppResourceActionLink, IAmPrincipal, IAmPrincipalGroup,
@@ -138,6 +138,45 @@ class PrincipalArchiveListJsonView(BaseDatatableView):
return qs
class PrincipalResourcePermissionEditView(LoginRequiredMixin, generic.View):
page_name = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP
model = IAmPrincipal
template_name = "module_iam/iam_principal_resource_permission_edit.html"
form_class = IAmPrincipalResourceLinkForm
success_url = reverse_lazy("module_iam:principal_group_link")
success_message = "Record Updated Successfully"
error_message = "An error occurred while saving the data"
def get_object(self):
pk = self.kwargs.get("pk")
return get_object_or_404(self.model, pk=pk) if pk else None
def get_context_data(self, **kwargs):
context = {
"page_name": self.page_name,
"operation": "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()
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()
form = self.form_class(request.POST, instance=self.object)
if not form.is_valid():
context = self.get_context_data(form=form)
return render(request, self.template_name, context=context)
form.save()
messages.success(request, self.success_message)
return redirect(self.success_url)
class PrincipalGroupLinkView(LoginRequiredMixin, generic.TemplateView):
page_name = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP
model = IAmPrincipal
@@ -197,8 +236,8 @@ class PrincipalGroupLinkSubAdminListJsonView(BaseDatatableView):
def render_column(self, row, column):
if column == "principal_type_name":
return row.principal_type.name if row.principal_type else None
if column == "groups":
return [{"name": group.name} for group in row.principal_group.all()]
if column == "permission":
return [{"name": resource.name} for resource in row.principal_resource.all()]
return super().render_column(row, column)
def filter_queryset(self, qs):

View File

@@ -0,0 +1,9 @@
from rest_framework import serializers
from ..models import InAppNotification
class InAppNotificationSerializer(serializers.ModelSerializer):
created_on = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
class Meta:
model = InAppNotification
fields = ['id', 'message', 'is_read', 'created_on']

View File

@@ -0,0 +1,11 @@
from django.urls import path
from . import views
urlpatterns = [
path("count/", views.InAppNotificationCountAPIView.as_view(), name="inapp_notification_count"),
path("list/", views.InAppNotificationListAPIView.as_view(), name="inapp_notification_list"),
path("read/", views.InAppNotificationReadAPIView.as_view(), name="inapp_notification_read"),
]

View File

@@ -0,0 +1,49 @@
from datetime import datetime
import requests
from django.conf import settings
from django.contrib.auth import authenticate
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.authentication import JWTAuthentication
from module_project.utils import ApiResponse
from module_project import constants
from .serializers import InAppNotificationSerializer
from ..models import InAppNotification
class InAppNotificationCountAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
model = InAppNotification
def get(self, request, *args, **kwargs):
count = InAppNotification.pending_read_count(user=request.user)
return ApiResponse.success(message=constants.SUCCESS, data={"count": count})
class InAppNotificationListAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
model = InAppNotification
serializer_class = InAppNotificationSerializer
def get(self, request, *args, **kwargs):
obj = InAppNotification.latest_15(user=request.user)
serializer_obj = self.serializer_class(obj, many=True)
return ApiResponse.success(message=constants.SUCCESS, data=serializer_obj.data)
class InAppNotificationReadAPIView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
model = InAppNotification
serializer_class = InAppNotificationSerializer
def get(self, request, *args, **kwargs):
obj = InAppNotification.objects.filter(user=request.user).update(is_read=True)
return ApiResponse.success(message=constants.SUCCESS)

View File

@@ -0,0 +1,46 @@
from datetime import datetime, timedelta
from .models import InAppNotification
from module_iam.models import IAmPrincipal, IAmPrincipalType
from module_activity.models import MealRecord, Bowel, MealSymptomRecord, Medication
def notification_for_meal_and_medication():
current_date = datetime.now()
fifteen_days_ago = current_date - timedelta(days=20)
users = IAmPrincipal.objects.filter(
last_login__gte=fifteen_days_ago,
principal_type=IAmPrincipalType.get_principal_user(),
).values_list("id", flat=True)
meal_obj = MealRecord.objects.filter(date=current_date).values_list(
"principal", flat=True
)
medication_obj = Medication.objects.filter(date=current_date).values_list(
"principal", flat=True
)
# Remove IDs of users who have recorded meals for the current day
users_without_meals = set(users) - set(meal_obj)
users_without_medications = set(users) - set(medication_obj)
print(f"user id {set(users)}")
print(
f"userwithoutmeal {users_without_meals} and users_without_medication {users_without_medications}"
)
notifications_to_create = []
for user_id in users_without_meals:
message = "Have you eaten yet? it's been a whiile since you logged a meal."
notifications_to_create.append(
InAppNotification(user_id=user_id, message=message)
)
for user_id in users_without_medications:
message = "Have you taken your medication today? Remember to log your medication to stay on track with your treatment!"
notifications_to_create.append(
InAppNotification(user_id=user_id, message=message)
)
# Bulk create notifications
if notifications_to_create:
InAppNotification.objects.bulk_create(notifications_to_create)

View File

@@ -0,0 +1,9 @@
from django.core.management.base import BaseCommand
from ...cron_job import notification_for_meal_and_medication
class Command(BaseCommand):
help = 'Sends notifications to users'
def handle(self, *args, **kwargs):
notification_for_meal_and_medication()

View File

@@ -0,0 +1,34 @@
# Generated by Django 5.0.2 on 2024-03-30 18:53
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('module_notification', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='InAppNotification',
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)),
('message', models.CharField(max_length=255)),
('is_read', models.BooleanField(default=False)),
('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)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-created_on'],
},
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 5.0.2 on 2024-03-30 18:54
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('module_notification', '0002_inappnotification'),
]
operations = [
migrations.AlterModelTable(
name='inappnotification',
table='inapp_notification',
),
]

View File

@@ -1,9 +1,29 @@
from django.db import models
from module_iam.models import BaseModel
from module_iam.models import BaseModel, IAmPrincipal
class InAppNotification(BaseModel):
user = models.ForeignKey(IAmPrincipal, on_delete=models.CASCADE, related_name='notifications')
message = models.CharField(max_length=255)
is_read = models.BooleanField(default=False)
class Meta:
db_table = "inapp_notification"
ordering = ['-created_on']
def __str__(self):
return self.message
@classmethod
def latest_15(cls, user):
return cls.objects.filter(user=user).order_by('-created_on')[:15]
@classmethod
def pending_read_count(cls, user):
return cls.objects.filter(user=user, is_read=False).count()
# Create your models here.
class PushNotification(BaseModel):
title = models.CharField(max_length=255)
banner_image = models.ImageField(upload_to='push_notification_images/', blank=True, null=True)

View File

@@ -198,7 +198,6 @@ class NotificationSendView(generic.View):
response = notification.send_notification(
headings=obj.title,
contents=obj.message,
# include_player_ids=["5643e132-5266-4dc2-9131-1b4a81f0cbd0"], # single player id
include_player_ids=player_ids,
)
except Exception as e:
@@ -210,3 +209,5 @@ class NotificationSendView(generic.View):
return JsonResponseUtil.error(**error_response)
return JsonResponseUtil.success(message="success")

View File

@@ -62,6 +62,7 @@ THIRD_PARTY_APPS = [
"rest_framework_simplejwt",
"taggit",
"django_quill",
"django_crontab",
]
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
@@ -259,8 +260,8 @@ LOGGING = {
# jwt configuration
# https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html#settings
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": datetime.timedelta(days=30),
"REFRESH_TOKEN_LIFETIME": datetime.timedelta(days=30),
"ACCESS_TOKEN_LIFETIME": datetime.timedelta(days=15),
"REFRESH_TOKEN_LIFETIME": datetime.timedelta(days=15),
"ROTATE_REFRESH_TOKENS": False,
"BLACKLIST_AFTER_ROTATION": False,
"UPDATE_LAST_LOGIN": False,
@@ -282,3 +283,10 @@ SIMPLE_JWT = {
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
"JTI_CLAIM": "jti",
}
CRONJOBS = [
('* * * * *', 'manage_notification.cron_job.notification_for_meal_and_medication'),
]
# Additional configuration for cron jobs
CRONTAB_LOCK_JOBS = True

View File

@@ -37,7 +37,7 @@ urlpatterns = [
path('api/activity/', include("module_activity.api.urls")),
path('notification/', include("module_notification.urls")),
# path('api/activity/', include("module_activity.api.urls")),
path('api/notification/', include("module_notification.api.urls")),
]
if settings.DEBUG:

View File

@@ -49,11 +49,11 @@
<li class="{% if page_name == iam_constants_context.RESOURCE_IAM_PRINCIPAL_GROUP %}active{% endif %}">
<a href="{% url 'module_iam:principal_group_link' %}"> IAM Principal</a>
</li>
<li class="{% if page_name == iam_constants_context.RESOURCE_IAM_GROUP %}active{% endif %}">
<!-- <li class="{% if page_name == iam_constants_context.RESOURCE_IAM_GROUP %}active{% endif %}">
<a href="{% url 'module_iam:principal_group' %}"> IAM Group </a>
</li>
<li class="{% if page_name == iam_constants_context.RESOURCE_IAM_ROLE %}active{% endif %}">
<a href="{% url 'module_iam:role' %}"> IAM Role </a>
<a href="{% url 'module_iam:role' %}"> IAM Role </a> -->
</li>
</ul>
</li>

View File

@@ -52,24 +52,24 @@
<thead>
<tr role="row">
<th class="checkbox-column text-center dt-no-sorting" tabindex="0"
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
aria-label=" Record Id : activate to sort column descending"
style="width: 69.2656px;"> # </th>
<th class="checkbox-column text-center sorting_asc" tabindex="0"
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
aria-label=" Record Id : activate to sort column descending"
style="width: 69.2656px;"> Id </th>
<th class="checkbox-column text-center sorting_asc" tabindex="0"
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
aria-label=" Record Id : activate to sort column descending"
style="width: 69.2656px;"> Id </th>
style="width: 69.2656px;">Record Id </th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
colspan="1" aria-label="First Name: activate to sort column ascending"
style="width: 44.2344px;">Name</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
colspan="1" aria-label="Email: activate to sort column ascending"
style="width: 44.2344px;">Email</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
<th class="sorting dt-no-sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
aria-label="Permission: activate to sort column ascending"
style="width: 79.7969px;">Permission</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
<th class="sorting dt-no-sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
aria-label="First Name: activate to sort column ascending"
style="width: 79.7969px;">Pricipal_type</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
@@ -106,24 +106,24 @@
<thead>
<tr role="row">
<th class="checkbox-column text-center sorting_asc" tabindex="0"
<th class="checkbox-column text-center dt-no-sorting" tabindex="0"
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
aria-label=" Record Id : activate to sort column descending"
style="width: 69.2656px;"> Id </th>
<th class="checkbox-column text-center sorting_asc" tabindex="0"
style="width: 69.2656px;"> # </th>
<th class="text-center sorting_asc" tabindex="0"
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
aria-label=" Record Id : activate to sort column descending"
style="width: 69.2656px;"> Id </th>
style="width: 69.2656px;"> Record Id </th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
colspan="1" aria-label="First Name: activate to sort column ascending"
style="width: 44.2344px;">Name</th>
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
colspan="1" aria-label="Email: activate to sort column ascending"
style="width: 44.2344px;">Email</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
<th class="sorting text-center dt-no-sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
aria-label="Permission: activate to sort column ascending"
style="width: 79.7969px;">Groups</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
style="width: 79.7969px;">Permission</th>
<th class="sorting dt-no-sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
aria-label="First Name: activate to sort column ascending"
style="width: 79.7969px;">Pricipal_type</th>
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
@@ -191,7 +191,7 @@ $(document).ready(function() {
tableId: '#table2',
MainUrl: "{% url 'module_iam:principal_group_link_list_sub' %}?deleted_flag=False",
actionUrl: "{% url 'module_iam:principal_group_link_action' %}",
editUrl: "{% url 'module_iam:principal_group_link_edit' pk=0 %}",
editUrl: "{% url 'module_iam:principal_resource_permission_edit' pk=0 %}",
viewArchiveUrl: "{% url 'module_iam:principal_archive' %}"
};
@@ -233,17 +233,12 @@ function initializeDataTable(tableSettings) {
debug: true,
columnDefs: [
{
targets: [1, 2],
targets: [1, 2, 3],
searchable: true,
orderable: true
},
{
targets: [3],
searchable: true,
orderable: false
},
{
targets: [0,-1], // Targeting the last column (action column)
targets: [0,4,5,-1], // Targeting the last column (action column)
searchable: false,
orderable: false
},
@@ -301,8 +296,8 @@ function initialize2DataTable(tableSettings) {
{ data: "first_name" },
{ data: "email" },
{
data: "groups",
render: renderGroups
data: "permission",
render: renderPermission
},
{ data: "principal_type_name", className: "text-center" },
{ data: "is_active", className: "text-center", render: renderSwitch },
@@ -315,17 +310,12 @@ function initialize2DataTable(tableSettings) {
debug: true,
columnDefs: [
{
targets: [1, 2],
targets: [1, 2, 3],
searchable: true,
orderable: true
},
{
targets: [3],
searchable: true,
orderable: false
},
{
targets: [0,-1], // Targeting the last column (action column)
targets: [0,4,5,-1], // Targeting the last column (action column)
searchable: false,
orderable: false
},
@@ -367,15 +357,15 @@ function initialize2DataTable(tableSettings) {
});
}
function renderGroups(data, type, row) {
if (type === 'display' && row.groups) {
let html = '<ul>';
for (const [index, group] of Object.entries(row.groups)) {
html += `<li class="mb-1"><span class="badge badge-warning">${group.name}</span></li>`;
function renderPermission(data, type, row) {
if (type === 'display' && row.permission) {
let html = '<ul><li class="mb-1 d-flex gap-2 p-2">';
for (const [index, permission] of Object.entries(row.permission)) {
html += `<span class="badge badge-warning">${permission.name}</span>`;
}
html += '</ul>';
html += '</li></ul>';
return html;
} else if (type === 'display' && !row.groups) {
} else if (type === 'display' && !row.permission) {
return '<span class="badge badge-danger">No Group assigned</span>';
} else {
return '';

View File

@@ -0,0 +1,64 @@
{% extends 'base_structure/layout/base_template.html' %}
{% load static %}
{% block stylesheet %}
<!-- include required css cdn link through html here -->
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
{% endblock %}
{% block content %}
<div class="row layout-top-spacing">
<div class="col-lg-12">
<div class="row mb-2">
<div class="col">
<h3>{{operation}} Principal Group Link</h3>
</div>
<div class="col text-end">
<button class="btn btn-dark mb-2 me-4" onclick="history.back()">
<i class="fa fa-arrow-left"></i>
Back
</button>
</div>
</div>
<div class="row layout-spacing">
<div class="col-lg-12">
<div class="statbox widget box box-shadow">
<div class="widget-content widget-content-area">
<form method="post" autocomplete="off">
{% csrf_token %}
{% include 'base_structure/includes/dynamic_template_form.html' with form=form %}
<div class="mt-4 mb-0">
<div class="d-grid"><button class="btn btn-primary btn-block" type="submit">Submit</button></div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
{% block javascript %}
<!-- include required js cdn link through html here -->
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
$(document).ready(function() {
$('.js-example-basic-multiple').select2({
placeholder: 'Select options',
allowClear: true,
tags: true, // Allow the user to enter custom tags
tokenSeparators: [',', ' '], // Customize token separators
closeOnSelect: false // Keep the dropdown open after selection
});
});
</script>
{% endblock %}