diff --git a/horilla/decorators.py b/horilla/decorators.py
index 7e6159226..7a2f2078d 100755
--- a/horilla/decorators.py
+++ b/horilla/decorators.py
@@ -256,3 +256,34 @@ def install_required(function):
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
return _function
+
+
+@decorator_with_arguments
+def meeting_manager_can_enter(function, perm, answerable = False):
+ def _function(request, *args, **kwargs):
+
+ user = request.user
+ employee = user.employee_get
+ is_answer_employee = False
+
+ is_manager = Employee.objects.filter(
+ meeting_manager__isnull=False,
+ ).filter(id=employee.id).exists()
+
+ if answerable:
+ is_answer_employee = Employee.objects.filter(
+ meeting_answer_employees=False,
+ ).filter(id=employee.id).exists()
+
+ if user.has_perm(perm) or is_manager or is_answer_employee:
+ return function(request, *args, **kwargs)
+ else:
+ messages.info(request, "You dont have permission.")
+ previous_url = request.META.get("HTTP_REFERER", "/")
+ script = f''
+ key = "HTTP_HX_REQUEST"
+ if key in request.META.keys():
+ return render(request, "decorator_404.html")
+ return HttpResponse(script)
+
+ return _function
\ No newline at end of file
diff --git a/pms/admin.py b/pms/admin.py
index 812deba58..2af5d6059 100644
--- a/pms/admin.py
+++ b/pms/admin.py
@@ -22,6 +22,7 @@ from .models import (
QuestionOptions,
Objective,
KeyResult,
+ Meetings,
)
@@ -36,4 +37,5 @@ admin.site.register(feedback)
admin.site.register(KeyResult)
admin.site.register(Objective)
admin.site.register(KeyResultFeedback)
+admin.site.register(Meetings)
admin.site.register(Comment, SimpleHistoryAdmin)
diff --git a/pms/filters.py b/pms/filters.py
index 95b42d095..84590a25d 100644
--- a/pms/filters.py
+++ b/pms/filters.py
@@ -6,10 +6,18 @@ the PMS (Performance Management System) app.
"""
import datetime
+import django
import django_filters
from django import forms
from django_filters import DateFilter
-from pms.models import EmployeeKeyResult, EmployeeObjective, Feedback, KeyResult, Objective
+from pms.models import (
+ EmployeeKeyResult,
+ EmployeeObjective,
+ Feedback,
+ KeyResult,
+ Meetings,
+ Objective,
+)
from base.methods import reload_queryset
from base.filters import FilterSet
@@ -227,6 +235,7 @@ class KeyResultFilter(CustomFilterSet):
model = EmployeeKeyResult
fields = "__all__"
+
class ActualKeyResultFilter(FilterSet):
"""
Filter through KeyResult model
@@ -236,12 +245,7 @@ class ActualKeyResultFilter(FilterSet):
class Meta:
model = KeyResult
- fields = [
- "progress_type",
- "target_value",
- "duration",
- 'company_id'
- ]
+ fields = ["progress_type", "target_value", "duration", "company_id"]
def search_method(self, queryset, _, value: str):
"""
@@ -250,8 +254,7 @@ class ActualKeyResultFilter(FilterSet):
values = value.split(" ")
empty = queryset.model.objects.none()
for split in values:
- empty = empty | (queryset.filter(title__icontains=split)
- )
+ empty = empty | (queryset.filter(title__icontains=split))
return empty.distinct()
@@ -267,6 +270,7 @@ class ObjectiveReGroup:
("status", "Status"),
]
+
class EmployeeObjectiveFilter(FilterSet):
"""
Filter through EmployeeObjective model
@@ -293,16 +297,16 @@ class EmployeeObjectiveFilter(FilterSet):
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',
- 'status',
+ "start_date",
+ "end_date",
+ "employee_id",
]
def search_method(self, queryset, _, value: str):
@@ -312,7 +316,88 @@ class EmployeeObjectiveFilter(FilterSet):
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))
-
+ 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
+ return empty.distinct()
+
+
+class MeetingsFilter(FilterSet):
+
+ search = django_filters.CharFilter(field_name="title", lookup_expr="icontains")
+ date = django_filters.DateFilter(
+ field_name="date",
+ lookup_expr="date",
+ widget=forms.DateInput(attrs={"type": "date"}),
+ )
+ date_greater = django_filters.DateFilter(
+ field_name="date",
+ lookup_expr="date__gte",
+ widget=forms.DateInput(attrs={"type": "date"}),
+ )
+ date_less = django_filters.DateFilter(
+ field_name="date",
+ lookup_expr="date__lte",
+ widget=forms.DateInput(attrs={"type": "date"}),
+ )
+
+ class Meta:
+ model = Meetings
+ fields = [
+ "employee_id",
+ "manager",
+ "question_template",
+ "is_active",
+ "employee_id__employee_work_info__department_id",
+ "employee_id__employee_work_info__company_id",
+ "employee_id__employee_work_info__job_position_id",
+ "employee_id__employee_work_info__work_type_id",
+ "employee_id__employee_work_info__shift_id",
+ "employee_id__employee_work_info__reporting_manager_id",
+ ]
+
+ # def __init__(self, *args, **kwargs):
+ # super().__init__(*args, **kwargs)
+ # queryset = self.filters["question_template"].queryset
+ # choices = [
+ # ("not_set", "Not Set"),
+ # ]
+ # choices.extend([(obj.id, str(obj)) for obj in queryset])
+ # self.form.fields["question_template"] = (
+ # forms.MultipleChoiceField(
+ # choices=choices,
+ # required=False,
+ # widget=forms.SelectMultiple(
+ # attrs={
+ # "class": "oh-select oh-select-2 select2-hidden-accessible",
+ # }
+ # ),
+ # )
+ # )
+
+ # def filter_queryset(self, queryset):
+ # """
+ # Override the default filtering behavior to handle None option.
+ # """
+ # from django.db.models import Q
+
+ # data = self.form.cleaned_data
+ # not_set_dict = {}
+ # for key, value in data.items():
+ # if isinstance(value, (list, django.db.models.query.QuerySet)):
+ # if value and "not_set" in value:
+ # not_set_dict[key] = value
+
+ # if not_set_dict:
+ # q_objects = Q()
+ # for key, values in not_set_dict.items():
+ # for value in values:
+ # if value == "not_set":
+ # q_objects |= Q(**{f"{key}__isnull": True})
+ # else:
+ # q_objects |= Q(**{key: value})
+ # return queryset.filter(q_objects)
+ # return super().filter_queryset(queryset)
diff --git a/pms/forms.py b/pms/forms.py
index 87633ca47..62bd6980c 100644
--- a/pms/forms.py
+++ b/pms/forms.py
@@ -23,6 +23,7 @@ from horilla_widgets.widgets.select_widgets import HorillaMultiSelectWidget
from pms.models import (
AnonymousFeedback,
KeyResult,
+ Meetings,
Objective,
Question,
EmployeeObjective,
@@ -929,3 +930,41 @@ class AnonymousFeedbackForm(BaseForm):
model = AnonymousFeedback
fields = "__all__"
exclude = ["status", "archive"]
+
+
+class MeetingsForm(BaseForm):
+ date = forms.DateTimeField(
+ widget=forms.DateTimeInput(attrs={"class": "oh-input w-100", "type": "datetime-local"}),
+ )
+ class Meta:
+ model = Meetings
+ fields = "__all__"
+ exclude = ["response", "is_active"]
+
+ def as_p(self):
+ """
+ Render the form fields as HTML table rows with Bootstrap styling.
+ """
+ context = {"form": self}
+ table_html = render_to_string("attendance_form.html", context)
+ return table_html
+
+ def clean(self):
+ cleaned_data = super().clean()
+ answerable_employees = self.data.getlist('answer_employees', [])
+ employees = Employee.objects.filter(id__in=answerable_employees)
+ cleaned_data["answer_employees"] = employees
+ return cleaned_data
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ if self.instance.pk:
+ employees = Employee.objects.filter(id__in=self.instance.employee_id.all())
+ self.fields["answer_employees"].queryset = employees
+ try:
+ if self.data.getlist("employee_id"):
+ employees = Employee.objects.filter(id__in=self.data.getlist("employee_id"))
+ self.fields["answer_employees"].queryset = employees
+ except:
+ pass
+
diff --git a/pms/models.py b/pms/models.py
index c041944db..4fcd0e0eb 100644
--- a/pms/models.py
+++ b/pms/models.py
@@ -233,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 = (
@@ -346,7 +346,7 @@ class EmployeeKeyResult(models.Model):
# unique_together = ("key_result_id", "employee_objective_id")
-"""360degree feedback section"""
+"""360degree feedback section"""
class QuestionTemplate(HorillaModel):
@@ -469,7 +469,7 @@ class Feedback(HorillaModel):
cyclic_next_end_date = models.DateField(null=True, blank=True)
objects = HorillaCompanyManager("employee_id__employee_work_info__company_id")
-
+
class Meta:
ordering = ["-id"]
@@ -478,16 +478,28 @@ class Feedback(HorillaModel):
end_date = self.end_date
cyclic_feedback_period = self.cyclic_feedback_period
cyclic_feedback_days_count = self.cyclic_feedback_days_count
-
+
if cyclic_feedback_period == "months":
- self.cyclic_next_start_date = self.start_date + relativedelta(months=cyclic_feedback_days_count)
- self.cyclic_next_end_date = end_date + relativedelta(months=cyclic_feedback_days_count)
+ self.cyclic_next_start_date = self.start_date + relativedelta(
+ months=cyclic_feedback_days_count
+ )
+ self.cyclic_next_end_date = end_date + relativedelta(
+ months=cyclic_feedback_days_count
+ )
elif cyclic_feedback_period == "years":
- self.cyclic_next_start_date = start_date + relativedelta(years=cyclic_feedback_days_count)
- self.cyclic_next_end_date = end_date + relativedelta(years=cyclic_feedback_days_count)
+ self.cyclic_next_start_date = start_date + relativedelta(
+ years=cyclic_feedback_days_count
+ )
+ self.cyclic_next_end_date = end_date + relativedelta(
+ years=cyclic_feedback_days_count
+ )
elif cyclic_feedback_period == "days":
- self.cyclic_next_start_date = start_date + relativedelta(days=cyclic_feedback_days_count)
- self.cyclic_next_end_date = end_date + relativedelta(days=cyclic_feedback_days_count)
+ self.cyclic_next_start_date = start_date + relativedelta(
+ days=cyclic_feedback_days_count
+ )
+ self.cyclic_next_end_date = end_date + relativedelta(
+ days=cyclic_feedback_days_count
+ )
super().save(*args, **kwargs)
@@ -626,6 +638,50 @@ class KeyResultFeedback(models.Model):
objects = HorillaCompanyManager("employee_id__employee_work_info__company_id")
+class Meetings(HorillaModel):
+ title = models.CharField(max_length=100)
+ date = models.DateTimeField(null=True, blank=True)
+ employee_id = models.ManyToManyField(Employee, related_name="meeting_employee",verbose_name="Employee")
+ manager = models.ManyToManyField(Employee, related_name="meeting_manager")
+ answer_employees = models.ManyToManyField(Employee,blank=True, related_name="meeting_answer_employees",verbose_name="Answerable Employees")
+ question_template = models.ForeignKey(
+ QuestionTemplate, on_delete=models.PROTECT, null=True, blank=True
+ )
+ response = models.TextField(null=True, blank=True)
+ show_response = models.BooleanField(default=False)
+
+ def __str__(self):
+ return self.title
+
+
+class MeetingsAnswer(models.Model):
+ """feedback answer model"""
+
+ answer = models.JSONField(max_length=200, null=True, blank=True)
+ question_id = models.ForeignKey(
+ Question,
+ on_delete=models.DO_NOTHING,
+ related_name="meeting_answer_question_id",
+ null=True,
+ blank=True,
+ )
+ employee_id = models.ForeignKey(
+ Employee,
+ on_delete=models.DO_NOTHING,
+ related_name="employee_meeting_answer",
+ null=True,
+ blank=True,
+ verbose_name="Employee"
+ )
+ meeting_id = models.ForeignKey(
+ Meetings, on_delete=models.PROTECT, related_name="meeting_answer"
+ )
+ objects = HorillaCompanyManager("employee_id__employee_work_info__company_id")
+
+ def __str__(self):
+ return f"{self.employee_id.employee_first_name} - {self.answer}"
+
+
def manipulate_existing_data():
from dateutil.relativedelta import relativedelta
from horilla.decorators import logger
diff --git a/pms/templates/meetings/form.html b/pms/templates/meetings/form.html
new file mode 100644
index 000000000..5e9a42958
--- /dev/null
+++ b/pms/templates/meetings/form.html
@@ -0,0 +1,43 @@
+{% load i18n %}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pms/templates/meetings/meeting_answer.html b/pms/templates/meetings/meeting_answer.html
new file mode 100644
index 000000000..e85fa384d
--- /dev/null
+++ b/pms/templates/meetings/meeting_answer.html
@@ -0,0 +1,302 @@
+{% load static i18n%}
+
+
+
diff --git a/pms/templates/meetings/meeting_answer_view.html b/pms/templates/meetings/meeting_answer_view.html
new file mode 100644
index 000000000..2bb08d726
--- /dev/null
+++ b/pms/templates/meetings/meeting_answer_view.html
@@ -0,0 +1,155 @@
+{% load static i18n %}
+ {% if answers %}
+
+ {% else %}
+
+

+
{% trans "Questions not answered yet." %}
+
+ {% endif %}
+
diff --git a/pms/templates/meetings/meeting_question_template_view.html b/pms/templates/meetings/meeting_question_template_view.html
new file mode 100644
index 000000000..f0443f3a8
--- /dev/null
+++ b/pms/templates/meetings/meeting_question_template_view.html
@@ -0,0 +1,49 @@
+{% load static i18n %}
+
+
+
+
+ {% if request.user.employee_get in meeting.manager.all %}
+
+ {% for employee in meeting.answer_employees.all %}
+
+ {% endfor %}
+
+ {% endif %}
+
diff --git a/pms/templates/meetings/meeting_single_view.html b/pms/templates/meetings/meeting_single_view.html
new file mode 100644
index 000000000..1ee7188df
--- /dev/null
+++ b/pms/templates/meetings/meeting_single_view.html
@@ -0,0 +1,114 @@
+{% load static i18n %}
+
+
+
+
+
+
+
diff --git a/pms/templates/meetings/meetings_filter.html b/pms/templates/meetings/meetings_filter.html
new file mode 100644
index 000000000..6922f2f5f
--- /dev/null
+++ b/pms/templates/meetings/meetings_filter.html
@@ -0,0 +1,139 @@
+{% load i18n %}
+
+
+
+
+
+
+
+ {{filter_form.employee_id}}
+
+
+
+ {{filter_form.manager}}
+
+
+
+ {{filter_form.is_active}}
+
+
+
+
+
+ {{filter_form.date}}
+
+
+
+ {{filter_form.question_template}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{filter_form.employee_id__employee_work_info__department_id}}
+
+
+
+ {{filter_form.employee_id__employee_work_info__company_id}}
+
+
+
+ {{filter_form.employee_id__employee_work_info__shift_id}}
+
+
+
+
+
+
+ {{filter_form.employee_id__employee_work_info__reporting_manager_id}}
+
+
+
+ {{filter_form.employee_id__employee_work_info__job_position_id}}
+
+
+
+ {{filter_form.employee_id__employee_work_info__work_type_id}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{filter_form.date_greater}}
+
+
+
+
+
+ {{filter_form.date_less}}
+
+
+
+
+
diff --git a/pms/templates/meetings/meetings_list.html b/pms/templates/meetings/meetings_list.html
new file mode 100644
index 000000000..d469de5b3
--- /dev/null
+++ b/pms/templates/meetings/meetings_list.html
@@ -0,0 +1,312 @@
+{% load i18n static %}
+{% include 'filter_tags.html' %}
+ {% if meetings %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{% trans "Title" %}
+
{% trans "Employees" %}
+
{% trans "Managers" %}
+
{% trans "Date" %}
+
{% trans "MoM" %}
+
{% trans "Actions" %}
+
+
+
+ {% for meeting in meetings %}
+
+
+
+
+ {{meeting}}
+
+
+
+ {% for employee in meeting.employee_id.all %}
+
+
+
+
+

+
+
{{employee.get_full_name|truncatechars:15}}
+
+ {% if perms.pms.change_meetings or request.user.employee_get in meeting.manager.all %}
+
+
+
+ {% endif %}
+
+
+ {% endfor %}
+
{{meeting.employee_id.all|length}} {% trans "Employees" %}
+
+
+
+ {% for manager in meeting.manager.all %}
+
+
+
+
+

+
+
{{manager.get_full_name|truncatechars:15}}
+
+ {% if "perms.pms.add_meetings" %}
+
+
+
+ {% endif %}
+
+
+ {% endfor %}
+
{{meeting.manager.all|length}} {% trans "Managers" %}
+
+
+ {{meeting.date}}
+
+
+ {{meeting.response|default:"-"}}
+
+
+
+ {% if perms.pms.add_meetings or request.user.employee_get in meeting.manager.all %}
+
+ {% endif %}
+ {% if request.user.employee_get in meeting.answer_employees.all or request.user.employee_get in meeting.manager.all %}
+ {% if meeting.question_template %}
+
+ {% else %}
+
+
+ edit_document
+
+
+ {% endif %}
+ {% else %}
+
+
+ edit_document
+
+
+ {% endif %}
+ {% if perms.pms.change_meetings or request.user.employee_get in meeting.manager.all %}
+
+ {% endif %}
+ {% if perms.pms.delete_meetings %}
+ {% if not meeting.is_active %}
+
+ {% else %}
+
+ {% endif %}
+ {% endif %}
+ {% if perms.pms.delete_meetings %}
+
+ {% endif %}
+
+
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+ {% else %}
+
+
+ groups
+
+
{% trans "There is no meetings has been created." %}
+
+ {% endif %}
diff --git a/pms/templates/meetings/meetings_nav.html b/pms/templates/meetings/meetings_nav.html
new file mode 100644
index 000000000..8c0f4de20
--- /dev/null
+++ b/pms/templates/meetings/meetings_nav.html
@@ -0,0 +1,101 @@
+{% load i18n %}
+
+
+
{% trans "Meetings" %}
+
+
+
+
+
+
+
diff --git a/pms/templates/meetings/view_meetings.html b/pms/templates/meetings/view_meetings.html
new file mode 100644
index 000000000..d52873aea
--- /dev/null
+++ b/pms/templates/meetings/view_meetings.html
@@ -0,0 +1,24 @@
+{% extends "index.html" %} {% load i18n %} {% load static %} {% block content %}
+
+
+
+ {% include "meetings/meetings_nav.html" %}
+
+
+ {% include 'meetings/meetings_list.html' %}
+
+
+
+
+
+
+
+
+{% endblock content %}
diff --git a/pms/urls.py b/pms/urls.py
index bdde9e660..1df7b61f5 100644
--- a/pms/urls.py
+++ b/pms/urls.py
@@ -339,4 +339,70 @@ urlpatterns = [
views.key_result_current_value_update,
name="key-result-current-value-update",
),
+ path(
+ "view-meetings",
+ views.view_meetings,
+ name="view-meetings",
+ ),
+ path(
+ "create-meeting",
+ views.create_meetings,
+ name="create-meeting",
+ ),
+ path(
+ "meetings-delete//",
+ object_delete,
+ name="meetings-delete",
+ kwargs={"model": models.Meetings, "redirect": "/pms/view-meetings"},
+ ),
+ path(
+ "archive-meeting//",
+ views.archive_meetings,
+ name="archive-meeting",
+ ),
+ path(
+ "filter-meeting",
+ views.filter_meetings,
+ name="filter-meeting",
+ ),
+ path(
+ "add-response//",
+ views.add_response,
+ name="add-response",
+ ),
+ path(
+ "meeting-answer-get/",
+ views.meeting_answer_get,
+ name="meeting-answer-get",
+ ),
+ path(
+ "meeting-answer-post/",
+ views.meeting_answer_post,
+ name="meeting-answer-post",
+ ),
+ path(
+ "meeting-answer-view//",
+ views.meeting_answer_view,
+ name="meeting-answer-view",
+ ),
+ path(
+ "meeting-question-template-view/",
+ views.meeting_question_template_view,
+ name="meeting-question-template-view",
+ ),
+ path(
+ "meeting-single-view/",
+ views.meeting_single_view,
+ name="meeting-single-view",
+ ),
+ path(
+ "meeting-manager-remove//",
+ views.meeting_manager_remove,
+ name="meeting-manager-remove",
+ ),
+ path(
+ "meeting-employee-remove//",
+ views.meeting_employee_remove,
+ name="meeting-employee-remove",
+ ),
]
diff --git a/pms/views.py b/pms/views.py
index b2a56213b..58cdcabf9 100644
--- a/pms/views.py
+++ b/pms/views.py
@@ -1,3013 +1,3321 @@
-"""
-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.db.models import Q
-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 django.http import HttpResponse, HttpResponseRedirect, JsonResponse
-from attendance.methods.group_by import group_by_queryset
-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 employee.models import Employee, EmployeeWorkInformation
-from pms.filters import (
- ActualKeyResultFilter,
- ActualObjectiveFilter,
- EmployeeObjectiveFilter,
- KeyResultFilter,
- ObjectiveFilter,
- FeedbackFilter,
- ObjectiveReGroup,
-)
-from pms.methods import pms_manager_can_enter, pms_owner_and_manager_can_enter
-from pms.models import (
- AnonymousFeedback,
- EmployeeKeyResult,
- EmployeeObjective,
- Comment,
- Feedback,
- KeyResult,
- Objective,
- QuestionTemplate,
- Question,
- Answer,
- Period,
- QuestionOptions,
- KeyResultFeedback,
-)
-from .forms import (
- AddAssigneesForm,
- AnonymousFeedbackForm,
- EmployeeObjectiveForm,
- EmployeekeyResultForm,
- KRForm,
- QuestionForm,
- ObjectiveForm,
- KeyResultForm,
- FeedbackForm,
- ObjectiveCommentForm,
- PeriodForm,
- QuestionTemplateForm,
-)
-
-
-# objectives
-@login_required
-def objective_list_view(request):
- """
- This view is used to show all the objectives and returns some objects.
- Returns:
- Objects based on userlevel.
- """
- user = request.user
- employee = Employee.objects.filter(employee_user_id=user).first()
- is_manager = Employee.objects.filter(
- employee_work_info__reporting_manager_id=employee
- )
- template = "okr/okr_view.html"
- objective_own = EmployeeObjective.objects.filter(employee_id=employee, archive = False)
- objective_own = objective_own.distinct()
- if request.user.has_perm("pms.view_employeeobjective"):
- # objective_own = EmployeeObjective.objects.filter(employee_id=employee)
- # objective_own = objective_own.distinct()
- objective_all = EmployeeObjective.objects.filter(archive = False)
- context = objective_filter_pagination(request, objective_own, objective_all)
-
- elif is_manager:
- # if user is a manager
- employees_ids = [employee.id for employee in is_manager]
- objective_all = EmployeeObjective.objects.filter(employee_id__in=employees_ids, archive = False)
- objective_all = objective_all.distinct()
- context = objective_filter_pagination(request, objective_own, objective_all)
- else:
- # for normal user
- # objective_own = EmployeeObjective.objects.filter(employee_id=employee)
- # objective_own = objective_own.distinct()
- objective_all = EmployeeObjective.objects.none()
- context = objective_filter_pagination(request, objective_own, objective_all)
- return render(request, template, context)
-
-
-def obj_form_save(request, objective_form):
- """
- This view is used to save objective form
- """
- objective = objective_form.save()
- assignees = objective_form.cleaned_data["assignees"]
- start_date = objective_form.cleaned_data["start_date"]
- default_krs = objective_form.cleaned_data["key_result_id"]
-
- messages.success(request, _("Objective created"))
- if assignees:
- for emp in assignees:
- emp_objective = EmployeeObjective(
- objective_id=objective, employee_id=emp, start_date=start_date
- )
- emp_objective.save()
- # assigning default key result
- if default_krs:
- 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(
- request.user.employee_get,
- recipient=emp.employee_user_id,
- verb="You got an OKR!.",
- verb_ar="لقد حققت هدفًا ونتيجة رئيسية!",
- verb_de="Du hast ein Ziel-Key-Ergebnis erreicht!",
- verb_es="¡Has logrado un Resultado Clave de Objetivo!",
- verb_fr="Vous avez atteint un Résultat Clé d'Objectif !",
- redirect=f"/pms/objective-detailed-view/{objective.id}",
- )
-
-
-@login_required
-@manager_can_enter(perm="pms.add_employeeobjective")
-def objective_creation(request):
- """
- This view is for objective creation , and returns a objective form.
- Returns:
- GET:
- objective form, period, department, job position, employee, department
- POST:
- 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:
- 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)
- return HttpResponse("")
- 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")
-def objective_update(request, obj_id):
- """
- This view takes one arguments, id , and returns a HttpResponse object.,using htmx
- Args:
- id (int): Primary key of EmployeeObjective.
- Returns:
- A HttpResponse object with the content Form errors.
- """
- instance = Objective.objects.get(id=obj_id)
- objective_form = ObjectiveForm(instance=instance)
- 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)
- if objective_form.is_valid():
- objective = objective_form.save()
- assignees = objective_form.cleaned_data["assignees"]
- start_date = objective_form.cleaned_data["start_date"]
- default_krs = objective_form.cleaned_data["key_result_id"]
- new_emp = [assignee for assignee in assignees]
-
- delete_list = []
- if objective.employee_objective.exists():
- emp_objectives = objective.employee_objective.all()
- existing_emp = [emp.employee_id for emp in emp_objectives]
- delete_list = [
- employee for employee in existing_emp if employee not in new_emp
- ]
- if len(delete_list) > 0:
- for emp in delete_list:
- EmployeeObjective.objects.filter(
- employee_id=emp, objective_id=objective
- ).delete()
- for emp in new_emp:
- if EmployeeObjective.objects.filter(
- employee_id=emp, objective_id=objective
- ).exists():
- emp_obj = EmployeeObjective.objects.filter(
- employee_id=emp, objective_id=objective
- ).first()
- emp_obj.start_date = start_date
- else:
- emp_obj = EmployeeObjective(
- employee_id=emp, objective_id=objective, start_date=start_date
- )
- emp_obj.save()
- # assiging default key result
- if default_krs:
- 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=key,
- progress_type=key.progress_type,
- target_value=key.target_value,
- )
- emp_kr.save()
-
- notify.send(
- request.user.employee_get,
- recipient=emp.employee_user_id,
- verb="You got an OKR!.",
- verb_ar="لقد حققت هدفًا ونتيجة رئيسية!",
- verb_de="Du hast ein Ziel-Key-Ergebnis erreicht!",
- verb_es="¡Has logrado un Resultado Clave de Objetivo!",
- verb_fr="Vous avez atteint un Résultat Clé d'Objectif !",
- redirect=f"/pms/objective-detailed-view/{objective.id}",
- )
- messages.success(
- request,
- _("Objective %(objective)s Updated") % {"objective": instance},
- )
- return HttpResponse("")
- 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")
-def view_key_result(request):
- """
- This method is used render template to view all the key result instances
- """
- 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,
- "f": krs_filter,
- "krs_ids": krs_ids,
- }
- return render(request, "okr/key_result/view_kr.html", context)
-
-
-@login_required
-@permission_required("payroll.view_key_result")
-def filter_key_result(request):
- """
- Filter and retrieve a list of key results based on the provided query parameters.
- """
- query_string = request.GET.urlencode()
- krs = ActualKeyResultFilter(request.GET).qs
- template = "okr/key_result/kr_card.html"
- if request.GET.get("view") == "list":
- template = "okr/key_result/kr_list.html"
- krs = sortby(request, krs, "sortby")
- krs = paginator_qry(krs, request.GET.get("page"))
- allowance_ids = json.dumps([instance.id for instance in krs.object_list])
- data_dict = parse_qs(query_string)
- get_key_instances(KeyResult, data_dict)
- return render(
- request,
- template,
- {
- "krs": krs,
- "pd": query_string,
- "filter_dict": data_dict,
- "krs_ids": allowance_ids,
- },
- )
-
-
-@login_required
-def key_result_create(request):
- """
- This method renders form and template to create Ticket type
- """
- form = KRForm()
- redirect_url = None
- if request.method == "POST":
- form = KRForm(request.POST)
- if form.is_valid():
- instance = form.save()
- messages.success(
- request,
- _("Key result %(key_result)s created successfully")
- % {"key_result": instance},
- )
-
- 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}"
- form = KRForm()
- 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):
- form=KRForm()
- kr = False
- key_result = False
- if kr_id is not None:
- key_result = KeyResult.objects.filter(id=kr_id).first()
- form = KRForm(instance=key_result)
- if request.method == "POST":
- 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},
- )
- return HttpResponse("")
-
- else:
- form = KRForm(request.POST)
- if form.is_valid():
- instance = form.save()
- messages.success(
- request,
- _("Key result %(key_result)s created successfully")
- % {"key_result": instance},
- )
- return HttpResponse("")
-
- 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
-def add_assignees(request, obj_id):
- """
- this function is used to add assigneesto the objective
- args:
- obj_id(int) : pimarykey of Objective
- return:
- redirect to add assignees form
- """
- objective = Objective.objects.get(id=obj_id)
- form = AddAssigneesForm(instance=objective)
- if request.method == "POST":
- form = AddAssigneesForm(request.POST, instance=objective)
- if form.is_valid():
- objective = form.save(commit=False)
- assignees = form.cleaned_data["assignees"]
- start_date = form.cleaned_data["start_date"]
- for emp in assignees:
- objective.assignees.add(emp)
- if not EmployeeObjective.objects.filter(
- employee_id=emp, objective_id=objective
- ).exists():
- emp_obj = EmployeeObjective(
- employee_id=emp, objective_id=objective, start_date=start_date
- )
- emp_obj.save()
- # assiging default key result
- default_krs = objective.key_result_id.all()
- if default_krs:
- 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=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,
- verb="You got an OKR!.",
- verb_ar="لقد حققت هدفًا ونتيجة رئيسية!",
- verb_de="Du hast ein Ziel-Key-Ergebnis erreicht!",
- verb_es="¡Has logrado un Resultado Clave de Objetivo!",
- verb_fr="Vous avez atteint un Résultat Clé d'Objectif !",
- redirect=f"/pms/objective-detailed-view/{objective.id}",
- )
- objective.save()
- messages.info(
- request,
- _("Objective %(objective)s Updated") % {"objective": objective},
- )
- return HttpResponse("")
- context = {
- "form": form,
- "objective": objective,
- }
- return render(request, "okr/add_assignees.html", context)
-
-
-@login_required
-@manager_can_enter(perm="pms.delete_employeeobjective")
-def objective_delete(request, obj_id):
- """
- This view takes one arguments, id and returns redirecting to a view.
- Args:
- id (int) : primarykey of the EmployeeObjective.
- Returns:
- Redirect to Objective_list_view".
- """
- try:
- objective = Objective.objects.get(id=obj_id)
- if not objective.employee_objective.exists():
- objective.delete()
- messages.success(
- request,
- _("Objective %(objective)s deleted") % {"objective": objective},
- )
- else:
- messages.warning(
- request,
- _("You can't delete objective %(objective)s,related entries exists")
- % {"objective": objective},
- )
- except EmployeeObjective.DoesNotExist:
- messages.error(request, _("Objective not found."))
- return redirect(objective_list_view)
-
-
-@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("")
-
-
-@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("")
-
-
-@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
- ).delete()
- objective.assignees.remove(emp_id)
-
- return HttpResponse()
-
-
-def objective_filter_pagination(request, objective_own, objective_all):
- """
- This view takes two arguments, all_objective,own_objecitve and returns some objects.
- Args:
- all_objective (queryset) : Queryset of objectives
- own_objective (queryset) : Queryset of objectives
- Returns:
- All the filtered and paginated object will return.
- """
- previous_data = request.GET.urlencode()
- initial_data = {"archive": False} # set initial value of archive filter to False
- field = request.GET.get("field")
- if request.GET.get("status") != "Closed":
- objective_own = objective_own
- objective_all = objective_all
- objective_filter_own = ObjectiveFilter(
- request.GET or initial_data, queryset=objective_own
- )
- objective_filer_form = objective_filter_own.form
- objective_filter_own = objective_filter_own.qs.order_by("-id")
- objective_filter_all = ObjectiveFilter(
- request.GET or initial_data, queryset=objective_all
- ).qs
- employee = request.user.employee_get
- manager = False
- objectives = Objective.objects.filter(Q(managers=employee) | Q(assignees=employee))
- if Objective.objects.filter(managers=employee).exists():
- manager = True
- objectives = Objective.objects.filter(managers=employee).distinct()
- if request.user.has_perm("pms.view_objective"):
- objectives = Objective.objects.all()
- if Objective.objects.filter(assignees=employee).exists():
- objectives = Objective.objects.filter(assignees=employee).distinct()
-
- objectives = ActualObjectiveFilter(request.GET or initial_data, queryset=objectives).qs
- objectives = Paginator(objectives, get_pagination())
- objective_paginator_own = Paginator(objective_filter_own, get_pagination())
- objective_paginator_all = Paginator(objective_filter_all, get_pagination())
- own_page = request.GET.get("page")
- all_page = request.GET.get("all_page")
- objectives_own = objective_paginator_own.get_page(own_page)
- objectives_all = objective_paginator_all.get_page(all_page)
- objectives = objectives.get_page(all_page)
-
- now = datetime.datetime.now()
- data_dict = parse_qs(previous_data)
- get_key_instances(EmployeeObjective, data_dict)
- context = {
- "manager": manager,
- "superuser": "true",
- "objectives": objectives,
- "own_objectives": objectives_own,
- "all_objectives": objectives_all,
- "objective_filer_form": ActualObjectiveFilter().form,
- "pg": previous_data,
- "current_date": now,
- "filter_dict": data_dict,
- "gp_fields": ObjectiveReGroup.fields,
- "field": field,
- }
- return context
-
-
-@login_required
-# @hx_request_required
-def objective_list_search(request):
- """
- This view is used to to search objective, returns searched and filtered objects.
- Returns:
- All the filtered and searched object will based on userlevel.
- """
- search_val = request.GET.get("search")
- if search_val is None:
- search_val = ""
-
- user = request.user
- employee = Employee.objects.filter(employee_user_id=user).first()
- is_manager = Employee.objects.filter(
- employee_work_info__reporting_manager_id=employee
- )
-
- if request.user.has_perm("pms.view_employeeobjective"):
- # based on permission
- objective_own = EmployeeObjective.objects.filter(employee_id=employee)
- objective_own = objective_own.distinct()
- objective_all = EmployeeObjective.objects.all()
- context = objective_filter_pagination(request, objective_own, objective_all)
-
- context = objective_filter_pagination(request, objective_own, objective_all)
-
- elif is_manager:
- # if user is a manager
- employees_ids = [employee.id for employee in is_manager]
- objective_own = EmployeeObjective.objects.filter(employee_id=employee).filter(
- objective__icontains=search_val
- )
- objective_all = EmployeeObjective.objects.filter(
- employee_id__in=employees_ids
- ).filter(objective__icontains=search_val)
- context = objective_filter_pagination(request, objective_own, objective_all)
-
- else:
- # for normal user
- objective_own = EmployeeObjective.objects.filter(employee_id=employee).filter(
- objective__icontains=search_val
- )
- objective_all = EmployeeObjective.objects.none()
- context = objective_filter_pagination(request, objective_own, objective_all)
- template = "okr/okr_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
-# @hx_request_required
-def objective_dashboard_view(request):
- """
- This view is used to to search objective, returns searched and filtered objects.
- Returns:
- All the filtered and searched object will based on userlevel.
- """
- emp_objectives = EmployeeObjectiveFilter(request.GET).qs
- return render(
- request,
- "okr/emp_objective/emp_objective_dashboard_view.html",
- {
- 'emp_objectives':emp_objectives
- }
- )
-
-
-def objective_history(emp_obj_id):
- """
- This view is used to get history of EmployeeObjective, return objects.
- Args:
- id (int): Primarykey of EmployeeObjective.
- Returns:
- All the history of EmployeeObjective.
- """
-
- def pair_history(iterable):
- """this function return two history pair"""
- a, b = tee(iterable)
- next(b, None)
- return zip(a, b)
-
- changed_key_results = []
-
- def key_result_history(key_result):
- """key result history"""
- key_result_iterator = (
- key_result.history.all().order_by("history_date").iterator()
- )
- for record_pair in pair_history(key_result_iterator):
- old_record, new_record = record_pair
- delta = new_record.diff_against(old_record)
- history_user_id = delta.new_record.history_user
- history_change_date = delta.new_record.history_date
- employee = Employee.objects.filter(employee_user_id=history_user_id).first()
- key_result = delta.new_record.key_result_id
- changed_key_results.append(
- {
- "delta": delta,
- "changed_user": employee,
- "changed_date": history_change_date,
- "k_r": key_result,
- }
- )
-
- obj_objective = EmployeeObjective.objects.get(id=emp_obj_id)
- all_key_results = EmployeeKeyResult.objects.filter(
- employee_objective_id=obj_objective
- )
-
- for key_result in all_key_results:
- # loop each key result and generate it's history
- key_result_history(key_result)
- changed_key_results.reverse()
- return changed_key_results
-
-
-@login_required
-def objective_detailed_view(request, obj_id, **kwargs):
- """
- this function is used to update the key result of objectives
- args:
- obj_id(int) : pimarykey of EmployeeObjective
- return:
- objects to objective_detailed_view
- """
-
- objective = Objective.objects.get(id=obj_id)
- 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()
- # progress of objective calculation
- # total_kr = key_results_all.count()
- # try:
- # progress = int(
- # sum(key_result.progress_percentage for key_result in key_results_all) / (total_kr)
- # )
- # except (ZeroDivisionError, TypeError):
- # progress = 0
-
- # objective_form = ObjectiveForm(instance=objective)
- # history = objective_history(emp_obj_id)
- previous_data = request.GET.urlencode()
- data_dict = parse_qs(previous_data)
- now = datetime.datetime.now()
- context = {
- "emp_objectives": emp_objectives,
- "pd": previous_data,
- "filter_dict": data_dict,
- # "employee_key_results": key_results,
- "objective": objective,
- # "comments": comments,
- # "historys": history,
- # "progress": progress,
- # "objective_form": objective_form,
- "key_result_form": KeyResultForm,
- "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)
-
-
-@login_required
-@hx_request_required
-def objective_detailed_view_activity(request, id):
- """
- This view is used to show objective activity template ,using htmx
- Args:
- id (int): Primary key of EmployeeObjective.
- Returns:
- it will return history,comment object to objective_detailed_view_activity.
- """
-
- objective = EmployeeObjective.objects.get(id=id)
- key_result_history = objective_history(id)
- history = objective.tracking()
- comments = Comment.objects.filter(employee_objective_id=objective)
- activity_list = []
- for hist in history:
- hist["date"] = hist["pair"][0].history_date
- activity_list.append(hist)
- for com in comments:
- comment = {
- "type": "comment",
- "comment": com,
- "date": com.created_at,
- }
- activity_list.append(comment)
-
- for key in key_result_history:
- key_result = {
- "type": "key_result",
- "key_result": key,
- "date": key["changed_date"],
- }
- activity_list.append(key_result)
-
- activity_list = sorted(activity_list, key=lambda x: x["date"], reverse=True)
-
- context = {
- "objective": objective,
- "historys": history,
- "comments": comments,
- "activity_list": activity_list,
- }
- return render(request, "okr/objective_detailed_view_activity.html", context)
-
-
-@login_required
-@hx_request_required
-def objective_detailed_view_comment(request, id):
- """
- This view is used to create comment object for objective activity, using htmx
- Args:
- id (int): Primary key of EmployeeObjective.
- Returns:
- it will redirect to objective_detailed_view_activity.
- """
- comment_form = ObjectiveCommentForm(request.POST)
- if comment_form.is_valid():
- objective = EmployeeObjective.objects.get(id=id)
- form = comment_form.save(commit=False)
- form.employee_id = request.user.employee_get
- form.employee_objective_id = objective
- form.save()
-
- return redirect(objective_detailed_view_activity, 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):
- """
- 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,
- }
- template = "okr/kr_list.html"
- return render(request, template, context)
-
-
-@login_required
-@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
- Args:
- obj_id (int): Primary key of EmployeeObjective.
- Returns:
- All the filtered and searched object will based on userlevel.
- """
-
- objective = EmployeeObjective.objects.get(id=id)
- status = request.POST.get("objective_status")
- objective.status = status
- objective.save()
- messages.info(
- request,
- _("Objective %(objective)s status updated")
- % {"objective": objective.objective},
- )
- return redirect(objective_detailed_view_activity, id)
-
-
-@login_required
-@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
- Args:
- obj_id (int): Primarykey of EmployeeObjective.
- kr_id (int): Primarykey of EmployeeKeyResult.
- Returns:
- All the filtered and searched object will based on userlevel.
- """
-
- status = request.POST.get("key_result_status")
- employee_key_result = EmployeeKeyResult.objects.get(id=kr_id)
-
- current_value = employee_key_result.current_value
- target_value = employee_key_result.target_value
-
- if current_value >= target_value:
- employee_key_result.status = "Closed"
- else:
- employee_key_result.status = status
- employee_key_result.save()
- messages.info(request, _("Status has been updated"))
- # return redirect(objective_detailed_view_activity, id=obj_id)
- response = redirect(objective_detailed_view_activity, id=obj_id)
- return HttpResponse(
- response.content.decode("utf-8") + ""
- )
-
-
-@login_required
-@hx_request_required
-def objective_detailed_view_current_value(request, kr_id):
- """
- This view is used to update current value of key result, return redirect to view . using htmx
- Args:
- kr_id (int): Primarykey of EmployeeKeyresult.
- Returns:
- All the history of EmployeeObjective.
- """
- if request.method == "POST":
- current_value = request.POST.get("current_value")
- employee_key_result = EmployeeKeyResult.objects.get(id=kr_id)
- target_value = employee_key_result.target_value
- objective_id = employee_key_result.employee_objective_id.id
- if int(current_value) < target_value:
- employee_key_result.current_value = current_value
- employee_key_result.save()
- messages.info(
- request,
- _("Current value of %(employee_key_result)s updated")
- % {"employee_key_result": employee_key_result},
- )
- return redirect(objective_detailed_view_activity, objective_id)
-
- elif int(current_value) == target_value:
- employee_key_result.current_value = current_value
- employee_key_result.status = "Closed"
- employee_key_result.save()
- messages.info(
- request,
- _("Current value of %(employee_key_result)s updated")
- % {"employee_key_result": employee_key_result},
- )
- # return redirect(objective_detailed_view_activity, objective_id)
- response = redirect(objective_detailed_view_activity, objective_id)
- return HttpResponse(
- response.content.decode("utf-8") + ""
- )
-
- elif int(current_value) > target_value:
- messages.warning(request, _("Current value is greater than target value"))
- return redirect(objective_detailed_view_activity, objective_id)
- messages.error(request, _("Error occurred during current value updation"))
- return redirect(objective_detailed_view_activity, objective_id)
-
-
-@login_required
-def objective_archive(request, id):
- """
- this function is used to archive the objective
- args:
- id(int) : pimarykey of EmployeeObjective
- return:
- redirect to objective_list_view
- """
- objective = Objective.objects.get(id=id)
- if objective.archive:
- objective.archive = False
- objective.save()
- messages.info(request, _("Objective un-archived successfully!."))
- elif not objective.archive:
- objective.archive = True
- objective.save()
- messages.info(request, _("Objective archived successfully!."))
- return redirect(f"/pms/objective-list-view?{request.environ['QUERY_STRING']}")
-
-
-@login_required
-@pms_owner_and_manager_can_enter(perm="pms.view_employeeobjective")
-def view_employee_objective(request, emp_obj_id):
- """
- This function is used to render individual view of the employee objective
- args:
- emp_obj_id(int) : pimarykey of EmployeeObjective
- return:
- redirect to individual view of employee objective
- """
- emp_objective = EmployeeObjective.objects.get(id=emp_obj_id)
- context = {
- "instance": emp_objective,
- "objective_key_result_status": EmployeeObjective.STATUS_CHOICES,
- }
- return render(request, "okr/emp_obj_single.html", context)
-
-
-@login_required
-@manager_can_enter(perm="pms.add_employeeobjective")
-def update_employee_objective(request, emp_obj_id):
- """
- This function is used to update the employee objective
- args:
- emp_obj_id(int) : pimarykey of EmployeeObjective
- return:
- redirect to form of employee objective
- """
- emp_objective = EmployeeObjective.objects.get(id=emp_obj_id)
- form = EmployeeObjectiveForm(instance=emp_objective)
- if request.method == "POST":
- form = EmployeeObjectiveForm(request.POST, instance=emp_objective)
- if form.is_valid:
- emp_obj = form.save(commit=False)
- emp_obj.save()
- messages.success(request, _("Employee objective Updated successfully"))
- return HttpResponse("")
- context = {"form": form, "k_form": KRForm()}
- return render(request, "okr/emp_objective_form.html", context=context)
-
-
-@login_required
-@manager_can_enter(perm="pms.delete_employeeobjective")
-def archive_employee_objective(request, emp_obj_id):
- """
- This function is used to archive or unarchive the employee objective
- args:
- emp_obj_id(int) : pimarykey of EmployeeObjective
- return:
- redirect to detailed of employee objective
- """
- emp_objective = EmployeeObjective.objects.get(id=emp_obj_id)
- obj_id = emp_objective.objective_id.id
- single_view = eval(request.GET.get("single_view"))
- if emp_objective.archive:
- emp_objective.archive = False
- emp_objective.save()
- messages.success(request, _("Objective un-archived successfully!."))
- elif not emp_objective.archive:
- emp_objective.archive = True
- emp_objective.save()
- messages.success(request, _("Objective archived successfully!."))
- if single_view:
- return redirect(f"/pms/objective-detailed-view/{obj_id}")
- else:
- return redirect(f"/pms/emp-objective-search/{obj_id}")
-
-
-
-@login_required
-@manager_can_enter(perm="pms.delete_employeeobjective")
-def delete_employee_objective(request, emp_obj_id):
- """
- This function is used to delete the employee objective
- args:
- emp_obj_id(int) : pimarykey of EmployeeObjective
- return:
- redirect to detailed of employee objective
- """
- emp_objective = EmployeeObjective.objects.get(id=emp_obj_id)
- employee = emp_objective.employee_id
- objective = emp_objective.objective_id
- single_view = request.GET.get("single_view")
- emp_objective.delete()
- objective.assignees.remove(employee)
- messages.success(request, _("Objective deleted successfully!."))
- if not single_view:
- return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
- else:
- return HttpResponse("")
-
-
-@login_required
-@manager_can_enter(perm="pms.change_employeeobjective")
-def change_employee_objective_status(request, emp_obj):
- """
- This function is used to change status of the employee objective
- args:
- emp_obj_id(int) : pimarykey of EmployeeObjective
- return:
- a message
- """
- emp_objective = EmployeeObjective.objects.get(id=emp_obj)
- status = request.POST.get("status")
- if emp_objective.status != status:
- emp_objective.status = status
- emp_objective.save()
- messages.success(
- 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}.",
- redirect=f"/pms/objective-detailed-view/{emp_objective.objective_id.id}",
- )
- else:
- messages.info(
- request, _("The status of the objective is the same as selected.")
- )
-
- return redirect(f"/pms/view-employee-objective/{emp_objective.id}/")
-
-
-@login_required
-@manager_can_enter(perm="pms.add_employeekeyresult")
-def key_result_view(request):
- """
- This view is used to view key result,
- Args:
- request: Request object.
- Returns:
- if errorr occur it will return errorr message.
- """
- krs =KeyResultFilter(request.GET).qs
- krs= group_by_queryset(
- krs, 'employee_objective_id__employee_id', request.GET.get("page"), "page"
- )
- context = {
- "krs": krs,
- "key_result_status": EmployeeKeyResult.STATUS_CHOICES,
- }
- return render(request, "okr/key_result/kr_dashboard_view.html", context=context)
-
-
-@login_required
-@manager_can_enter(perm="pms.add_employeekeyresult")
-def key_result_creation(request, obj_id, obj_type):
- """
- This view is used to create key result,
- Args:
- id (int): Primarykey of EmployeeObjective.
- obj_type (str): type of objecitve
- Returns:
- if errorr occur it will return errorr message .
- """
-
- employee = request.user.employee_get
- if obj_type == "individual":
- objective = EmployeeObjective.objects.filter(id=int(obj_id))
- start_date = None
- end_date = None
- for obj in objective:
- start_date = obj.start_date
- end_date = obj.end_date
- key_result_form = KeyResultForm(
- employee=employee, initial={"start_date": start_date, "end_date": end_date}
- )
- else:
- objective_ids = json.loads(obj_id)
- for objective_id in objective_ids:
- objective = EmployeeObjective.objects.filter(id=objective_id).first()
- start_date = objective.start_date
- end_date = objective.end_date
- key_result_form = KeyResultForm(
- employee=employee, initial={"start_date": start_date, "end_date": end_date}
- )
- context = {
- "key_result_form": key_result_form,
- "objective_id": obj_id,
- "objective_type": obj_type,
- }
- if obj_type == "multiple":
- # for job position or department the context should have all the related object ids
- value = context.pop("objective_id")
- context["objective_ids"] = value
- if request.method == "POST":
- if obj_type == "individual":
- employee_objective_id = EmployeeObjective.objects.get(id=int(obj_id))
- form_key_result = KeyResultForm(
- request.POST, initial={"employee_objective_id": employee_objective_id}
- )
- if form_key_result.is_valid():
- form = form_key_result.save(commit=False)
- form.start_value = form.current_value
- form.employee_objective_id = employee_objective_id
- form.save()
- messages.success(request, _("Key result created"))
- return redirect(objective_detailed_view, obj_id)
- else:
- context["key_result_form"] = form_key_result
-
- elif obj_type == "multiple":
- # If the objective is for job postion or department
- # The id will be list of objective id
- objective_ids = json.loads(obj_id)
- for objective_id in objective_ids:
- objective = EmployeeObjective.objects.filter(id=objective_id).first()
- form_key_result = KeyResultForm(
- request.POST, initial={"employee_objective_id": objective}
- )
- if form_key_result.is_valid():
- form = form_key_result.save(commit=False)
- form.start_value = form.current_value
- form.employee_id = objective.employee_id
- form.employee_objective_id = objective
- form.save()
- else:
- context["key_result_form"] = form_key_result
- return render(
- request, "okr/key_result/key_result_creation.html", context
- )
- messages.success(request, _("Key results created"))
- return redirect(objective_list_view)
- return render(request, "okr/key_result/key_result_creation.html", context)
-
-
-@login_required
-@hx_request_required
-@manager_can_enter(perm="pms.add_employeekeyresult")
-def key_result_creation_htmx(request, id):
- """
- This view is used to create key result in objective detailed view, using htmx
- Args:
- id (int): Primarykey of EmployeeObjective.
- obj_type (str): type of objecitve
- Returns:
- if errorr occure it will return errorr message .
- """
- object = EmployeeObjective.objects.filter(id=id)
- start_date = None
- end_date = None
- for obj in object:
- start_date = obj.start_date
- end_date = obj.end_date
- key_result_form = KeyResultForm(
- initial={"start_date": start_date, "end_date": end_date}
- )
- context = {"key_result_form": key_result_form, "objecitve_id": id}
- objective = EmployeeObjective.objects.get(id=id)
- if request.method == "POST":
- initial_data = {"employee_objective_id": objective}
- form_key_result = KeyResultForm(request.POST, initial=initial_data)
- if form_key_result.is_valid():
- form = form_key_result.save(commit=False)
- form.start_value = form.current_value
- form.employee_objective_id = objective
- form.save()
- messages.success(request, _("Key result created"))
- response = render(
- request, "okr/key_result/key_result_creation_htmx.html", context
- )
- return HttpResponse(
- response.content.decode("utf-8") + ""
- )
- context["key_result_form"] = form_key_result
- return render(request, "okr/key_result/key_result_creation_htmx.html", context)
-
-
-@login_required
-@hx_request_required
-@manager_can_enter(perm="pms.update_employeekeyresult")
-def key_result_update(request, id):
- """
- This view is used to update key result, using htmx
- Args:
- id (int): Primarykey of EmployeeKeyResult.
- Returns:
- success or errors message.
- """
-
- key_result = EmployeeKeyResult.objects.get(id=id)
- key_result_form = KeyResultForm(instance=key_result)
- context = {"key_result_form": key_result_form, "key_result_id": key_result.id}
- if request.method == "POST":
- key_result_form = KeyResultForm(request.POST, instance=key_result)
- key_result_form.initial["employee_objective_id"] = (
- key_result.employee_objective_id
- ) # adding intial objective value to the form
- if key_result_form.is_valid():
- key_result_form.save()
- messages.info(request, _("Key result updated"))
- response = render(request, "okr/key_result/key_result_update.html", context)
- return HttpResponse(
- response.content.decode("utf-8") + ""
- )
- else:
- context["key_result_form"] = key_result_form
- return render(request, "okr/key_result/key_result_update.html", context)
-
-
-# feedback section
-def send_feedback_notifications(request, form):
- # Send notification to employee
- if form.employee_id:
- employee = form.employee_id
- notify.send(
- request.user.employee_get,
- recipient=employee.employee_user_id,
- verb="You have received feedback!",
- verb_ar="لقد تلقيت ملاحظات!",
- verb_de="Sie haben Feedback erhalten!",
- verb_es="¡Has recibido retroalimentación!",
- verb_fr="Vous avez reçu des commentaires !",
- redirect=f"/pms/feedback-detailed-view/{form.id}",
- icon="chatbox-ellipses",
- )
-
- # Send notification to manager
- if form.manager_id:
- manager = form.manager_id
- notify.send(
- request.user.employee_get,
- recipient=manager.employee_user_id,
- verb="You have been assigned as a manager in a feedback!",
- verb_ar="لقد تم تعيينك كمدير في ملاحظة!",
- verb_de="Sie wurden als Manager in einem Feedback zugewiesen!",
- verb_es="¡Has sido asignado como manager en un feedback!",
- verb_fr="Vous avez été désigné comme manager dans un commentaire !",
- redirect=f"/pms/feedback-detailed-view/{form.id}",
- icon="chatbox-ellipses",
- )
-
- # Send notification to subordinates
- if form.subordinate_id:
- subordinates = form.subordinate_id.all()
- for subordinate in subordinates:
- notify.send(
- request.user.employee_get,
- recipient=subordinate.employee_user_id,
- verb="You have been assigned as a subordinate in a feedback!",
- verb_ar="لقد تم تعيينك كمرؤوس في ملاحظة!",
- verb_de="Sie wurden als Untergebener in einem Feedback zugewiesen!",
- verb_es="¡Has sido asignado como subordinado en un feedback!",
- verb_fr="Vous avez été désigné comme subordonné dans un commentaire !",
- redirect=f"/pms/feedback-detailed-view/{form.id}",
- icon="chatbox-ellipses",
- )
-
- # Send notification to colleagues
- if form.colleague_id:
- colleagues = form.colleague_id.all()
- for colleague in colleagues:
- notify.send(
- request.user.employee_get,
- recipient=colleague.employee_user_id,
- verb="You have been assigned as a colleague in a feedback!",
- verb_ar="لقد تم تعيينك كزميل في ملاحظة!",
- verb_de="Sie wurden als Kollege in einem Feedback zugewiesen!",
- verb_es="¡Has sido asignado como colega en un feedback!",
- verb_fr="Vous avez été désigné comme collègue dans un commentaire !",
- redirect=f"/pms/feedback-detailed-view/{form.id}",
- icon="chatbox-ellipses",
- )
-
-
-@login_required
-@manager_can_enter(perm="pms.add_feedback")
-def feedback_creation(request):
- """
- This view is used to create feedback object.
- Returns:
- it will return feedback creation html.
- """
- employee = request.user.employee_get
- # if employe
- form = FeedbackForm(employee=employee)
- context = {
- "feedback_form": form,
- }
- if request.method == "POST":
- form = FeedbackForm(request.POST)
- if form.is_valid():
- if key_result_ids := request.POST.getlist("employee_key_results_id"):
- for key_result_id in key_result_ids:
- key_result = EmployeeKeyResult.objects.filter(
- id=key_result_id
- ).first()
- feedback_form = form.save()
- feedback_form.employee_key_results_id.add(key_result)
- instance = form.save()
- messages.success(request, _("Feedback created successfully."))
- send_feedback_notifications(request, form=instance)
- return redirect(feedback_list_view)
- else:
- context["feedback_form"] = form
- return render(request, "feedback/feedback_creation.html", context)
-
-
-@login_required
-@manager_can_enter(perm="pms.add_feedback")
-def feedback_creation_ajax(request):
- """
- This view is used to create feedback object.
- Returns:
- it will return feedback creation html.
- """
- # this ajax request is used to get the Key result and manager of the choosen employee
- is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
- if is_ajax:
- if request.method == "POST":
- employee_id = request.POST.get("employee_id")
- key_results = EmployeeKeyResult.objects.filter(
- employee_objective_id__employee_id=employee_id
- ).values()
- employee_work_info = EmployeeWorkInformation.objects.filter(
- employee_id__id=employee_id
- ).first()
- reporting_manager_id = employee_work_info.reporting_manager_id
- if reporting_manager_id:
- reporting_manager = {
- "id": reporting_manager_id.id or None,
- "employee_first_name": reporting_manager_id.employee_first_name
- or None,
- "employee_last_name": reporting_manager_id.employee_last_name
- or None,
- }
- else:
- reporting_manager = None
- return JsonResponse(
- {
- "key_results": list(key_results),
- "reporting_manager": reporting_manager,
- }
- )
- return JsonResponse({"status": "Invalid request"}, status=400)
-
-
-@login_required
-@hx_request_required
-@manager_can_enter(perm="pms.change_feedback")
-def feedback_update(request, id):
- """
- This view is used to update the feedback.
- Args:
- id(int) : primarykey of the feedback.
- Returns:
- it will redirect to feedback_detailed_view.
- """
-
- feedback = Feedback.objects.get(id=id)
- form = FeedbackForm(instance=feedback)
- feedback_started = Answer.objects.filter(feedback_id=feedback)
- context = {"feedback_form": form}
- if feedback_started:
- messages.error(request, _("Ongoing feedback is not editable!."))
- response = render(request, "feedback/feedback_update.html", context)
- return HttpResponse(
- response.content.decode("utf-8") + ""
- )
-
- if request.method == "POST":
- form = FeedbackForm(request.POST, instance=feedback)
- if form.is_valid():
- form = form.save()
- messages.info(request, _("Feedback updated successfully!."))
- send_feedback_notifications(request, form)
- response = render(request, "feedback/feedback_update.html", context)
- return HttpResponse(
- response.content.decode("utf-8") + ""
- )
- else:
- context["feedback_form"] = form
- return render(request, "feedback/feedback_update.html", context)
-
-
-@login_required
-def filter_pagination_feedback(
- request, self_feedback, requested_feedback, all_feedback, anonymous_feedback
-):
- """
- This view is used to filter or search the feedback object ,
-
- Args:
- self_feedback (queryset): self feedback filtered queryset.
- requested_feedback (queryset): requested feedback filtered queryset.
- all_feedback (queryset): all feedback filtered queryset.
-
- Returns:
- it will return the filtered and searched object.
-
- """
- previous_data = request.GET.urlencode()
- initial_data = {"archive": False} # set initial value of archive filter to False
- feedback_filter_own = FeedbackFilter(
- request.GET or initial_data, queryset=self_feedback
- )
- feedback_filter_requested = FeedbackFilter(
- request.GET or initial_data, queryset=requested_feedback
- )
- feedback_filter_all = FeedbackFilter(
- request.GET or initial_data, queryset=all_feedback
- )
- anonymous_feedback = anonymous_feedback
- feedback_paginator_own = Paginator(feedback_filter_own.qs, get_pagination())
- feedback_paginator_requested = Paginator(
- feedback_filter_requested.qs, get_pagination()
- )
- feedback_paginator_all = Paginator(feedback_filter_all.qs, get_pagination())
- page_number = request.GET.get("page")
-
- feedbacks_own = feedback_paginator_own.get_page(page_number)
- feedbacks_requested = feedback_paginator_requested.get_page(page_number)
- feedbacks_all = feedback_paginator_all.get_page(page_number)
- now = datetime.datetime.now()
- data_dict = parse_qs(previous_data)
- get_key_instances(Feedback, data_dict)
- context = {
- "superuser": "true",
- "self_feedback": feedbacks_own,
- "requested_feedback": feedbacks_requested,
- "anonymous_feedback": anonymous_feedback,
- "all_feedbacks": feedbacks_all,
- "feedback_filter_form": feedback_filter_own.form,
- "pg": previous_data,
- "current_date": now,
- "filter_dict": data_dict,
- }
- return context
-
-
-@login_required
-# @hx_request_required
-def feedback_list_search(request):
- """
- This view is used to filter or search the feedback object ,
- Args:
- Returns:
- it will return the filtered and searched object.
- """
- feedback = request.GET.get("search") # if the search is none the filter will works
- if feedback is None:
- feedback = ""
- employee_id = Employee.objects.get(employee_user_id=request.user)
- self_feedback = Feedback.objects.filter(employee_id=employee_id).filter(
- review_cycle__icontains=feedback
- )
-
- requested_feedback_ids = []
- requested_feedback_ids.extend(
- [i.id for i in Feedback.objects.filter(manager_id=employee_id)]
- )
- requested_feedback_ids.extend(
- [i.id for i in Feedback.objects.filter(colleague_id=employee_id)]
- )
- requested_feedback_ids.extend(
- [i.id for i in Feedback.objects.filter(subordinate_id=employee_id)]
- )
-
- requested_feedback = Feedback.objects.filter(pk__in=requested_feedback_ids).filter(
- review_cycle__icontains=feedback
- )
- all_feedback = Feedback.objects.all().filter(review_cycle__icontains=feedback)
- anonymous_feedback = (
- AnonymousFeedback.objects.filter(employee_id=employee_id)
- if not request.user.has_perm("pms.view_feedback")
- else AnonymousFeedback.objects.all()
- )
-
- reporting_manager_to = employee_id.reporting_manager.all()
- if request.user.has_perm("pms.view_feedback"):
- context = filter_pagination_feedback(
- request, self_feedback, requested_feedback, all_feedback, anonymous_feedback
- )
- elif reporting_manager_to:
- employees_id = [i.id for i in reporting_manager_to]
- all_feedback = Feedback.objects.filter(employee_id__in=employees_id).filter(
- review_cycle__icontains=feedback
- )
- context = filter_pagination_feedback(
- request, self_feedback, requested_feedback, all_feedback, anonymous_feedback
- )
- else:
- all_feedback = Feedback.objects.none()
- context = filter_pagination_feedback(
- request, self_feedback, requested_feedback, all_feedback, anonymous_feedback
- )
-
- return render(request, "feedback/feedback_list.html", context)
-
-
-@login_required
-def feedback_list_view(request):
- """
- This view is used to filter or search the feedback object ,
- Args:
- Returns:
- it will return the filtered and searched object.
- """
- user = request.user
- employee = Employee.objects.filter(employee_user_id=user).first()
- feedback_requested_ids = Feedback.objects.filter(
- Q(manager_id=employee, manager_id__is_active=True)
- | Q(colleague_id=employee, colleague_id__is_active=True)
- | Q(subordinate_id=employee, subordinate_id__is_active=True)
- ).values_list("id", flat=True)
- feedback_own = Feedback.objects.filter(employee_id=employee).filter(
- archive=False, employee_id__is_active=True
- )
- feedback_requested = Feedback.objects.filter(pk__in=feedback_requested_ids).filter(
- archive=False, employee_id__is_active=True
- )
- feedback_all = Feedback.objects.all().filter(
- archive=False, employee_id__is_active=True
- )
- anonymous_feedback = (
- AnonymousFeedback.objects.filter(employee_id=employee, archive=False)
- if not request.user.has_perm("pms.view_feedback")
- else AnonymousFeedback.objects.filter(archive=False)
- )
- anonymous_feedback = anonymous_feedback | AnonymousFeedback.objects.filter(
- anonymous_feedback_id=request.user.id, archive=False
- )
- employees = Employee.objects.filter(
- employee_work_info__reporting_manager_id=employee, is_active=True
- ) # checking the user is reporting manager or not
- feedback_available = Feedback.objects.all()
- if request.user.has_perm("pms.view_feedback"):
- context = filter_pagination_feedback(
- request, feedback_own, feedback_requested, feedback_all, anonymous_feedback
- )
- elif employees:
- # based on the reporting manager
- feedback_all = Feedback.objects.filter(employee_id__in=employees)
- context = filter_pagination_feedback(
- request, feedback_own, feedback_requested, feedback_all, anonymous_feedback
- )
- else:
- feedback_all = Feedback.objects.none()
- context = filter_pagination_feedback(
- request, feedback_own, feedback_requested, feedback_all, anonymous_feedback
- )
- if feedback_available.exists():
- template = "feedback/feedback_list_view.html"
- else:
- template = "feedback/feedback_empty.html"
- return render(request, template, context)
-
-
-@login_required
-def feedback_detailed_view(request, id, **kwargs):
- """
- This view is used to for detailed view of feedback,
- Args:
- id(int) : primarykey of the feedback
- Returns:
- it will return the feedback object to feedback_detailed_view template .
- """
- feedback = Feedback.objects.get(id=id)
- current_date = datetime.datetime.now()
- context = {
- "feedback": feedback,
- "feedback_status": Feedback.STATUS_CHOICES,
- "current_date": current_date,
- }
- return render(request, "feedback/feedback_detailed_view.html", context)
-
-
-def feedback_detailed_view_answer(request, id, emp_id):
- """
- This view is used show answer ,
- Args:
- id(int) : primarykey of the feedback.
- emp_id(int) : primarykey of the Employee.
- Returns:
- it will return the answers .
- """
- employee = Employee.objects.filter(id=emp_id).first()
- feedback = Feedback.objects.filter(id=id).first()
- answers = Answer.objects.filter(employee_id=employee, feedback_id=feedback)
- context = {
- "answers": answers,
- }
- return render(request, "feedback/feedback_detailed_view_answer.html", context)
-
-
-@login_required
-def feedback_answer_get(request, id, **kwargs):
- """
- This view is used to render the feedback questions ,
- Args:
- id(int) : primarykey of the feedback.
- Returns:
- it will redirect to feedaback_answer.html .
- """
-
- user = request.user
- employee = Employee.objects.filter(employee_user_id=user).first()
- feedback = Feedback.objects.get(id=id)
- answer = Answer.objects.filter(feedback_id=feedback, employee_id=employee)
- question_template = feedback.question_template_id
- questions = question_template.question.all()
- options = QuestionOptions.objects.all()
- feedback_employees = (
- [feedback.employee_id]
- + [feedback.manager_id]
- + list(feedback.colleague_id.all())
- + list(feedback.subordinate_id.all())
- )
- if not employee in feedback_employees:
- messages.info(request, _("You are not allowed to answer"))
- return redirect(feedback_list_view)
-
- # Employee does not have an answer object
- for employee in feedback_employees:
- has_answer = Answer.objects.filter(
- employee_id=employee, feedback_id=feedback
- ).exists()
- has_answer = has_answer and has_answer
- if has_answer:
- feedback.status = "Closed"
- feedback.save()
-
- # Check if the feedback has already been answered
- if answer:
- messages.info(request, _("Feedback already answered"))
- return redirect(feedback_list_view)
-
- context = {
- "questions": questions,
- "options": options,
- "feedback": feedback,
- }
-
- return render(request, "feedback/answer/feedback_answer.html", context)
-
-
-@login_required
-def feedback_answer_post(request, id):
- """
- This view is used to create feedback answer ,
- Args:
- id(int) : primarykey of the feedback.
- Returns:
- it will redirect to feedback_list_view if the form was success full.
- """
-
- user = request.user
- employee = Employee.objects.filter(employee_user_id=user).first()
- feedback = Feedback.objects.get(id=id)
- question_template = feedback.question_template_id
- questions = question_template.question.all()
-
- if request.method == "POST":
- for question in questions:
- if request.POST.get(f"answer{question.id}"):
- answer = request.POST.get(f"answer{question.id}")
- Answer.objects.get_or_create(
- answer={"answer": answer},
- question_id=question,
- feedback_id=feedback,
- employee_id=employee,
- )
- feedback.status = "On Track"
- feedback.save()
- for key_result in feedback.employee_key_results_id.all():
- if request.POST.get(f"key_result{key_result.id}"):
- answer = request.POST.get(f"key_result{key_result.id}")
- KeyResultFeedback.objects.get_or_create(
- answer={"answer": answer},
- key_result_id=key_result,
- feedback_id=feedback,
- employee_id=request.user.employee_get,
- )
- messages.success(
- request,
- _("Feedback %(review_cycle)s has been answered successfully!.")
- % {"review_cycle": feedback.review_cycle},
- )
- return redirect(feedback_list_view)
-
-
-@login_required
-def feedback_answer_view(request, id, **kwargs):
- """
- This view is used to view the feedback for employee.
- Args:
- id(int) : primarykey of the feedback.
- Returns:
- it will return feedback answer object to feedback_answer_view.
- """
-
- user = request.user
- employee = Employee.objects.filter(employee_user_id=user).first()
- feedback = Feedback.objects.get(id=id)
- answers = Answer.objects.filter(feedback_id=feedback, employee_id=employee)
- key_result_feedback = KeyResultFeedback.objects.filter(
- feedback_id=feedback, employee_id=employee
- )
-
- if not answers:
- messages.info(request, _("Feedback is not answered yet"))
- return redirect(feedback_list_view)
-
- context = {
- "answers": answers,
- "feedback_id": feedback,
- "key_result_feedback": key_result_feedback,
- }
- return render(request, "feedback/answer/feedback_answer_view.html", context)
-
-
-@login_required
-@manager_can_enter(perm="pms.delete_feedback")
-def feedback_delete(request, id):
- """
- This view is used to delete the feedback.
- Args:
- id(int) : primarykey of the feedback.
- Returns:
- it will redirect to feedback_list_view.
- """
- try:
- feedback = Feedback.objects.filter(id=id).first()
- answered = Answer.objects.filter(feedback_id=feedback).first()
- if (
- feedback.status == "Closed"
- or feedback.status == "Not Started"
- and not answered
- ):
- feedback.delete()
- messages.success(
- request,
- _("Feedback %(review_cycle)s deleted successfully!")
- % {"review_cycle": feedback.review_cycle},
- )
-
- else:
- messages.warning(
- request,
- _("You can't delete feedback %(review_cycle)s with status %(status)s")
- % {"review_cycle": feedback.review_cycle, "status": feedback.status},
- )
- return redirect(feedback_list_view)
-
- except Feedback.DoesNotExist:
- messages.error(request, _("Feedback not found."))
- except ProtectedError:
- messages.error(request, _("Related entries exists"))
- return redirect(feedback_list_view)
-
-
-@login_required
-@hx_request_required
-def feedback_detailed_view_status(request, id):
- """
- This view is used to update status of feedback.
- Args:
- obj_id (int): Primarykey of feedback.
- Returns:
- message to the view
- """
- status = request.POST.get("feedback_status")
- feedback = get_object_or_404(Feedback, id=id)
- answer = Answer.objects.filter(feedback_id=feedback)
- if status == "Not Started" and answer:
- messages.warning(request, _("Feedback is already started"))
- return render(request, "messages.html")
- feedback.status = status
- feedback.save()
- if (feedback.status) == status:
- messages.info(
- request, _("Feedback status updated to %(status)s") % {"status": _(status)}
- )
- return render(request, "messages.html")
- messages.info(
- request,
- _("Error occurred during status update to %(status)s") % {"status": _(status)},
- )
- return render(request, "message.html")
-
-
-@login_required
-def feedback_archive(request, id):
- """
- this function is used to archive the feedback for employee
- args:
- id(int): primarykey of feedback
- """
-
- feedback = Feedback.objects.get(id=id)
- if feedback.archive:
- feedback.archive = False
- feedback.save()
- messages.info(request, _("Feedback un-archived successfully!."))
- elif not feedback.archive:
- feedback.archive = True
- feedback.save()
- messages.info(request, _("Feedback archived successfully!."))
- return redirect(feedback_list_view)
-
-
-@login_required
-def feedback_status(request):
- """this function is used to un-archive the feedback
- args:
- id(int): primarykey of feedback
- emp_id(int): primarykey of feedback
- """
-
- is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
- if is_ajax:
- if request.method == "POST":
- employee_id = request.POST.get("employee_id")
- feedback_id = request.POST.get("feedback_id")
- feedback = Feedback.objects.get(id=feedback_id)
- employee = Employee.objects.filter(id=employee_id).first()
- answer = Answer.objects.filter(employee_id=employee, feedback_id=feedback)
- status = _("Completed") if answer else _("Not-completed")
- return JsonResponse({"status": status})
- return JsonResponse({"status": "Invalid request"}, status=400)
-
-
-@login_required
-@manager_can_enter(perm="pms.add_question")
-def question_creation(request, id):
- """
- This view is used to create question object.
- Args:
- id(int) : primarykey of the question template.
-
- Returns:
- it will redirect to question_template_detailed_view.
- """
- if request.method == "POST":
- form = QuestionForm(request.POST)
- question_template = QuestionTemplate.objects.get(id=id)
- feedback_ongoing = Feedback.objects.filter(
- question_template_id=question_template
- ).first()
- if feedback_ongoing:
- messages.info(request, _("Question template is used in feedback."))
- return redirect(question_template_detailed_view, id)
- if form.is_valid():
- obj_question = form.save(commit=False)
- obj_question.template_id = question_template
- obj_question.save()
-
- if obj_question.question_type == "4":
- # checking the question type is multichoice
- option_a = request.POST.get("option_a")
- option_b = request.POST.get("option_b")
- option_c = request.POST.get("option_c")
- option_d = request.POST.get("option_d")
- QuestionOptions(
- question_id=obj_question,
- option_a=option_a,
- option_b=option_b,
- option_c=option_c,
- option_d=option_d,
- ).save()
- messages.success(request, _("Question created successfully."))
- return redirect(question_template_detailed_view, id)
- messages.success(request, _("Question created successfully."))
- return redirect(question_template_detailed_view, id)
- else:
- messages.error(request, _("Error occurred during question creation!"))
- return redirect(question_template_detailed_view, id)
-
-
-@login_required
-def question_view(request, id):
- """
- This view is used to view question object.
- Args:
- id(int) : primarykey of the question template.
- Returns:
- it will redirect to question_template_detailed_view.
- """
- question_template = QuestionTemplate.objects.get(id=id)
- question_formset = modelformset_factory(Question, form=QuestionForm, extra=0)
-
- questions = question_template.question.all()
- formset = question_formset(queryset=questions)
- options = []
- question_types = ["text", "ratings", "boolean", "Multi-choices", "likert"]
-
- for question in questions:
- question_options = QuestionOptions.objects.filter(question_id=question)
- options.extend(question_options)
- context = {
- "question_template": question_template,
- "questions": questions,
- "question_options": options,
- "question_types": question_types,
- "formset": formset,
- }
- return render(
- request,
- "feedback/question_template/question_template_detailed_view.html",
- context,
- )
-
-
-@login_required
-@manager_can_enter(perm="pms.change_question")
-def question_update(request, temp_id, q_id):
- """
- This view is used to update question object.
- Args:
- id (int): primarykey of question
- temp_id (int): primarykey of question_template
- Returns:
- it will redirect to question_template_detailed_view.
-
- """
- if request.method == "POST":
- question = Question.objects.get(id=q_id)
- form = QuestionForm(request.POST, instance=question)
- if form.is_valid():
- question_type = form.cleaned_data["question_type"]
- if question_type == "4":
- # if question is Multi-choices
- option_a = form.cleaned_data["option_a"]
- option_b = form.cleaned_data["option_b"]
- option_c = form.cleaned_data["option_c"]
- option_d = form.cleaned_data["option_d"]
- options, created = QuestionOptions.objects.get_or_create(
- question_id=question
- )
- options.option_a = option_a
- options.option_b = option_b
- options.option_c = option_c
- options.option_d = option_d
- options.save()
- form.save()
- messages.info(request, _("Question updated successfully."))
- return redirect(question_template_detailed_view, temp_id)
- else:
- form.save()
- question_options = QuestionOptions.objects.filter(question_id=question)
- if question_options:
- question_options.delete()
- messages.info(request, _("Question updated successfully."))
- return redirect(question_template_detailed_view, temp_id)
- else:
- # Form submission had errors
- messages.error(
- request,
- "\n".join(
- [
- f"{field}: {error}"
- for field, errors in form.errors.items()
- for error in errors
- ]
- ),
- )
- return redirect(question_template_detailed_view, temp_id)
-
-
-@login_required
-@manager_can_enter(perm="pms.delete_question")
-def question_delete(request, id):
- """
- This view is used to delete question object.
- Args:
- id (int): primarykey of question
- Returns:
- it will redirect to question_template_detailed_view.
- """
-
- try:
- # Code that may trigger the FOREIGN KEY constraint failed error
- question = Question.objects.filter(id=id).first()
- temp_id = question.template_id.id
- QuestionOptions.objects.filter(question_id=question).delete()
- question.delete()
- messages.success(request, _("Question deleted successfully!"))
- return redirect(question_template_detailed_view, temp_id)
-
- except IntegrityError:
- # Code to handle the FOREIGN KEY constraint failed error
- messages.error(
- request, _("Failed to delete question: Question template is in use.")
- )
-
- except Question.DoesNotExist:
- messages.error(request, _("Question not found."))
- except ProtectedError:
- messages.error(request, _("Related entries exists"))
- return redirect(question_template_detailed_view, temp_id)
-
-
-@login_required
-@manager_can_enter(perm="pms.add_questiontemplate")
-def question_template_creation(request):
- """
- This view is used to create question template object.
- Args:
- Returns:
- it will redirect to question_template_detailed_view.
- """
- if request.method == "POST":
- form = QuestionTemplateForm(request.POST)
- if form.is_valid():
- instance = form.save()
- return redirect(question_template_detailed_view, instance.id)
- else:
- messages.error(
- request,
- "\n".join(
- [
- f"{field}: {error}"
- for field, errors in form.errors.items()
- for error in errors
- ]
- ),
- )
- return redirect(question_template_view)
-
-
-@login_required
-@manager_can_enter(perm="pms.view_questiontemplate")
-def question_template_view(request):
- """
- This view is used to view question template object.
- Returns:
- it will redirect to question_template_detailed_view.
- """
- question_templates = QuestionTemplate.objects.all()
- context = {"form": QuestionTemplateForm, "question_templates": question_templates}
- return render(
- request, "feedback/question_template/question_template_view.html", context
- )
-
-
-@login_required
-@manager_can_enter(perm="pms.view_questiontemplate")
-def question_template_hx_view(request):
- """
- This view is used to view question template object in htmx.
- """
- question_templates = QuestionTemplate.objects.all()
- context = {"question_templates": question_templates}
- return render(
- request, "feedback/question_template/question_template_list.html", context
- )
-
-
-@login_required
-@manager_can_enter(perm="pms.view_questiontemplate")
-def question_template_detailed_view(request, template_id, **kwargs):
- """
- This view is used to view question template object.
- Args:
- id (int): primarykey of question template
- temp_id (int): primarykey of question_template
- Returns:
- it will redirect to question_template_detailed_view.
- """
-
- question_template = QuestionTemplate.objects.filter(id=template_id).first()
- if not question_template:
- messages.error(request, _("Question template does not exist"))
- return redirect(question_template_view)
- questions = question_template.question.all().order_by("-id")
- question_types = ["text", "ratings", "boolean", "multi-choices", "likert"]
- options = QuestionOptions.objects.filter(question_id__in=questions)
-
- # passing individual form
- question_form_list = [QuestionForm(instance=question) for question in questions]
- context = {
- "question_template": question_template,
- "questions": questions,
- "question_options": options,
- "question_types": question_types,
- "form": QuestionForm,
- "form_list": question_form_list,
- }
- return render(
- request,
- "feedback/question_template/question_template_detailed_view.html",
- context,
- )
-
-
-@login_required
-@manager_can_enter(perm="pms.change_questiontemplate")
-def question_template_update(request, template_id):
- """
- This view is used to update question template object.
- Args:
- id (int): primarykey of question template
- Returns:
- it will redirect to question_template_view.
-
- """
- question_template = QuestionTemplate.objects.filter(id=template_id).first()
- question_update_form = QuestionTemplateForm(instance=question_template)
- context = {"question_update_form": question_update_form}
- if request.method == "POST":
- form = QuestionTemplateForm(request.POST, instance=question_template)
- if form.is_valid():
- form.save()
- messages.info(request, _("Question template updated"))
- # return redirect(question_template_view)
- context["question_update_form"] = form
- return render(
- request, "feedback/question_template/question_template_update.html", context
- )
-
-
-@login_required
-@manager_can_enter(perm="pms.delete_questiontemplate")
-def question_template_delete(request, template_id):
- """
- This view is used to delete question template object.
- Args:
- id (int): primarykey of question template
- Returns:
- it will redirect to question_template_view.
- """
- try:
- question_template = QuestionTemplate.objects.get(id=template_id)
- if Feedback.objects.filter(question_template_id=question_template):
- messages.info(request, _("This template is using in a feedback"))
- else:
- question_template.delete()
- messages.success(
- request, _("The question template is deleted successfully !.")
- )
- except QuestionTemplate.DoesNotExist:
- messages.error(request, _("question template not found."))
- except ProtectedError:
- messages.error(request, _("Related entries exists"))
- return redirect("question-template-hx-view")
-
-
-@login_required
-@manager_can_enter(perm="pms.view_period")
-def period_view(request):
- """
- This view is used to view period objects.
- Returns:
- it will return to period_view.
- """
-
- periods = Period.objects.all()
- context = {
- "periods": periods,
- }
- return render(request, "period/period_view.html", context)
-
-
-@login_required
-@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,
- }
- return render(request, "period/period_list.html", context=context)
-
-
-@login_required
-@manager_can_enter(perm="pms.add_period")
-@hx_request_required
-def period_create(request):
- """
- This view is used to create period objects.
- Returns:
- it will redirect to period_view.
- """
- context = {"form": PeriodForm()}
- if request.method == "POST":
- form = PeriodForm(request.POST)
- if form.is_valid():
- form.save()
- messages.success(request, _("Period creation was Successful "))
- else:
- context["form"] = form
- return render(request, "period/period_create.html", context)
-
-
-@login_required
-@manager_can_enter(perm="pms.change_period")
-def period_update(request, period_id):
- """
- This view is used to update period objects.
- Args:
- id (int): primarykey of period
- Returns:
- it will redirect to period_view.
- """
-
- period = Period.objects.filter(id=period_id).first()
- form = PeriodForm(instance=period)
- context = {"form": form}
- if request.method == "POST":
- form = PeriodForm(request.POST, instance=period)
- if form.is_valid():
- form.save()
- messages.info(request, _("Period updated Successfully. "))
- else:
- context["form"] = form
- return render(request, "period/period_update.html", context)
-
-
-@login_required
-@manager_can_enter(perm="pms.delete_period")
-def period_delete(request, period_id):
- """
- This view is used to delete period objects.
- Args:
- id (int): primarykey of period
- Returns:
- it will redirect to period_view.
- """
- try:
- obj_period = Period.objects.get(id=period_id)
- obj_period.delete()
- messages.info(request, _("Period deleted successfully."))
- except Period.DoesNotExist:
- messages.error(request, _("Period not found."))
- except ProtectedError:
- messages.error(request, _("Related entries exists"))
- return redirect("period-hx-view")
-
-
-@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
- """
- is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
- if is_ajax:
- if request.method == "POST":
- data = json.load(request)
- period_obj = Period.objects.get(id=data)
- start_date = period_obj.start_date
- end_date = period_obj.end_date
- return JsonResponse({"start_date": start_date, "end_date": end_date})
- return JsonResponse({"failed": "failed"})
- return HttpResponse(status=204)
-
-
-@login_required
-def dashboard_view(request):
- """
- This view is used to view dashboard.
- Returns:
- it will redirect to dashboard.
- """
- user = request.user
- employee = Employee.objects.filter(employee_user_id=user).first()
- is_manager = Employee.objects.filter(
- employee_work_info__reporting_manager_id=employee
- )
-
- if user.has_perm("pms.view_employeeobjective") and user.has_perm(
- "pms.view_feedback"
- ):
- count_objective = EmployeeObjective.objects.all().count()
- count_key_result = EmployeeKeyResult.objects.all().count()
- count_feedback = Feedback.objects.all().count()
- okr_at_risk = EmployeeObjective.objects.filter(status="At Risk")
- elif is_manager:
- employees_ids = [employee.id for employee in is_manager]
- count_objective = EmployeeObjective.objects.filter(
- employee_idemployee_objective_id__employee_id__in=employees_ids
- ).count()
- count_key_result = EmployeeObjective.objects.filter(
- emp_obj_id__employee_id__in=employees_ids
- ).count()
- count_feedback = Feedback.objects.filter(employee_id__in=employees_ids).count()
- okr_at_risk = EmployeeObjective.objects.filter(
- employee_objective_id__employee_id__in=employees_ids
- ).filter(status="At Risk")
- else:
- count_objective = EmployeeObjective.objects.filter(employee_id=employee).count()
- count_key_result = EmployeeKeyResult.objects.filter(
- employee_objective_id__employee_id=employee
- ).count()
- count_feedback = Feedback.objects.filter(employee_id=employee).count()
- okr_at_risk = EmployeeObjective.objects.filter(employee_id=employee).filter(
- status="At Risk"
- )
- context = {
- "count_objective": count_objective,
- "count_key_result": count_key_result,
- "count_feedback": count_feedback,
- "okr_at_risk": okr_at_risk,
- }
- return render(request, "dashboard/pms_dashboard.html", context)
-
-
-@login_required
-def dashboard_objective_status(request):
- """objective dashboard data"""
- is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
- if is_ajax and request.method == "GET":
- objective_status = EmployeeObjective.STATUS_CHOICES
- data = {"message": _("No data Found...")}
- for status in objective_status:
- objectives = EmployeeObjective.objects.filter(status=status[0],archive=False)
- objectives_count = filtersubordinates(
- request, queryset=objectives, perm="pms.view_employeeobjective"
- ).count()
- if objectives_count:
- data.setdefault("objective_label", []).append(status[1])
- data.setdefault("objective_value", []).append(objectives_count)
- return JsonResponse(data)
-
-
-@login_required
-def dashboard_key_result_status(request):
- """key result dashboard data"""
- is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
- if is_ajax and request.method == "GET":
- key_result_status = EmployeeKeyResult.STATUS_CHOICES
- data = {"message": _("No data Found...")}
- for i in key_result_status:
- key_results = EmployeeKeyResult.objects.filter(status=i[0])
- key_results_count = filtersubordinates(
- request, queryset=key_results, perm="pms.view_employeekeyresult", field="employee_objective_id__employee_id"
- ).count()
- if key_results_count:
- data.setdefault("key_result_label", []).append(i[1])
- data.setdefault("key_result_value", []).append(key_results_count)
- return JsonResponse(data)
-
-
-@login_required
-def dashboard_feedback_status(request):
- """feedback dashboard data"""
- is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
- if is_ajax and request.method == "GET":
- feedback_status = Feedback.STATUS_CHOICES
- data = {"message": _("No data Found...")}
- for i in feedback_status:
- feedbacks = Feedback.objects.filter(status=i[0])
- feedback_count = filtersubordinates(
- request, queryset=feedbacks, perm="pms.view_feedback"
- ).count()
- if feedback_count:
- data.setdefault("feedback_label", []).append(i[1])
- data.setdefault("feedback_value", []).append(feedback_count)
- return JsonResponse(data)
-
-
-def filtersubordinates(request, queryset, perm=None, field = None):
- """
- This method is used to filter out subordinates queryset element.
- """
- user = request.user
- if user.has_perm(perm):
- return queryset
- manager = Employee.objects.filter(employee_user_id=user).first()
- if manager:
- if field is not None:
- queryset = queryset.filter(
- **{f"{field}__employee_work_info__reporting_manager_id": manager}
- ) | queryset.filter(**{field: manager})
- else:
- queryset = queryset.filter(
- employee_id__employee_work_info__reporting_manager_id=manager
- ) | queryset.filter(employee_id=manager)
- return queryset
- else:
- queryset = queryset.filter(employee_id=user.employee_get)
- return queryset
-
-
-@login_required
-def create_period(request):
- """
- This is an ajax method to return json response to create stage related
- to the project in the task-all form fields
- """
-
- if request.method == "GET":
- form = PeriodForm()
- if request.method == "POST":
- form = PeriodForm(request.POST)
- if form.is_valid():
- instance = form.save()
- return JsonResponse(
- {
- "id": instance.id,
- "name": instance.period_name,
- "start_date": instance.start_date,
- "end_date": instance.end_date,
- }
- )
- errors = form.errors.as_json()
- return JsonResponse({"errors": errors})
- return render(request, "okr/create_period.html", context={"form": form})
-
-
-@login_required
-def objective_bulk_archive(request):
- """
- This method is used to archive/un-archive bulk objectivs
- """
- ids = request.POST["ids"]
- ids = json.loads(ids)
- is_active = False
- message = _("un-archived")
- if request.GET.get("is_active") == "False":
- is_active = True
- message = _("archived")
- for objective_id in ids:
- objective_obj = EmployeeObjective.objects.get(id=objective_id)
- objective_obj.archive = is_active
- objective_obj.save()
- messages.success(
- request,
- _("{objective} is {message}").format(
- objective=objective_obj, message=message
- ),
- )
- return JsonResponse({"message": "Success"})
-
-
-@login_required
-@manager_can_enter(perm="pms.delete_employeeobjective")
-def objective_bulk_delete(request):
- """
- This method is used to bulk delete objective
- """
- ids = request.POST["ids"]
- ids = json.loads(ids)
- for objective_id in ids:
- try:
- objective = EmployeeObjective.objects.get(id=objective_id)
- if objective.status == "Not Started" or objective.status == "Closed":
- objective.delete()
- messages.success(
- request,
- _("%(employee)s's %(objective)s deleted")
- % {
- "objective": objective.objective,
- "employee": objective.employee_id,
- },
- )
- else:
- messages.warning(
- request,
- _("You can't delete objective %(objective)s with status %(status)s")
- % {"objective": objective.objective, "status": objective.status},
- )
- except EmployeeObjective.DoesNotExist:
- messages.error(request, _("Objective not found."))
-
- return JsonResponse({"message": "Success"})
-
-
-@login_required
-def feedback_bulk_archive(request):
- """
- This method is used to archive/un-archive bulk feedbacks
- """
- ids = request.POST["ids"]
- ids = json.loads(ids)
- is_active = False
- message = _("un-archived")
- if request.GET.get("is_active") == "False":
- is_active = True
- message = _("archived")
- for feedback_id in ids:
- feedback_id = Feedback.objects.get(id=feedback_id)
- feedback_id.archive = is_active
- feedback_id.save()
- messages.success(
- request,
- _("{feedback} is {message}").format(feedback=feedback_id, message=message),
- )
- return JsonResponse({"message": "Success"})
-
-
-@login_required
-@manager_can_enter(perm="pms.delete_feedback")
-def feedback_bulk_delete(request):
- """
- This method is used to bulk delete feedbacks
- """
- ids = request.POST["ids"]
- ids = json.loads(ids)
- for feedback_id in ids:
- try:
- feedback = Feedback.objects.get(id=feedback_id)
- if feedback.status == "Closed" or feedback.status == "Not Started":
- feedback.delete()
- messages.success(
- request,
- _("Feedback %(review_cycle)s deleted successfully!")
- % {"review_cycle": feedback.review_cycle},
- )
- else:
- messages.warning(
- request,
- _(
- "You can't delete feedback %(review_cycle)s with status %(status)s"
- )
- % {
- "review_cycle": feedback.review_cycle,
- "status": feedback.status,
- },
- )
-
- except Feedback.DoesNotExist:
- messages.error(request, _("Feedback not found."))
- return JsonResponse({"message": "Success"})
-
-
-@login_required
-def objective_select(request):
- """
- This method is used to return all the id of the employees to select the employee row
- """
- page_number = request.GET.get("page")
- table = request.GET.get("tableName")
- user = request.user.employee_get
- employees = EmployeeObjective.objects.filter(employee_id=user, archive=False)
- if page_number == "all":
- if table == "all":
- if request.user.has_perm("pms.view_employeeobjective"):
- employees = EmployeeObjective.objects.filter(archive=False)
- else:
- employees = EmployeeObjective.objects.filter(
- employee_id__employee_user_id=request.user
- ) | EmployeeObjective.objects.filter(
- employee_id__employee_work_info__reporting_manager_id__employee_user_id=request.user
- )
- else:
- employees = EmployeeObjective.objects.filter(
- employee_id=user, archive=False
- )
-
- employee_ids = [str(emp.id) for emp in employees]
- total_count = employees.count()
-
- context = {"employee_ids": employee_ids, "total_count": total_count}
-
- return JsonResponse(context, safe=False)
-
-
-@login_required
-def objective_select_filter(request):
- """
- This method is used to return all the ids of the filtered employees
- """
- page_number = request.GET.get("page")
- filtered = request.GET.get("filter")
- filters = json.loads(filtered) if filtered else {}
- table = request.GET.get("tableName")
- user = request.user.employee_get
-
- employee_filter = ObjectiveFilter(filters, queryset=EmployeeObjective.objects.all())
- if page_number == "all":
- if table == "all":
- if request.user.has_perm("pms.view_employeeobjective"):
- employee_filter = ObjectiveFilter(
- filters, queryset=EmployeeObjective.objects.all()
- )
- else:
- employee_filter = ObjectiveFilter(
- filters,
- queryset=EmployeeObjective.objects.filter(
- employee_id__employee_work_info__reporting_manager_id__employee_user_id=request.user
- ),
- )
- else:
- employee_filter = ObjectiveFilter(
- filters, queryset=EmployeeObjective.objects.filter(employee_id=user)
- )
- # Get the filtered queryset
- filtered_employees = employee_filter.qs
-
- employee_ids = [str(emp.id) for emp in filtered_employees]
- total_count = filtered_employees.count()
-
- context = {"employee_ids": employee_ids, "total_count": total_count}
-
- return JsonResponse(context)
-
-
-@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
-
- if form.is_valid():
- feedback = form.save(commit=False)
- feedback.anonymous_feedback_id = anonymous_id
- feedback.save()
- if feedback.based_on == "employee":
- try:
- notify.send(
- User.objects.filter(username="Horilla Bot").first(),
- recipient=feedback.employee_id.employee_user_id,
- verb="You received an anonymous feedback!",
- verb_ar="لقد تلقيت تقييمًا مجهولًا!",
- verb_de="Sie haben anonymes Feedback erhalten!",
- verb_es="¡Has recibido un comentario anónimo!",
- verb_fr="Vous avez reçu un feedback anonyme!",
- redirect="/pms/feedback-view/",
- icon="bag-check",
- )
- except:
- pass
- return HttpResponse("")
- else:
- form = AnonymousFeedbackForm()
-
- context = {"form": form, "create": True}
- return render(request, "anonymous/anonymous_feedback_form.html", context)
-
-
-@login_required
-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":
- form = AnonymousFeedbackForm(request.POST, instance=feedback)
- if form.is_valid():
- feedback = form.save(commit=False)
- feedback.anonymous_feedback_id = anonymous_id
- feedback.save()
- return HttpResponse("")
- context = {"form": form, "create": False}
- return render(request, "anonymous/anonymous_feedback_form.html", context)
-
-
-@login_required
-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=obj_id)
- if feedback.archive:
- feedback.archive = False
- feedback.save()
- messages.info(request, _("Feedback un-archived successfully!."))
- elif not feedback.archive:
- feedback.archive = True
- feedback.save()
- messages.info(request, _("Feedback archived successfully!."))
- return redirect(feedback_list_view)
-
-
-@login_required
-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=obj_id)
- feedback.delete()
- messages.success(request, _("Feedback deleted successfully!"))
-
- except IntegrityError:
- messages.error(
- request, _("Failed to delete feedback: Feedback template is in use.")
- )
-
- except AnonymousFeedback.DoesNotExist:
- messages.error(request, _("Feedback not found."))
-
- except ProtectedError:
- messages.error(request, _("Related entries exists"))
-
- return redirect(feedback_list_view)
-
-
-@login_required
-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})
-
-
-@login_required
-@manager_can_enter(perm="pms.add_employeekeyresult")
-def employee_keyresult_creation(request, emp_obj_id):
- """
- This view is for employee keyresult creation , and returns a employee keyresult form.
- Returns:
- GET:
- employee keyresult form
- POST:
- employee keyresult created, and returnes to employee objective details view
- """
- emp_objective = EmployeeObjective.objects.get(id=emp_obj_id)
- employee = emp_objective.employee_id
- emp_key_result = EmployeekeyResultForm(
- initial={"employee_objective_id": emp_objective}
- )
- if request.method == "POST":
- emp_key_result = EmployeekeyResultForm(request.POST)
- if emp_key_result.is_valid():
- emp_key_result.save()
- emp_objective.update_objective_progress()
- key_result = emp_key_result.cleaned_data["key_result_id"]
-
- emp_objective.key_result_id.add(key_result)
- # assignees = emp_key_result.cleaned_data['assignees']
- # start_date =emp_key_result.cleaned_data['start_date']
-
- messages.success(request, _("Key result assigned sucessfully."))
-
- notify.send(
- request.user.employee_get,
- recipient=employee.employee_user_id,
- verb="You got an Key Result!.",
- verb_ar="لقد حصلت على نتيجة رئيسية!",
- verb_de="Du hast ein Schlüsselergebnis erreicht!",
- verb_es="¡Has conseguido un Resultado Clave!",
- verb_fr="Vous avez obtenu un Résultat Clé!",
- redirect=f"/pms/objective-detailed-view/{emp_objective.objective_id.id}",
- )
- return HttpResponse("")
- context = {
- "form": emp_key_result,
- "emp_objective": emp_objective,
- "k_form": KRForm(),
- }
- return render(request, "okr/key_result/kr_form.html", context=context)
-
-
-@login_required
-@manager_can_enter(perm="pms.add_employeekeyresult")
-def employee_keyresult_update(request, kr_id):
- """
- This function is for update employee keyresult, and returns a employee keyresult form.
- Returns:
- GET:
- employee keyresult form
- POST:
- employee keyresult updated, and returnes to employee objective details view
- """
- emp_kr = EmployeeKeyResult.objects.get(id=kr_id)
- employee = emp_kr.employee_objective_id.employee_id
- emp_key_result = EmployeekeyResultForm(instance=emp_kr)
- if request.method == "POST":
- emp_key_result = EmployeekeyResultForm(request.POST, instance=emp_kr)
- if emp_key_result.is_valid():
- emp_key_result.save()
- emp_kr.employee_objective_id.update_objective_progress()
-
- # assignees = emp_key_result.cleaned_data['assignees']
- # start_date =emp_key_result.cleaned_data['start_date']
-
- messages.success(request, _("Key result Updated sucessfully."))
-
- notify.send(
- request.user.employee_get,
- recipient=employee.employee_user_id,
- verb="Your Key Result updated.",
- verb_ar="تم تحديث نتيجتك الرئيسية.",
- verb_de="Ihr Schlüsselergebnis wurde aktualisiert.",
- verb_es="Se ha actualizado su Resultado Clave.",
- verb_fr="Votre Résultat Clé a été mis à jour.",
- redirect=f"/pms/objective-detailed-view/{emp_kr.employee_objective_id.objective_id.id}",
- )
- return HttpResponse("")
-
- context = {
- "form": emp_key_result,
- "update": True,
- }
- return render(request, "okr/key_result/kr_form.html", context=context)
-
-
-@login_required
-@manager_can_enter(perm="pms.delete_employeekeyresult")
-def delete_employee_keyresult(request, kr_id):
- """
- This function is used to delete the employee key result
- args:
- kr_id(int) : pimarykey of EmployeeKeyResult
- return:
- redirect to detailed of employee objective
- """
- emp_kr = EmployeeKeyResult.objects.get(id=kr_id)
- # employee = emp_kr.employee_id
- objective = emp_kr.employee_objective_id.objective_id
- emp_objective = emp_kr.employee_objective_id
- emp_kr.delete()
- emp_objective.update_objective_progress()
- # objective.assignees.remove(employee)
- messages.success(request, _("Objective deleted successfully!."))
- if request.GET.get('dashboard'):
- return redirect(f"/pms/dashboard-view")
- return redirect(f"/pms/objective-detailed-view/{objective.id}")
-
-
-@login_required
-def employee_keyresult_update_status(request, kr_id):
- """
- This function is used to delete the employee key result
- args:
- kr_id(int) : pimarykey of EmployeeKeyResult
- return:
- redirect to detailed of employee objective
- """
- emp_kr = EmployeeKeyResult.objects.get(id=kr_id)
- status = request.POST.get("key_result_status")
- emp_kr.status = status
- emp_kr.save()
- messages.success(request, _("Key result sattus changed to {}.").format(status))
- return redirect(
- f"/pms/kr-table-view/{emp_kr.employee_objective_id.id}?&objective_id={emp_kr.employee_objective_id.objective_id.id}"
- )
-
-
-@login_required
-def key_result_current_value_update(request):
- """
- This method is used to update keyresult current value
- """
- try:
- current_value = eval(request.POST.get("current_value"))
- emp_kr_id = eval(request.POST.get("emp_key_result_id"))
- emp_kr = EmployeeKeyResult.objects.get(id=emp_kr_id)
- if current_value <= emp_kr.target_value:
- emp_kr.current_value = current_value
- emp_kr.save()
- emp_kr.employee_objective_id.update_objective_progress()
- return JsonResponse({"type": "sucess"})
- except:
- return JsonResponse({"type": "error"})
+"""
+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.db.models import Q
+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 django.http import HttpResponse, HttpResponseRedirect, JsonResponse
+from attendance.methods.group_by import group_by_queryset
+from horilla.decorators import manager_can_enter, meeting_manager_can_enter, permission_required
+from horilla.decorators import login_required, hx_request_required
+from notifications.signals import notify
+from base.methods import closest_numbers, get_key_instances, get_pagination, sortby
+from base.views import paginator_qry
+from employee.models import Employee, EmployeeWorkInformation
+from pms.filters import (
+ ActualKeyResultFilter,
+ ActualObjectiveFilter,
+ EmployeeObjectiveFilter,
+ KeyResultFilter,
+ MeetingsFilter,
+ ObjectiveFilter,
+ FeedbackFilter,
+ ObjectiveReGroup,
+)
+from pms.methods import pms_manager_can_enter, pms_owner_and_manager_can_enter
+from pms.models import (
+ AnonymousFeedback,
+ EmployeeKeyResult,
+ EmployeeObjective,
+ Comment,
+ Feedback,
+ KeyResult,
+ Meetings,
+ MeetingsAnswer,
+ Objective,
+ QuestionTemplate,
+ Question,
+ Answer,
+ Period,
+ QuestionOptions,
+ KeyResultFeedback,
+)
+from .forms import (
+ AddAssigneesForm,
+ AnonymousFeedbackForm,
+ EmployeeObjectiveForm,
+ EmployeekeyResultForm,
+ KRForm,
+ MeetingsForm,
+ QuestionForm,
+ ObjectiveForm,
+ KeyResultForm,
+ FeedbackForm,
+ ObjectiveCommentForm,
+ PeriodForm,
+ QuestionTemplateForm,
+)
+
+
+# objectives
+@login_required
+def objective_list_view(request):
+ """
+ This view is used to show all the objectives and returns some objects.
+ Returns:
+ Objects based on userlevel.
+ """
+ user = request.user
+ employee = Employee.objects.filter(employee_user_id=user).first()
+ is_manager = Employee.objects.filter(
+ employee_work_info__reporting_manager_id=employee
+ )
+ template = "okr/okr_view.html"
+ objective_own = EmployeeObjective.objects.filter(
+ employee_id=employee, archive=False
+ )
+ objective_own = objective_own.distinct()
+ if request.user.has_perm("pms.view_employeeobjective"):
+ # objective_own = EmployeeObjective.objects.filter(employee_id=employee)
+ # objective_own = objective_own.distinct()
+ objective_all = EmployeeObjective.objects.filter(archive=False)
+ context = objective_filter_pagination(request, objective_own, objective_all)
+
+ elif is_manager:
+ # if user is a manager
+ employees_ids = [employee.id for employee in is_manager]
+ objective_all = EmployeeObjective.objects.filter(
+ employee_id__in=employees_ids, archive=False
+ )
+ objective_all = objective_all.distinct()
+ context = objective_filter_pagination(request, objective_own, objective_all)
+ else:
+ # for normal user
+ # objective_own = EmployeeObjective.objects.filter(employee_id=employee)
+ # objective_own = objective_own.distinct()
+ objective_all = EmployeeObjective.objects.none()
+ context = objective_filter_pagination(request, objective_own, objective_all)
+ return render(request, template, context)
+
+
+def obj_form_save(request, objective_form):
+ """
+ This view is used to save objective form
+ """
+ objective = objective_form.save()
+ assignees = objective_form.cleaned_data["assignees"]
+ start_date = objective_form.cleaned_data["start_date"]
+ default_krs = objective_form.cleaned_data["key_result_id"]
+
+ messages.success(request, _("Objective created"))
+ if assignees:
+ for emp in assignees:
+ emp_objective = EmployeeObjective(
+ objective_id=objective, employee_id=emp, start_date=start_date
+ )
+ emp_objective.save()
+ # assigning default key result
+ if default_krs:
+ 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(
+ request.user.employee_get,
+ recipient=emp.employee_user_id,
+ verb="You got an OKR!.",
+ verb_ar="لقد حققت هدفًا ونتيجة رئيسية!",
+ verb_de="Du hast ein Ziel-Key-Ergebnis erreicht!",
+ verb_es="¡Has logrado un Resultado Clave de Objetivo!",
+ verb_fr="Vous avez atteint un Résultat Clé d'Objectif !",
+ redirect=f"/pms/objective-detailed-view/{objective.id}",
+ )
+
+
+@login_required
+@manager_can_enter(perm="pms.add_employeeobjective")
+def objective_creation(request):
+ """
+ This view is for objective creation , and returns a objective form.
+ Returns:
+ GET:
+ objective form, period, department, job position, employee, department
+ POST:
+ 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:
+ 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)
+ return HttpResponse("")
+ 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")
+def objective_update(request, obj_id):
+ """
+ This view takes one arguments, id , and returns a HttpResponse object.,using htmx
+ Args:
+ id (int): Primary key of EmployeeObjective.
+ Returns:
+ A HttpResponse object with the content Form errors.
+ """
+ instance = Objective.objects.get(id=obj_id)
+ objective_form = ObjectiveForm(instance=instance)
+ 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)
+ if objective_form.is_valid():
+ objective = objective_form.save()
+ assignees = objective_form.cleaned_data["assignees"]
+ start_date = objective_form.cleaned_data["start_date"]
+ default_krs = objective_form.cleaned_data["key_result_id"]
+ new_emp = [assignee for assignee in assignees]
+
+ delete_list = []
+ if objective.employee_objective.exists():
+ emp_objectives = objective.employee_objective.all()
+ existing_emp = [emp.employee_id for emp in emp_objectives]
+ delete_list = [
+ employee for employee in existing_emp if employee not in new_emp
+ ]
+ if len(delete_list) > 0:
+ for emp in delete_list:
+ EmployeeObjective.objects.filter(
+ employee_id=emp, objective_id=objective
+ ).delete()
+ for emp in new_emp:
+ if EmployeeObjective.objects.filter(
+ employee_id=emp, objective_id=objective
+ ).exists():
+ emp_obj = EmployeeObjective.objects.filter(
+ employee_id=emp, objective_id=objective
+ ).first()
+ emp_obj.start_date = start_date
+ else:
+ emp_obj = EmployeeObjective(
+ employee_id=emp, objective_id=objective, start_date=start_date
+ )
+ emp_obj.save()
+ # assiging default key result
+ if default_krs:
+ 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=key,
+ progress_type=key.progress_type,
+ target_value=key.target_value,
+ )
+ emp_kr.save()
+
+ notify.send(
+ request.user.employee_get,
+ recipient=emp.employee_user_id,
+ verb="You got an OKR!.",
+ verb_ar="لقد حققت هدفًا ونتيجة رئيسية!",
+ verb_de="Du hast ein Ziel-Key-Ergebnis erreicht!",
+ verb_es="¡Has logrado un Resultado Clave de Objetivo!",
+ verb_fr="Vous avez atteint un Résultat Clé d'Objectif !",
+ redirect=f"/pms/objective-detailed-view/{objective.id}",
+ )
+ messages.success(
+ request,
+ _("Objective %(objective)s Updated") % {"objective": instance},
+ )
+ return HttpResponse("")
+ 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")
+def view_key_result(request):
+ """
+ This method is used render template to view all the key result instances
+ """
+ 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,
+ "f": krs_filter,
+ "krs_ids": krs_ids,
+ }
+ return render(request, "okr/key_result/view_kr.html", context)
+
+
+@login_required
+@permission_required("payroll.view_key_result")
+def filter_key_result(request):
+ """
+ Filter and retrieve a list of key results based on the provided query parameters.
+ """
+ query_string = request.GET.urlencode()
+ krs = ActualKeyResultFilter(request.GET).qs
+ template = "okr/key_result/kr_card.html"
+ if request.GET.get("view") == "list":
+ template = "okr/key_result/kr_list.html"
+ krs = sortby(request, krs, "sortby")
+ krs = paginator_qry(krs, request.GET.get("page"))
+ allowance_ids = json.dumps([instance.id for instance in krs.object_list])
+ data_dict = parse_qs(query_string)
+ get_key_instances(KeyResult, data_dict)
+ return render(
+ request,
+ template,
+ {
+ "krs": krs,
+ "pd": query_string,
+ "filter_dict": data_dict,
+ "krs_ids": allowance_ids,
+ },
+ )
+
+
+@login_required
+def key_result_create(request):
+ """
+ This method renders form and template to create Ticket type
+ """
+ form = KRForm()
+ redirect_url = None
+ if request.method == "POST":
+ form = KRForm(request.POST)
+ if form.is_valid():
+ instance = form.save()
+ messages.success(
+ request,
+ _("Key result %(key_result)s created successfully")
+ % {"key_result": instance},
+ )
+
+ 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}"
+ form = KRForm()
+ 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):
+ form = KRForm()
+ kr = False
+ key_result = False
+ if kr_id is not None:
+ key_result = KeyResult.objects.filter(id=kr_id).first()
+ form = KRForm(instance=key_result)
+ if request.method == "POST":
+ 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},
+ )
+ return HttpResponse("")
+
+ else:
+ form = KRForm(request.POST)
+ if form.is_valid():
+ instance = form.save()
+ messages.success(
+ request,
+ _("Key result %(key_result)s created successfully")
+ % {"key_result": instance},
+ )
+ return HttpResponse("")
+
+ 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
+def add_assignees(request, obj_id):
+ """
+ this function is used to add assigneesto the objective
+ args:
+ obj_id(int) : pimarykey of Objective
+ return:
+ redirect to add assignees form
+ """
+ objective = Objective.objects.get(id=obj_id)
+ form = AddAssigneesForm(instance=objective)
+ if request.method == "POST":
+ form = AddAssigneesForm(request.POST, instance=objective)
+ if form.is_valid():
+ objective = form.save(commit=False)
+ assignees = form.cleaned_data["assignees"]
+ start_date = form.cleaned_data["start_date"]
+ for emp in assignees:
+ objective.assignees.add(emp)
+ if not EmployeeObjective.objects.filter(
+ employee_id=emp, objective_id=objective
+ ).exists():
+ emp_obj = EmployeeObjective(
+ employee_id=emp, objective_id=objective, start_date=start_date
+ )
+ emp_obj.save()
+ # assiging default key result
+ default_krs = objective.key_result_id.all()
+ if default_krs:
+ 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=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,
+ verb="You got an OKR!.",
+ verb_ar="لقد حققت هدفًا ونتيجة رئيسية!",
+ verb_de="Du hast ein Ziel-Key-Ergebnis erreicht!",
+ verb_es="¡Has logrado un Resultado Clave de Objetivo!",
+ verb_fr="Vous avez atteint un Résultat Clé d'Objectif !",
+ redirect=f"/pms/objective-detailed-view/{objective.id}",
+ )
+ objective.save()
+ messages.info(
+ request,
+ _("Objective %(objective)s Updated") % {"objective": objective},
+ )
+ return HttpResponse("")
+ context = {
+ "form": form,
+ "objective": objective,
+ }
+ return render(request, "okr/add_assignees.html", context)
+
+
+@login_required
+@manager_can_enter(perm="pms.delete_employeeobjective")
+def objective_delete(request, obj_id):
+ """
+ This view takes one arguments, id and returns redirecting to a view.
+ Args:
+ id (int) : primarykey of the EmployeeObjective.
+ Returns:
+ Redirect to Objective_list_view".
+ """
+ try:
+ objective = Objective.objects.get(id=obj_id)
+ if not objective.employee_objective.exists():
+ objective.delete()
+ messages.success(
+ request,
+ _("Objective %(objective)s deleted") % {"objective": objective},
+ )
+ else:
+ messages.warning(
+ request,
+ _("You can't delete objective %(objective)s,related entries exists")
+ % {"objective": objective},
+ )
+ except EmployeeObjective.DoesNotExist:
+ messages.error(request, _("Objective not found."))
+ return redirect(objective_list_view)
+
+
+@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("")
+
+
+@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("")
+
+
+@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
+ ).delete()
+ objective.assignees.remove(emp_id)
+
+ return HttpResponse()
+
+
+def objective_filter_pagination(request, objective_own, objective_all):
+ """
+ This view takes two arguments, all_objective,own_objecitve and returns some objects.
+ Args:
+ all_objective (queryset) : Queryset of objectives
+ own_objective (queryset) : Queryset of objectives
+ Returns:
+ All the filtered and paginated object will return.
+ """
+ previous_data = request.GET.urlencode()
+ initial_data = {"archive": False} # set initial value of archive filter to False
+ field = request.GET.get("field")
+ if request.GET.get("status") != "Closed":
+ objective_own = objective_own
+ objective_all = objective_all
+ objective_filter_own = ObjectiveFilter(
+ request.GET or initial_data, queryset=objective_own
+ )
+ objective_filer_form = objective_filter_own.form
+ objective_filter_own = objective_filter_own.qs.order_by("-id")
+ objective_filter_all = ObjectiveFilter(
+ request.GET or initial_data, queryset=objective_all
+ ).qs
+ employee = request.user.employee_get
+ manager = False
+ objectives = Objective.objects.filter(Q(managers=employee) | Q(assignees=employee))
+ if Objective.objects.filter(managers=employee).exists():
+ manager = True
+ objectives = Objective.objects.filter(managers=employee).distinct()
+ if request.user.has_perm("pms.view_objective"):
+ objectives = Objective.objects.all()
+ if Objective.objects.filter(assignees=employee).exists():
+ objectives = Objective.objects.filter(assignees=employee).distinct()
+
+ objectives = ActualObjectiveFilter(
+ request.GET or initial_data, queryset=objectives
+ ).qs
+ objectives = Paginator(objectives, get_pagination())
+ objective_paginator_own = Paginator(objective_filter_own, get_pagination())
+ objective_paginator_all = Paginator(objective_filter_all, get_pagination())
+ own_page = request.GET.get("page")
+ all_page = request.GET.get("all_page")
+ objectives_own = objective_paginator_own.get_page(own_page)
+ objectives_all = objective_paginator_all.get_page(all_page)
+ objectives = objectives.get_page(all_page)
+
+ now = datetime.datetime.now()
+ data_dict = parse_qs(previous_data)
+ get_key_instances(EmployeeObjective, data_dict)
+ context = {
+ "manager": manager,
+ "superuser": "true",
+ "objectives": objectives,
+ "own_objectives": objectives_own,
+ "all_objectives": objectives_all,
+ "objective_filer_form": ActualObjectiveFilter().form,
+ "pg": previous_data,
+ "current_date": now,
+ "filter_dict": data_dict,
+ "gp_fields": ObjectiveReGroup.fields,
+ "field": field,
+ }
+ return context
+
+
+@login_required
+# @hx_request_required
+def objective_list_search(request):
+ """
+ This view is used to to search objective, returns searched and filtered objects.
+ Returns:
+ All the filtered and searched object will based on userlevel.
+ """
+ search_val = request.GET.get("search")
+ if search_val is None:
+ search_val = ""
+
+ user = request.user
+ employee = Employee.objects.filter(employee_user_id=user).first()
+ is_manager = Employee.objects.filter(
+ employee_work_info__reporting_manager_id=employee
+ )
+
+ if request.user.has_perm("pms.view_employeeobjective"):
+ # based on permission
+ objective_own = EmployeeObjective.objects.filter(employee_id=employee)
+ objective_own = objective_own.distinct()
+ objective_all = EmployeeObjective.objects.all()
+ context = objective_filter_pagination(request, objective_own, objective_all)
+
+ context = objective_filter_pagination(request, objective_own, objective_all)
+
+ elif is_manager:
+ # if user is a manager
+ employees_ids = [employee.id for employee in is_manager]
+ objective_own = EmployeeObjective.objects.filter(employee_id=employee).filter(
+ objective__icontains=search_val
+ )
+ objective_all = EmployeeObjective.objects.filter(
+ employee_id__in=employees_ids
+ ).filter(objective__icontains=search_val)
+ context = objective_filter_pagination(request, objective_own, objective_all)
+
+ else:
+ # for normal user
+ objective_own = EmployeeObjective.objects.filter(employee_id=employee).filter(
+ objective__icontains=search_val
+ )
+ objective_all = EmployeeObjective.objects.none()
+ context = objective_filter_pagination(request, objective_own, objective_all)
+ template = "okr/okr_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
+# @hx_request_required
+def objective_dashboard_view(request):
+ """
+ This view is used to to search objective, returns searched and filtered objects.
+ Returns:
+ All the filtered and searched object will based on userlevel.
+ """
+ emp_objectives = EmployeeObjectiveFilter(request.GET).qs
+ return render(
+ request,
+ "okr/emp_objective/emp_objective_dashboard_view.html",
+ {
+ 'emp_objectives':emp_objectives
+ }
+ )
+
+
+def objective_history(emp_obj_id):
+ """
+ This view is used to get history of EmployeeObjective, return objects.
+ Args:
+ id (int): Primarykey of EmployeeObjective.
+ Returns:
+ All the history of EmployeeObjective.
+ """
+
+ def pair_history(iterable):
+ """this function return two history pair"""
+ a, b = tee(iterable)
+ next(b, None)
+ return zip(a, b)
+
+ changed_key_results = []
+
+ def key_result_history(key_result):
+ """key result history"""
+ key_result_iterator = (
+ key_result.history.all().order_by("history_date").iterator()
+ )
+ for record_pair in pair_history(key_result_iterator):
+ old_record, new_record = record_pair
+ delta = new_record.diff_against(old_record)
+ history_user_id = delta.new_record.history_user
+ history_change_date = delta.new_record.history_date
+ employee = Employee.objects.filter(employee_user_id=history_user_id).first()
+ key_result = delta.new_record.key_result_id
+ changed_key_results.append(
+ {
+ "delta": delta,
+ "changed_user": employee,
+ "changed_date": history_change_date,
+ "k_r": key_result,
+ }
+ )
+
+ obj_objective = EmployeeObjective.objects.get(id=emp_obj_id)
+ all_key_results = EmployeeKeyResult.objects.filter(
+ employee_objective_id=obj_objective
+ )
+
+ for key_result in all_key_results:
+ # loop each key result and generate it's history
+ key_result_history(key_result)
+ changed_key_results.reverse()
+ return changed_key_results
+
+
+@login_required
+def objective_detailed_view(request, obj_id, **kwargs):
+ """
+ this function is used to update the key result of objectives
+ args:
+ obj_id(int) : pimarykey of EmployeeObjective
+ return:
+ objects to objective_detailed_view
+ """
+
+ objective = Objective.objects.get(id=obj_id)
+ 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()
+ # progress of objective calculation
+ # total_kr = key_results_all.count()
+ # try:
+ # progress = int(
+ # sum(key_result.progress_percentage for key_result in key_results_all) / (total_kr)
+ # )
+ # except (ZeroDivisionError, TypeError):
+ # progress = 0
+
+ # objective_form = ObjectiveForm(instance=objective)
+ # history = objective_history(emp_obj_id)
+ previous_data = request.GET.urlencode()
+ data_dict = parse_qs(previous_data)
+ now = datetime.datetime.now()
+ context = {
+ "emp_objectives": emp_objectives,
+ "pd": previous_data,
+ "filter_dict": data_dict,
+ # "employee_key_results": key_results,
+ "objective": objective,
+ # "comments": comments,
+ # "historys": history,
+ # "progress": progress,
+ # "objective_form": objective_form,
+ "key_result_form": KeyResultForm,
+ "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)
+
+
+@login_required
+@hx_request_required
+def objective_detailed_view_activity(request, id):
+ """
+ This view is used to show objective activity template ,using htmx
+ Args:
+ id (int): Primary key of EmployeeObjective.
+ Returns:
+ it will return history,comment object to objective_detailed_view_activity.
+ """
+
+ objective = EmployeeObjective.objects.get(id=id)
+ key_result_history = objective_history(id)
+ history = objective.tracking()
+ comments = Comment.objects.filter(employee_objective_id=objective)
+ activity_list = []
+ for hist in history:
+ hist["date"] = hist["pair"][0].history_date
+ activity_list.append(hist)
+ for com in comments:
+ comment = {
+ "type": "comment",
+ "comment": com,
+ "date": com.created_at,
+ }
+ activity_list.append(comment)
+
+ for key in key_result_history:
+ key_result = {
+ "type": "key_result",
+ "key_result": key,
+ "date": key["changed_date"],
+ }
+ activity_list.append(key_result)
+
+ activity_list = sorted(activity_list, key=lambda x: x["date"], reverse=True)
+
+ context = {
+ "objective": objective,
+ "historys": history,
+ "comments": comments,
+ "activity_list": activity_list,
+ }
+ return render(request, "okr/objective_detailed_view_activity.html", context)
+
+
+@login_required
+@hx_request_required
+def objective_detailed_view_comment(request, id):
+ """
+ This view is used to create comment object for objective activity, using htmx
+ Args:
+ id (int): Primary key of EmployeeObjective.
+ Returns:
+ it will redirect to objective_detailed_view_activity.
+ """
+ comment_form = ObjectiveCommentForm(request.POST)
+ if comment_form.is_valid():
+ objective = EmployeeObjective.objects.get(id=id)
+ form = comment_form.save(commit=False)
+ form.employee_id = request.user.employee_get
+ form.employee_objective_id = objective
+ form.save()
+
+ return redirect(objective_detailed_view_activity, 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):
+ """
+ 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,
+ }
+ template = "okr/kr_list.html"
+ return render(request, template, context)
+
+
+@login_required
+@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
+ Args:
+ obj_id (int): Primary key of EmployeeObjective.
+ Returns:
+ All the filtered and searched object will based on userlevel.
+ """
+
+ objective = EmployeeObjective.objects.get(id=id)
+ status = request.POST.get("objective_status")
+ objective.status = status
+ objective.save()
+ messages.info(
+ request,
+ _("Objective %(objective)s status updated")
+ % {"objective": objective.objective},
+ )
+ return redirect(objective_detailed_view_activity, id)
+
+
+@login_required
+@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
+ Args:
+ obj_id (int): Primarykey of EmployeeObjective.
+ kr_id (int): Primarykey of EmployeeKeyResult.
+ Returns:
+ All the filtered and searched object will based on userlevel.
+ """
+
+ status = request.POST.get("key_result_status")
+ employee_key_result = EmployeeKeyResult.objects.get(id=kr_id)
+
+ current_value = employee_key_result.current_value
+ target_value = employee_key_result.target_value
+
+ if current_value >= target_value:
+ employee_key_result.status = "Closed"
+ else:
+ employee_key_result.status = status
+ employee_key_result.save()
+ messages.info(request, _("Status has been updated"))
+ # return redirect(objective_detailed_view_activity, id=obj_id)
+ response = redirect(objective_detailed_view_activity, id=obj_id)
+ return HttpResponse(
+ response.content.decode("utf-8") + ""
+ )
+
+
+@login_required
+@hx_request_required
+def objective_detailed_view_current_value(request, kr_id):
+ """
+ This view is used to update current value of key result, return redirect to view . using htmx
+ Args:
+ kr_id (int): Primarykey of EmployeeKeyresult.
+ Returns:
+ All the history of EmployeeObjective.
+ """
+ if request.method == "POST":
+ current_value = request.POST.get("current_value")
+ employee_key_result = EmployeeKeyResult.objects.get(id=kr_id)
+ target_value = employee_key_result.target_value
+ objective_id = employee_key_result.employee_objective_id.id
+ if int(current_value) < target_value:
+ employee_key_result.current_value = current_value
+ employee_key_result.save()
+ messages.info(
+ request,
+ _("Current value of %(employee_key_result)s updated")
+ % {"employee_key_result": employee_key_result},
+ )
+ return redirect(objective_detailed_view_activity, objective_id)
+
+ elif int(current_value) == target_value:
+ employee_key_result.current_value = current_value
+ employee_key_result.status = "Closed"
+ employee_key_result.save()
+ messages.info(
+ request,
+ _("Current value of %(employee_key_result)s updated")
+ % {"employee_key_result": employee_key_result},
+ )
+ # return redirect(objective_detailed_view_activity, objective_id)
+ response = redirect(objective_detailed_view_activity, objective_id)
+ return HttpResponse(
+ response.content.decode("utf-8") + ""
+ )
+
+ elif int(current_value) > target_value:
+ messages.warning(request, _("Current value is greater than target value"))
+ return redirect(objective_detailed_view_activity, objective_id)
+ messages.error(request, _("Error occurred during current value updation"))
+ return redirect(objective_detailed_view_activity, objective_id)
+
+
+@login_required
+def objective_archive(request, id):
+ """
+ this function is used to archive the objective
+ args:
+ id(int) : pimarykey of EmployeeObjective
+ return:
+ redirect to objective_list_view
+ """
+ objective = Objective.objects.get(id=id)
+ if objective.archive:
+ objective.archive = False
+ objective.save()
+ messages.info(request, _("Objective un-archived successfully!."))
+ elif not objective.archive:
+ objective.archive = True
+ objective.save()
+ messages.info(request, _("Objective archived successfully!."))
+ return redirect(f"/pms/objective-list-view?{request.environ['QUERY_STRING']}")
+
+
+@login_required
+@pms_owner_and_manager_can_enter(perm="pms.view_employeeobjective")
+def view_employee_objective(request, emp_obj_id):
+ """
+ This function is used to render individual view of the employee objective
+ args:
+ emp_obj_id(int) : pimarykey of EmployeeObjective
+ return:
+ redirect to individual view of employee objective
+ """
+ emp_objective = EmployeeObjective.objects.get(id=emp_obj_id)
+ context = {
+ "instance": emp_objective,
+ "objective_key_result_status": EmployeeObjective.STATUS_CHOICES,
+ }
+ return render(request, "okr/emp_obj_single.html", context)
+
+
+@login_required
+@manager_can_enter(perm="pms.add_employeeobjective")
+def update_employee_objective(request, emp_obj_id):
+ """
+ This function is used to update the employee objective
+ args:
+ emp_obj_id(int) : pimarykey of EmployeeObjective
+ return:
+ redirect to form of employee objective
+ """
+ emp_objective = EmployeeObjective.objects.get(id=emp_obj_id)
+ form = EmployeeObjectiveForm(instance=emp_objective)
+ if request.method == "POST":
+ form = EmployeeObjectiveForm(request.POST, instance=emp_objective)
+ if form.is_valid:
+ emp_obj = form.save(commit=False)
+ emp_obj.save()
+ messages.success(request, _("Employee objective Updated successfully"))
+ return HttpResponse("")
+ context = {"form": form, "k_form": KRForm()}
+ return render(request, "okr/emp_objective_form.html", context=context)
+
+
+@login_required
+@manager_can_enter(perm="pms.delete_employeeobjective")
+def archive_employee_objective(request, emp_obj_id):
+ """
+ This function is used to archive or unarchive the employee objective
+ args:
+ emp_obj_id(int) : pimarykey of EmployeeObjective
+ return:
+ redirect to detailed of employee objective
+ """
+ emp_objective = EmployeeObjective.objects.get(id=emp_obj_id)
+ obj_id = emp_objective.objective_id.id
+ single_view = eval(request.GET.get("single_view"))
+ if emp_objective.archive:
+ emp_objective.archive = False
+ emp_objective.save()
+ messages.success(request, _("Objective un-archived successfully!."))
+ elif not emp_objective.archive:
+ emp_objective.archive = True
+ emp_objective.save()
+ messages.success(request, _("Objective archived successfully!."))
+ if single_view:
+ return redirect(f"/pms/objective-detailed-view/{obj_id}")
+ else:
+ return redirect(objective_list_view)
+
+
+
+@login_required
+@manager_can_enter(perm="pms.delete_employeeobjective")
+def delete_employee_objective(request, emp_obj_id):
+ """
+ This function is used to delete the employee objective
+ args:
+ emp_obj_id(int) : pimarykey of EmployeeObjective
+ return:
+ redirect to detailed of employee objective
+ """
+ emp_objective = EmployeeObjective.objects.get(id=emp_obj_id)
+ employee = emp_objective.employee_id
+ objective = emp_objective.objective_id
+ single_view = request.GET.get("single_view")
+ emp_objective.delete()
+ objective.assignees.remove(employee)
+ messages.success(request, _("Objective deleted successfully!."))
+ if not single_view:
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
+ else:
+ return HttpResponse("")
+
+
+@login_required
+@manager_can_enter(perm="pms.change_employeeobjective")
+def change_employee_objective_status(request, emp_obj):
+ """
+ This function is used to change status of the employee objective
+ args:
+ emp_obj_id(int) : pimarykey of EmployeeObjective
+ return:
+ a message
+ """
+ emp_objective = EmployeeObjective.objects.get(id=emp_obj)
+ status = request.POST.get("status")
+ if emp_objective.status != status:
+ emp_objective.status = status
+ emp_objective.save()
+ messages.success(
+ 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}.",
+ redirect=f"/pms/objective-detailed-view/{emp_objective.objective_id.id}",
+ )
+ else:
+ messages.info(
+ request, _("The status of the objective is the same as selected.")
+ )
+
+ return redirect(f"/pms/view-employee-objective/{emp_objective.id}/")
+
+
+@login_required
+@manager_can_enter(perm="pms.add_employeekeyresult")
+def key_result_view(request):
+ """
+ This view is used to view key result,
+ Args:
+ request: Request object.
+ Returns:
+ if errorr occur it will return errorr message.
+ """
+ krs =KeyResultFilter(request.GET).qs
+ krs= group_by_queryset(
+ krs, 'employee_objective_id__employee_id', request.GET.get("page"), "page"
+ )
+ context = {
+ "krs": krs,
+ "key_result_status": EmployeeKeyResult.STATUS_CHOICES,
+ }
+ return render(request, "okr/key_result/kr_dashboard_view.html", context=context)
+
+
+@login_required
+@manager_can_enter(perm="pms.add_employeekeyresult")
+def key_result_creation(request, obj_id, obj_type):
+ """
+ This view is used to create key result,
+ Args:
+ id (int): Primarykey of EmployeeObjective.
+ obj_type (str): type of objecitve
+ Returns:
+ if errorr occur it will return errorr message .
+ """
+
+ employee = request.user.employee_get
+ if obj_type == "individual":
+ objective = EmployeeObjective.objects.filter(id=int(obj_id))
+ start_date = None
+ end_date = None
+ for obj in objective:
+ start_date = obj.start_date
+ end_date = obj.end_date
+ key_result_form = KeyResultForm(
+ employee=employee, initial={"start_date": start_date, "end_date": end_date}
+ )
+ else:
+ objective_ids = json.loads(obj_id)
+ for objective_id in objective_ids:
+ objective = EmployeeObjective.objects.filter(id=objective_id).first()
+ start_date = objective.start_date
+ end_date = objective.end_date
+ key_result_form = KeyResultForm(
+ employee=employee, initial={"start_date": start_date, "end_date": end_date}
+ )
+ context = {
+ "key_result_form": key_result_form,
+ "objective_id": obj_id,
+ "objective_type": obj_type,
+ }
+ if obj_type == "multiple":
+ # for job position or department the context should have all the related object ids
+ value = context.pop("objective_id")
+ context["objective_ids"] = value
+ if request.method == "POST":
+ if obj_type == "individual":
+ employee_objective_id = EmployeeObjective.objects.get(id=int(obj_id))
+ form_key_result = KeyResultForm(
+ request.POST, initial={"employee_objective_id": employee_objective_id}
+ )
+ if form_key_result.is_valid():
+ form = form_key_result.save(commit=False)
+ form.start_value = form.current_value
+ form.employee_objective_id = employee_objective_id
+ form.save()
+ messages.success(request, _("Key result created"))
+ return redirect(objective_detailed_view, obj_id)
+ else:
+ context["key_result_form"] = form_key_result
+
+ elif obj_type == "multiple":
+ # If the objective is for job postion or department
+ # The id will be list of objective id
+ objective_ids = json.loads(obj_id)
+ for objective_id in objective_ids:
+ objective = EmployeeObjective.objects.filter(id=objective_id).first()
+ form_key_result = KeyResultForm(
+ request.POST, initial={"employee_objective_id": objective}
+ )
+ if form_key_result.is_valid():
+ form = form_key_result.save(commit=False)
+ form.start_value = form.current_value
+ form.employee_id = objective.employee_id
+ form.employee_objective_id = objective
+ form.save()
+ else:
+ context["key_result_form"] = form_key_result
+ return render(
+ request, "okr/key_result/key_result_creation.html", context
+ )
+ messages.success(request, _("Key results created"))
+ return redirect(objective_list_view)
+ return render(request, "okr/key_result/key_result_creation.html", context)
+
+
+@login_required
+@hx_request_required
+@manager_can_enter(perm="pms.add_employeekeyresult")
+def key_result_creation_htmx(request, id):
+ """
+ This view is used to create key result in objective detailed view, using htmx
+ Args:
+ id (int): Primarykey of EmployeeObjective.
+ obj_type (str): type of objecitve
+ Returns:
+ if errorr occure it will return errorr message .
+ """
+ object = EmployeeObjective.objects.filter(id=id)
+ start_date = None
+ end_date = None
+ for obj in object:
+ start_date = obj.start_date
+ end_date = obj.end_date
+ key_result_form = KeyResultForm(
+ initial={"start_date": start_date, "end_date": end_date}
+ )
+ context = {"key_result_form": key_result_form, "objecitve_id": id}
+ objective = EmployeeObjective.objects.get(id=id)
+ if request.method == "POST":
+ initial_data = {"employee_objective_id": objective}
+ form_key_result = KeyResultForm(request.POST, initial=initial_data)
+ if form_key_result.is_valid():
+ form = form_key_result.save(commit=False)
+ form.start_value = form.current_value
+ form.employee_objective_id = objective
+ form.save()
+ messages.success(request, _("Key result created"))
+ response = render(
+ request, "okr/key_result/key_result_creation_htmx.html", context
+ )
+ return HttpResponse(
+ response.content.decode("utf-8") + ""
+ )
+ context["key_result_form"] = form_key_result
+ return render(request, "okr/key_result/key_result_creation_htmx.html", context)
+
+
+@login_required
+@hx_request_required
+@manager_can_enter(perm="pms.update_employeekeyresult")
+def key_result_update(request, id):
+ """
+ This view is used to update key result, using htmx
+ Args:
+ id (int): Primarykey of EmployeeKeyResult.
+ Returns:
+ success or errors message.
+ """
+
+ key_result = EmployeeKeyResult.objects.get(id=id)
+ key_result_form = KeyResultForm(instance=key_result)
+ context = {"key_result_form": key_result_form, "key_result_id": key_result.id}
+ if request.method == "POST":
+ key_result_form = KeyResultForm(request.POST, instance=key_result)
+ key_result_form.initial["employee_objective_id"] = (
+ key_result.employee_objective_id
+ ) # adding intial objective value to the form
+ if key_result_form.is_valid():
+ key_result_form.save()
+ messages.info(request, _("Key result updated"))
+ response = render(request, "okr/key_result/key_result_update.html", context)
+ return HttpResponse(
+ response.content.decode("utf-8") + ""
+ )
+ else:
+ context["key_result_form"] = key_result_form
+ return render(request, "okr/key_result/key_result_update.html", context)
+
+
+# feedback section
+def send_feedback_notifications(request, form):
+ # Send notification to employee
+ if form.employee_id:
+ employee = form.employee_id
+ notify.send(
+ request.user.employee_get,
+ recipient=employee.employee_user_id,
+ verb="You have received feedback!",
+ verb_ar="لقد تلقيت ملاحظات!",
+ verb_de="Sie haben Feedback erhalten!",
+ verb_es="¡Has recibido retroalimentación!",
+ verb_fr="Vous avez reçu des commentaires !",
+ redirect=f"/pms/feedback-detailed-view/{form.id}",
+ icon="chatbox-ellipses",
+ )
+
+ # Send notification to manager
+ if form.manager_id:
+ manager = form.manager_id
+ notify.send(
+ request.user.employee_get,
+ recipient=manager.employee_user_id,
+ verb="You have been assigned as a manager in a feedback!",
+ verb_ar="لقد تم تعيينك كمدير في ملاحظة!",
+ verb_de="Sie wurden als Manager in einem Feedback zugewiesen!",
+ verb_es="¡Has sido asignado como manager en un feedback!",
+ verb_fr="Vous avez été désigné comme manager dans un commentaire !",
+ redirect=f"/pms/feedback-detailed-view/{form.id}",
+ icon="chatbox-ellipses",
+ )
+
+ # Send notification to subordinates
+ if form.subordinate_id:
+ subordinates = form.subordinate_id.all()
+ for subordinate in subordinates:
+ notify.send(
+ request.user.employee_get,
+ recipient=subordinate.employee_user_id,
+ verb="You have been assigned as a subordinate in a feedback!",
+ verb_ar="لقد تم تعيينك كمرؤوس في ملاحظة!",
+ verb_de="Sie wurden als Untergebener in einem Feedback zugewiesen!",
+ verb_es="¡Has sido asignado como subordinado en un feedback!",
+ verb_fr="Vous avez été désigné comme subordonné dans un commentaire !",
+ redirect=f"/pms/feedback-detailed-view/{form.id}",
+ icon="chatbox-ellipses",
+ )
+
+ # Send notification to colleagues
+ if form.colleague_id:
+ colleagues = form.colleague_id.all()
+ for colleague in colleagues:
+ notify.send(
+ request.user.employee_get,
+ recipient=colleague.employee_user_id,
+ verb="You have been assigned as a colleague in a feedback!",
+ verb_ar="لقد تم تعيينك كزميل في ملاحظة!",
+ verb_de="Sie wurden als Kollege in einem Feedback zugewiesen!",
+ verb_es="¡Has sido asignado como colega en un feedback!",
+ verb_fr="Vous avez été désigné comme collègue dans un commentaire !",
+ redirect=f"/pms/feedback-detailed-view/{form.id}",
+ icon="chatbox-ellipses",
+ )
+
+
+@login_required
+@manager_can_enter(perm="pms.add_feedback")
+def feedback_creation(request):
+ """
+ This view is used to create feedback object.
+ Returns:
+ it will return feedback creation html.
+ """
+ employee = request.user.employee_get
+ # if employe
+ form = FeedbackForm(employee=employee)
+ context = {
+ "feedback_form": form,
+ }
+ if request.method == "POST":
+ form = FeedbackForm(request.POST)
+ if form.is_valid():
+ if key_result_ids := request.POST.getlist("employee_key_results_id"):
+ for key_result_id in key_result_ids:
+ key_result = EmployeeKeyResult.objects.filter(
+ id=key_result_id
+ ).first()
+ feedback_form = form.save()
+ feedback_form.employee_key_results_id.add(key_result)
+ instance = form.save()
+ messages.success(request, _("Feedback created successfully."))
+ send_feedback_notifications(request, form=instance)
+ return redirect(feedback_list_view)
+ else:
+ context["feedback_form"] = form
+ return render(request, "feedback/feedback_creation.html", context)
+
+
+@login_required
+@manager_can_enter(perm="pms.add_feedback")
+def feedback_creation_ajax(request):
+ """
+ This view is used to create feedback object.
+ Returns:
+ it will return feedback creation html.
+ """
+ # this ajax request is used to get the Key result and manager of the choosen employee
+ is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
+ if is_ajax:
+ if request.method == "POST":
+ employee_id = request.POST.get("employee_id")
+ key_results = EmployeeKeyResult.objects.filter(
+ employee_objective_id__employee_id=employee_id
+ ).values()
+ employee_work_info = EmployeeWorkInformation.objects.filter(
+ employee_id__id=employee_id
+ ).first()
+ reporting_manager_id = employee_work_info.reporting_manager_id
+ if reporting_manager_id:
+ reporting_manager = {
+ "id": reporting_manager_id.id or None,
+ "employee_first_name": reporting_manager_id.employee_first_name
+ or None,
+ "employee_last_name": reporting_manager_id.employee_last_name
+ or None,
+ }
+ else:
+ reporting_manager = None
+ return JsonResponse(
+ {
+ "key_results": list(key_results),
+ "reporting_manager": reporting_manager,
+ }
+ )
+ return JsonResponse({"status": "Invalid request"}, status=400)
+
+
+@login_required
+@hx_request_required
+@manager_can_enter(perm="pms.change_feedback")
+def feedback_update(request, id):
+ """
+ This view is used to update the feedback.
+ Args:
+ id(int) : primarykey of the feedback.
+ Returns:
+ it will redirect to feedback_detailed_view.
+ """
+
+ feedback = Feedback.objects.get(id=id)
+ form = FeedbackForm(instance=feedback)
+ feedback_started = Answer.objects.filter(feedback_id=feedback)
+ context = {"feedback_form": form}
+ if feedback_started:
+ messages.error(request, _("Ongoing feedback is not editable!."))
+ response = render(request, "feedback/feedback_update.html", context)
+ return HttpResponse(
+ response.content.decode("utf-8") + ""
+ )
+
+ if request.method == "POST":
+ form = FeedbackForm(request.POST, instance=feedback)
+ if form.is_valid():
+ form = form.save()
+ messages.info(request, _("Feedback updated successfully!."))
+ send_feedback_notifications(request, form)
+ response = render(request, "feedback/feedback_update.html", context)
+ return HttpResponse(
+ response.content.decode("utf-8") + ""
+ )
+ else:
+ context["feedback_form"] = form
+ return render(request, "feedback/feedback_update.html", context)
+
+
+@login_required
+def filter_pagination_feedback(
+ request, self_feedback, requested_feedback, all_feedback, anonymous_feedback
+):
+ """
+ This view is used to filter or search the feedback object ,
+
+ Args:
+ self_feedback (queryset): self feedback filtered queryset.
+ requested_feedback (queryset): requested feedback filtered queryset.
+ all_feedback (queryset): all feedback filtered queryset.
+
+ Returns:
+ it will return the filtered and searched object.
+
+ """
+ previous_data = request.GET.urlencode()
+ initial_data = {"archive": False} # set initial value of archive filter to False
+ feedback_filter_own = FeedbackFilter(
+ request.GET or initial_data, queryset=self_feedback
+ )
+ feedback_filter_requested = FeedbackFilter(
+ request.GET or initial_data, queryset=requested_feedback
+ )
+ feedback_filter_all = FeedbackFilter(
+ request.GET or initial_data, queryset=all_feedback
+ )
+ anonymous_feedback = anonymous_feedback
+ feedback_paginator_own = Paginator(feedback_filter_own.qs, get_pagination())
+ feedback_paginator_requested = Paginator(
+ feedback_filter_requested.qs, get_pagination()
+ )
+ feedback_paginator_all = Paginator(feedback_filter_all.qs, get_pagination())
+ page_number = request.GET.get("page")
+
+ feedbacks_own = feedback_paginator_own.get_page(page_number)
+ feedbacks_requested = feedback_paginator_requested.get_page(page_number)
+ feedbacks_all = feedback_paginator_all.get_page(page_number)
+ now = datetime.datetime.now()
+ data_dict = parse_qs(previous_data)
+ get_key_instances(Feedback, data_dict)
+ context = {
+ "superuser": "true",
+ "self_feedback": feedbacks_own,
+ "requested_feedback": feedbacks_requested,
+ "anonymous_feedback": anonymous_feedback,
+ "all_feedbacks": feedbacks_all,
+ "feedback_filter_form": feedback_filter_own.form,
+ "pg": previous_data,
+ "current_date": now,
+ "filter_dict": data_dict,
+ }
+ return context
+
+
+@login_required
+# @hx_request_required
+def feedback_list_search(request):
+ """
+ This view is used to filter or search the feedback object ,
+ Args:
+ Returns:
+ it will return the filtered and searched object.
+ """
+ feedback = request.GET.get("search") # if the search is none the filter will works
+ if feedback is None:
+ feedback = ""
+ employee_id = Employee.objects.get(employee_user_id=request.user)
+ self_feedback = Feedback.objects.filter(employee_id=employee_id).filter(
+ review_cycle__icontains=feedback
+ )
+
+ requested_feedback_ids = []
+ requested_feedback_ids.extend(
+ [i.id for i in Feedback.objects.filter(manager_id=employee_id)]
+ )
+ requested_feedback_ids.extend(
+ [i.id for i in Feedback.objects.filter(colleague_id=employee_id)]
+ )
+ requested_feedback_ids.extend(
+ [i.id for i in Feedback.objects.filter(subordinate_id=employee_id)]
+ )
+
+ requested_feedback = Feedback.objects.filter(pk__in=requested_feedback_ids).filter(
+ review_cycle__icontains=feedback
+ )
+ all_feedback = Feedback.objects.all().filter(review_cycle__icontains=feedback)
+ anonymous_feedback = (
+ AnonymousFeedback.objects.filter(employee_id=employee_id)
+ if not request.user.has_perm("pms.view_feedback")
+ else AnonymousFeedback.objects.all()
+ )
+
+ reporting_manager_to = employee_id.reporting_manager.all()
+ if request.user.has_perm("pms.view_feedback"):
+ context = filter_pagination_feedback(
+ request, self_feedback, requested_feedback, all_feedback, anonymous_feedback
+ )
+ elif reporting_manager_to:
+ employees_id = [i.id for i in reporting_manager_to]
+ all_feedback = Feedback.objects.filter(employee_id__in=employees_id).filter(
+ review_cycle__icontains=feedback
+ )
+ context = filter_pagination_feedback(
+ request, self_feedback, requested_feedback, all_feedback, anonymous_feedback
+ )
+ else:
+ all_feedback = Feedback.objects.none()
+ context = filter_pagination_feedback(
+ request, self_feedback, requested_feedback, all_feedback, anonymous_feedback
+ )
+
+ return render(request, "feedback/feedback_list.html", context)
+
+
+@login_required
+def feedback_list_view(request):
+ """
+ This view is used to filter or search the feedback object ,
+ Args:
+ Returns:
+ it will return the filtered and searched object.
+ """
+ user = request.user
+ employee = Employee.objects.filter(employee_user_id=user).first()
+ feedback_requested_ids = Feedback.objects.filter(
+ Q(manager_id=employee, manager_id__is_active=True)
+ | Q(colleague_id=employee, colleague_id__is_active=True)
+ | Q(subordinate_id=employee, subordinate_id__is_active=True)
+ ).values_list("id", flat=True)
+ feedback_own = Feedback.objects.filter(employee_id=employee).filter(
+ archive=False, employee_id__is_active=True
+ )
+ feedback_requested = Feedback.objects.filter(pk__in=feedback_requested_ids).filter(
+ archive=False, employee_id__is_active=True
+ )
+ feedback_all = Feedback.objects.all().filter(
+ archive=False, employee_id__is_active=True
+ )
+ anonymous_feedback = (
+ AnonymousFeedback.objects.filter(employee_id=employee, archive=False)
+ if not request.user.has_perm("pms.view_feedback")
+ else AnonymousFeedback.objects.filter(archive=False)
+ )
+ anonymous_feedback = anonymous_feedback | AnonymousFeedback.objects.filter(
+ anonymous_feedback_id=request.user.id, archive=False
+ )
+ employees = Employee.objects.filter(
+ employee_work_info__reporting_manager_id=employee, is_active=True
+ ) # checking the user is reporting manager or not
+ feedback_available = Feedback.objects.all()
+ if request.user.has_perm("pms.view_feedback"):
+ context = filter_pagination_feedback(
+ request, feedback_own, feedback_requested, feedback_all, anonymous_feedback
+ )
+ elif employees:
+ # based on the reporting manager
+ feedback_all = Feedback.objects.filter(employee_id__in=employees)
+ context = filter_pagination_feedback(
+ request, feedback_own, feedback_requested, feedback_all, anonymous_feedback
+ )
+ else:
+ feedback_all = Feedback.objects.none()
+ context = filter_pagination_feedback(
+ request, feedback_own, feedback_requested, feedback_all, anonymous_feedback
+ )
+ if feedback_available.exists():
+ template = "feedback/feedback_list_view.html"
+ else:
+ template = "feedback/feedback_empty.html"
+ return render(request, template, context)
+
+
+@login_required
+def feedback_detailed_view(request, id, **kwargs):
+ """
+ This view is used to for detailed view of feedback,
+ Args:
+ id(int) : primarykey of the feedback
+ Returns:
+ it will return the feedback object to feedback_detailed_view template .
+ """
+ feedback = Feedback.objects.get(id=id)
+ current_date = datetime.datetime.now()
+ context = {
+ "feedback": feedback,
+ "feedback_status": Feedback.STATUS_CHOICES,
+ "current_date": current_date,
+ }
+ return render(request, "feedback/feedback_detailed_view.html", context)
+
+
+def feedback_detailed_view_answer(request, id, emp_id):
+ """
+ This view is used show answer ,
+ Args:
+ id(int) : primarykey of the feedback.
+ emp_id(int) : primarykey of the Employee.
+ Returns:
+ it will return the answers .
+ """
+ employee = Employee.objects.filter(id=emp_id).first()
+ feedback = Feedback.objects.filter(id=id).first()
+ answers = Answer.objects.filter(employee_id=employee, feedback_id=feedback)
+ context = {
+ "answers": answers,
+ }
+ return render(request, "feedback/feedback_detailed_view_answer.html", context)
+
+
+@login_required
+def feedback_answer_get(request, id, **kwargs):
+ """
+ This view is used to render the feedback questions ,
+ Args:
+ id(int) : primarykey of the feedback.
+ Returns:
+ it will redirect to feedaback_answer.html .
+ """
+
+ user = request.user
+ employee = Employee.objects.filter(employee_user_id=user).first()
+ feedback = Feedback.objects.get(id=id)
+ answer = Answer.objects.filter(feedback_id=feedback, employee_id=employee)
+ question_template = feedback.question_template_id
+ questions = question_template.question.all()
+ options = QuestionOptions.objects.all()
+ feedback_employees = (
+ [feedback.employee_id]
+ + [feedback.manager_id]
+ + list(feedback.colleague_id.all())
+ + list(feedback.subordinate_id.all())
+ )
+ if not employee in feedback_employees:
+ messages.info(request, _("You are not allowed to answer"))
+ return redirect(feedback_list_view)
+
+ # Employee does not have an answer object
+ for employee in feedback_employees:
+ has_answer = Answer.objects.filter(
+ employee_id=employee, feedback_id=feedback
+ ).exists()
+ if has_answer:
+ feedback.status = "Closed"
+ feedback.save()
+
+ # Check if the feedback has already been answered
+ if answer:
+ messages.info(request, _("Feedback already answered"))
+ return redirect(feedback_list_view)
+
+ context = {
+ "questions": questions,
+ "options": options,
+ "feedback": feedback,
+ }
+
+ return render(request, "feedback/answer/feedback_answer.html", context)
+
+
+@login_required
+def feedback_answer_post(request, id):
+ """
+ This view is used to create feedback answer ,
+ Args:
+ id(int) : primarykey of the feedback.
+ Returns:
+ it will redirect to feedback_list_view if the form was success full.
+ """
+
+ user = request.user
+ employee = Employee.objects.filter(employee_user_id=user).first()
+ feedback = Feedback.objects.get(id=id)
+ question_template = feedback.question_template_id
+ questions = question_template.question.all()
+
+ if request.method == "POST":
+ for question in questions:
+ if request.POST.get(f"answer{question.id}"):
+ answer = request.POST.get(f"answer{question.id}")
+ Answer.objects.get_or_create(
+ answer={"answer": answer},
+ question_id=question,
+ feedback_id=feedback,
+ employee_id=employee,
+ )
+ feedback.status = "On Track"
+ feedback.save()
+ for key_result in feedback.employee_key_results_id.all():
+ if request.POST.get(f"key_result{key_result.id}"):
+ answer = request.POST.get(f"key_result{key_result.id}")
+ KeyResultFeedback.objects.get_or_create(
+ answer={"answer": answer},
+ key_result_id=key_result,
+ feedback_id=feedback,
+ employee_id=request.user.employee_get,
+ )
+ messages.success(
+ request,
+ _("Feedback %(review_cycle)s has been answered successfully!.")
+ % {"review_cycle": feedback.review_cycle},
+ )
+ return redirect(feedback_list_view)
+
+
+@login_required
+def feedback_answer_view(request, id, **kwargs):
+ """
+ This view is used to view the feedback for employee.
+ Args:
+ id(int) : primarykey of the feedback.
+ Returns:
+ it will return feedback answer object to feedback_answer_view.
+ """
+
+ user = request.user
+ employee = Employee.objects.filter(employee_user_id=user).first()
+ feedback = Feedback.objects.get(id=id)
+ answers = Answer.objects.filter(feedback_id=feedback, employee_id=employee)
+ key_result_feedback = KeyResultFeedback.objects.filter(
+ feedback_id=feedback, employee_id=employee
+ )
+
+ if not answers:
+ messages.info(request, _("Feedback is not answered yet"))
+ return redirect(feedback_list_view)
+
+ context = {
+ "answers": answers,
+ "feedback_id": feedback,
+ "key_result_feedback": key_result_feedback,
+ }
+ return render(request, "feedback/answer/feedback_answer_view.html", context)
+
+
+@login_required
+@manager_can_enter(perm="pms.delete_feedback")
+def feedback_delete(request, id):
+ """
+ This view is used to delete the feedback.
+ Args:
+ id(int) : primarykey of the feedback.
+ Returns:
+ it will redirect to feedback_list_view.
+ """
+ try:
+ feedback = Feedback.objects.filter(id=id).first()
+ answered = Answer.objects.filter(feedback_id=feedback).first()
+ if (
+ feedback.status == "Closed"
+ or feedback.status == "Not Started"
+ and not answered
+ ):
+ feedback.delete()
+ messages.success(
+ request,
+ _("Feedback %(review_cycle)s deleted successfully!")
+ % {"review_cycle": feedback.review_cycle},
+ )
+
+ else:
+ messages.warning(
+ request,
+ _("You can't delete feedback %(review_cycle)s with status %(status)s")
+ % {"review_cycle": feedback.review_cycle, "status": feedback.status},
+ )
+ return redirect(feedback_list_view)
+
+ except Feedback.DoesNotExist:
+ messages.error(request, _("Feedback not found."))
+ except ProtectedError:
+ messages.error(request, _("Related entries exists"))
+ return redirect(feedback_list_view)
+
+
+@login_required
+@hx_request_required
+def feedback_detailed_view_status(request, id):
+ """
+ This view is used to update status of feedback.
+ Args:
+ obj_id (int): Primarykey of feedback.
+ Returns:
+ message to the view
+ """
+ status = request.POST.get("feedback_status")
+ feedback = get_object_or_404(Feedback, id=id)
+ answer = Answer.objects.filter(feedback_id=feedback)
+ if status == "Not Started" and answer:
+ messages.warning(request, _("Feedback is already started"))
+ return render(request, "messages.html")
+ feedback.status = status
+ feedback.save()
+ if (feedback.status) == status:
+ messages.info(
+ request, _("Feedback status updated to %(status)s") % {"status": _(status)}
+ )
+ return render(request, "messages.html")
+ messages.info(
+ request,
+ _("Error occurred during status update to %(status)s") % {"status": _(status)},
+ )
+ return render(request, "message.html")
+
+
+@login_required
+def feedback_archive(request, id):
+ """
+ this function is used to archive the feedback for employee
+ args:
+ id(int): primarykey of feedback
+ """
+
+ feedback = Feedback.objects.get(id=id)
+ if feedback.archive:
+ feedback.archive = False
+ feedback.save()
+ messages.info(request, _("Feedback un-archived successfully!."))
+ elif not feedback.archive:
+ feedback.archive = True
+ feedback.save()
+ messages.info(request, _("Feedback archived successfully!."))
+ return redirect(feedback_list_view)
+
+
+@login_required
+def feedback_status(request):
+ """this function is used to un-archive the feedback
+ args:
+ id(int): primarykey of feedback
+ emp_id(int): primarykey of feedback
+ """
+
+ is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
+ if is_ajax:
+ if request.method == "POST":
+ employee_id = request.POST.get("employee_id")
+ feedback_id = request.POST.get("feedback_id")
+ feedback = Feedback.objects.get(id=feedback_id)
+ employee = Employee.objects.filter(id=employee_id).first()
+ answer = Answer.objects.filter(employee_id=employee, feedback_id=feedback)
+ status = _("Completed") if answer else _("Not-completed")
+ return JsonResponse({"status": status})
+ return JsonResponse({"status": "Invalid request"}, status=400)
+
+
+@login_required
+@manager_can_enter(perm="pms.add_question")
+def question_creation(request, id):
+ """
+ This view is used to create question object.
+ Args:
+ id(int) : primarykey of the question template.
+
+ Returns:
+ it will redirect to question_template_detailed_view.
+ """
+ if request.method == "POST":
+ form = QuestionForm(request.POST)
+ question_template = QuestionTemplate.objects.get(id=id)
+ feedback_ongoing = Feedback.objects.filter(
+ question_template_id=question_template
+ ).first()
+ if feedback_ongoing:
+ messages.info(request, _("Question template is used in feedback."))
+ return redirect(question_template_detailed_view, id)
+ if form.is_valid():
+ obj_question = form.save(commit=False)
+ obj_question.template_id = question_template
+ obj_question.save()
+
+ if obj_question.question_type == "4":
+ # checking the question type is multichoice
+ option_a = request.POST.get("option_a")
+ option_b = request.POST.get("option_b")
+ option_c = request.POST.get("option_c")
+ option_d = request.POST.get("option_d")
+ QuestionOptions(
+ question_id=obj_question,
+ option_a=option_a,
+ option_b=option_b,
+ option_c=option_c,
+ option_d=option_d,
+ ).save()
+ messages.success(request, _("Question created successfully."))
+ return redirect(question_template_detailed_view, id)
+ messages.success(request, _("Question created successfully."))
+ return redirect(question_template_detailed_view, id)
+ else:
+ messages.error(request, _("Error occurred during question creation!"))
+ return redirect(question_template_detailed_view, id)
+
+
+@login_required
+def question_view(request, id):
+ """
+ This view is used to view question object.
+ Args:
+ id(int) : primarykey of the question template.
+ Returns:
+ it will redirect to question_template_detailed_view.
+ """
+ question_template = QuestionTemplate.objects.get(id=id)
+ question_formset = modelformset_factory(Question, form=QuestionForm, extra=0)
+
+ questions = question_template.question.all()
+ formset = question_formset(queryset=questions)
+ options = []
+ question_types = ["text", "ratings", "boolean", "Multi-choices", "likert"]
+
+ for question in questions:
+ question_options = QuestionOptions.objects.filter(question_id=question)
+ options.extend(question_options)
+ context = {
+ "question_template": question_template,
+ "questions": questions,
+ "question_options": options,
+ "question_types": question_types,
+ "formset": formset,
+ }
+ return render(
+ request,
+ "feedback/question_template/question_template_detailed_view.html",
+ context,
+ )
+
+
+@login_required
+@manager_can_enter(perm="pms.change_question")
+def question_update(request, temp_id, q_id):
+ """
+ This view is used to update question object.
+ Args:
+ id (int): primarykey of question
+ temp_id (int): primarykey of question_template
+ Returns:
+ it will redirect to question_template_detailed_view.
+
+ """
+ if request.method == "POST":
+ question = Question.objects.get(id=q_id)
+ form = QuestionForm(request.POST, instance=question)
+ if form.is_valid():
+ question_type = form.cleaned_data["question_type"]
+ if question_type == "4":
+ # if question is Multi-choices
+ option_a = form.cleaned_data["option_a"]
+ option_b = form.cleaned_data["option_b"]
+ option_c = form.cleaned_data["option_c"]
+ option_d = form.cleaned_data["option_d"]
+ options, created = QuestionOptions.objects.get_or_create(
+ question_id=question
+ )
+ options.option_a = option_a
+ options.option_b = option_b
+ options.option_c = option_c
+ options.option_d = option_d
+ options.save()
+ form.save()
+ messages.info(request, _("Question updated successfully."))
+ return redirect(question_template_detailed_view, temp_id)
+ else:
+ form.save()
+ question_options = QuestionOptions.objects.filter(question_id=question)
+ if question_options:
+ question_options.delete()
+ messages.info(request, _("Question updated successfully."))
+ return redirect(question_template_detailed_view, temp_id)
+ else:
+ # Form submission had errors
+ messages.error(
+ request,
+ "\n".join(
+ [
+ f"{field}: {error}"
+ for field, errors in form.errors.items()
+ for error in errors
+ ]
+ ),
+ )
+ return redirect(question_template_detailed_view, temp_id)
+
+
+@login_required
+@manager_can_enter(perm="pms.delete_question")
+def question_delete(request, id):
+ """
+ This view is used to delete question object.
+ Args:
+ id (int): primarykey of question
+ Returns:
+ it will redirect to question_template_detailed_view.
+ """
+
+ try:
+ # Code that may trigger the FOREIGN KEY constraint failed error
+ question = Question.objects.filter(id=id).first()
+ temp_id = question.template_id.id
+ QuestionOptions.objects.filter(question_id=question).delete()
+ question.delete()
+ messages.success(request, _("Question deleted successfully!"))
+ return redirect(question_template_detailed_view, temp_id)
+
+ except IntegrityError:
+ # Code to handle the FOREIGN KEY constraint failed error
+ messages.error(
+ request, _("Failed to delete question: Question template is in use.")
+ )
+
+ except Question.DoesNotExist:
+ messages.error(request, _("Question not found."))
+ except ProtectedError:
+ messages.error(request, _("Related entries exists"))
+ return redirect(question_template_detailed_view, temp_id)
+
+
+@login_required
+@manager_can_enter(perm="pms.add_questiontemplate")
+def question_template_creation(request):
+ """
+ This view is used to create question template object.
+ Args:
+ Returns:
+ it will redirect to question_template_detailed_view.
+ """
+ if request.method == "POST":
+ form = QuestionTemplateForm(request.POST)
+ if form.is_valid():
+ instance = form.save()
+ return redirect(question_template_detailed_view, instance.id)
+ else:
+ messages.error(
+ request,
+ "\n".join(
+ [
+ f"{field}: {error}"
+ for field, errors in form.errors.items()
+ for error in errors
+ ]
+ ),
+ )
+ return redirect(question_template_view)
+
+
+@login_required
+@manager_can_enter(perm="pms.view_questiontemplate")
+def question_template_view(request):
+ """
+ This view is used to view question template object.
+ Returns:
+ it will redirect to question_template_detailed_view.
+ """
+ question_templates = QuestionTemplate.objects.all()
+ context = {"form": QuestionTemplateForm, "question_templates": question_templates}
+ return render(
+ request, "feedback/question_template/question_template_view.html", context
+ )
+
+
+@login_required
+@manager_can_enter(perm="pms.view_questiontemplate")
+def question_template_hx_view(request):
+ """
+ This view is used to view question template object in htmx.
+ """
+ question_templates = QuestionTemplate.objects.all()
+ context = {"question_templates": question_templates}
+ return render(
+ request, "feedback/question_template/question_template_list.html", context
+ )
+
+
+@login_required
+@manager_can_enter(perm="pms.view_questiontemplate")
+def question_template_detailed_view(request, template_id, **kwargs):
+ """
+ This view is used to view question template object.
+ Args:
+ id (int): primarykey of question template
+ temp_id (int): primarykey of question_template
+ Returns:
+ it will redirect to question_template_detailed_view.
+ """
+
+ question_template = QuestionTemplate.objects.filter(id=template_id).first()
+ if not question_template:
+ messages.error(request, _("Question template does not exist"))
+ return redirect(question_template_view)
+ questions = question_template.question.all().order_by("-id")
+ question_types = ["text", "ratings", "boolean", "multi-choices", "likert"]
+ options = QuestionOptions.objects.filter(question_id__in=questions)
+
+ # passing individual form
+ question_form_list = [QuestionForm(instance=question) for question in questions]
+ context = {
+ "question_template": question_template,
+ "questions": questions,
+ "question_options": options,
+ "question_types": question_types,
+ "form": QuestionForm,
+ "form_list": question_form_list,
+ }
+ return render(
+ request,
+ "feedback/question_template/question_template_detailed_view.html",
+ context,
+ )
+
+
+@login_required
+@manager_can_enter(perm="pms.change_questiontemplate")
+def question_template_update(request, template_id):
+ """
+ This view is used to update question template object.
+ Args:
+ id (int): primarykey of question template
+ Returns:
+ it will redirect to question_template_view.
+
+ """
+ question_template = QuestionTemplate.objects.filter(id=template_id).first()
+ question_update_form = QuestionTemplateForm(instance=question_template)
+ context = {"question_update_form": question_update_form}
+ if request.method == "POST":
+ form = QuestionTemplateForm(request.POST, instance=question_template)
+ if form.is_valid():
+ form.save()
+ messages.info(request, _("Question template updated"))
+ # return redirect(question_template_view)
+ context["question_update_form"] = form
+ return render(
+ request, "feedback/question_template/question_template_update.html", context
+ )
+
+
+@login_required
+@manager_can_enter(perm="pms.delete_questiontemplate")
+def question_template_delete(request, template_id):
+ """
+ This view is used to delete question template object.
+ Args:
+ id (int): primarykey of question template
+ Returns:
+ it will redirect to question_template_view.
+ """
+ try:
+ question_template = QuestionTemplate.objects.get(id=template_id)
+ if Feedback.objects.filter(question_template_id=question_template):
+ messages.info(request, _("This template is using in a feedback"))
+ else:
+ question_template.delete()
+ messages.success(
+ request, _("The question template is deleted successfully !.")
+ )
+ except QuestionTemplate.DoesNotExist:
+ messages.error(request, _("question template not found."))
+ except ProtectedError:
+ messages.error(request, _("Related entries exists"))
+ return redirect("question-template-hx-view")
+
+
+@login_required
+@manager_can_enter(perm="pms.view_period")
+def period_view(request):
+ """
+ This view is used to view period objects.
+ Returns:
+ it will return to period_view.
+ """
+
+ periods = Period.objects.all()
+ context = {
+ "periods": periods,
+ }
+ return render(request, "period/period_view.html", context)
+
+
+@login_required
+@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,
+ }
+ return render(request, "period/period_list.html", context=context)
+
+
+@login_required
+@manager_can_enter(perm="pms.add_period")
+@hx_request_required
+def period_create(request):
+ """
+ This view is used to create period objects.
+ Returns:
+ it will redirect to period_view.
+ """
+ context = {"form": PeriodForm()}
+ if request.method == "POST":
+ form = PeriodForm(request.POST)
+ if form.is_valid():
+ form.save()
+ messages.success(request, _("Period creation was Successful "))
+ else:
+ context["form"] = form
+ return render(request, "period/period_create.html", context)
+
+
+@login_required
+@manager_can_enter(perm="pms.change_period")
+def period_update(request, period_id):
+ """
+ This view is used to update period objects.
+ Args:
+ id (int): primarykey of period
+ Returns:
+ it will redirect to period_view.
+ """
+
+ period = Period.objects.filter(id=period_id).first()
+ form = PeriodForm(instance=period)
+ context = {"form": form}
+ if request.method == "POST":
+ form = PeriodForm(request.POST, instance=period)
+ if form.is_valid():
+ form.save()
+ messages.info(request, _("Period updated Successfully. "))
+ else:
+ context["form"] = form
+ return render(request, "period/period_update.html", context)
+
+
+@login_required
+@manager_can_enter(perm="pms.delete_period")
+def period_delete(request, period_id):
+ """
+ This view is used to delete period objects.
+ Args:
+ id (int): primarykey of period
+ Returns:
+ it will redirect to period_view.
+ """
+ try:
+ obj_period = Period.objects.get(id=period_id)
+ obj_period.delete()
+ messages.info(request, _("Period deleted successfully."))
+ except Period.DoesNotExist:
+ messages.error(request, _("Period not found."))
+ except ProtectedError:
+ messages.error(request, _("Related entries exists"))
+ return redirect("period-hx-view")
+
+
+@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
+ """
+ is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
+ if is_ajax:
+ if request.method == "POST":
+ data = json.load(request)
+ period_obj = Period.objects.get(id=data)
+ start_date = period_obj.start_date
+ end_date = period_obj.end_date
+ return JsonResponse({"start_date": start_date, "end_date": end_date})
+ return JsonResponse({"failed": "failed"})
+ return HttpResponse(status=204)
+
+
+@login_required
+def dashboard_view(request):
+ """
+ This view is used to view dashboard.
+ Returns:
+ it will redirect to dashboard.
+ """
+ user = request.user
+ employee = Employee.objects.filter(employee_user_id=user).first()
+ is_manager = Employee.objects.filter(
+ employee_work_info__reporting_manager_id=employee
+ )
+
+ if user.has_perm("pms.view_employeeobjective") and user.has_perm(
+ "pms.view_feedback"
+ ):
+ count_objective = EmployeeObjective.objects.all().count()
+ count_key_result = EmployeeKeyResult.objects.all().count()
+ count_feedback = Feedback.objects.all().count()
+ okr_at_risk = EmployeeObjective.objects.filter(status="At Risk")
+ elif is_manager:
+ employees_ids = [employee.id for employee in is_manager]
+ count_objective = EmployeeObjective.objects.filter(
+ employee_idemployee_objective_id__employee_id__in=employees_ids
+ ).count()
+ count_key_result = EmployeeObjective.objects.filter(
+ emp_obj_id__employee_id__in=employees_ids
+ ).count()
+ count_feedback = Feedback.objects.filter(employee_id__in=employees_ids).count()
+ okr_at_risk = EmployeeObjective.objects.filter(
+ employee_objective_id__employee_id__in=employees_ids
+ ).filter(status="At Risk")
+ else:
+ count_objective = EmployeeObjective.objects.filter(employee_id=employee).count()
+ count_key_result = EmployeeKeyResult.objects.filter(
+ employee_objective_id__employee_id=employee
+ ).count()
+ count_feedback = Feedback.objects.filter(employee_id=employee).count()
+ okr_at_risk = EmployeeObjective.objects.filter(employee_id=employee).filter(
+ status="At Risk"
+ )
+ context = {
+ "count_objective": count_objective,
+ "count_key_result": count_key_result,
+ "count_feedback": count_feedback,
+ "okr_at_risk": okr_at_risk,
+ }
+ return render(request, "dashboard/pms_dashboard.html", context)
+
+
+@login_required
+def dashboard_objective_status(request):
+ """objective dashboard data"""
+ is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
+ if is_ajax and request.method == "GET":
+ objective_status = EmployeeObjective.STATUS_CHOICES
+ data = {"message": _("No data Found...")}
+ for status in objective_status:
+ objectives = EmployeeObjective.objects.filter(status=status[0],archive=False)
+ objectives_count = filtersubordinates(
+ request, queryset=objectives, perm="pms.view_employeeobjective"
+ ).count()
+ if objectives_count:
+ data.setdefault("objective_label", []).append(status[1])
+ data.setdefault("objective_value", []).append(objectives_count)
+ return JsonResponse(data)
+
+
+@login_required
+def dashboard_key_result_status(request):
+ """key result dashboard data"""
+ is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
+ if is_ajax and request.method == "GET":
+ key_result_status = EmployeeKeyResult.STATUS_CHOICES
+ data = {"message": _("No data Found...")}
+ for i in key_result_status:
+ key_results = EmployeeKeyResult.objects.filter(status=i[0])
+ key_results_count = filtersubordinates(
+ request,
+ queryset=key_results,
+ perm="pms.view_employeekeyresult",
+ field="employee_objective_id__employee_id",
+ ).count()
+ if key_results_count:
+ data.setdefault("key_result_label", []).append(i[1])
+ data.setdefault("key_result_value", []).append(key_results_count)
+ return JsonResponse(data)
+
+
+@login_required
+def dashboard_feedback_status(request):
+ """feedback dashboard data"""
+ is_ajax = request.headers.get("X-Requested-With") == "XMLHttpRequest"
+ if is_ajax and request.method == "GET":
+ feedback_status = Feedback.STATUS_CHOICES
+ data = {"message": _("No data Found...")}
+ for i in feedback_status:
+ feedbacks = Feedback.objects.filter(status=i[0])
+ feedback_count = filtersubordinates(
+ request, queryset=feedbacks, perm="pms.view_feedback"
+ ).count()
+ if feedback_count:
+ data.setdefault("feedback_label", []).append(i[1])
+ data.setdefault("feedback_value", []).append(feedback_count)
+ return JsonResponse(data)
+
+
+def filtersubordinates(request, queryset, perm=None, field=None):
+ """
+ This method is used to filter out subordinates queryset element.
+ """
+ user = request.user
+ if user.has_perm(perm):
+ return queryset
+ manager = Employee.objects.filter(employee_user_id=user).first()
+ if manager:
+ if field is not None:
+ queryset = queryset.filter(
+ **{f"{field}__employee_work_info__reporting_manager_id": manager}
+ ) | queryset.filter(**{field: manager})
+ else:
+ queryset = queryset.filter(
+ employee_id__employee_work_info__reporting_manager_id=manager
+ ) | queryset.filter(employee_id=manager)
+ return queryset
+ else:
+ queryset = queryset.filter(employee_id=user.employee_get)
+ return queryset
+
+
+@login_required
+def create_period(request):
+ """
+ This is an ajax method to return json response to create stage related
+ to the project in the task-all form fields
+ """
+
+ if request.method == "GET":
+ form = PeriodForm()
+ if request.method == "POST":
+ form = PeriodForm(request.POST)
+ if form.is_valid():
+ instance = form.save()
+ return JsonResponse(
+ {
+ "id": instance.id,
+ "name": instance.period_name,
+ "start_date": instance.start_date,
+ "end_date": instance.end_date,
+ }
+ )
+ errors = form.errors.as_json()
+ return JsonResponse({"errors": errors})
+ return render(request, "okr/create_period.html", context={"form": form})
+
+
+@login_required
+def objective_bulk_archive(request):
+ """
+ This method is used to archive/un-archive bulk objectivs
+ """
+ ids = request.POST["ids"]
+ ids = json.loads(ids)
+ is_active = False
+ message = _("un-archived")
+ if request.GET.get("is_active") == "False":
+ is_active = True
+ message = _("archived")
+ for objective_id in ids:
+ objective_obj = EmployeeObjective.objects.get(id=objective_id)
+ objective_obj.archive = is_active
+ objective_obj.save()
+ messages.success(
+ request,
+ _("{objective} is {message}").format(
+ objective=objective_obj, message=message
+ ),
+ )
+ return JsonResponse({"message": "Success"})
+
+
+@login_required
+@manager_can_enter(perm="pms.delete_employeeobjective")
+def objective_bulk_delete(request):
+ """
+ This method is used to bulk delete objective
+ """
+ ids = request.POST["ids"]
+ ids = json.loads(ids)
+ for objective_id in ids:
+ try:
+ objective = EmployeeObjective.objects.get(id=objective_id)
+ if objective.status == "Not Started" or objective.status == "Closed":
+ objective.delete()
+ messages.success(
+ request,
+ _("%(employee)s's %(objective)s deleted")
+ % {
+ "objective": objective.objective,
+ "employee": objective.employee_id,
+ },
+ )
+ else:
+ messages.warning(
+ request,
+ _("You can't delete objective %(objective)s with status %(status)s")
+ % {"objective": objective.objective, "status": objective.status},
+ )
+ except EmployeeObjective.DoesNotExist:
+ messages.error(request, _("Objective not found."))
+
+ return JsonResponse({"message": "Success"})
+
+
+@login_required
+def feedback_bulk_archive(request):
+ """
+ This method is used to archive/un-archive bulk feedbacks
+ """
+ ids = request.POST["ids"]
+ ids = json.loads(ids)
+ is_active = False
+ message = _("un-archived")
+ if request.GET.get("is_active") == "False":
+ is_active = True
+ message = _("archived")
+ for feedback_id in ids:
+ feedback_id = Feedback.objects.get(id=feedback_id)
+ feedback_id.archive = is_active
+ feedback_id.save()
+ messages.success(
+ request,
+ _("{feedback} is {message}").format(feedback=feedback_id, message=message),
+ )
+ return JsonResponse({"message": "Success"})
+
+
+@login_required
+@manager_can_enter(perm="pms.delete_feedback")
+def feedback_bulk_delete(request):
+ """
+ This method is used to bulk delete feedbacks
+ """
+ ids = request.POST["ids"]
+ ids = json.loads(ids)
+ for feedback_id in ids:
+ try:
+ feedback = Feedback.objects.get(id=feedback_id)
+ if feedback.status == "Closed" or feedback.status == "Not Started":
+ feedback.delete()
+ messages.success(
+ request,
+ _("Feedback %(review_cycle)s deleted successfully!")
+ % {"review_cycle": feedback.review_cycle},
+ )
+ else:
+ messages.warning(
+ request,
+ _(
+ "You can't delete feedback %(review_cycle)s with status %(status)s"
+ )
+ % {
+ "review_cycle": feedback.review_cycle,
+ "status": feedback.status,
+ },
+ )
+
+ except Feedback.DoesNotExist:
+ messages.error(request, _("Feedback not found."))
+ return JsonResponse({"message": "Success"})
+
+
+@login_required
+def objective_select(request):
+ """
+ This method is used to return all the id of the employees to select the employee row
+ """
+ page_number = request.GET.get("page")
+ table = request.GET.get("tableName")
+ user = request.user.employee_get
+ employees = EmployeeObjective.objects.filter(employee_id=user, archive=False)
+ if page_number == "all":
+ if table == "all":
+ if request.user.has_perm("pms.view_employeeobjective"):
+ employees = EmployeeObjective.objects.filter(archive=False)
+ else:
+ employees = EmployeeObjective.objects.filter(
+ employee_id__employee_user_id=request.user
+ ) | EmployeeObjective.objects.filter(
+ employee_id__employee_work_info__reporting_manager_id__employee_user_id=request.user
+ )
+ else:
+ employees = EmployeeObjective.objects.filter(
+ employee_id=user, archive=False
+ )
+
+ employee_ids = [str(emp.id) for emp in employees]
+ total_count = employees.count()
+
+ context = {"employee_ids": employee_ids, "total_count": total_count}
+
+ return JsonResponse(context, safe=False)
+
+
+@login_required
+def objective_select_filter(request):
+ """
+ This method is used to return all the ids of the filtered employees
+ """
+ page_number = request.GET.get("page")
+ filtered = request.GET.get("filter")
+ filters = json.loads(filtered) if filtered else {}
+ table = request.GET.get("tableName")
+ user = request.user.employee_get
+
+ employee_filter = ObjectiveFilter(filters, queryset=EmployeeObjective.objects.all())
+ if page_number == "all":
+ if table == "all":
+ if request.user.has_perm("pms.view_employeeobjective"):
+ employee_filter = ObjectiveFilter(
+ filters, queryset=EmployeeObjective.objects.all()
+ )
+ else:
+ employee_filter = ObjectiveFilter(
+ filters,
+ queryset=EmployeeObjective.objects.filter(
+ employee_id__employee_work_info__reporting_manager_id__employee_user_id=request.user
+ ),
+ )
+ else:
+ employee_filter = ObjectiveFilter(
+ filters, queryset=EmployeeObjective.objects.filter(employee_id=user)
+ )
+ # Get the filtered queryset
+ filtered_employees = employee_filter.qs
+
+ employee_ids = [str(emp.id) for emp in filtered_employees]
+ total_count = filtered_employees.count()
+
+ context = {"employee_ids": employee_ids, "total_count": total_count}
+
+ return JsonResponse(context)
+
+
+@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
+
+ if form.is_valid():
+ feedback = form.save(commit=False)
+ feedback.anonymous_feedback_id = anonymous_id
+ feedback.save()
+ if feedback.based_on == "employee":
+ try:
+ notify.send(
+ User.objects.filter(username="Horilla Bot").first(),
+ recipient=feedback.employee_id.employee_user_id,
+ verb="You received an anonymous feedback!",
+ verb_ar="لقد تلقيت تقييمًا مجهولًا!",
+ verb_de="Sie haben anonymes Feedback erhalten!",
+ verb_es="¡Has recibido un comentario anónimo!",
+ verb_fr="Vous avez reçu un feedback anonyme!",
+ redirect="/pms/feedback-view/",
+ icon="bag-check",
+ )
+ except:
+ pass
+ return HttpResponse("")
+ else:
+ form = AnonymousFeedbackForm()
+
+ context = {"form": form, "create": True}
+ return render(request, "anonymous/anonymous_feedback_form.html", context)
+
+
+@login_required
+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":
+ form = AnonymousFeedbackForm(request.POST, instance=feedback)
+ if form.is_valid():
+ feedback = form.save(commit=False)
+ feedback.anonymous_feedback_id = anonymous_id
+ feedback.save()
+ return HttpResponse("")
+ context = {"form": form, "create": False}
+ return render(request, "anonymous/anonymous_feedback_form.html", context)
+
+
+@login_required
+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=obj_id)
+ if feedback.archive:
+ feedback.archive = False
+ feedback.save()
+ messages.info(request, _("Feedback un-archived successfully!."))
+ elif not feedback.archive:
+ feedback.archive = True
+ feedback.save()
+ messages.info(request, _("Feedback archived successfully!."))
+ return redirect(feedback_list_view)
+
+
+@login_required
+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=obj_id)
+ feedback.delete()
+ messages.success(request, _("Feedback deleted successfully!"))
+
+ except IntegrityError:
+ messages.error(
+ request, _("Failed to delete feedback: Feedback template is in use.")
+ )
+
+ except AnonymousFeedback.DoesNotExist:
+ messages.error(request, _("Feedback not found."))
+
+ except ProtectedError:
+ messages.error(request, _("Related entries exists"))
+
+ return redirect(feedback_list_view)
+
+
+@login_required
+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})
+
+
+@login_required
+@manager_can_enter(perm="pms.add_employeekeyresult")
+def employee_keyresult_creation(request, emp_obj_id):
+ """
+ This view is for employee keyresult creation , and returns a employee keyresult form.
+ Returns:
+ GET:
+ employee keyresult form
+ POST:
+ employee keyresult created, and returnes to employee objective details view
+ """
+ emp_objective = EmployeeObjective.objects.get(id=emp_obj_id)
+ employee = emp_objective.employee_id
+ emp_key_result = EmployeekeyResultForm(
+ initial={"employee_objective_id": emp_objective}
+ )
+ if request.method == "POST":
+ emp_key_result = EmployeekeyResultForm(request.POST)
+ if emp_key_result.is_valid():
+ emp_key_result.save()
+ emp_objective.update_objective_progress()
+ key_result = emp_key_result.cleaned_data["key_result_id"]
+
+ emp_objective.key_result_id.add(key_result)
+ # assignees = emp_key_result.cleaned_data['assignees']
+ # start_date =emp_key_result.cleaned_data['start_date']
+
+ messages.success(request, _("Key result assigned sucessfully."))
+
+ notify.send(
+ request.user.employee_get,
+ recipient=employee.employee_user_id,
+ verb="You got an Key Result!.",
+ verb_ar="لقد حصلت على نتيجة رئيسية!",
+ verb_de="Du hast ein Schlüsselergebnis erreicht!",
+ verb_es="¡Has conseguido un Resultado Clave!",
+ verb_fr="Vous avez obtenu un Résultat Clé!",
+ redirect=f"/pms/objective-detailed-view/{emp_objective.objective_id.id}",
+ )
+ return HttpResponse("")
+ context = {
+ "form": emp_key_result,
+ "emp_objective": emp_objective,
+ "k_form": KRForm(),
+ }
+ return render(request, "okr/key_result/kr_form.html", context=context)
+
+
+@login_required
+@manager_can_enter(perm="pms.add_employeekeyresult")
+def employee_keyresult_update(request, kr_id):
+ """
+ This function is for update employee keyresult, and returns a employee keyresult form.
+ Returns:
+ GET:
+ employee keyresult form
+ POST:
+ employee keyresult updated, and returnes to employee objective details view
+ """
+ emp_kr = EmployeeKeyResult.objects.get(id=kr_id)
+ employee = emp_kr.employee_objective_id.employee_id
+ emp_key_result = EmployeekeyResultForm(instance=emp_kr)
+ if request.method == "POST":
+ emp_key_result = EmployeekeyResultForm(request.POST, instance=emp_kr)
+ if emp_key_result.is_valid():
+ emp_key_result.save()
+ emp_kr.employee_objective_id.update_objective_progress()
+
+ # assignees = emp_key_result.cleaned_data['assignees']
+ # start_date =emp_key_result.cleaned_data['start_date']
+
+ messages.success(request, _("Key result Updated sucessfully."))
+
+ notify.send(
+ request.user.employee_get,
+ recipient=employee.employee_user_id,
+ verb="Your Key Result updated.",
+ verb_ar="تم تحديث نتيجتك الرئيسية.",
+ verb_de="Ihr Schlüsselergebnis wurde aktualisiert.",
+ verb_es="Se ha actualizado su Resultado Clave.",
+ verb_fr="Votre Résultat Clé a été mis à jour.",
+ redirect=f"/pms/objective-detailed-view/{emp_kr.employee_objective_id.objective_id.id}",
+ )
+ return HttpResponse("")
+
+ context = {
+ "form": emp_key_result,
+ "update": True,
+ }
+ return render(request, "okr/key_result/kr_form.html", context=context)
+
+
+@login_required
+@manager_can_enter(perm="pms.delete_employeekeyresult")
+def delete_employee_keyresult(request, kr_id):
+ """
+ This function is used to delete the employee key result
+ args:
+ kr_id(int) : pimarykey of EmployeeKeyResult
+ return:
+ redirect to detailed of employee objective
+ """
+ emp_kr = EmployeeKeyResult.objects.get(id=kr_id)
+ # employee = emp_kr.employee_id
+ objective = emp_kr.employee_objective_id.objective_id
+ emp_objective = emp_kr.employee_objective_id
+ emp_kr.delete()
+ emp_objective.update_objective_progress()
+ # objective.assignees.remove(employee)
+ messages.success(request, _("Objective deleted successfully!."))
+ if request.GET.get('dashboard'):
+ return redirect(f"/pms/dashboard-view")
+ return redirect(f"/pms/objective-detailed-view/{objective.id}")
+
+
+@login_required
+def employee_keyresult_update_status(request, kr_id):
+ """
+ This function is used to delete the employee key result
+ args:
+ kr_id(int) : pimarykey of EmployeeKeyResult
+ return:
+ redirect to detailed of employee objective
+ """
+ emp_kr = EmployeeKeyResult.objects.get(id=kr_id)
+ status = request.POST.get("key_result_status")
+ emp_kr.status = status
+ emp_kr.save()
+ messages.success(request, _("Key result sattus changed to {}.").format(status))
+ return redirect(
+ f"/pms/kr-table-view/{emp_kr.employee_objective_id.id}?&objective_id={emp_kr.employee_objective_id.objective_id.id}"
+ )
+
+
+@login_required
+def key_result_current_value_update(request):
+ """
+ This method is used to update keyresult current value
+ """
+ try:
+ current_value = eval(request.POST.get("current_value"))
+ emp_kr_id = eval(request.POST.get("emp_key_result_id"))
+ emp_kr = EmployeeKeyResult.objects.get(id=emp_kr_id)
+ if current_value <= emp_kr.target_value:
+ emp_kr.current_value = current_value
+ emp_kr.save()
+ emp_kr.employee_objective_id.update_objective_progress()
+ return JsonResponse({"type": "sucess"})
+ except:
+ return JsonResponse({"type": "error"})
+
+
+@login_required
+def view_meetings(request):
+ """
+ This view is used to view the meeting ,
+ Returns:
+ it will redirect to view_meetings.html .
+ """
+ previous_data = request.GET.urlencode()
+ meetings = Meetings.objects.filter(is_active=True)
+ if not request.user.has_perm("pms.view_meetings"):
+ employee_id = request.user.employee_get
+ meetings = meetings.filter(Q(employee_id = employee_id) | Q(manager = employee_id)).distinct().order_by("-id")
+ filter_form = MeetingsFilter()
+
+ meetings = paginator_qry(meetings, request.GET.get("page"))
+ requests_ids = json.dumps([instance.id for instance in meetings.object_list])
+ data_dict = parse_qs(previous_data)
+ get_key_instances(Meetings, data_dict)
+
+ context = {
+ "meetings": meetings,
+ "filter_form": filter_form.form,
+ "requests_ids":requests_ids,
+ }
+ return render(request, "meetings/view_meetings.html", context)
+
+
+@login_required
+@permission_required("pms.add_meetings")
+def create_meetings(request):
+ """
+ This view is used to create the meeting ,
+ Returns:
+ Get:
+ it renders form.html to create the meeting.
+ Post:
+ it will redirect to view_meetings.html .
+ """
+ instance_id = eval(str(request.GET.get("instance_id")))
+ instance = None
+ initial = {"manager": request.user.employee_get,"employee_id":None}
+ if instance_id and isinstance(instance_id, int):
+ instance = Meetings.objects.filter(id=instance_id).first()
+ initial = {}
+ form = MeetingsForm(instance=instance, initial = initial)
+ if request.method == "POST":
+ form = MeetingsForm(request.POST, instance=instance)
+ if form.is_valid():
+ form.save()
+ messages.success(request, _("Meeting added successfully"))
+ return HttpResponse("")
+ return render(
+ request,
+ "meetings/form.html",
+ {
+ "form": form,
+ },
+ )
+
+
+@login_required
+@permission_required("pms.change_meetings")
+def archive_meetings(request, id):
+ """
+ This view is used to archive and unarchive the meeting ,
+ Args:
+ meet_id(int) : primarykey of the meeting.
+ employee_id(int) : primarykey of the employee
+ Returns:
+ it will redirect to view_meetings.html .
+ """
+ meeting = Meetings.objects.filter(id=id).first()
+ meeting.is_active = not meeting.is_active
+ meeting.save()
+
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
+
+
+@login_required
+@permission_required("pms.change_meetings")
+def meeting_manager_remove(request,meet_id,manager_id):
+ """
+ This view is used to remove the manager from the meeting ,
+ Args:
+ meet_id(int) : primarykey of the meeting.
+ employee_id(int) : primarykey of the employee
+ Returns:
+ it will redirect to view_meetings.html .
+ """
+ meeting = Meetings.objects.filter(id=meet_id).first()
+ meeting.manager.remove(manager_id)
+ meeting.save()
+ return HttpResponse("")
+
+
+@login_required
+def meeting_employee_remove(request,meet_id,employee_id):
+ """
+ This view is used to remove the employees from the meeting ,
+ Args:
+ meet_id(int) : primarykey of the meeting.
+ employee_id(int) : primarykey of the employee
+ Returns:
+ it will redirect to view_meetings.html .
+ """
+ meeting = Meetings.objects.filter(id=meet_id).first()
+ meeting.employee_id.remove(employee_id)
+ meeting.save()
+ return HttpResponse("")
+
+
+@login_required
+def filter_meetings(request):
+ """
+ This view is used to filter the meeting ,
+ Returns:
+ it will render to meeting_list.html .
+ """
+ previous_data = request.GET.urlencode()
+ filter_obj = MeetingsFilter(request.GET).qs
+
+ if not request.user.has_perm("pms.view_meetings"):
+ employee_id = request.user.employee_get
+ filter_obj = filter_obj.filter(Q(employee_id = employee_id) | Q(manager = employee_id)).distinct().order_by("-id")
+ filter_obj = sortby(request, filter_obj, "sortby")
+ filter_obj = paginator_qry(filter_obj, request.GET.get("page"))
+ requests_ids = json.dumps([instance.id for instance in filter_obj.object_list])
+
+ data_dict = parse_qs(previous_data)
+ get_key_instances(EmployeeObjective, data_dict)
+
+ return render(
+ request,
+ "meetings/meetings_list.html",
+ {
+ "meetings": filter_obj,
+ "pd": previous_data,
+ "filter_dict": data_dict,
+ "requests_ids":requests_ids,
+ },
+ )
+
+
+@login_required
+@meeting_manager_can_enter("pms.change_meetings")
+def add_response(request, id):
+ """
+ This view is used to add the MoM to the meeting ,
+ Args:
+ id(int) : primarykey of the meeting.
+ Returns:
+ it will redirect to view_meetings.html .
+ """
+ meeting = Meetings.objects.filter(id=id).first()
+ if request.method == "POST":
+ response = request.POST.get("response")
+ meeting.response = response
+ meeting.save()
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
+
+
+@login_required
+@meeting_manager_can_enter("pms.change_meetings", answerable=True)
+def meeting_answer_get(request, id, **kwargs):
+ """
+ This view is used to render the Meeting questions ,
+ Args:
+ id(int) : primarykey of the meeting.
+ Returns:
+ it will redirect to meeting_answer.html .
+ """
+
+ employee = request.user.employee_get
+ if employee_id := request.GET.get('emp_id'):
+ employee = Employee.objects.filter(id = employee_id).first()
+ meeting = Meetings.objects.get(id=id)
+ answer = MeetingsAnswer.objects.filter(meeting_id=meeting, employee_id=employee)
+ questions = meeting.question_template.question.all()
+ options = QuestionOptions.objects.all()
+ meeting_employees = meeting.manager.all() | meeting.employee_id.all()
+
+
+ if answer or request.GET.get('emp_id'):
+ return redirect('meeting-answer-view',id=meeting.id, emp_id = employee.id)
+
+ if not employee in meeting_employees:
+ messages.info(request, _("You are not allowed to answer"))
+ return redirect(view_meetings)
+
+ context = {
+ "questions": questions,
+ "options": options,
+ "meeting": meeting,
+ }
+
+ return render(request, "meetings/meeting_answer.html", context)
+
+
+@login_required
+@meeting_manager_can_enter("pms.change_meetings", answerable=True)
+def meeting_answer_post(request, id):
+ """
+ This view is used to create meeting answer ,
+ Args:
+ id(int) : primarykey of the meeting.
+ Returns:
+ it will redirect to view_meeting if the form was success full.
+ """
+
+ employee = request.user.employee_get
+ meeting = Meetings.objects.get(id=id)
+ question_template = meeting.question_template.question.all()
+
+ if request.method == "POST":
+ for question in question_template:
+ if request.POST.get(f"answer{question.id}"):
+ answer = request.POST.get(f"answer{question.id}")
+ MeetingsAnswer.objects.get_or_create(
+ answer={"answer": answer},
+ question_id=question,
+ meeting_id=meeting,
+ employee_id=employee,
+ )
+ messages.success(
+ request,
+ _("Questions for meeting %(meeting)s has been answered successfully!.")
+ % {"meeting": meeting.title},
+ )
+ return redirect(view_meetings)
+
+
+@login_required
+@meeting_manager_can_enter("pms.change_meetings", answerable=True)
+def meeting_answer_view(request, id,emp_id, **kwargs):
+ """
+ This view is used to view the meeting for employee.
+ Args:
+ id(int) : primarykey of the meeting.
+ emp_id(int) : id of the employee
+ Returns:
+ it will return meeting answer object to meeting_answer_view.
+ """
+
+ employee = Employee.objects.filter(id = emp_id).first()
+ meeting = Meetings.objects.get(id=id)
+ answers = MeetingsAnswer.objects.filter(meeting_id=meeting, employee_id=employee)
+
+ context = {
+ "answers": answers,
+ "meeting": meeting,
+ }
+ return render(request, "meetings/meeting_answer_view.html", context)
+
+
+@login_required
+@meeting_manager_can_enter("pms.change_meetings", answerable=True)
+def meeting_question_template_view(request, meet_id):
+ """
+ This view is used to view the activity sidebar page for employee.
+ Args:
+ id(int) : primarykey of the meeting.
+ Returns:
+ it will return meeting answer object to meeting_question_template_view.
+ """
+ employee = request.user.employee_get
+ meeting = Meetings.objects.get(id=meet_id)
+ answer = MeetingsAnswer.objects.filter(meeting_id=meeting, employee_id=employee)
+ is_answered = False
+ if answer:
+ is_answered = True
+ context = {
+ "is_answered": is_answered,
+ "meeting": meeting,
+ }
+ return render(request,"meetings/meeting_question_template_view.html",context)
+
+@login_required
+def meeting_single_view(request, id):
+ meeting = Meetings.objects.filter(id=id).first()
+ context = {"meeting":meeting}
+ requests_ids_json = request.GET.get("requests_ids")
+ if requests_ids_json:
+ requests_ids = json.loads(requests_ids_json)
+ previous_id, next_id = closest_numbers(requests_ids, id)
+ context["requests_ids"] = requests_ids_json
+ context["previous"] = previous_id
+ context["next"] = next_id
+ return render(request,"meetings/meeting_single_view.html",context)
\ No newline at end of file
diff --git a/static/images/ui/unknown_document.svg b/static/images/ui/unknown_document.svg
new file mode 100644
index 000000000..7bea45090
--- /dev/null
+++ b/static/images/ui/unknown_document.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/templates/sidebar.html b/templates/sidebar.html
index c7b833d6b..3c5c45ed8 100755
--- a/templates/sidebar.html
+++ b/templates/sidebar.html
@@ -734,6 +734,13 @@
>{% trans "360 Feedback" %}
+
{% if perms.pms.view_period or request.user|is_reportingmanager %}