diff --git a/config/settings/base.py b/config/settings/base.py index 1b1b024e..ffd4a3a3 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -115,20 +115,19 @@ TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - str(BASE_DIR / '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', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [str(BASE_DIR / "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", # ctx processeor to attach institute data in templates - 'context_processors.dj_sms_context_processor.attach_institute_data_ctx_processor', - 'context_processors.dj_sms_context_processor.attach_urls_for_common_templates', + "context_processors.attach_resources.attach_institute_data_ctx_processor", + "context_processors.attach_resources.attach_urls_for_common_templates", + "context_processors.attach_resources.attach_dashboard_menu_items", ], }, }, diff --git a/context_processors/dj_sms_context_processor.py b/context_processors/attach_resources.py similarity index 59% rename from context_processors/dj_sms_context_processor.py rename to context_processors/attach_resources.py index 2a1b6e2b..03bbaec6 100644 --- a/context_processors/dj_sms_context_processor.py +++ b/context_processors/attach_resources.py @@ -1,8 +1,12 @@ from django_school_management.academics.constants import AcademicsURLConstants from django_school_management.accounts.constants import AccountURLConstants -from django_school_management.institute.models import (InstituteProfile, - TextWidget, ListWidget - ) +from django_school_management.accounts.services.menu import MenuService +from django_school_management.accounts.utils.menu_config import MENU_CONFIG +from django_school_management.institute.models import ( + InstituteProfile, + TextWidget, + ListWidget, +) from django_school_management.articles.models import Category @@ -12,33 +16,33 @@ def attach_institute_data_ctx_processor(request): except: institute = None ctx = { - 'request_institute': institute, + "request_institute": institute, } - if 'articles' in request.resolver_match._func_path.split('.'): + if "articles" in request.resolver_match._func_path.split("."): # If request is coming for articles app's views, # only then pass registered_navlinks in the context. # Registered navlinks are Category objects from the articles app. try: registered_navlinks = Category.objects.filter( display_on_menu=True, - ).order_by('-created') + ).order_by("-created") except: registered_navlinks = [] try: - # Setting up widgets for footer and other places of the + # Setting up widgets for footer and other places of the # website. (TODO: Add location hints to the widget models). # first text-widget for the footer (footer-col-number-1) - first_widget = TextWidget.objects.get(widget_number=0) + first_widget = TextWidget.objects.get(widget_number=0) # get rest three widgets list_widgets = ListWidget.objects.filter(widget_number__lte=4) except (TextWidget.DoesNotExist, Exception): first_widget = None list_widgets = None - ctx['registered_navlinks'] = registered_navlinks - ctx['first_widget'] = first_widget - ctx['list_widgets'] = list_widgets + ctx["registered_navlinks"] = registered_navlinks + ctx["first_widget"] = first_widget + ctx["list_widgets"] = list_widgets return ctx @@ -47,3 +51,15 @@ def attach_urls_for_common_templates(request): account_urls=AccountURLConstants, academic_urls=AcademicsURLConstants, ) + + +def attach_dashboard_menu_items(request): + menu_service = MenuService(MENU_CONFIG) + return dict( + student_menu_items=menu_service.get_menu_items( + "student", request.user + ), + teacher_menu_items=menu_service.get_menu_items( + "teacher", request.user + ), + ) diff --git a/django_school_management/accounts/services/auth.py b/django_school_management/accounts/services/auth.py index f21f91a9..f44a54b3 100644 --- a/django_school_management/accounts/services/auth.py +++ b/django_school_management/accounts/services/auth.py @@ -1,6 +1,14 @@ -from django_school_management.accounts.constants import ProfileApprovalStatusEnum, AccountTypesEnum -from django_school_management.accounts.models import CommonUserProfile, CustomGroup -from django_school_management.accounts.request_context import RequestUserContext +from django_school_management.accounts.constants import ( + ProfileApprovalStatusEnum, + AccountTypesEnum, +) +from django_school_management.accounts.models import ( + CommonUserProfile, + CustomGroup, +) +from django_school_management.accounts.request_context import ( + RequestUserContext, +) def create_profile_for_approved_account(user): @@ -18,8 +26,15 @@ def handle_superuser_creation(user): def assign_role_based_groups(user): - already_assigned_to_group = user.groups.filter(name=user.requested_role).exists() - if user.approval_status == ProfileApprovalStatusEnum.approved.value and not already_assigned_to_group: + already_assigned_to_group = user.groups.filter( + name=user.requested_role + ).exists() + if ( + user.approval_status == ProfileApprovalStatusEnum.approved.value + and not already_assigned_to_group + ): group_creator = RequestUserContext.get_current_user() - group, created = CustomGroup.objects.get_or_create(name=user.requested_role, group_creator=group_creator) + group, created = CustomGroup.objects.get_or_create( + name=user.requested_role, group_creator=group_creator + ) user.groups.add(group) diff --git a/django_school_management/accounts/services/menu.py b/django_school_management/accounts/services/menu.py new file mode 100644 index 00000000..2ca82d9e --- /dev/null +++ b/django_school_management/accounts/services/menu.py @@ -0,0 +1,44 @@ +from django.urls import reverse +from django_school_management.accounts.models import User +from typing import List, Dict, Any, Set + + +class MenuService: + def __init__(self, menu_config: Dict[str, Any]): + """ + Initialize the MenuService with the given configuration. + """ + self.menu_config = menu_config + + def get_user_groups(self, user: User) -> Set[str]: + """ + Retrieve the groups of the user as a set. + """ + return set(user.groups.values_list("name", flat=True)) + + def add_urls_to_menu( + self, menu_items: List[Dict[str, str]], urls: List[Dict[str, str]] + ) -> None: + """ + Add reversed URLs and titles to the menu_items list. + """ + for url in urls: + menu_items.append( + {"url": reverse(url["name"]), "title": url["title"]} + ) + + def get_menu_items(self, section: str, user: User) -> List[Dict[str, str]]: + """ + Retrieve menu items for a given section based on the user's groups. + """ + user_groups = self.get_user_groups(user) + menu_items = [] + + if section not in self.menu_config: + return menu_items + + for entry in self.menu_config[section]: + if user_groups & entry["groups"]: + self.add_urls_to_menu(menu_items, entry["urls"]) + + return menu_items diff --git a/django_school_management/accounts/utils/menu_config.py b/django_school_management/accounts/utils/menu_config.py new file mode 100644 index 00000000..81683680 --- /dev/null +++ b/django_school_management/accounts/utils/menu_config.py @@ -0,0 +1,76 @@ +from django_school_management.students.constants import StudentsURLConstants +from django_school_management.teachers.constants import TeachersURLConstants + + +all_teachers = dict(name=TeachersURLConstants.all_teacher, title="Teachers") +articles_manage = dict(name="articles:dashboard_manage", title="Articles") +teacher_designations = dict(name=TeachersURLConstants.designations, title="Designations") +write_article = dict( + name="articles:publish_article_from_dashboard", + title="Write Article", +) + +add_student = { + "name": StudentsURLConstants.add_student, + "title": "New Application", +} +all_students = {"name": StudentsURLConstants.all_student, "title": "Students List"} +add_result = {"name": "result:result_entry", "title": "Add Result"} +results_index = {"name": "result:result_home", "title": "Results"} +alumnus = {"name": StudentsURLConstants.alumnus, "title": "Alumnus"} + +MENU_CONFIG = { + "student": [ + { + "groups": {"teacher", "admin"}, + "urls": [ + all_students, + add_result, + ], + }, + { + "groups": {"student", "teacher", "admin"}, + "urls": [ + results_index, + alumnus, + ], + }, + { + "groups": {"academic_officer"}, + "urls": [ + add_student, + all_students, + alumnus, + ], + }, + ], + "teacher": [ + { + "groups": {"teacher", "admin"}, + "urls": [ + write_article, + all_teachers, + ], + }, + { + "groups": {"student"}, + "urls": [all_teachers], + }, + { + "groups": {"editor"}, + "urls": [ + all_teachers, + articles_manage, + write_article, + ], + }, + { + "groups": {"academic_officer"}, + "urls": [all_teachers], + }, + { + "groups": {"admin"}, + "urls": [teacher_designations], + }, + ], +} diff --git a/django_school_management/students/constants.py b/django_school_management/students/constants.py new file mode 100644 index 00000000..a515c930 --- /dev/null +++ b/django_school_management/students/constants.py @@ -0,0 +1,55 @@ +from enum import Enum + + +class StudentsURLEnums(Enum): + students_dashboard_index = "" + test_pdf = "pdf/" + add_student = "add/" + all_student = "all/" + alumnus = "alumnus/" + all_applicants = "applicants/" + unpaid_registrants = "applicants/unpaid/" + mark_as_paid_or_unpaid = "applicants/mark-paid/" + add_counseling_data = "add-counsel-data//" + admitted_student_list = "admitted-students/" + admission_confirmation = "admission-confirm/" + get_json_batch_data = "api/batches//" + yearly_graph_api = "api/yearly-graph/" + admit_student = "online-applicants//admit/" + paid_registrants = "paid-registrants/" + rejected_registrants = "rejected-registrants/" + update_online_registrant = "update-registrant//" + update_student = "update//" + student_details = "/detail/" + delete_student = "/delete/" + students_by_dept = "/students/" + counsel_monthly_report = "counsel-report/" + counsel_monthly_report_typed = "counsel-report//" + counsel_report_monthly_with_date = "counsel-report///" + + +class StudentsURLConstants: + students_dashboard_index = f"students:{StudentsURLEnums.students_dashboard_index.name}" + test_pdf = f"students:{StudentsURLEnums.test_pdf.name}" + add_student = f"students:{StudentsURLEnums.add_student.name}" + all_student = f"students:{StudentsURLEnums.all_student.name}" + alumnus = f"students:{StudentsURLEnums.alumnus.name}" + all_applicants = f"students:{StudentsURLEnums.all_applicants.name}" + unpaid_registrants = f"students:{StudentsURLEnums.unpaid_registrants.name}" + mark_as_paid_or_unpaid = f"students:{StudentsURLEnums.mark_as_paid_or_unpaid.name}" + add_counseling_data = f"students:{StudentsURLEnums.add_counseling_data.name}" + admitted_student_list = f"students:{StudentsURLEnums.admitted_student_list.name}" + admission_confirmation = f"students:{StudentsURLEnums.admission_confirmation.name}" + get_json_batch_data = f"students:{StudentsURLEnums.get_json_batch_data.name}" + yearly_graph_api = f"students:{StudentsURLEnums.yearly_graph_api.name}" + admit_student = f"students:{StudentsURLEnums.admit_student.name}" + paid_registrants = f"students:{StudentsURLEnums.paid_registrants.name}" + rejected_registrants = f"students:{StudentsURLEnums.rejected_registrants.name}" + update_online_registrant = f"students:{StudentsURLEnums.update_online_registrant.name}" + update_student = f"students:{StudentsURLEnums.update_student.name}" + student_details = f"students:{StudentsURLEnums.student_details.name}" + delete_student = f"students:{StudentsURLEnums.delete_student.name}" + students_by_dept = f"students:{StudentsURLEnums.students_by_dept.name}" + counsel_monthly_report = f"students:{StudentsURLEnums.counsel_monthly_report.name}" + counsel_monthly_report_typed = f"students:{StudentsURLEnums.counsel_monthly_report_typed.name}" + counsel_report_monthly_with_date = f"students:{StudentsURLEnums.counsel_report_monthly_with_date.name}" diff --git a/django_school_management/students/urls.py b/django_school_management/students/urls.py index fe9ef280..538fb931 100644 --- a/django_school_management/students/urls.py +++ b/django_school_management/students/urls.py @@ -2,66 +2,148 @@ from django.urls import path, register_converter +from django_school_management.students.constants import StudentsURLEnums + from .views import students_views as views from .views import pdf_views from .views import report_views -app_name = 'students' +app_name = "students" class DateConverter: - # Convert a string passed as date in datetime object - regex = '\d{4}-\d{2}-\d{2}' + # Convert a string passed as date in datetime object + regex = "\d{4}-\d{2}-\d{2}" + + def to_python(self, value): + return datetime.strptime(value, "%Y-%m-%d") + + def to_url(self, value): + return value - def to_python(self, value): - return datetime.strptime(value, '%Y-%m-%d') - - def to_url(self, value): - return value -register_converter(DateConverter, 'date') +register_converter(DateConverter, "date") urlpatterns = [ - path('', views.students_dashboard_index, - name='students_dashboard_index'), - # TEST PDF PRINTER - path('pdf/', pdf_views.test_pdf, name='test_pdf'), - path('add/', views.add_student_view, name='add_student'), - path('all/', views.students_view, name='all_student'), - path('alumnus/', views.AlumnusListView.as_view(), name='alumnus'), - path('applicants/', views.all_applicants, name='all_applicants'), - path('applicants/unpaid/', views.unpaid_registrants, name='unpaid_registrants'), - path('applicants/unpaid/mark-paid/', views.mark_as_paid_or_unpaid, - name='mark_as_paid_or_unpaid'), - path('add-counsel-data//', views.add_counseling_data, - name='add_counseling_data'), - path('admitted-students/', views.admitted_students_list, - name='admitted_student_list'), - path('admission-confirm/', views.admission_confirmation, - name='admission_confirmation'), - path('api/batches//', views.get_json_batch_data, - name='get_json_batch_data'), - path('api/yearly-graph/', report_views.yearly_graph_api, - name='yearly_graph_api'), - path('online-applicants//admit/', views.admit_student, - name='admit_student'), - path('paid-registrants/', views.paid_registrants, - name='paid_registrants'), - path('rejected-registrants/', views.rejected_registrants, - name='rejected_registrants'), - path('update-registrant//', views.update_online_registrant, - name='update_online_registrant'), - path('update//', views.StudentUpdateView.as_view(), - name='update_student'), - path('/detail/', views.StudentDetailsView.as_view(), - name='student_details'), - path('/delete/', views.student_delete_view, name='delete_student'), - path('/students/', views.students_by_department_view, - name='students_by_dept'), - path('counsel-report/', report_views.counsel_monthly_report, name='counsel_monthly_report'), - path('counsel-report//', report_views.counsel_monthly_report, - name='counsel_monthly_report_typed'), - path('counsel-report///', - report_views.counsel_monthly_report, - name='counsel_report_monthly_with_date'), + path( + StudentsURLEnums.students_dashboard_index.value, + views.students_dashboard_index, + name=StudentsURLEnums.students_dashboard_index.name, + ), + # TEST PDF PRINTER + path( + StudentsURLEnums.test_pdf.value, + pdf_views.test_pdf, + name=StudentsURLEnums.test_pdf.name, + ), + path( + StudentsURLEnums.add_student.value, + views.add_student_view, + name=StudentsURLEnums.add_student.name, + ), + path( + StudentsURLEnums.all_student.value, + views.students_view, + name=StudentsURLEnums.all_student.name, + ), + path( + StudentsURLEnums.alumnus.value, + views.AlumnusListView.as_view(), + name=StudentsURLEnums.alumnus.name, + ), + path( + StudentsURLEnums.all_applicants.value, + views.all_applicants, + name=StudentsURLEnums.all_applicants.name, + ), + path( + StudentsURLEnums.unpaid_registrants.value, + views.unpaid_registrants, + name=StudentsURLEnums.unpaid_registrants.name, + ), + path( + StudentsURLEnums.mark_as_paid_or_unpaid.value, + views.mark_as_paid_or_unpaid, + name=StudentsURLEnums.mark_as_paid_or_unpaid.name, + ), + path( + StudentsURLEnums.add_counseling_data.value, + views.add_counseling_data, + name=StudentsURLEnums.add_counseling_data.name, + ), + path( + StudentsURLEnums.admitted_student_list.value, + views.admitted_students_list, + name=StudentsURLEnums.admitted_student_list.name, + ), + path( + StudentsURLEnums.admission_confirmation.value, + views.admission_confirmation, + name=StudentsURLEnums.admission_confirmation.name, + ), + path( + StudentsURLEnums.get_json_batch_data.value, + views.get_json_batch_data, + name=StudentsURLEnums.get_json_batch_data.name, + ), + path( + StudentsURLEnums.yearly_graph_api.value, + report_views.yearly_graph_api, + name=StudentsURLEnums.yearly_graph_api.name, + ), + path( + StudentsURLEnums.admit_student.value, + views.admit_student, + name=StudentsURLEnums.admit_student.name, + ), + path( + StudentsURLEnums.paid_registrants.value, + views.paid_registrants, + name=StudentsURLEnums.paid_registrants.name, + ), + path( + StudentsURLEnums.rejected_registrants.value, + views.rejected_registrants, + name=StudentsURLEnums.rejected_registrants.name, + ), + path( + StudentsURLEnums.update_online_registrant.value, + views.update_online_registrant, + name=StudentsURLEnums.update_online_registrant.name, + ), + path( + StudentsURLEnums.update_student.value, + views.StudentUpdateView.as_view(), + name=StudentsURLEnums.update_student.name, + ), + path( + StudentsURLEnums.student_details.value, + views.StudentDetailsView.as_view(), + name=StudentsURLEnums.student_details.name, + ), + path( + StudentsURLEnums.delete_student.value, + views.student_delete_view, + name=StudentsURLEnums.delete_student.name, + ), + path( + StudentsURLEnums.students_by_dept.value, + views.students_by_department_view, + name=StudentsURLEnums.students_by_dept.name, + ), + path( + StudentsURLEnums.counsel_monthly_report.value, + report_views.counsel_monthly_report, + name=StudentsURLEnums.counsel_monthly_report.name, + ), + path( + StudentsURLEnums.counsel_monthly_report_typed.value, + report_views.counsel_monthly_report, + name=StudentsURLEnums.counsel_monthly_report_typed.name, + ), + path( + StudentsURLEnums.counsel_report_monthly_with_date.value, + report_views.counsel_monthly_report, + name=StudentsURLEnums.counsel_report_monthly_with_date.name, + ), ] diff --git a/django_school_management/teachers/constants.py b/django_school_management/teachers/constants.py new file mode 100644 index 00000000..e96841cd --- /dev/null +++ b/django_school_management/teachers/constants.py @@ -0,0 +1,21 @@ +from enum import Enum + + +class TeachersURLEnums(Enum): + all_teacher = 'all/' + add_teacher = 'add/' + teacher_details = '/details/' + update_teacher = '/update/' + delete_teacher = '/delete/' + designations = 'designations/' + create_designation = 'create/designation/' + + +class TeachersURLConstants: + all_teacher = f"teachers:{TeachersURLEnums.all_teacher.name}" + add_teacher = f"teachers:{TeachersURLEnums.add_teacher.name}" + teacher_details = f"teachers:{TeachersURLEnums.teacher_details.name}" + update_teacher = f"teachers:{TeachersURLEnums.update_teacher.name}" + delete_teacher = f"teachers:{TeachersURLEnums.delete_teacher.name}" + designations = f"teachers:{TeachersURLEnums.designations.name}" + create_designation = f"teachers:{TeachersURLEnums.create_designation.name}" diff --git a/django_school_management/teachers/urls.py b/django_school_management/teachers/urls.py index 3240ce8f..2fc18b36 100644 --- a/django_school_management/teachers/urls.py +++ b/django_school_management/teachers/urls.py @@ -1,18 +1,45 @@ from django.urls import path + +from django_school_management.teachers.constants import TeachersURLEnums from . import views -app_name = 'teachers' +app_name = "teachers" urlpatterns = [ - path('all/', views.teachers_view, name='all_teacher'), - path('add/', views.add_teacher_view, name='add_teacher'), - path('/details/', views.teacher_detail_view, name='teacher_details'), - path('/update/', views.teacher_update_view.as_view(), - name='update_teacher'), - path('/delete/', views.teacher_delete_view, name='delete_teacher'), - path('designations/', views.designation_list_view.as_view(), - name='designations'), - path('create/designation/', views.create_designation, - name='create_designation') + path( + TeachersURLEnums.all_teacher.value, + views.teachers_view, + name=TeachersURLEnums.all_teacher.name, + ), + path( + TeachersURLEnums.add_teacher.value, + views.add_teacher_view, + name=TeachersURLEnums.add_teacher.value, + ), + path( + TeachersURLEnums.teacher_details.value, + views.teacher_detail_view, + name=TeachersURLEnums.teacher_details.name, + ), + path( + TeachersURLEnums.update_teacher.value, + views.teacher_update_view.as_view(), + name=TeachersURLEnums.update_teacher.name, + ), + path( + TeachersURLEnums.delete_teacher.value, + views.teacher_delete_view, + name=TeachersURLEnums.delete_teacher.name, + ), + path( + TeachersURLEnums.designations.value, + views.designation_list_view.as_view(), + name=TeachersURLEnums.designations.name, + ), + path( + TeachersURLEnums.create_designation.value, + views.create_designation, + name=TeachersURLEnums.create_designation.name, + ), ] diff --git a/templates/_sidebar.html b/templates/_sidebar.html index 8572e9a1..6bf5a707 100644 --- a/templates/_sidebar.html +++ b/templates/_sidebar.html @@ -73,33 +73,13 @@ @@ -111,41 +91,17 @@ - {% if user.approval_status == 'a' and user|has_role:'academic_officer, admin' %} + {% if user|has_role:'academic_officer, admin' %} + {% endif %} - {% if user.approval_status == 'a' and user|has_role:'accounts, admin' %} + {% if user|has_role:'accounts, admin' %} {% endif %} - {% if user.approval_status == 'a' and user|has_role:'admin' %} + {% if user|has_role:'admin' %}