Refactor i am principal
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -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,
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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):
|
||||
|
||||
9
module_notification/api/serializers.py
Normal file
9
module_notification/api/serializers.py
Normal 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']
|
||||
11
module_notification/api/urls.py
Normal file
11
module_notification/api/urls.py
Normal 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"),
|
||||
|
||||
]
|
||||
49
module_notification/api/views.py
Normal file
49
module_notification/api/views.py
Normal 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)
|
||||
46
module_notification/cron_job.py
Normal file
46
module_notification/cron_job.py
Normal 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)
|
||||
@@ -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()
|
||||
34
module_notification/migrations/0002_inappnotification.py
Normal file
34
module_notification/migrations/0002_inappnotification.py
Normal 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'],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -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',
|
||||
),
|
||||
]
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 '';
|
||||
|
||||
@@ -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 %}
|
||||
Reference in New Issue
Block a user