wbhuman_resources 2.2.1__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 (102) 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 +22 -0
  9. wbhuman_resources/dynamic_preferences_registry.py +118 -0
  10. wbhuman_resources/factories/__init__.py +38 -0
  11. wbhuman_resources/factories/absence.py +108 -0
  12. wbhuman_resources/factories/calendars.py +59 -0
  13. wbhuman_resources/factories/employee.py +79 -0
  14. wbhuman_resources/factories/kpi.py +154 -0
  15. wbhuman_resources/filters/__init__.py +20 -0
  16. wbhuman_resources/filters/absence.py +108 -0
  17. wbhuman_resources/filters/absence_graphs.py +84 -0
  18. wbhuman_resources/filters/calendars.py +28 -0
  19. wbhuman_resources/filters/employee.py +80 -0
  20. wbhuman_resources/filters/kpi.py +34 -0
  21. wbhuman_resources/filters/review.py +133 -0
  22. wbhuman_resources/filters/signals.py +26 -0
  23. wbhuman_resources/management/__init__.py +23 -0
  24. wbhuman_resources/migrations/0001_initial_squashed_squashed_0015_alter_absencerequest_calendaritem_ptr_and_more.py +949 -0
  25. wbhuman_resources/migrations/0016_alter_employeehumanresource_options.py +20 -0
  26. wbhuman_resources/migrations/0017_absencerequest_crossborder_country_and_more.py +55 -0
  27. wbhuman_resources/migrations/0018_remove_position_group_position_groups.py +32 -0
  28. wbhuman_resources/migrations/0019_alter_absencerequest_options_alter_kpi_options_and_more.py +44 -0
  29. wbhuman_resources/migrations/0020_alter_employeeyearbalance_year_alter_review_year.py +27 -0
  30. wbhuman_resources/migrations/0021_alter_position_color.py +18 -0
  31. wbhuman_resources/migrations/0022_remove_review_editable_mode.py +64 -0
  32. wbhuman_resources/migrations/__init__.py +0 -0
  33. wbhuman_resources/models/__init__.py +23 -0
  34. wbhuman_resources/models/absence.py +889 -0
  35. wbhuman_resources/models/calendars.py +370 -0
  36. wbhuman_resources/models/employee.py +1211 -0
  37. wbhuman_resources/models/kpi.py +199 -0
  38. wbhuman_resources/models/preferences.py +39 -0
  39. wbhuman_resources/models/review.py +978 -0
  40. wbhuman_resources/permissions/__init__.py +0 -0
  41. wbhuman_resources/permissions/backend.py +25 -0
  42. wbhuman_resources/serializers/__init__.py +49 -0
  43. wbhuman_resources/serializers/absence.py +296 -0
  44. wbhuman_resources/serializers/calendars.py +71 -0
  45. wbhuman_resources/serializers/employee.py +266 -0
  46. wbhuman_resources/serializers/kpi.py +79 -0
  47. wbhuman_resources/serializers/review.py +414 -0
  48. wbhuman_resources/tasks.py +189 -0
  49. wbhuman_resources/tests/__init__.py +1 -0
  50. wbhuman_resources/tests/conftest.py +96 -0
  51. wbhuman_resources/tests/models/__init__.py +0 -0
  52. wbhuman_resources/tests/models/test_absences.py +477 -0
  53. wbhuman_resources/tests/models/test_calendars.py +208 -0
  54. wbhuman_resources/tests/models/test_employees.py +501 -0
  55. wbhuman_resources/tests/models/test_review.py +102 -0
  56. wbhuman_resources/tests/models/test_utils.py +109 -0
  57. wbhuman_resources/tests/signals.py +107 -0
  58. wbhuman_resources/tests/test_permission.py +63 -0
  59. wbhuman_resources/tests/test_tasks.py +73 -0
  60. wbhuman_resources/urls.py +220 -0
  61. wbhuman_resources/utils.py +40 -0
  62. wbhuman_resources/viewsets/__init__.py +61 -0
  63. wbhuman_resources/viewsets/absence.py +327 -0
  64. wbhuman_resources/viewsets/absence_charts.py +327 -0
  65. wbhuman_resources/viewsets/buttons/__init__.py +7 -0
  66. wbhuman_resources/viewsets/buttons/absence.py +31 -0
  67. wbhuman_resources/viewsets/buttons/employee.py +43 -0
  68. wbhuman_resources/viewsets/buttons/kpis.py +16 -0
  69. wbhuman_resources/viewsets/buttons/review.py +194 -0
  70. wbhuman_resources/viewsets/calendars.py +102 -0
  71. wbhuman_resources/viewsets/display/__init__.py +39 -0
  72. wbhuman_resources/viewsets/display/absence.py +344 -0
  73. wbhuman_resources/viewsets/display/calendars.py +84 -0
  74. wbhuman_resources/viewsets/display/employee.py +253 -0
  75. wbhuman_resources/viewsets/display/kpis.py +91 -0
  76. wbhuman_resources/viewsets/display/review.py +427 -0
  77. wbhuman_resources/viewsets/employee.py +209 -0
  78. wbhuman_resources/viewsets/endpoints/__init__.py +42 -0
  79. wbhuman_resources/viewsets/endpoints/absence.py +74 -0
  80. wbhuman_resources/viewsets/endpoints/calendars.py +18 -0
  81. wbhuman_resources/viewsets/endpoints/employee.py +57 -0
  82. wbhuman_resources/viewsets/endpoints/kpis.py +58 -0
  83. wbhuman_resources/viewsets/endpoints/review.py +203 -0
  84. wbhuman_resources/viewsets/kpi.py +279 -0
  85. wbhuman_resources/viewsets/menu/__init__.py +22 -0
  86. wbhuman_resources/viewsets/menu/absence.py +50 -0
  87. wbhuman_resources/viewsets/menu/administration.py +15 -0
  88. wbhuman_resources/viewsets/menu/calendars.py +33 -0
  89. wbhuman_resources/viewsets/menu/employee.py +44 -0
  90. wbhuman_resources/viewsets/menu/kpis.py +18 -0
  91. wbhuman_resources/viewsets/menu/review.py +97 -0
  92. wbhuman_resources/viewsets/mixins.py +13 -0
  93. wbhuman_resources/viewsets/review.py +836 -0
  94. wbhuman_resources/viewsets/titles/__init__.py +18 -0
  95. wbhuman_resources/viewsets/titles/absence.py +29 -0
  96. wbhuman_resources/viewsets/titles/employee.py +18 -0
  97. wbhuman_resources/viewsets/titles/kpis.py +14 -0
  98. wbhuman_resources/viewsets/titles/review.py +61 -0
  99. wbhuman_resources/viewsets/utils.py +28 -0
  100. wbhuman_resources-2.2.1.dist-info/METADATA +9 -0
  101. wbhuman_resources-2.2.1.dist-info/RECORD +102 -0
  102. wbhuman_resources-2.2.1.dist-info/WHEEL +5 -0
@@ -0,0 +1,108 @@
1
+ import random
2
+
3
+ import factory
4
+ from faker import Faker
5
+ from pandas.tseries.offsets import BDay
6
+ from psycopg.types.range import TimestamptzRange
7
+ from wbcore.contrib.icons import WBIcon
8
+ from wbhuman_resources.models import (
9
+ AbsenceRequest,
10
+ AbsenceRequestPeriods,
11
+ AbsenceRequestType,
12
+ )
13
+
14
+ from .calendars import DefaultDailyPeriodFactory
15
+ from .employee import EmployeeYearBalanceFactory
16
+
17
+ fake = Faker()
18
+
19
+
20
+ class AbsenceRequestTypeFactory(factory.django.DjangoModelFactory):
21
+ class Meta:
22
+ model = AbsenceRequestType
23
+ django_get_or_create = ["title"]
24
+
25
+ title = factory.Faker("text", max_nb_chars=64)
26
+ icon = factory.Iterator(WBIcon.values)
27
+ is_vacation = factory.Faker("boolean")
28
+ is_timeoff = factory.Faker("boolean")
29
+ is_extensible = factory.Faker("boolean")
30
+ auto_approve = False
31
+ days_in_advance = factory.Faker("pyint", min_value=0, max_value=5)
32
+
33
+ @factory.post_generation
34
+ def crossborder_countries(self, create, extracted, **kwargs):
35
+ if not create:
36
+ return
37
+
38
+ if extracted:
39
+ for country in extracted:
40
+ self.crossborder_countries.add(country)
41
+
42
+
43
+ def _get_random_period(calendar):
44
+ lower = Faker().future_datetime() + BDay(0)
45
+ upper = lower + BDay(random.randint(1, 7))
46
+ return TimestamptzRange(
47
+ lower=lower.to_pydatetime().astimezone(calendar.timezone),
48
+ upper=upper.to_pydatetime().astimezone(calendar.timezone),
49
+ )
50
+
51
+
52
+ class VacationTypeFactory(AbsenceRequestTypeFactory):
53
+ title = "Vacation"
54
+ is_vacation = True
55
+ is_timeoff = True
56
+ is_extensible = False
57
+ auto_approve = False
58
+
59
+
60
+ class TimeOffTypeFactory(AbsenceRequestTypeFactory):
61
+ title = "TimeOff"
62
+ is_vacation = False
63
+ is_timeoff = True
64
+ is_extensible = True
65
+ auto_approve = True
66
+
67
+
68
+ class AbsenceRequestFactory(factory.django.DjangoModelFactory):
69
+ class Meta:
70
+ model = AbsenceRequest
71
+
72
+ period = factory.LazyAttribute(lambda o: _get_random_period(o.employee.calendar))
73
+ attachment = factory.django.FileField()
74
+ type = factory.SubFactory("wbhuman_resources.factories.AbsenceRequestTypeFactory")
75
+ employee = factory.SubFactory("wbhuman_resources.factories.EmployeeHumanResourceFactory")
76
+ notes = factory.Faker("text")
77
+ reason = factory.Faker("text")
78
+
79
+
80
+ class VacationRequestFactory(AbsenceRequestFactory):
81
+ type = factory.SubFactory(VacationTypeFactory)
82
+ status = AbsenceRequest.Status.APPROVED
83
+
84
+
85
+ class TimeOffRequestFactory(AbsenceRequestFactory):
86
+ type = factory.SubFactory(TimeOffTypeFactory)
87
+ status = AbsenceRequest.Status.APPROVED
88
+
89
+
90
+ class AbsenceRequestPeriodsFactory(factory.django.DjangoModelFactory):
91
+ class Meta:
92
+ model = AbsenceRequestPeriods
93
+ django_get_or_create = ["employee", "default_period", "date"]
94
+
95
+ date = factory.LazyAttribute(lambda o: (fake.date_object() + BDay(0)).date())
96
+ employee = factory.SubFactory("wbhuman_resources.factories.EmployeeHumanResourceFactory")
97
+ default_period = factory.LazyAttribute(lambda o: DefaultDailyPeriodFactory.create(calendar=o.employee.calendar))
98
+ request = factory.LazyAttribute(
99
+ lambda o: AbsenceRequestFactory.create(
100
+ status=AbsenceRequest.Status.APPROVED,
101
+ type=VacationTypeFactory.create(),
102
+ employee=o.employee,
103
+ period=TimestamptzRange(
104
+ lower=o.default_period.get_lower_datetime(o.date), upper=o.default_period.get_upper_datetime(o.date)
105
+ ),
106
+ )
107
+ )
108
+ balance = factory.LazyAttribute(lambda o: EmployeeYearBalanceFactory.create(year=o.date.year, employee=o.employee))
@@ -0,0 +1,59 @@
1
+ import zoneinfo
2
+ from datetime import time
3
+
4
+ import factory
5
+ from faker import Faker
6
+ from wbhuman_resources.models.calendars import (
7
+ DayOff,
8
+ DayOffCalendar,
9
+ DefaultDailyPeriod,
10
+ )
11
+
12
+ fake = Faker()
13
+
14
+
15
+ class BaseDayOffCalendarFactory(factory.django.DjangoModelFactory):
16
+ title = "Default Calendar"
17
+ resource = "europe.Germany"
18
+ timezone = zoneinfo.ZoneInfo("UTC")
19
+
20
+ class Meta:
21
+ model = DayOffCalendar
22
+ django_get_or_create = ["resource"]
23
+
24
+
25
+ class DayOffCalendarFactory(BaseDayOffCalendarFactory):
26
+ @factory.post_generation
27
+ def post_gen(self, create, extracted, **kwargs):
28
+ if create:
29
+ DefaultDailyPeriodFactory.create(calendar=self)
30
+ DefaultDailyPeriodFactory.create(
31
+ calendar=self,
32
+ lower_time=time(
33
+ 14,
34
+ 0,
35
+ 0,
36
+ ),
37
+ upper_time=time(18, 0, 0),
38
+ title="afternoon",
39
+ )
40
+
41
+
42
+ class DefaultDailyPeriodFactory(factory.django.DjangoModelFactory):
43
+ lower_time = time(9, 0, 0)
44
+ upper_time = time(13, 0, 0)
45
+ title = "morning"
46
+ calendar = factory.SubFactory("wbhuman_resources.factories.BaseDayOffCalendarFactory")
47
+
48
+ class Meta:
49
+ model = DefaultDailyPeriod
50
+ django_get_or_create = ["lower_time", "upper_time", "calendar"]
51
+
52
+
53
+ class DayOffFactory(factory.django.DjangoModelFactory):
54
+ class Meta:
55
+ model = DayOff
56
+
57
+ title = factory.Faker("text", max_nb_chars=64)
58
+ date = factory.Faker("date_object")
59
+ calendar = factory.SubFactory("wbhuman_resources.factories.DayOffCalendarFactory")
@@ -0,0 +1,79 @@
1
+ import factory
2
+ from django.contrib.auth.models import Group
3
+ from django.db.models.signals import post_save
4
+ from faker import Faker
5
+ from wbhuman_resources.models import (
6
+ BalanceHourlyAllowance,
7
+ EmployeeHumanResource,
8
+ EmployeeWeeklyOffPeriods,
9
+ EmployeeYearBalance,
10
+ Position,
11
+ )
12
+
13
+ fake = Faker()
14
+
15
+
16
+ class PositionFactory(factory.django.DjangoModelFactory):
17
+ class Meta:
18
+ model = Position
19
+
20
+ name = factory.Faker("text", max_nb_chars=64)
21
+ manager = None
22
+
23
+ @factory.post_generation
24
+ def post_gen(self, create, extracted, **kwargs):
25
+ group = Group.objects.get_or_create(name="Test Group")[0]
26
+ self.groups.add(group)
27
+
28
+
29
+ @factory.django.mute_signals(post_save)
30
+ class EmployeeHumanResourceFactory(factory.django.DjangoModelFactory):
31
+ class Meta:
32
+ model = EmployeeHumanResource
33
+ django_get_or_create = ["profile"]
34
+
35
+ is_active = True
36
+ profile = factory.SubFactory("wbcore.contrib.authentication.factories.AuthenticatedPersonFactory")
37
+ direct_manager = factory.SubFactory("wbcore.contrib.directory.factories.PersonFactory")
38
+ calendar = factory.SubFactory("wbhuman_resources.factories.DayOffCalendarFactory")
39
+ position = factory.SubFactory(PositionFactory)
40
+ enrollment_at = factory.Faker("past_date")
41
+ extra_days_frequency = EmployeeHumanResource.ExtraDaysBalanceFrequency.YEARLY
42
+
43
+ contract_type = EmployeeHumanResource.ContractType.INTERNAL
44
+ occupancy_rate = factory.Faker("pyfloat", min_value=0, max_value=1)
45
+
46
+
47
+ class EmployeeYearBalanceFactory(factory.django.DjangoModelFactory):
48
+ class Meta:
49
+ model = EmployeeYearBalance
50
+ django_get_or_create = ["employee", "year"]
51
+
52
+ employee = factory.SubFactory("wbhuman_resources.factories.EmployeeHumanResourceFactory")
53
+ year = factory.Faker("pyint", min_value=1970, max_value=2022)
54
+ extra_balance = factory.Faker("pyint", min_value=0, max_value=20)
55
+
56
+ @factory.post_generation
57
+ def post_gen(self, create, extracted, **kwargs):
58
+ if create:
59
+ BalanceHourlyAllowanceFactory.create(balance=self)
60
+
61
+
62
+ class BalanceHourlyAllowanceFactory(factory.django.DjangoModelFactory):
63
+ balance = factory.SubFactory("wbhuman_resources.factories.EmployeeYearBalanceFactory")
64
+ period_index = 1
65
+ hourly_allowance = factory.Faker("pyint", min_value=160, max_value=200)
66
+
67
+ class Meta:
68
+ model = BalanceHourlyAllowance
69
+ django_get_or_create = ["balance", "period_index"]
70
+
71
+
72
+ class EmployeeWeeklyOffPeriodsFactory(factory.django.DjangoModelFactory):
73
+ employee = factory.SubFactory("wbhuman_resources.factories.EmployeeHumanResourceFactory")
74
+ period = factory.LazyAttribute(lambda x: x.employee.calendar.default_periods.order_by("?")[0])
75
+ weekday = factory.Faker("pyint", min_value=0, max_value=7)
76
+
77
+ class Meta:
78
+ model = EmployeeWeeklyOffPeriods
79
+ django_get_or_create = ["employee", "period", "weekday"]
@@ -0,0 +1,154 @@
1
+ import random
2
+ from datetime import timedelta
3
+
4
+ import factory
5
+ import pytz
6
+ from django.utils import timezone
7
+ from faker import Faker
8
+ from psycopg.types.range import DateRange
9
+ from wbcore.contrib.authentication.factories import InternalUserFactory
10
+ from wbcore.contrib.directory.factories.entries import PersonFactory
11
+ from wbhuman_resources.models import (
12
+ KPI,
13
+ Evaluation,
14
+ Review,
15
+ ReviewAnswer,
16
+ ReviewGroup,
17
+ ReviewQuestion,
18
+ ReviewQuestionCategory,
19
+ )
20
+
21
+ fake = Faker()
22
+
23
+
24
+ class ReviewGroupFactory(factory.django.DjangoModelFactory):
25
+ class Meta:
26
+ model = ReviewGroup
27
+
28
+ name = factory.Faker("text", max_nb_chars=64)
29
+
30
+ @factory.post_generation
31
+ def employees(self, create, extracted, **kwargs):
32
+ if not create:
33
+ return
34
+
35
+ if extracted:
36
+ for employee in extracted:
37
+ self.employees.add(employee)
38
+
39
+
40
+ class ReviewAbstractFactory(factory.django.DjangoModelFactory):
41
+ class Meta:
42
+ model = Review
43
+ django_get_or_create = ["moderator"]
44
+
45
+ review_group = factory.SubFactory(ReviewGroupFactory)
46
+ review = factory.Faker("date_time_between", start_date="+5d", end_date="+6d", tzinfo=pytz.utc)
47
+ moderator = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
48
+ year = factory.Faker("pyint", min_value=1000, max_value=9999)
49
+
50
+
51
+ class ReviewFactory(ReviewAbstractFactory):
52
+ class Meta:
53
+ model = Review
54
+ django_get_or_create = ["moderator", "reviewee", "reviewer"]
55
+
56
+ from_date = factory.Faker("date_between", start_date="+2d", end_date="+3d")
57
+ to_date = factory.Faker("date_between", start_date="+4d", end_date="+5d")
58
+ review_deadline = factory.Faker("date_between", start_date="+5d", end_date="+6d")
59
+ reviewee = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
60
+ reviewer = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
61
+ feedback_reviewee = factory.Faker("text")
62
+ feedback_reviewer = factory.Faker("text")
63
+
64
+ signed_reviewee = factory.Faker("date_time", tzinfo=pytz.utc)
65
+ signed_reviewer = factory.Faker("date_time", tzinfo=pytz.utc)
66
+ completely_filled_reviewee = factory.Faker("date_time", tzinfo=pytz.utc)
67
+ completely_filled_reviewer = factory.Faker("date_time", tzinfo=pytz.utc)
68
+
69
+
70
+ class CompletedFilledReviewFactory(ReviewFactory):
71
+ completely_filled_reviewee = factory.Faker("date_time", tzinfo=pytz.utc)
72
+ completely_filled_reviewer = factory.Faker("date_time", tzinfo=pytz.utc)
73
+
74
+
75
+ class SignedReviewFactory(CompletedFilledReviewFactory):
76
+ signed_reviewee = factory.Faker("date_time", tzinfo=pytz.utc)
77
+ signed_reviewer = factory.Faker("date_time", tzinfo=pytz.utc)
78
+
79
+
80
+ class ReviewTemplateFactory(ReviewAbstractFactory):
81
+ is_template = True
82
+
83
+
84
+ class ReviewQuestionCategoryFactory(factory.django.DjangoModelFactory):
85
+ class Meta:
86
+ model = ReviewQuestionCategory
87
+
88
+ name = factory.Faker("text", max_nb_chars=64)
89
+ order = factory.Faker("pyint", min_value=0, max_value=9999)
90
+ weight = factory.Faker("pydecimal", right_digits=1, min_value=0, max_value=9999)
91
+
92
+
93
+ class ReviewQuestionNoCategoryFactory(factory.django.DjangoModelFactory):
94
+ class Meta:
95
+ model = ReviewQuestion
96
+
97
+ review = factory.SubFactory(ReviewFactory)
98
+ question = factory.Faker("text")
99
+ order = factory.Faker("pyint", min_value=0, max_value=9999)
100
+ weight = factory.Faker("pydecimal", right_digits=1, min_value=0, max_value=9999)
101
+
102
+
103
+ class ReviewQuestionFactory(ReviewQuestionNoCategoryFactory):
104
+ category = factory.SubFactory(ReviewQuestionCategoryFactory)
105
+
106
+
107
+ class ReviewAnswerFactory(factory.django.DjangoModelFactory):
108
+ class Meta:
109
+ model = ReviewAnswer
110
+
111
+ question = factory.SubFactory(ReviewQuestionFactory)
112
+ answered_by = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
113
+ answered_anonymized = hash(factory.SelfAttribute("answered_by"))
114
+ answer_number = factory.Faker("pyint", min_value=0, max_value=9999)
115
+ answer_text = factory.Faker("text")
116
+
117
+
118
+ class ReviewAnswerNoCategoryFactory(ReviewAnswerFactory):
119
+ question = factory.SubFactory(ReviewQuestionNoCategoryFactory)
120
+
121
+
122
+ class KPIFactory(factory.django.DjangoModelFactory):
123
+ class Meta:
124
+ model = KPI
125
+
126
+ name = factory.Faker("pystr")
127
+ goal = factory.Faker("pyint", min_value=0, max_value=9999)
128
+ period = DateRange(timezone.now().date(), timezone.now().date() + timedelta(days=random.randint(4, 5)))
129
+ handler = "wbcrm.kpi_handlers.activities.NumberOfActivityKPI"
130
+
131
+ @factory.post_generation
132
+ def evaluated_persons(self, create, extracted, **kwargs):
133
+ if not create:
134
+ return
135
+
136
+ if extracted:
137
+ for person in extracted:
138
+ self.evaluated_persons.add(person)
139
+
140
+
141
+ class DefaultPersonKPIFactory(KPIFactory):
142
+ @factory.post_generation
143
+ def evaluated_persons(self, create, extracted, **kwargs):
144
+ self.evaluated_persons.add(PersonFactory())
145
+
146
+
147
+ class EvaluationFactory(factory.django.DjangoModelFactory):
148
+ class Meta:
149
+ model = Evaluation
150
+
151
+ kpi = factory.SubFactory(KPIFactory)
152
+ person = factory.SubFactory("wbcore.contrib.directory.factories.PersonFactory")
153
+ evaluated_period = DateRange(timezone.now().date(), timezone.now().date() + timedelta(days=random.randint(4, 5)))
154
+ evaluation_date = timezone.now().date()
@@ -0,0 +1,20 @@
1
+ from .absence import (
2
+ AbsenceRequestEmployeeHumanResourceFilterSet,
3
+ AbsenceRequestFilter,
4
+ AbsenceTypeCountEmployeeModelFilterSet,
5
+ )
6
+ from .absence_graphs import AbsenceRequestPlannerFilter, AbsenceTableFilter
7
+ from .calendars import DayOffFilter
8
+ from .employee import EmployeeBalanceFilterSet, EmployeeFilterSet, PositionFilterSet
9
+ from .kpi import KPIFilterSet, KPIEvaluationFilterSet, KPIEvaluationPandasFilter
10
+ from .review import (
11
+ ReviewGroupFilter,
12
+ ReviewTemplateFilter,
13
+ ReviewFilter,
14
+ ReviewQuestionCategoryFilter,
15
+ ReviewQuestionFilter,
16
+ ReviewAnswerFilter,
17
+ ReviewProgressReviewFilter,
18
+ RatingReviewAnswerReviewFilter,
19
+ )
20
+ from .signals import *
@@ -0,0 +1,108 @@
1
+ from datetime import date, timedelta
2
+
3
+ import pandas as pd
4
+ from django.utils.timezone import localdate
5
+ from django.utils.translation import gettext_lazy as _
6
+ from psycopg.types.range import TimestamptzRange
7
+ from wbcore import filters as wb_filters
8
+ from wbcore.contrib.agenda.filters import CalendarItemPeriodBaseFilterSet
9
+ from wbhuman_resources.models import (
10
+ AbsenceRequest,
11
+ AbsenceRequestPeriods,
12
+ AbsenceRequestType,
13
+ Position,
14
+ )
15
+
16
+
17
+ def current_year_date_start(*args, **kwargs):
18
+ d = localdate()
19
+ return date(d.year, 1, 1)
20
+
21
+
22
+ def current_year_date_end(*args, **kwargs):
23
+ d = localdate()
24
+ return max((d + pd.tseries.offsets.YearEnd(1)).date(), d + timedelta(days=60))
25
+
26
+
27
+ class AbsenceRequestFilter(CalendarItemPeriodBaseFilterSet):
28
+ conference_room = boolean_conference_room = None
29
+ department = wb_filters.ModelChoiceFilter(
30
+ label=_("Department"),
31
+ queryset=Position.objects.all(),
32
+ endpoint=Position.get_representation_endpoint(),
33
+ value_key=Position.get_representation_value_key(),
34
+ label_key=Position.get_representation_label_key(),
35
+ method="filter_position",
36
+ )
37
+ is_active_employee = wb_filters.BooleanFilter(
38
+ label=_("Is Employee Active"), method="boolean_is_active_employee", default=True
39
+ )
40
+ _total_hours_in_days__gte = wb_filters.NumberFilter(
41
+ lookup_expr="gte", label="Total hours", field_name="_total_hours_in_days"
42
+ )
43
+ _total_vacation_hours_in_days__gte = wb_filters.NumberFilter(
44
+ lookup_expr="gte", label="Total hours", field_name="_total_vacation_hours_in_days"
45
+ )
46
+ _total_hours_in_days__lte = wb_filters.NumberFilter(
47
+ lookup_expr="lte", label="Total hours", field_name="_total_hours_in_days"
48
+ )
49
+ _total_vacation_hours_in_days__lte = wb_filters.NumberFilter(
50
+ lookup_expr="lte", label="Total hours", field_name="_total_vacation_hours_in_days"
51
+ )
52
+
53
+ def get_default_period(self):
54
+ return TimestamptzRange(lower=current_year_date_start(), upper=current_year_date_end())
55
+
56
+ def boolean_is_active_employee(self, queryset, name, value):
57
+ if value:
58
+ return queryset.filter(employee__is_active=True)
59
+ # return queryset.filter(employee__in=EmployeeHumanResource.active_internal_employees.all())
60
+ return queryset
61
+
62
+ def filter_position(self, queryset, name, value):
63
+ if value:
64
+ return queryset.filter(department__in=value.get_descendants(include_self=True)).distinct()
65
+ return queryset
66
+
67
+ class Meta:
68
+ model = AbsenceRequest
69
+ fields = {
70
+ "employee": ["exact"],
71
+ "status": ["exact"],
72
+ "type": ["exact"],
73
+ "created": ["gte", "exact", "lte"],
74
+ }
75
+
76
+
77
+ class AbsenceTypeCountEmployeeModelFilterSet(wb_filters.FilterSet):
78
+ year = wb_filters.YearFilter(field_name="year", lookup_expr="exact")
79
+
80
+ absence_type = wb_filters.ModelChoiceFilter(
81
+ label=_("Type"),
82
+ queryset=AbsenceRequestType.objects.all(),
83
+ endpoint=AbsenceRequestType.get_representation_endpoint(),
84
+ value_key=AbsenceRequestType.get_representation_value_key(),
85
+ label_key=AbsenceRequestType.get_representation_label_key(),
86
+ method="filter_absence_type",
87
+ )
88
+
89
+ def filter_absence_type(self, queryset, label, value):
90
+ if value:
91
+ return queryset.filter(request__type=value)
92
+ return queryset
93
+
94
+ class Meta:
95
+ model = AbsenceRequestPeriods
96
+ fields = {}
97
+
98
+
99
+ class AbsenceRequestEmployeeHumanResourceFilterSet(AbsenceRequestFilter):
100
+ department = is_active_employee = None
101
+
102
+ class Meta:
103
+ model = AbsenceRequest
104
+ fields = {
105
+ "status": ["exact"],
106
+ "type": ["exact"],
107
+ "created": ["gte", "exact", "lte"],
108
+ }
@@ -0,0 +1,84 @@
1
+ from datetime import timedelta
2
+
3
+ import pandas as pd
4
+ from django.utils.timezone import localdate
5
+ from django.utils.translation import gettext_lazy as _
6
+ from dynamic_preferences.registries import global_preferences_registry
7
+ from psycopg.types.range import TimestamptzRange
8
+ from wbcore import filters as wb_filters
9
+ from wbhuman_resources.models import AbsenceRequestPeriods, DayOffCalendar, Position
10
+
11
+
12
+ def current_year_date_range(*args, **kwargs):
13
+ d = localdate()
14
+ return TimestamptzRange(
15
+ (localdate() - pd.tseries.offsets.Week(weekday=0)).date(),
16
+ max((d + pd.tseries.offsets.YearEnd(1)).date(), d + timedelta(days=60)),
17
+ )
18
+
19
+
20
+ def monday_of_current_week(*args, **kwargs):
21
+ today = localdate()
22
+ return today - timedelta(days=today.weekday())
23
+
24
+
25
+ def get_calendar_default(field, request, view, **kwargs) -> int | None:
26
+ if (profile := request.user.profile) and (employee := getattr(profile, "human_resources", None)):
27
+ return employee.calendar.id
28
+ if calendar := global_preferences_registry.manager()["wbhuman_resources__employee_default_calendar"]:
29
+ return calendar.id
30
+ try:
31
+ return DayOffCalendar.objects.first().id
32
+ except AttributeError:
33
+ return None
34
+
35
+
36
+ class AbsenceRequestPlannerFilter(wb_filters.FilterSet):
37
+ calendar = wb_filters.ModelChoiceFilter(
38
+ label=_("Calendar"),
39
+ required=True,
40
+ clearable=False,
41
+ queryset=DayOffCalendar.objects.all(),
42
+ endpoint=DayOffCalendar.get_representation_endpoint(),
43
+ value_key=DayOffCalendar.get_representation_value_key(),
44
+ label_key=DayOffCalendar.get_representation_label_key(),
45
+ default=get_calendar_default,
46
+ method=lambda queryset, label, value: queryset,
47
+ )
48
+
49
+ date = wb_filters.DateRangeFilter(
50
+ label=_("Date Range"),
51
+ method=lambda queryset, label, value: queryset,
52
+ required=True,
53
+ clearable=False,
54
+ default=current_year_date_range,
55
+ )
56
+ only_employee_with_absence_periods = wb_filters.BooleanFilter(
57
+ default=False,
58
+ label=_("Only Employee With Absence periods"),
59
+ method=lambda queryset, label, value: queryset,
60
+ )
61
+
62
+ position = wb_filters.ModelChoiceFilter(
63
+ label=_("Position"),
64
+ queryset=Position.objects.all(),
65
+ endpoint=Position.get_representation_endpoint(),
66
+ value_key=Position.get_representation_value_key(),
67
+ label_key=Position.get_representation_label_key(),
68
+ method=lambda queryset, label, value: queryset,
69
+ )
70
+
71
+ class Meta:
72
+ model = AbsenceRequestPeriods
73
+ fields = {}
74
+
75
+
76
+ class AbsenceTableFilter(AbsenceRequestPlannerFilter):
77
+ date_gte = date_lte = None
78
+ date = wb_filters.DateFilter(
79
+ label=_("Week Day"),
80
+ method=lambda queryset, label, value: queryset,
81
+ default=monday_of_current_week,
82
+ required=True,
83
+ help_text="Change this date to any day on the week you are interested in seeing the presence table",
84
+ )
@@ -0,0 +1,28 @@
1
+ from django.utils.translation import gettext_lazy as _
2
+ from wbcore import filters as wb_filters
3
+ from wbcore.filters.defaults import current_year_date_range
4
+ from wbhuman_resources.models import DayOff
5
+
6
+
7
+ class DayOffFilter(wb_filters.FilterSet):
8
+ date = wb_filters.DateRangeFilter(
9
+ label=_("Date Range"),
10
+ required=True,
11
+ clearable=False,
12
+ method=wb_filters.DateRangeFilter.base_date_range_filter_method,
13
+ default=current_year_date_range,
14
+ )
15
+
16
+ def start_filter(self, queryset, name, value):
17
+ if value:
18
+ return queryset.filter(date__gte=value)
19
+ return queryset
20
+
21
+ def end_filter(self, queryset, name, value):
22
+ if value:
23
+ return queryset.filter(date__lte=value)
24
+ return queryset
25
+
26
+ class Meta:
27
+ model = DayOff
28
+ fields = {"count_as_holiday": ["exact"], "calendar": ["exact"]}