From a9540b0b96b266f70a0fbdc4cc844e4084e77ee4 Mon Sep 17 00:00:00 2001 From: Horilla Date: Thu, 11 Apr 2024 12:34:55 +0530 Subject: [PATCH] [ADD] PMS: Employee objective filter and search methods --- pms/filters.py | 49 +++ pms/forms.py | 72 ++-- pms/models.py | 37 +- .../okr/emp_objective/emp_objective_list.html | 13 +- .../okr/emp_objective/emp_objective_nav.html | 227 ++++++++++++ pms/templates/okr/okr_detailed_view.html | 3 +- pms/urls.py | 13 +- pms/views.py | 338 ++++++++++++++---- 8 files changed, 608 insertions(+), 144 deletions(-) create mode 100644 pms/templates/okr/emp_objective/emp_objective_nav.html diff --git a/pms/filters.py b/pms/filters.py index 3288b7384..5ccf6a95a 100644 --- a/pms/filters.py +++ b/pms/filters.py @@ -265,3 +265,52 @@ class ObjectiveReGroup: ("employee_id", "Owner"), ("status", "Status"), ] + +class EmployeeObjectiveFilter(FilterSet): + """ + Filter through EmployeeObjective model + """ + + search = django_filters.CharFilter(method="search_method") + start_date_from = django_filters.DateFilter( + field_name="start_date", + lookup_expr="gte", + widget=forms.DateInput(attrs={"type": "date"}), + ) + start_date_till = django_filters.DateFilter( + field_name="start_date", + lookup_expr="lte", + widget=forms.DateInput(attrs={"type": "date"}), + ) + end_date_from = django_filters.DateFilter( + field_name="end_date", + lookup_expr="gte", + widget=forms.DateInput(attrs={"type": "date"}), + ) + end_date_till = django_filters.DateFilter( + field_name="end_date", + lookup_expr="lte", + widget=forms.DateInput(attrs={"type": "date"}), + ) + class Meta: + model = EmployeeObjective + fields = [ + "status", + "archive", + "key_result_id", + 'start_date', + 'end_date', + 'employee_id', + ] + + def search_method(self, queryset, _, value: str): + """ + This method is used to search employees and objective + """ + values = value.split(" ") + empty = queryset.model.objects.none() + for split in values: + empty = empty | (queryset.filter(employee_id__employee_first_name__icontains=split))|(queryset.filter(employee_id__employee_last_name__icontains=split)) + + + return empty.distinct() \ No newline at end of file diff --git a/pms/forms.py b/pms/forms.py index 250b5079f..87633ca47 100644 --- a/pms/forms.py +++ b/pms/forms.py @@ -115,27 +115,25 @@ class ObjectiveForm(BaseForm): "managers", "description", "duration", - 'key_result_id', + "key_result_id", "add_assignees", "assignees", - # 'period', "start_date", - # 'end_date', - # 'archive', ] + exclude = ["is_active"] widgets = { - "key_result_id":forms.SelectMultiple( + "key_result_id": forms.SelectMultiple( attrs={ "class": "oh-select oh-select-2 select2-hidden-accessible", "onchange": "keyResultChange($(this))", } ), - # "start_date": forms.DateInput( - # attrs={"class": "oh-input w-100", "type": "date"} - # ), - # "end_date": forms.DateInput( - # attrs={"class": "oh-input w-100", "type": "date"} - # ), + # "start_date": forms.DateInput( + # attrs={"class": "oh-input w-100", "type": "date"} + # ), + # "end_date": forms.DateInput( + # attrs={"class": "oh-input w-100", "type": "date"} + # ), } def __init__(self, *args, **kwargs): @@ -285,6 +283,7 @@ class EmployeeObjectiveForm(BaseForm): "status", "archive", ] + exclude = ["is_active"] widgets = { "objective_id": forms.HiddenInput(), "start_date": forms.DateInput( @@ -302,7 +301,6 @@ class EmployeeObjectiveForm(BaseForm): del self.fields["key_result_id"] except Exception as _err: pass - def as_p(self): """ @@ -402,6 +400,7 @@ class KRForm(MF): exclude = [ "history", "objects", + "is_active", ] def as_p(self): @@ -411,33 +410,32 @@ class KRForm(MF): context = {"form": self} table_html = render_to_string("common_form.html", context) return table_html - + def clean(self): cleaned_data = super().clean() - duration = cleaned_data.get('duration') - target_value = cleaned_data.get('target_value') - progress_type = cleaned_data.get('progress_type') + duration = cleaned_data.get("duration") + target_value = cleaned_data.get("target_value") + progress_type = cleaned_data.get("progress_type") - if duration is None or duration == '': - raise ValidationError({ - 'duration':'This field is required' - }) - if target_value is None or target_value == '': - raise ValidationError({ - 'target_value':'This field is required' - }) + if duration is None or duration == "": + raise ValidationError({"duration": "This field is required"}) + if target_value is None or target_value == "": + raise ValidationError({"target_value": "This field is required"}) if duration <= 0: - raise ValidationError({ - 'duration':'Duration cannot be less than or equal to zero' - }) + raise ValidationError( + {"duration": "Duration cannot be less than or equal to zero"} + ) if target_value <= 0: - raise ValidationError({ - 'target_value':'Duration cannot be less than or equal to zero' - }) - if progress_type == '%' and target_value > 100 : - raise ValidationError({ - 'target_value':'Target value cannot be greater than hundred for progress type "percentage"' - }) + raise ValidationError( + {"target_value": "Duration cannot be less than or equal to zero"} + ) + if progress_type == "%" and target_value > 100: + raise ValidationError( + { + "target_value": 'Target value cannot be greater than hundred for progress type "percentage"' + } + ) + class KeyResultForm(ModelForm): """ @@ -615,7 +613,7 @@ class FeedbackForm(ModelForm): model = Feedback fields = "__all__" - exclude = ["status", "archive"] + exclude = ["status", "archive", "is_active"] widgets = { "review_cycle": forms.TextInput( @@ -753,6 +751,7 @@ class QuestionTemplateForm(ModelForm): model = QuestionTemplate fields = "__all__" + exclude = ["is_active"] def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) @@ -816,7 +815,7 @@ class QuestionForm(ModelForm): """ model = Question - exclude = ["question_option_id", "template_id"] + exclude = ["question_option_id", "template_id", "is_active"] widgets = { "question_type": forms.Select( attrs={ @@ -890,6 +889,7 @@ class PeriodForm(ModelForm): model = Period fields = "__all__" + exclude = ["is_active"] widgets = { "period_name": forms.TextInput( attrs={"placeholder": "Q1.", "class": "oh-input w-100"} diff --git a/pms/models.py b/pms/models.py index d0dc0d365..c041944db 100644 --- a/pms/models.py +++ b/pms/models.py @@ -2,21 +2,19 @@ from django import forms from django.db import models from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ -from base.models import Company, Department, JobPosition -from base.horilla_company_manager import HorillaCompanyManager -from horilla_audit.methods import get_diff - -# importing simple history -from simple_history.models import HistoricalRecords -from employee.models import Employee -from horilla_audit.models import HorillaAuditLog, HorillaAuditInfo from django.core.validators import MinValueValidator from dateutil.relativedelta import relativedelta +from horilla.models import HorillaModel +from horilla_audit.methods import get_diff +from horilla_audit.models import HorillaAuditLog, HorillaAuditInfo +from base.models import Company, Department, JobPosition +from base.horilla_company_manager import HorillaCompanyManager +from employee.models import Employee """Objectives and key result section""" -class Period(models.Model): +class Period(HorillaModel): """this is a period model used for creating period""" period_name = models.CharField(max_length=150, unique=True) @@ -29,7 +27,7 @@ class Period(models.Model): return self.period_name -class KeyResult(models.Model): +class KeyResult(HorillaModel): """model used to create key results""" PROGRESS_CHOICES = ( @@ -71,7 +69,7 @@ class KeyResult(models.Model): return f"{self.title}" -class Objective(models.Model): +class Objective(HorillaModel): """Model used for creating objectives""" title = models.CharField( @@ -96,7 +94,6 @@ class Objective(models.Model): verbose_name="Default Key results", ) duration = models.IntegerField(default=1, validators=[MinValueValidator(0)]) - created_at = models.DateField(auto_now_add=True) add_assignees = models.BooleanField(default=False) archive = models.BooleanField(default=False, null=True, blank=True) history = HorillaAuditLog(bases=[HorillaAuditInfo]) @@ -122,7 +119,7 @@ class Objective(models.Model): return f"{self.title}" -class EmployeeObjective(models.Model): +class EmployeeObjective(HorillaModel): """this is a EmployObjective model used for creating Employee objectives""" STATUS_CHOICES = ( @@ -236,7 +233,7 @@ class Comment(models.Model): return f"{self.employee_id.employee_first_name} - {self.comment} " -class EmployeeKeyResult(models.Model): +class EmployeeKeyResult(models.Model): """employee key result creation""" PROGRESS_CHOICES = ( @@ -349,10 +346,10 @@ class EmployeeKeyResult(models.Model): # unique_together = ("key_result_id", "employee_objective_id") -"""360degree feedback section""" +"""360degree feedback section""" -class QuestionTemplate(models.Model): +class QuestionTemplate(HorillaModel): """question template creation""" question_template = models.CharField( @@ -366,7 +363,7 @@ class QuestionTemplate(models.Model): return self.question_template -class Question(models.Model): +class Question(HorillaModel): """question creation""" QUESTION_TYPE_CHOICE = ( @@ -393,7 +390,7 @@ class Question(models.Model): return self.question -class QuestionOptions(models.Model): +class QuestionOptions(HorillaModel): """options for question""" question_id = models.ForeignKey( @@ -410,7 +407,7 @@ class QuestionOptions(models.Model): objects = HorillaCompanyManager("question_id__template_id__company_id") -class Feedback(models.Model): +class Feedback(HorillaModel): """feedback model for creating feedback""" STATUS_CHOICES = ( @@ -456,7 +453,6 @@ class Feedback(models.Model): status = models.CharField( max_length=50, choices=STATUS_CHOICES, default="Not Started" ) - created_at = models.DateField(auto_now_add=True) archive = models.BooleanField(null=True, blank=True, default=False) start_date = models.DateField(null=False, blank=False) end_date = models.DateField(null=True, blank=False) @@ -549,6 +545,7 @@ class AnonymousFeedback(models.Model): max_length=10, null=True, blank=False, editable=False ) feedback_description = models.TextField(null=True, blank=True, max_length=255) + objects = models.Manager() def __str__(self) -> str: return f"Feedback based on a {self.based_on}" diff --git a/pms/templates/okr/emp_objective/emp_objective_list.html b/pms/templates/okr/emp_objective/emp_objective_list.html index 4e50c04bf..b0f9f9a37 100644 --- a/pms/templates/okr/emp_objective/emp_objective_list.html +++ b/pms/templates/okr/emp_objective/emp_objective_list.html @@ -1,12 +1,16 @@ {% load static i18n %} {% load i18n %} {% load widget_tweaks %} {% load basefilters %} {% load pmsfilters %} - -{% for emp_objective in objective.employee_objective.all %} +{% include "filter_tags.html" %} +{% for emp_objective in emp_objectives %} {% if perms.pms.view_employeeobjective or emp_objective|is_manager_or_owner:request.user %}
-
- + + {% endif %} {% if request.user|is_reportingmanager or perms.pms.add_employeekeyresult %} + +
+ + {% comment %}
+ + +
{% endcomment %} + + {% comment %}
+
+ + +
+
{% endcomment %} + + + {% comment %} {% if perms.pms.add_employeeobjective or request.user|filtersubordinates %} + + {% endif %} {% endcomment %} +
+
+ + \ No newline at end of file diff --git a/pms/templates/okr/okr_detailed_view.html b/pms/templates/okr/okr_detailed_view.html index 35137ed20..c990fda48 100644 --- a/pms/templates/okr/okr_detailed_view.html +++ b/pms/templates/okr/okr_detailed_view.html @@ -77,6 +77,7 @@ width: 200px; } +{% include "okr/emp_objective/emp_objective_nav.html" %}
@@ -127,7 +128,7 @@
{% for manager in objective.managers.all %} - + {% endfor %}
diff --git a/pms/urls.py b/pms/urls.py index 01903eeba..29b271062 100644 --- a/pms/urls.py +++ b/pms/urls.py @@ -70,6 +70,11 @@ urlpatterns = [ "objective-detailed-view-activity/", views.objective_detailed_view_activity, name="objective-detailed-view-activity", + ), + path( + "emp-objective-search/", + views.emp_objective_search, + name="emp-objective-search", ), path( "objective-manager-remove//", @@ -260,22 +265,22 @@ urlpatterns = [ name="add-anonymous-feedback", ), path( - "edit-anonymous-feedback//", + "edit-anonymous-feedback//", views.edit_anonymous_feedback, name="edit-anonymous-feedback", ), path( - "archive-anonymous-feedback//", + "archive-anonymous-feedback//", views.archive_anonymous_feedback, name="archive-anonymous-feedback", ), path( - "delete-anonymous-feedback//", + "delete-anonymous-feedback//", views.delete_anonymous_feedback, name="delete-anonymous-feedback", ), path( - "single-anonymous-feedback-view//", + "single-anonymous-feedback-view//", views.view_single_anonymous_feedback, name="single-anonymous-feedback-view", ), diff --git a/pms/views.py b/pms/views.py index de1055512..50c16880a 100644 --- a/pms/views.py +++ b/pms/views.py @@ -1,33 +1,38 @@ +""" +views.py + +This module contains the view functions for handling HTTP requests and rendering +responses in pms app. +""" import json import datetime from urllib.parse import parse_qs from itertools import tee -from django.http import HttpResponse, HttpResponseRedirect, JsonResponse -from django.db.utils import IntegrityError from django.db.models import Q -from django.forms import modelformset_factory from django.contrib import messages +from django.db.utils import IntegrityError from django.contrib.auth.models import User +from django.db.models import ProtectedError from django.core.paginator import Paginator +from django.forms import modelformset_factory from django.utils.translation import gettext_lazy as _ from django.shortcuts import get_object_or_404, render, redirect -from attendance.methods.group_by import group_by_queryset +from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from horilla.decorators import manager_can_enter, permission_required from horilla.decorators import login_required, hx_request_required from notifications.signals import notify from base.methods import get_key_instances, get_pagination, sortby from base.views import paginator_qry -from base.models import Department, JobPosition from employee.models import Employee, EmployeeWorkInformation from pms.filters import ( ActualKeyResultFilter, ActualObjectiveFilter, + EmployeeObjectiveFilter, KeyResultFilter, ObjectiveFilter, FeedbackFilter, ObjectiveReGroup, ) -from django.db.models import ProtectedError from pms.models import ( AnonymousFeedback, EmployeeKeyResult, @@ -58,7 +63,8 @@ from .forms import ( QuestionTemplateForm, ) -# objectives + +# objectives @login_required def objective_list_view(request): """ @@ -82,7 +88,7 @@ def objective_list_view(request): elif is_manager: # if user is a manager - employees_ids = [employee.id for employee in is_manager] + employees_ids = [employee.id for employee in is_manager] objective_all = EmployeeObjective.objects.filter(employee_id__in=employees_ids) objective_all = objective_all.distinct() context = objective_filter_pagination(request, objective_own, objective_all) @@ -94,7 +100,8 @@ def objective_list_view(request): context = objective_filter_pagination(request, objective_own, objective_all) return render(request, template, context) -def obj_form_save(request,objective_form): + +def obj_form_save(request, objective_form): """ This view is used to save objective form """ @@ -110,11 +117,14 @@ def obj_form_save(request,objective_form): objective_id=objective, employee_id=emp, start_date=start_date ) emp_objective.save() - # assiging default key result + # assigning default key result if default_krs: - for kr in default_krs: - emp_kr=EmployeeKeyResult( - employee_objective_id=emp_objective,key_result_id=kr,progress_type=kr.progress_type,target_value=kr.target_value + for key in default_krs: + emp_kr = EmployeeKeyResult( + employee_objective_id=emp_objective, + key_result_id=key, + progress_type=key.progress_type, + target_value=key.target_value, ) emp_kr.save() notify.send( @@ -128,6 +138,7 @@ def obj_form_save(request,objective_form): redirect=f"/pms/objective-detailed-view/{objective.id}", ) + @login_required @manager_can_enter(perm="pms.add_employeeobjective") def objective_creation(request): @@ -137,22 +148,27 @@ def objective_creation(request): GET: objective form, period, department, job position, employee, department POST: - Objective created, and returnes to key result creation function + Objective created, and returns to key result creation function """ employee = request.user.employee_get objective_form = ObjectiveForm(employee=employee) - if request.GET.get('key_result_id') is not None: + if request.GET.get("key_result_id") is not None: objective_form = ObjectiveForm(request.GET) if request.method == "POST": objective_form = ObjectiveForm(request.POST) if objective_form.is_valid(): - obj_form_save(request,objective_form) + obj_form_save(request, objective_form) return HttpResponse("") - context = {"objective_form": objective_form, "p_form": PeriodForm(),"k_form": KRForm()} + context = { + "objective_form": objective_form, + "p_form": PeriodForm(), + "k_form": KRForm(), + } return render(request, "okr/objective_creation.html", context=context) + @login_required @hx_request_required @manager_can_enter(perm="pms.change_employeeobjective") @@ -166,7 +182,7 @@ def objective_update(request, obj_id): """ instance = Objective.objects.get(id=obj_id) objective_form = ObjectiveForm(instance=instance) - if request.GET.get('key_result_id') is not None: + if request.GET.get("key_result_id") is not None: objective_form = ObjectiveForm(request.GET) if request.method == "POST": objective_form = ObjectiveForm(request.POST, instance=instance) @@ -204,14 +220,17 @@ def objective_update(request, obj_id): emp_obj.save() # assiging default key result if default_krs: - for kr in default_krs: - if not EmployeeKeyResult.objects.filter(employee_objective_id=emp_obj, key_result_id=kr).exists(): + for key in default_krs: + if not EmployeeKeyResult.objects.filter( + employee_objective_id=emp_obj, key_result_id=key + ).exists(): emp_kr = EmployeeKeyResult.objects.create( employee_objective_id=emp_obj, - key_result_id=kr, - progress_type=kr.progress_type, - target_value=kr.target_value + key_result_id=key, + progress_type=key.progress_type, + target_value=key.target_value, ) + emp_kr.save() notify.send( request.user.employee_get, @@ -228,10 +247,11 @@ def objective_update(request, obj_id): _("Objective %(objective)s Updated") % {"objective": instance}, ) return HttpResponse("") - context = {"objective_form": objective_form,"k_form": KRForm(), "update": True} + context = {"objective_form": objective_form, "k_form": KRForm(), "update": True} return render(request, "okr/objective_creation.html", context) + # key result @login_required @permission_required("pms.view_keyresult") @@ -239,12 +259,12 @@ def view_key_result(request): """ This method is used render template to view all the key result instances """ - krs=KeyResult.objects.all() + krs = KeyResult.objects.all() krs_filter = ActualKeyResultFilter(request.GET) krs = paginator_qry(krs, request.GET.get("page")) krs_ids = json.dumps([instance.id for instance in krs.object_list]) - context={ - 'krs':krs, + context = { + "krs": krs, "f": krs_filter, "krs_ids": krs_ids, } @@ -278,6 +298,7 @@ def filter_key_result(request): }, ) + @login_required def key_result_create(request): """ @@ -291,35 +312,54 @@ def key_result_create(request): instance = form.save() messages.success( request, - _("Key result %(key_result)s created successfully") % {"key_result": instance}, + _("Key result %(key_result)s created successfully") + % {"key_result": instance}, ) - if request.POST.get('dynamic_create'): + if request.POST.get("dynamic_create"): obj_data = request.POST.get("dyanamic_create") obj_data = obj_data.replace("create_new_key_result", str(instance.id)) - + # Redirect to the desired URL with encoded query parameters - redirect_url = f'/pms/objective-creation?{obj_data}' + redirect_url = f"/pms/objective-creation?{obj_data}" form = KRForm() - return render(request,'okr/key_result/key_result_form.html',{'k_form':form,'redirect_url':redirect_url}) + return render( + request, + "okr/key_result/key_result_form.html", + {"k_form": form, "redirect_url": redirect_url}, + ) + @login_required @permission_required("payroll.add_key_result") +def kr_create_or_update(request, kr_id=None): + """ + View function for creating or updating a Key Result. + + Parameters: + - request: HttpRequest object. + - kr_id: ID of the Key Result to update (optional). + + Returns: + Renders a form to create or update a Key Result. + """ + form = KRForm() + key_result = False def kr_create_or_update(request,kr_id=None): - print('----------kr_create_or_update-------------') form=KRForm() kr = False if kr_id is not None: - kr=KeyResult.objects.filter(id=kr_id).first() - form=KRForm(instance=kr) + key_result = KeyResult.objects.filter(id=kr_id).first() + form = KRForm(instance=key_result) if request.method == "POST": - if kr: - form = KRForm(request.POST, instance=kr) + if key_result: + form = KRForm(request.POST, instance=key_result) if form.is_valid(): instance = form.save() messages.success( request, - _("Key result %(key_result)s updated successfully") % {"key_result": instance}, + _("Key result %(key_result)s updated successfully") + % {"key_result": instance}, ) return HttpResponse("") @@ -329,15 +369,13 @@ def kr_create_or_update(request,kr_id=None): instance = form.save() messages.success( request, - _("Key result %(key_result)s created successfully") % {"key_result": instance}, + _("Key result %(key_result)s created successfully") + % {"key_result": instance}, ) return HttpResponse("") - else: - print('errors======',form.errors) - - print(form.instance) - print('-----------------------') + return render(request, "okr/key_result/real_kr_form.html", {"form": form}) + return render(request,'okr/key_result/real_kr_form.html',{'form':form}) @login_required @@ -368,14 +406,17 @@ def add_assignees(request, obj_id): # assiging default key result default_krs = objective.key_result_id.all() if default_krs: - for kr in default_krs: - if not EmployeeKeyResult.objects.filter(employee_objective_id=emp_obj, key_result_id=kr).exists(): + for key_result in default_krs: + if not EmployeeKeyResult.objects.filter( + employee_objective_id=emp_obj, key_result_id=key_result + ).exists(): emp_kr = EmployeeKeyResult.objects.create( employee_objective_id=emp_obj, - key_result_id=kr, - progress_type=kr.progress_type, - target_value=kr.target_value + key_result_id=key_result, + progress_type=key_result.progress_type, + target_value=key_result.target_value, ) + emp_kr.save() notify.send( request.user.employee_get, recipient=emp.employee_user_id, @@ -397,6 +438,7 @@ def add_assignees(request, obj_id): } return render(request, "okr/add_assignees.html", context) + @login_required @manager_can_enter(perm="pms.delete_employeeobjective") def objective_delete(request, obj_id): @@ -429,6 +471,17 @@ def objective_delete(request, obj_id): @login_required @permission_required("pms.change_objective") def objective_manager_remove(request, obj_id, manager_id): + """ + Removes a manager from an objective. + + Parameters: + - request: HttpRequest object. + - obj_id: ID of the Objective from which to remove the manager. + - manager_id: ID of the manager to be removed. + + Returns: + HttpResponse indicating success. + """ objective = get_object_or_404(Objective, id=obj_id) objective.managers.remove(manager_id) return HttpResponse("") @@ -437,6 +490,17 @@ def objective_manager_remove(request, obj_id, manager_id): @login_required @permission_required("pms.delete_keyresult") def key_result_remove(request, obj_id, kr_id): + """ + Removes a Key Result from an objective. + + Parameters: + - request: HttpRequest object. + - obj_id: ID of the Objective from which to remove the Key Result. + - kr_id: ID of the Key Result to be removed. + + Returns: + HttpResponse indicating success. + """ objective = get_object_or_404(Objective, id=obj_id) objective.key_result_id.remove(kr_id) return HttpResponse("") @@ -445,6 +509,17 @@ def key_result_remove(request, obj_id, kr_id): @login_required @manager_can_enter("pms.delete_employeeobjective") def assignees_remove(request, obj_id, emp_id): + """ + Removes an assignee from an objective. + + Parameters: + - request: HttpRequest object. + - obj_id: ID of the Objective from which to remove the assignee. + - emp_id: ID of the employee to be removed as an assignee. + + Returns: + HttpResponse indicating success. + """ objective = get_object_or_404(Objective, id=obj_id) get_object_or_404( EmployeeObjective, employee_id=emp_id, objective_id=obj_id @@ -499,7 +574,7 @@ def objective_filter_pagination(request, objective_own, objective_all): data_dict = parse_qs(previous_data) get_key_instances(EmployeeObjective, data_dict) context = { - 'manager':manager, + "manager": manager, "superuser": "true", "objectives": objectives, "own_objectives": objectives_own, @@ -514,7 +589,6 @@ def objective_filter_pagination(request, objective_own, objective_all): return context - @login_required # @hx_request_required def objective_list_search(request): @@ -627,7 +701,9 @@ def objective_detailed_view(request, obj_id, **kwargs): """ objective = Objective.objects.get(id=obj_id) - emp_objectives = EmployeeObjective.objects.filter(objective_id=objective) + emp_objectives = EmployeeObjective.objects.filter(objective_id=objective,archive=False) + + # key_results = EmployeeKeyResult.objects.filter(employee_objective_id=objective) # comments = Comment.objects.filter(employee_objective_id=emp_objectives) # key_results_all = objective.obj_id.all() @@ -635,7 +711,7 @@ def objective_detailed_view(request, obj_id, **kwargs): # total_kr = key_results_all.count() # try: # progress = int( - # sum(kr.progress_percentage for kr in key_results_all) / (total_kr) + # sum(key_result.progress_percentage for key_result in key_results_all) / (total_kr) # ) # except (ZeroDivisionError, TypeError): # progress = 0 @@ -655,6 +731,7 @@ def objective_detailed_view(request, obj_id, **kwargs): "objective_key_result_status": EmployeeKeyResult.STATUS_CHOICES, "comment_form": ObjectiveCommentForm, "current_date": now, + 'emp_obj_form':EmployeeObjectiveFilter(), } # return render(request, "okr/objective_detailed_view.html", context) return render(request, "okr/okr_detailed_view.html", context) @@ -728,15 +805,50 @@ def objective_detailed_view_comment(request, id): return redirect(objective_detailed_view_activity, id) +@login_required +# @hx_request_required +def emp_objective_search(request,obj_id): + """ + This view is used to to search employee objective,returns searched and filtered objects. + Returns: + All the filtered and searched object will based on userlevel. + """ + objective= Objective.objects.get(id=obj_id) + emp_objectives=objective.employee_objective.all() + search_val = request.GET.get("search") + if search_val is None: + search_val = "" + emp_objectives = EmployeeObjectiveFilter(request.GET,emp_objectives).qs + previous_data = request.GET.urlencode() + data_dict = parse_qs(previous_data) + get_key_instances(EmployeeObjective, data_dict) + context={ + 'emp_objectives':emp_objectives, + "filter_dict": data_dict, + } + template = "okr/emp_objective/emp_objective_list.html" + if request.GET.get("field") != "" and request.GET.get("field") is not None: + template = "okr/group_by.html" + return render(request, template, context) + @login_required def kr_table_view(request, emp_objective_id): - objective_id = request.GET.get("objective_id") + """ + Renders a table view of Key Results associated with an employee objective. + + Parameters: + - request: HttpRequest object. + - emp_objective_id: ID of the EmployeeObjective to display Key Results for. + + Returns: + Renders the 'okr/kr_list.html' template with Key Results associated with the specified EmployeeObjective. + """ emp_objective = EmployeeObjective.objects.get(id=emp_objective_id) krs = emp_objective.employee_key_result.all() context = { "krs": krs, "key_result_status": EmployeeKeyResult.STATUS_CHOICES, - 'emp_objective':emp_objective + "emp_objective": emp_objective, } template = "okr/kr_list.html" return render(request, template, context) @@ -746,9 +858,10 @@ def kr_table_view(request, emp_objective_id): @hx_request_required def objective_detailed_view_objective_status(request, id): """ - This view is used to update status of objective in objective detailed view, redirect to objective_detailed_view_activity. using htmx + This view is used to update status of objective in objective detailed view, + redirect to objective_detailed_view_activity. using htmx Args: - obj_id (int): Primarykey of EmployeeObjective. + obj_id (int): Primary key of EmployeeObjective. Returns: All the filtered and searched object will based on userlevel. """ @@ -769,7 +882,8 @@ def objective_detailed_view_objective_status(request, id): @hx_request_required def objective_detailed_view_key_result_status(request, obj_id, kr_id): """ - This view is used to update status of key result in objective detailed view, redirect to objective_detailed_view_activity. using htmx + This view is used to update status of key result in objective detailed view, + redirect to objective_detailed_view_activity. using htmx Args: obj_id (int): Primarykey of EmployeeObjective. kr_id (int): Primarykey of EmployeeKeyResult. @@ -864,7 +978,6 @@ def objective_archive(request, id): return redirect(f"/pms/objective-list-view?{request.environ['QUERY_STRING']}") - @login_required @manager_can_enter(perm="pms.view_employeeobjective") def view_employee_objective(request, emp_obj_id): @@ -970,18 +1083,27 @@ def change_employee_objective_status(request, emp_obj): status = request.POST.get("status") if emp_objective.status != status: emp_objective.status = status - emp_objective.save() + emp_objective.save() messages.success( - request, _(f"The status of the objective '{emp_objective.objective_id}' has been changed to {emp_objective.status}.") + request, + _( + f"The status of the objective '{emp_objective.objective_id}'\ + has been changed to {emp_objective.status}." + ), ) notify.send( request.user.employee_get, recipient=emp_objective.employee_id.employee_user_id, - verb=f"The status of the objective '{emp_objective.objective_id}' has been changed to {emp_objective.status}.", - verb_ar=f"تم تغيير حالة الهدف '{emp_objective.objective_id}' إلى {emp_objective.status}.", - verb_de=f"Der Status des Ziels '{emp_objective.objective_id}' wurde zu {emp_objective.status} geändert.", - verb_es=f"El estado del objetivo '{emp_objective.objective_id}' ha sido cambiado a {emp_objective.status}.", - verb_fr=f"Le statut de l'objectif '{emp_objective.objective_id}' a été changé à {emp_objective.status}.", + verb=f"The status of the objective '{emp_objective.objective_id}'\ + has been changed to {emp_objective.status}.", + verb_ar=f"تم تغيير حالة الهدف '{emp_objective.objective_id}'\ + إلى {emp_objective.status}.", + verb_de=f"Der Status des Ziels '{emp_objective.objective_id}'\ + wurde zu {emp_objective.status} geändert.", + verb_es=f"El estado del objetivo '{emp_objective.objective_id}'\ + ha sido cambiado a {emp_objective.status}.", + verb_fr=f"Le statut de l'objectif '{emp_objective.objective_id}'\ + a été changé à {emp_objective.status}.", redirect=f"/pms/objective-detailed-view/{emp_objective.objective_id.id}", ) else: @@ -1004,7 +1126,7 @@ def key_result_view(request): """ key_results = KeyResultFilter(request.GET).qs context = { - "current_date":datetime.date.today(), + "current_date": datetime.date.today(), "key_results": key_results, "objective_key_result_status": EmployeeKeyResult.STATUS_CHOICES, } @@ -2124,6 +2246,15 @@ def period_view(request): @manager_can_enter(perm="pms.view_period") @hx_request_required def period_hx_view(request): + """ + Renders a view displaying periods used for tracking Key Results' completion time. + + Parameters: + - request: HttpRequest object. + + Returns: + Renders the 'period/period_list.html' template with a list of historical periods used for tracking Key Results. + """ periods = Period.objects.all() context = { "periods": periods, @@ -2198,7 +2329,10 @@ def period_delete(request, period_id): @login_required def period_change(request): - """this function is used to detect the period change and return the start and end date of that period""" + """ + this function is used to detect the period change and + return the start and end date of that period + """ is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest" if is_ajax: if request.method == "POST": @@ -2549,6 +2683,19 @@ def objective_select_filter(request): @login_required def anonymous_feedback_add(request): + """ + View function for adding anonymous feedback. + + Parameters: + - request: HttpRequest object. + + Returns: + - If request method is POST and form is valid: + Saves the submitted feedback and sends a notification if based on an employee. + Returns a JavaScript snippet to reload the page. + - If request method is GET or form is invalid: + Renders the 'anonymous/anonymous_feedback_form.html' template with the feedback form. + """ if request.method == "POST": form = AnonymousFeedbackForm(request.POST) anonymous_id = request.user.id @@ -2581,8 +2728,22 @@ def anonymous_feedback_add(request): @login_required -def edit_anonymous_feedback(request, id): - feedback = AnonymousFeedback.objects.get(id=id) +def edit_anonymous_feedback(request, obj_id): + """ + View function for editing anonymous feedback. + + Parameters: + - request: HttpRequest object. + - id: ID of the AnonymousFeedback instance to be edited. + + Returns: + - If request method is POST and form is valid: + Saves the edited feedback. + Returns a JavaScript snippet to reload the page. + - If request method is GET or form is invalid: + Renders the 'anonymous/anonymous_feedback_form.html' template with the feedback form pre-filled with existing data. + """ + feedback = AnonymousFeedback.objects.get(id=obj_id) form = AnonymousFeedbackForm(instance=feedback) anonymous_id = request.user.id if request.method == "POST": @@ -2597,14 +2758,14 @@ def edit_anonymous_feedback(request, id): @login_required -def archive_anonymous_feedback(request, id): +def archive_anonymous_feedback(request, obj_id): """ this function is used to archive the feedback for employee args: id(int): primarykey of feedback """ - feedback = AnonymousFeedback.objects.get(id=id) + feedback = AnonymousFeedback.objects.get(id=obj_id) if feedback.archive: feedback.archive = False feedback.save() @@ -2617,9 +2778,19 @@ def archive_anonymous_feedback(request, id): @login_required -def delete_anonymous_feedback(request, id): +def delete_anonymous_feedback(request, obj_id): + """ + Deletes an anonymous feedback entry. + + Parameters: + - request: HttpRequest object. + - id: ID of the AnonymousFeedback instance to be deleted. + + Returns: + Redirects to the feedback list view after deleting the feedback. + """ try: - feedback = AnonymousFeedback.objects.get(id=id) + feedback = AnonymousFeedback.objects.get(id=obj_id) feedback.delete() messages.success(request, _("Feedback deleted successfully!")) @@ -2638,8 +2809,18 @@ def delete_anonymous_feedback(request, id): @login_required -def view_single_anonymous_feedback(request, id): - feedback = AnonymousFeedback.objects.get(id=id) +def view_single_anonymous_feedback(request, obj_id): + """ + Renders a view to display a single anonymous feedback entry. + + Parameters: + - request: HttpRequest object. + - id: ID of the AnonymousFeedback instance to be displayed. + + Returns: + Renders the 'anonymous/single_view.html' template with the details of the specified anonymous feedback. + """ + feedback = AnonymousFeedback.objects.get(id=obj_id) return render(request, "anonymous/single_view.html", {"feedback": feedback}) @@ -2664,9 +2845,9 @@ def employee_keyresult_creation(request, emp_obj_id): if emp_key_result.is_valid(): emp_key_result.save() emp_objective.update_objective_progress() - kr = emp_key_result.cleaned_data["key_result_id"] + key_result = emp_key_result.cleaned_data["key_result_id"] - emp_objective.key_result_id.add(kr) + emp_objective.key_result_id.add(key_result) # assignees = emp_key_result.cleaned_data['assignees'] # start_date =emp_key_result.cleaned_data['start_date'] @@ -2775,7 +2956,6 @@ def employee_keyresult_update_status(request, kr_id): ) - @login_required def key_result_current_value_update(request): """