wbhuman_resources 1.58.4__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. wbhuman_resources/__init__.py +1 -0
  2. wbhuman_resources/admin/__init__.py +5 -0
  3. wbhuman_resources/admin/absence.py +113 -0
  4. wbhuman_resources/admin/calendars.py +37 -0
  5. wbhuman_resources/admin/employee.py +109 -0
  6. wbhuman_resources/admin/kpi.py +21 -0
  7. wbhuman_resources/admin/review.py +157 -0
  8. wbhuman_resources/apps.py +23 -0
  9. wbhuman_resources/dynamic_preferences_registry.py +119 -0
  10. wbhuman_resources/factories/__init__.py +38 -0
  11. wbhuman_resources/factories/absence.py +109 -0
  12. wbhuman_resources/factories/calendars.py +60 -0
  13. wbhuman_resources/factories/employee.py +80 -0
  14. wbhuman_resources/factories/kpi.py +155 -0
  15. wbhuman_resources/filters/__init__.py +20 -0
  16. wbhuman_resources/filters/absence.py +109 -0
  17. wbhuman_resources/filters/absence_graphs.py +85 -0
  18. wbhuman_resources/filters/calendars.py +28 -0
  19. wbhuman_resources/filters/employee.py +81 -0
  20. wbhuman_resources/filters/kpi.py +35 -0
  21. wbhuman_resources/filters/review.py +134 -0
  22. wbhuman_resources/filters/signals.py +27 -0
  23. wbhuman_resources/locale/de/LC_MESSAGES/django.mo +0 -0
  24. wbhuman_resources/locale/de/LC_MESSAGES/django.po +2207 -0
  25. wbhuman_resources/locale/de/LC_MESSAGES/django.po.translated +2456 -0
  26. wbhuman_resources/locale/en/LC_MESSAGES/django.mo +0 -0
  27. wbhuman_resources/locale/en/LC_MESSAGES/django.po +2091 -0
  28. wbhuman_resources/locale/fr/LC_MESSAGES/django.mo +0 -0
  29. wbhuman_resources/locale/fr/LC_MESSAGES/django.po +2093 -0
  30. wbhuman_resources/management/__init__.py +23 -0
  31. wbhuman_resources/migrations/0001_initial_squashed_squashed_0015_alter_absencerequest_calendaritem_ptr_and_more.py +949 -0
  32. wbhuman_resources/migrations/0016_alter_employeehumanresource_options.py +20 -0
  33. wbhuman_resources/migrations/0017_absencerequest_crossborder_country_and_more.py +55 -0
  34. wbhuman_resources/migrations/0018_remove_position_group_position_groups.py +32 -0
  35. wbhuman_resources/migrations/0019_alter_absencerequest_options_alter_kpi_options_and_more.py +44 -0
  36. wbhuman_resources/migrations/0020_alter_employeeyearbalance_year_alter_review_year.py +27 -0
  37. wbhuman_resources/migrations/0021_alter_position_color.py +18 -0
  38. wbhuman_resources/migrations/0022_remove_review_editable_mode.py +64 -0
  39. wbhuman_resources/migrations/__init__.py +0 -0
  40. wbhuman_resources/models/__init__.py +23 -0
  41. wbhuman_resources/models/absence.py +903 -0
  42. wbhuman_resources/models/calendars.py +370 -0
  43. wbhuman_resources/models/employee.py +1241 -0
  44. wbhuman_resources/models/kpi.py +199 -0
  45. wbhuman_resources/models/preferences.py +40 -0
  46. wbhuman_resources/models/review.py +982 -0
  47. wbhuman_resources/permissions/__init__.py +0 -0
  48. wbhuman_resources/permissions/backend.py +26 -0
  49. wbhuman_resources/serializers/__init__.py +49 -0
  50. wbhuman_resources/serializers/absence.py +308 -0
  51. wbhuman_resources/serializers/calendars.py +73 -0
  52. wbhuman_resources/serializers/employee.py +267 -0
  53. wbhuman_resources/serializers/kpi.py +80 -0
  54. wbhuman_resources/serializers/review.py +415 -0
  55. wbhuman_resources/signals.py +4 -0
  56. wbhuman_resources/tasks.py +195 -0
  57. wbhuman_resources/templates/review/review_report.html +322 -0
  58. wbhuman_resources/tests/__init__.py +1 -0
  59. wbhuman_resources/tests/conftest.py +96 -0
  60. wbhuman_resources/tests/models/__init__.py +0 -0
  61. wbhuman_resources/tests/models/test_absences.py +478 -0
  62. wbhuman_resources/tests/models/test_calendars.py +209 -0
  63. wbhuman_resources/tests/models/test_employees.py +502 -0
  64. wbhuman_resources/tests/models/test_review.py +103 -0
  65. wbhuman_resources/tests/models/test_utils.py +110 -0
  66. wbhuman_resources/tests/signals.py +108 -0
  67. wbhuman_resources/tests/test_permission.py +64 -0
  68. wbhuman_resources/tests/test_tasks.py +74 -0
  69. wbhuman_resources/urls.py +221 -0
  70. wbhuman_resources/utils.py +43 -0
  71. wbhuman_resources/viewsets/__init__.py +61 -0
  72. wbhuman_resources/viewsets/absence.py +312 -0
  73. wbhuman_resources/viewsets/absence_charts.py +328 -0
  74. wbhuman_resources/viewsets/buttons/__init__.py +7 -0
  75. wbhuman_resources/viewsets/buttons/absence.py +32 -0
  76. wbhuman_resources/viewsets/buttons/employee.py +44 -0
  77. wbhuman_resources/viewsets/buttons/kpis.py +16 -0
  78. wbhuman_resources/viewsets/buttons/review.py +195 -0
  79. wbhuman_resources/viewsets/calendars.py +103 -0
  80. wbhuman_resources/viewsets/display/__init__.py +39 -0
  81. wbhuman_resources/viewsets/display/absence.py +334 -0
  82. wbhuman_resources/viewsets/display/calendars.py +83 -0
  83. wbhuman_resources/viewsets/display/employee.py +254 -0
  84. wbhuman_resources/viewsets/display/kpis.py +92 -0
  85. wbhuman_resources/viewsets/display/review.py +429 -0
  86. wbhuman_resources/viewsets/employee.py +210 -0
  87. wbhuman_resources/viewsets/endpoints/__init__.py +42 -0
  88. wbhuman_resources/viewsets/endpoints/absence.py +57 -0
  89. wbhuman_resources/viewsets/endpoints/calendars.py +18 -0
  90. wbhuman_resources/viewsets/endpoints/employee.py +51 -0
  91. wbhuman_resources/viewsets/endpoints/kpis.py +53 -0
  92. wbhuman_resources/viewsets/endpoints/review.py +191 -0
  93. wbhuman_resources/viewsets/kpi.py +280 -0
  94. wbhuman_resources/viewsets/menu/__init__.py +22 -0
  95. wbhuman_resources/viewsets/menu/absence.py +50 -0
  96. wbhuman_resources/viewsets/menu/administration.py +15 -0
  97. wbhuman_resources/viewsets/menu/calendars.py +33 -0
  98. wbhuman_resources/viewsets/menu/employee.py +44 -0
  99. wbhuman_resources/viewsets/menu/kpis.py +18 -0
  100. wbhuman_resources/viewsets/menu/review.py +97 -0
  101. wbhuman_resources/viewsets/mixins.py +14 -0
  102. wbhuman_resources/viewsets/review.py +837 -0
  103. wbhuman_resources/viewsets/titles/__init__.py +18 -0
  104. wbhuman_resources/viewsets/titles/absence.py +30 -0
  105. wbhuman_resources/viewsets/titles/employee.py +18 -0
  106. wbhuman_resources/viewsets/titles/kpis.py +15 -0
  107. wbhuman_resources/viewsets/titles/review.py +62 -0
  108. wbhuman_resources/viewsets/utils.py +28 -0
  109. wbhuman_resources-1.58.4.dist-info/METADATA +8 -0
  110. wbhuman_resources-1.58.4.dist-info/RECORD +111 -0
  111. wbhuman_resources-1.58.4.dist-info/WHEEL +5 -0
@@ -0,0 +1,92 @@
1
+ from typing import Optional
2
+
3
+ from django.utils.translation import gettext as _
4
+ from wbcore.metadata.configs import display as dp
5
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
6
+ Display,
7
+ create_simple_display,
8
+ create_simple_section,
9
+ )
10
+ from wbcore.metadata.configs.display.instance_display.utils import repeat_field
11
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
12
+
13
+ from wbhuman_resources.models.kpi import KPI
14
+
15
+
16
+ class KPIDisplayConfig(DisplayViewConfig):
17
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
18
+ return dp.ListDisplay(
19
+ fields=[
20
+ dp.Field(key="name", label=_("Name")),
21
+ dp.Field(key="handler", label=_("KPI")),
22
+ dp.Field(key="evaluated_persons", label=_("Persons")),
23
+ dp.Field(key="period", label=_("Period")),
24
+ dp.Field(key="parameters", label=_("Parameters")),
25
+ dp.Field(key="is_active", label=_("Is Active")),
26
+ ]
27
+ )
28
+
29
+ def get_instance_display(self) -> Display:
30
+ sections = []
31
+ grid_fields = [
32
+ [repeat_field(2, "name")],
33
+ [repeat_field(2, "handler")],
34
+ [repeat_field(2, "evaluated_persons")],
35
+ ["evaluated_intervals", "is_active"],
36
+ ["goal", "individual_evaluation"],
37
+ ["period", "last_update"],
38
+ ]
39
+ if pk := self.view.kwargs.get("pk", None):
40
+ handler = KPI.objects.get(id=pk).get_handler()
41
+ sections = [
42
+ create_simple_section("parameters_section", _("Parameters"), handler.get_display_grid()),
43
+ create_simple_section("evaluation_section", _("Evaluations"), [["evaluations"]], "evaluations"),
44
+ ]
45
+ grid_fields.extend([["parameters_section", "evaluation_section"]])
46
+ return create_simple_display(grid_fields, sections)
47
+
48
+
49
+ class KPIEvaluationDisplayConfig(DisplayViewConfig):
50
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
51
+ return dp.ListDisplay(
52
+ fields=[
53
+ dp.Field(key="person", label=_("Person")),
54
+ dp.Field(key="evaluated_score", label=_("Evaluated Score")),
55
+ dp.Field(key="goal", label=_("Goal")),
56
+ dp.Field(key="evaluation_date", label=_("Evaluation Date")),
57
+ dp.Field(key="evaluated_period", label=_("Evaluated Period")),
58
+ ]
59
+ )
60
+
61
+ def get_instance_display(self) -> Display:
62
+ return create_simple_display(
63
+ [
64
+ [repeat_field(2, "kpi")],
65
+ [repeat_field(2, "person")],
66
+ [repeat_field(2, "evaluated_period")],
67
+ ["evaluation_date", "last_update"],
68
+ [repeat_field(2, "evaluated_score")],
69
+ ]
70
+ )
71
+
72
+
73
+ class KPIEvaluationPandasDisplayConfig(DisplayViewConfig):
74
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
75
+ pandas_fields = [
76
+ dp.Field(key="person", label=_("Person")),
77
+ dp.Field(key="kpi_name", label=_("KPI")),
78
+ dp.Field(key="period", label=_("Period")),
79
+ dp.Field(key="evaluation_date", label=_("Evaluation Date")),
80
+ dp.Field(key="evaluated_score", label=_("Evaluated Score")),
81
+ dp.Field(
82
+ key="goal",
83
+ label=_("Goal"),
84
+ formatting_rules=[
85
+ dp.FormattingRule(
86
+ style={"fontWeight": "bold"},
87
+ ),
88
+ ],
89
+ ),
90
+ dp.Field(key="progression", label=_("Progression")),
91
+ ]
92
+ return dp.ListDisplay(fields=pandas_fields)
@@ -0,0 +1,429 @@
1
+ from typing import TYPE_CHECKING, Optional
2
+
3
+ from django.utils.translation import gettext as _
4
+ from django.utils.translation import gettext_lazy
5
+ from wbcore.contrib.color.enums import WBColor
6
+ from wbcore.enums import Operator, Unit
7
+ from wbcore.metadata.configs import display as dp
8
+ from wbcore.metadata.configs.display.instance_display import (
9
+ Display,
10
+ Inline,
11
+ Layout,
12
+ Page,
13
+ Style,
14
+ create_simple_display,
15
+ create_simple_section,
16
+ )
17
+ from wbcore.metadata.configs.display.instance_display.operators import default
18
+ from wbcore.metadata.configs.display.instance_display.utils import repeat_field
19
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
20
+
21
+ from wbhuman_resources.models import Review, ReviewQuestion
22
+
23
+ if TYPE_CHECKING:
24
+ from wbhuman_resources.viewsets.review import ReviewAnswerReviewPandasViewSet
25
+
26
+
27
+ def get_legends(model=None):
28
+ list_format = []
29
+ if model:
30
+ for _status, color in model.Status.get_color_map():
31
+ list_format.append(dp.LegendItem(icon=color, label=_status.label, value=_status.value))
32
+ return [dp.Legend(key="status", items=list_format)]
33
+
34
+
35
+ def get_list_formatting(model=None):
36
+ color_conditions = []
37
+ if model:
38
+ for _status, color in model.Status.get_color_map():
39
+ color_conditions.append(
40
+ dp.FormattingRule(condition=("==", _status.name), style={"backgroundColor": color})
41
+ )
42
+ return [
43
+ dp.Formatting(column="status", formatting_rules=color_conditions),
44
+ ]
45
+
46
+
47
+ class ReviewGroupDisplayConfig(DisplayViewConfig):
48
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
49
+ return dp.ListDisplay(
50
+ fields=[
51
+ dp.Field(key="name", label=_("Name")),
52
+ ]
53
+ )
54
+
55
+ def get_instance_display(self) -> Display:
56
+ return create_simple_display(
57
+ [["name", "employees"], [repeat_field(2, "review_section")]],
58
+ [create_simple_section("review_section", _("Review"), [["review"]], "review", collapsed=False)],
59
+ )
60
+
61
+
62
+ class ReviewDisplayConfig(DisplayViewConfig):
63
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
64
+ return dp.ListDisplay(
65
+ fields=[
66
+ dp.Field(key="year", label=gettext_lazy("Year"), width=Unit.PIXEL(80)),
67
+ dp.Field(key="type", label=gettext_lazy("Type"), width=Unit.PIXEL(100)),
68
+ dp.Field(key="reviewee", label=gettext_lazy("Reviewee"), width=Unit.PIXEL(150)),
69
+ dp.Field(key="reviewer", label=gettext_lazy("Reviewer"), width=Unit.PIXEL(150)),
70
+ dp.Field(key="moderator", label=gettext_lazy("Moderator"), width=Unit.PIXEL(150)),
71
+ dp.Field(key="from_date", label=gettext_lazy("From"), width=Unit.PIXEL(140)),
72
+ dp.Field(key="to_date", label=gettext_lazy("To"), width=Unit.PIXEL(140)),
73
+ dp.Field(key="review_deadline", label=gettext_lazy("Deadline"), width=Unit.PIXEL(140)),
74
+ dp.Field(key="status", label=gettext_lazy("Status"), width=Unit.PIXEL(150)),
75
+ dp.Field(key="review", label=gettext_lazy("Review date"), width=Unit.PIXEL(140)),
76
+ dp.Field(key="is_template", label=gettext_lazy("Is Template"), width=Unit.PIXEL(100)),
77
+ dp.Field(key="changed", label=gettext_lazy("Changed"), width=Unit.PIXEL(140)),
78
+ dp.Field(key="review_group", label=gettext_lazy("Group"), width=Unit.PIXEL(150)),
79
+ ],
80
+ legends=get_legends(Review),
81
+ formatting=get_list_formatting(Review),
82
+ )
83
+
84
+ def get_main_information_page(self):
85
+ grid_areas = [
86
+ ["year", "type", repeat_field(2, "status")],
87
+ ["review_group", "is_template", "moderator", "auto_apply_deadline"],
88
+ ]
89
+ if self.view.kwargs.get("pk"):
90
+ instance = self.view.get_object()
91
+ if not instance.is_template:
92
+ grid_areas = [
93
+ ["year", "type", repeat_field(2, "status")],
94
+ ["from_date", "to_date", "review_deadline", "review"],
95
+ ["reviewee", "reviewer", "moderator", "auto_apply_deadline"],
96
+ ]
97
+ if instance.status != Review.Status.PREPARATION_OF_REVIEW:
98
+ if self.request.user.profile in [instance.reviewer, instance.reviewee]:
99
+ grid_areas.append(
100
+ ["completely_filled_reviewee", "completely_filled_reviewer", repeat_field(2, "global_rating")]
101
+ )
102
+ else:
103
+ grid_areas.append(
104
+ ["completely_filled_reviewee", "completely_filled_reviewer", repeat_field(2, ".")]
105
+ )
106
+
107
+ if instance.status in [Review.Status.EVALUATION, Review.Status.VALIDATION]:
108
+ grid_areas.append([repeat_field(2, "signed_reviewee"), repeat_field(2, "signed_reviewer")])
109
+ grid_areas.append([repeat_field(2, "feedback_reviewee"), repeat_field(2, "feedback_reviewer")])
110
+
111
+ return Page(
112
+ title=_("Main Information"),
113
+ layouts={
114
+ default(): Layout(
115
+ grid_template_areas=grid_areas,
116
+ ),
117
+ },
118
+ )
119
+
120
+ def get_answers_page(self, instance):
121
+ nb_columns = 4
122
+ grid_areas = []
123
+ sections = []
124
+ inlines = []
125
+ grid_template_rows = []
126
+ if instance.status == Review.Status.PREPARATION_OF_REVIEW:
127
+ grid_areas.append([repeat_field(nb_columns, "category_section")])
128
+ sections.append(
129
+ create_simple_section(
130
+ "category_section", _("Question Categories"), [["category"]], "category", collapsed=True
131
+ )
132
+ )
133
+ categories = instance.get_question_categories()
134
+ total, count = categories.count() + 1, 0
135
+ for count, category in enumerate(categories, start=1):
136
+ category_key = f"questioncategory{category.id}"
137
+ grid_areas.append([repeat_field(nb_columns, f"{category_key}_section")])
138
+ sections.append(
139
+ create_simple_section(
140
+ f"{category_key}_section",
141
+ ("({count}/{total}). {title}").format(count=count, total=total, title=category.name),
142
+ [[category_key]],
143
+ category_key,
144
+ collapsed=True,
145
+ )
146
+ )
147
+
148
+ grid_areas.append([repeat_field(nb_columns, "questionnocategory_section")])
149
+ sections.append(
150
+ create_simple_section(
151
+ "questionnocategory_section",
152
+ _("({count}/{total}). No Question Category").format(count=count + 1, total=total),
153
+ [["questionnocategory"]],
154
+ "questionnocategory",
155
+ collapsed=False,
156
+ )
157
+ )
158
+
159
+ elif instance.status in [Review.Status.FILL_IN_REVIEW, Review.Status.REVIEW]:
160
+ categories = instance.get_answer_categories_for_user(self.request.user)
161
+ total, count = categories.count(), 0
162
+ if no_category := ReviewQuestion.objects.filter(review=instance, category=None).exists():
163
+ total += 1
164
+
165
+ for count, category in enumerate(categories, start=1):
166
+ category_key = f"reviewanswerquestioncategory{category.id}"
167
+ grid_areas.append([repeat_field(nb_columns, f"{category_key}_section")])
168
+ sections.append(
169
+ create_simple_section(
170
+ f"{category_key}_section",
171
+ ("({count}/{total}). {title}").format(count=count, total=total, title=category.name),
172
+ [[category_key]],
173
+ category_key,
174
+ collapsed=True,
175
+ )
176
+ )
177
+
178
+ if no_category:
179
+ grid_areas.append([repeat_field(nb_columns, "reviewanswerquestionnocategory_section")])
180
+ sections.append(
181
+ create_simple_section(
182
+ "reviewanswerquestionnocategory_section",
183
+ _("({count}/{total}). No Question Category").format(count=count + 1, total=total),
184
+ [["reviewanswerquestionnocategory"]],
185
+ "reviewanswerquestionnocategory",
186
+ collapsed=False,
187
+ )
188
+ )
189
+
190
+ elif instance.status in [Review.Status.EVALUATION, Review.Status.VALIDATION]:
191
+ grid_areas += [
192
+ [repeat_field(nb_columns, "rating_review_answer_section")],
193
+ [repeat_field(nb_columns, "text_review_answer_inline")],
194
+ ]
195
+ sections.append(
196
+ create_simple_section(
197
+ key="rating_review_answer_section",
198
+ title=_("Review of questions to be answered on a scale of 1 to 4"),
199
+ grid_template_areas=[["rating_review_answer_key"]],
200
+ inline_key="rating_review_answer_key",
201
+ collapsed=False,
202
+ )
203
+ )
204
+ inlines.append(Inline(key="text_review_answer_inline", endpoint="text_review_answer_key"))
205
+ grid_template_rows += [Style.MAX_CONTENT, Style.fr(1)]
206
+
207
+ return Page(
208
+ title=_("Q&A"),
209
+ layouts={
210
+ default(): Layout(
211
+ grid_template_areas=grid_areas,
212
+ sections=sections,
213
+ inlines=inlines,
214
+ grid_template_rows=grid_template_rows,
215
+ ),
216
+ },
217
+ )
218
+
219
+ def get_instance_display(self) -> Display:
220
+ pages = []
221
+ if self.view.kwargs.get("pk"):
222
+ instance = self.view.get_object()
223
+ pages.append(self.get_answers_page(instance))
224
+ pages.append(self.get_main_information_page())
225
+ return Display(pages=pages)
226
+
227
+
228
+ class ReviewTemplateDisplayConfig(ReviewDisplayConfig):
229
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
230
+ return dp.ListDisplay(
231
+ fields=[
232
+ dp.Field(key="year", label=_("Year")),
233
+ dp.Field(key="type", label=_("Type")),
234
+ dp.Field(key="moderator", label=_("Moderator")),
235
+ dp.Field(key="auto_apply_deadline", label=_("Auto Apply Deadline")),
236
+ dp.Field(key="status", label=_("Status")),
237
+ dp.Field(key="review", label=_("Review")),
238
+ dp.Field(key="review_group", label=_("Group")),
239
+ dp.Field(key="changed", label=_("Changed")),
240
+ ],
241
+ formatting=get_list_formatting(Review),
242
+ )
243
+
244
+
245
+ class ReviewQuestionCategoryDisplayConfig(DisplayViewConfig):
246
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
247
+ return dp.ListDisplay(
248
+ fields=[
249
+ dp.Field(key="name", label=_("Name"), width=Unit.PIXEL(1000)),
250
+ dp.Field(key="weight", label=_("Weight"), width=Unit.PIXEL(150)),
251
+ ]
252
+ )
253
+
254
+ def get_instance_display(self) -> Display:
255
+ return create_simple_display(
256
+ [["name", "weight"], [repeat_field(2, "question_section")]],
257
+ [
258
+ create_simple_section(
259
+ "question_section", _("Question"), [["reviewquestion"]], "reviewquestion", collapsed=False
260
+ )
261
+ ],
262
+ )
263
+
264
+
265
+ class ReviewQuestionDisplayConfig(DisplayViewConfig):
266
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
267
+ return dp.ListDisplay(
268
+ fields=[
269
+ dp.Field(key="question", label=gettext_lazy("Question"), width=Unit.PIXEL(600)),
270
+ dp.Field(key="answer_type", label=gettext_lazy("Type"), width=Unit.PIXEL(200)),
271
+ dp.Field(key="mandatory", label=gettext_lazy("Mandatory"), width=Unit.PIXEL(200)),
272
+ dp.Field(
273
+ key="review_for",
274
+ label=gettext_lazy("Reviewee/ Reviewer/ Department/ Company"),
275
+ width=Unit.PIXEL(260),
276
+ ),
277
+ dp.Field(key="weight", label=gettext_lazy("Weight")),
278
+ dp.Field(key="category", label=gettext_lazy("Category"), width=Unit.PIXEL(200), hide=True),
279
+ dp.Field(key="review", label=gettext_lazy("Review"), hide=True),
280
+ ]
281
+ )
282
+
283
+ def get_instance_display(self) -> Display:
284
+ return create_simple_display(
285
+ [
286
+ ["answer_type", "category", "review"],
287
+ ["mandatory", "for_reviewee", "for_reviewer"],
288
+ ["weight", "for_department_peers", "for_company_peers"],
289
+ [repeat_field(3, "question")],
290
+ ]
291
+ )
292
+
293
+
294
+ class ReviewQuestionReviewDisplayConfig(ReviewQuestionDisplayConfig):
295
+ def get_instance_display(self) -> Display:
296
+ if self.view.review.status in [Review.Status.EVALUATION, Review.Status.VALIDATION]:
297
+ return create_simple_display(
298
+ [["review_answers_table"]],
299
+ inlines=[Inline(key="review_answers_table", endpoint="review_answers_table")],
300
+ )
301
+ return super().get_instance_display()
302
+
303
+
304
+ class ReviewAnswerDisplayConfig(DisplayViewConfig):
305
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
306
+ fields = [
307
+ dp.Field(key="question_name", label=_("Question"), width=Unit.PIXEL(720)),
308
+ dp.Field(key="mandatory", label=_("Mandatory"), width=Unit.PIXEL(80)),
309
+ ]
310
+ if self.view.get_queryset().filter(question__answer_type=ReviewQuestion.ANSWERTYPE.RATING):
311
+ fields += [dp.Field(key="answer_number", label=_("Rating"), width=Unit.PIXEL(170))]
312
+
313
+ fields += [dp.Field(key="answer_text", label=_("Comment"), width=Unit.PIXEL(720))]
314
+
315
+ if review_id := self.view.kwargs.get("review_id"):
316
+ review = Review.objects.get(id=review_id)
317
+ if review.status in [Review.Status.EVALUATION, Review.Status.VALIDATION]:
318
+ fields = [
319
+ dp.Field(key="question_name", label=_("Question"), width=Unit.PIXEL(740)),
320
+ dp.Field(key="answered_by", label=_("Answered By"), width=Unit.PIXEL(140)),
321
+ dp.Field(key="answer_text", label=_("Comment"), width=Unit.PIXEL(740)),
322
+ ]
323
+ return dp.ListDisplay(fields=fields)
324
+
325
+ def get_instance_display(self) -> Display:
326
+ grid_fields = [[repeat_field(2, "question_name")], ["mandatory", "."]]
327
+ if self.view.kwargs.get("pk", None):
328
+ instance = self.view.get_object()
329
+ if instance.question.answer_type == ReviewQuestion.ANSWERTYPE.RATING:
330
+ grid_fields += [[repeat_field(2, "answer_number")]]
331
+ grid_fields += [[repeat_field(2, "answer_text")]]
332
+ return create_simple_display(grid_fields)
333
+
334
+
335
+ class ReviewProgressReviewPandasDisplayConfig(DisplayViewConfig):
336
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
337
+ _fields = [
338
+ dp.Field(key="answered_by_name", label=_("Answered By")),
339
+ dp.Field(key="progress", label=_("Progress")),
340
+ ]
341
+ return dp.ListDisplay(fields=_fields)
342
+
343
+
344
+ class ReviewAnswerReviewPandasDisplayConfig(DisplayViewConfig):
345
+ view: "ReviewAnswerReviewPandasViewSet"
346
+
347
+ def _get_formatting_rules(self):
348
+ return [
349
+ dp.Formatting(
350
+ column="deviation",
351
+ formatting_rules=[
352
+ dp.FormattingRule(
353
+ style={
354
+ "backgroundColor": WBColor.YELLOW_LIGHT.value,
355
+ },
356
+ condition=dp.Condition(operator=Operator.EQUAL, value="LESS"),
357
+ ),
358
+ dp.FormattingRule(
359
+ style={
360
+ "backgroundColor": WBColor.GREEN_LIGHT.value,
361
+ },
362
+ condition=dp.Condition(operator=Operator.EQUAL, value="EQUAL"),
363
+ ),
364
+ dp.FormattingRule(
365
+ style={
366
+ "backgroundColor": WBColor.RED_LIGHT.value,
367
+ },
368
+ condition=dp.Condition(operator=Operator.EQUAL, value="GREAT"),
369
+ ),
370
+ ],
371
+ )
372
+ ]
373
+
374
+ def _get_legends(self):
375
+ return [
376
+ dp.Legend(
377
+ key="deviation",
378
+ items=[
379
+ dp.LegendItem(
380
+ icon=WBColor.RED_LIGHT.value,
381
+ label=_("Great Difference"),
382
+ value="GREAT",
383
+ ),
384
+ dp.LegendItem(
385
+ icon=WBColor.YELLOW_LIGHT.value,
386
+ label=_("Little Difference"),
387
+ value="LESS",
388
+ ),
389
+ dp.LegendItem(
390
+ icon=WBColor.GREEN_LIGHT.value,
391
+ label=_("Equal"),
392
+ value="EQUAL",
393
+ ),
394
+ ],
395
+ ),
396
+ ]
397
+
398
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
399
+ hide_rating = self.request.GET.get("answer_type") == ReviewQuestion.ANSWERTYPE.TEXT
400
+ fields = [
401
+ dp.Field(key="category_question_name", label=_("Category"), width=Unit.PIXEL(200), hide=True),
402
+ dp.Field(key="question_name", label=_("Question"), width=Unit.PIXEL(780)),
403
+ ]
404
+ if self.view.review.moderator == self.view.request.user.profile:
405
+ fields.append(dp.Field(key="weight", label=_("Weight")))
406
+ fields += [
407
+ dp.Field(key="reviewee", label=_("Reviewee"), hide=hide_rating),
408
+ dp.Field(key="reviewer", label=_("Reviewer"), hide=hide_rating),
409
+ dp.Field(key="peers", label=_("Peers"), hide=True),
410
+ dp.Field(key="comment_reviewee", label=_("Reviewee"), width=Unit.PIXEL(450)),
411
+ dp.Field(key="comment_reviewer", label=_("Reviewer"), width=Unit.PIXEL(450)),
412
+ dp.Field(key="comment_peers", label=_("Peers"), width=Unit.PIXEL(450), hide=True),
413
+ ]
414
+ return (
415
+ dp.ListDisplay(fields=fields)
416
+ if hide_rating
417
+ else dp.ListDisplay(fields=fields, formatting=self._get_formatting_rules(), legends=self._get_legends())
418
+ )
419
+
420
+
421
+ class ReviewProgressPandasDisplayConfig(DisplayViewConfig):
422
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
423
+ _fields = [
424
+ dp.Field(key="review_name", label=_("Review"), width=Unit.PIXEL(800)),
425
+ dp.Field(key="reviewee", label=_("Reviewee")),
426
+ dp.Field(key="reviewer", label=_("Reviewer")),
427
+ dp.Field(key="peers", label=_("Peers")),
428
+ ]
429
+ return dp.ListDisplay(fields=_fields)