import logging from datetime import datetime from typing import Any from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin from django.db import transaction from django.db.models import Q from django.db.models.base import Model as Model from django.db.models.query import QuerySet from django.http import JsonResponse from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse_lazy from django.views import generic from django_datatables_view.base_datatable_view import BaseDatatableView from module_iam import iam_constant, permission from module_project import constants from module_project.mixins import ActionMixin from module_project.utils import JsonResponseUtil from .forms import (CustomAuthenticationForm, IAmPrincipalForm, IAmPrincipalResourceLinkForm, IAmPrincipalGroupLinkForm, IAmPrincipalGroupRoleLinkForm, IAmPrincipalRoleAppResourceActionLinkForm, ProfileEditForm) from .models import (IAmAppResourceActionLink, IAmPrincipal, IAmPrincipalGroup, IAmPrincipalType, IAmRole) logger = logging.getLogger(__name__) # Create your views here. class DashboardView(LoginRequiredMixin, generic.TemplateView): page_name = iam_constant.RESOURCE_MANAGE_DASHBOARD resource = iam_constant.RESOURCE_MANAGE_DASHBOARD template_name = "base_structure/layout/dashboard.html" def get_user_count(self): obj = IAmPrincipal.objects.all() # Count active users active_user_count = obj.filter(is_active=True).count() # Count total users total_user_count = obj.count() return active_user_count, total_user_count def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) active_user_count, total_user_count = self.get_user_count() context["active_user_count"] = active_user_count context["total_user_count"] = total_user_count context["page_name"] = self.page_name return context class PrincipalCreateOrUpdateView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.View): page_name = iam_constant.RESOURCE_IAM_PRINCIPAL resource = iam_constant.RESOURCE_IAM_PRINCIPAL model = IAmPrincipal form_class = IAmPrincipalForm template_name = "module_iam/iam_principal_add.html" success_url = reverse_lazy("module_iam:principal_group_link") success_message = "Saved 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" if self.object else "Add", } 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) @transaction.atomic def post(self, request, *args, **kwargs): print(request.POST) self.object = self.get_object() form = self.form_class(request.POST, instance=self.object) try: if form.is_valid(): principal = form.save(commit=False) # Check if it's a new object (create action) or an existing one (update action) if not principal.pk: # pk is None for new objects principal.created_by = request.user principal.modified_by = request.user principal.modified_on = datetime.now() # Save the object principal.save() messages.success(request, "Form submitted successfully") return redirect(self.success_url) except Exception as e: self.error_message = constants.ERROR_OCCURR.format(str(e)) print(self.error_message) messages.error(request, self.error_message) context = self.get_context_data(form=form) return render(request, template_name=self.template_name, context=context) class PrincipalArchiveListJsonView(BaseDatatableView): model = IAmPrincipal columns = ["id", "first_name", "email", "is_active"] order_columns = ["id", "first_name", "email", "is_active"] def get_initial_queryset(self): deleted_flag = self.request.GET.get("deleted_flag", False) return self.model.objects.filter( deleted=deleted_flag, principal_type__name__in=( iam_constant.PRINCIPAL_TYPE_ADMIN, iam_constant.PRINCIPAL_TYPE_SUBADMIN, ), ) def render_column(self, row, column): if column == "principal_type_name": return row.principal_type.name if row.principal_type else None return super().render_column(row, column) def filter_queryset(self, qs): search_value = self.request.GET.get("search[value]", None) if search_value: qs = qs.filter( Q(id__icontains=search_value) | Q(first_name__icontains=search_value) | Q(email__icontains=search_value) ) return qs class PrincipalResourcePermissionEditView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.View): page_name = iam_constant.RESOURCE_IAM_PRINCIPAL resource = 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(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.TemplateView): page_name = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP resource = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP model = IAmPrincipal template_name = "module_iam/iam_principal_group_link.html" def get_context_data(self, **kwargs) -> dict[str, Any]: context = super().get_context_data(**kwargs) context["page_name"] = self.page_name return context class PrincipalArchiveView(PrincipalGroupLinkView): page_name = iam_constant.RESOURCE_IAM_PRINCIPAL resource = iam_constant.RESOURCE_IAM_PRINCIPAL action = None template_name = "module_iam/iam_principal_archive.html" class PrincipalGroupLinkAdminListJsonView(BaseDatatableView): model = IAmPrincipal columns = ["id", "first_name", "email", "is_active"] order_columns = ["id", "first_name", "email", "is_active"] def get_initial_queryset(self): deleted_flag = self.request.GET.get("deleted_flag", False) return self.model.objects.filter( deleted=deleted_flag, principal_type__name=iam_constant.PRINCIPAL_TYPE_ADMIN ) def render_column(self, row, column): if column == "principal_type_name": return row.principal_type.name if row.principal_type else None return super().render_column(row, column) def filter_queryset(self, qs): search_value = self.request.GET.get("search[value]", None) if search_value: qs = qs.filter( Q(id__icontains=search_value) | Q(first_name__icontains=search_value) | Q(email__icontains=search_value) ) return qs class PrincipalGroupLinkSubAdminListJsonView(permission.ResourcePermissionRequiredMixin, BaseDatatableView): model = IAmPrincipal columns = ["id", "first_name", "email", "is_active"] order_columns = ["id", "first_name", "email", "is_active"] def get_initial_queryset(self): deleted_flag = self.request.GET.get("deleted_flag", False) return self.model.objects.filter( deleted=deleted_flag, principal_type__name=iam_constant.PRINCIPAL_TYPE_SUBADMIN, ) def render_column(self, row, column): if column == "principal_type_name": return row.principal_type.name if row.principal_type else None 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): search_value = self.request.GET.get("search[value]", None) if search_value: qs = qs.filter( Q(id__icontains=search_value) | Q(first_name__icontains=search_value) | Q(email__icontains=search_value) ) return qs class PrincipalGroupLinkEditView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.View): page_name = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP resource = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP model = IAmPrincipal template_name = "module_iam/iam_principal_group_link_edit.html" form_class = IAmPrincipalGroupLinkForm 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 PrincipalGroupLinkActionView(generic.View): model = IAmPrincipal def post(self, request, *args, **kwargs): if self.model is None: raise NotImplementedError( "Subclasses of BaseActionView must define a 'model' attribute." ) action = request.POST.get("action") # 'archive', 'active', or 'unarchive' ids = request.POST.getlist("ids[]") # List of 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, is_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(is_active=active.capitalize()) message = "Record updated 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 PrincipalGroupView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.TemplateView): page_name = iam_constant.RESOURCE_IAM_GROUP resource = iam_constant.RESOURCE_IAM_GROUP model = IAmPrincipalGroup template_name = "module_iam/iam_group.html" def get_context_data(self, **kwargs) -> dict[str, Any]: context = super().get_context_data(**kwargs) context["page_name"] = self.page_name return context class PrincipalGroupListJsonView(BaseDatatableView): model = IAmPrincipalGroup columns = ["id", "name", "active"] order_columns = ["id", "name", "active"] def get_initial_queryset(self): deleted_flag = self.request.GET.get("deleted_flag", False) return self.model.objects.filter(deleted=deleted_flag) def render_column(self, row, column): if column == "roles": return [{"name": role.name} for role in row.role.all()] return super().render_column(row, column) def filter_queryset(self, qs): search_value = self.request.GET.get("search[value]", None) if search_value: qs = qs.filter( Q(id__icontains=search_value) | Q(name__icontains=search_value) ) return qs class PrincipalGroupCreateOrUpdateView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.View): page_name = iam_constant.RESOURCE_IAM_GROUP resource = iam_constant.RESOURCE_IAM_GROUP page_title = "Principal Group" model = IAmPrincipalGroup template_name = "module_iam/iam_group_add.html" form_class = IAmPrincipalGroupRoleLinkForm success_url = reverse_lazy("module_iam:principal_group") error_message = "An error occurred while saving the data." def get_success_message(self): self.success_message = ( constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED ) return self.success_message 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": "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() 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(): 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 PrincipalGroupActionView(ActionMixin): model = IAmPrincipalGroup class PrincipalGroupArchiveView(PrincipalGroupView): template_name = "module_iam/iam_group_archive_list.html" class AppRoleView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.TemplateView): page_name = iam_constant.RESOURCE_IAM_ROLE resource = iam_constant.RESOURCE_IAM_ROLE model = IAmRole template_name = "module_iam/iam_role.html" def get_context_data(self, **kwargs) -> dict[str, Any]: context = super().get_context_data(**kwargs) context["page_name"] = self.page_name return context class AppRoleListJsonView(BaseDatatableView): model = IAmRole columns = ["id", "name", "active", "resources"] order_columns = ["id", "name"] def get_initial_queryset(self): deleted_flag = self.request.GET.get("deleted_flag", False) return ( super(AppRoleListJsonView, self) .get_initial_queryset() .prefetch_related( "app_resource_action", "app_resource_action__app_resource", "app_resource_action__app_action", ) .filter(deleted=deleted_flag) ) def render_column(self, row, column): if column == "resources": resources = {} # Loop through all the app_resource_action links for the current ro for link in row.app_resource_action.all(): resource = link.app_resource.name action = link.app_action.name # If the resource is already in the dictionary, append the action to the list of actions if resource in resources: resources[resource].append(action) # Otherwise, add the resource to the dictionary with a list containing the action else: resources[resource] = [action] return resources return super().render_column(row, column) def filter_queryset(self, qs): search_value = self.request.GET.get("search[value]", None) if search_value: qs = qs.filter( Q(id__icontains=search_value) | Q(name__icontains=search_value) | Q(app_resource_action__app_resource__name__icontains=search_value) | Q(app_resource_action__app_action__name__icontains=search_value) ) return qs class AppRoleCreateOrUpdateView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.View): page_name = iam_constant.RESOURCE_IAM_ROLE resource = iam_constant.RESOURCE_IAM_ROLE model = IAmRole template_name = "module_iam/iam_role_add.html" form_class = IAmPrincipalRoleAppResourceActionLinkForm success_url = reverse_lazy("module_iam:role") success_message = "Saved Successfully" error_message = "An error occurred while saving the data." def get_success_message(self): self.success_message = ( f"Record {'Created' if not self.object else 'Updated'} Successfully" ) return self.success_message 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": "Add" if not self.object else "Edit", "app_resource_action": IAmAppResourceActionLink.objects.generate_app_resource_action_data(), } context.update(kwargs) # Include any additional context data passed to the view return context def get(self, request, *args, **kwargs): try: 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) except Exception as e: messages.error(request, str(e)) return redirect(self.success_url) def post(self, request, *args, **kwargs): try: self.object = self.get_object() 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) except Exception as e: messages.error(self.request, str(e)) return redirect(self.success_url) class AppRoleActionView(LoginRequiredMixin, ActionMixin): model = IAmRole class AppRoleArchiveView(AppRoleView): template_name = "module_iam/iam_role_archive.html" class PrincipalProfileView( LoginRequiredMixin, generic.TemplateView): page_name = iam_constant.RESOURCE_MANAGE_DASHBOARD resource = iam_constant.RESOURCE_MANAGE_DASHBOARD model = IAmPrincipal template_name = "module_iam/profile_details.html" def get_object(self, queryset=None): user = self.request.user.id return get_object_or_404( self.model.objects.select_related("principal_type", "principal_source"), pk=user, ) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["page_name"] = self.page_name context["data_obj"] = self.get_object() return context class PrincipalProfileEditView(LoginRequiredMixin, generic.View): page_name = iam_constant.RESOURCE_MANAGE_DASHBOARD resource = iam_constant.RESOURCE_MANAGE_DASHBOARD model = IAmPrincipal template_name = "module_iam/profile_details_edit.html" form_class = ProfileEditForm success_url = reverse_lazy("module_iam:profile_details") success_message = "Saved Successfully" error_message = "An error occurred while saving the data." def get_success_message(self): self.success_message = ( f"Record {'Created' if not self.object else 'Updated'} Successfully" ) return self.success_message def get_object(self): return self.request.user def get_context_data(self, **kwargs): context = { # "page_name": self.page_name, "operation": "Edit", "page_name": self.page_name, } context.update(kwargs) # Include any additional context data passed to the view return context def get(self, request, *args, **kwargs): # try: 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, request.FILES, 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)