diff --git a/requirements-dev.txt b/requirements-dev.txt index 6e91530d..a0691924 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,24 +1,25 @@ -r requirements.txt black==22.3.0 -coverage==5.5 +coverage==6.4.1 factory-boy==3.2.1 -flake8==3.9.2 -flake8-blind-except==0.2.0 -flake8-debugger==4.0.0 +flake8==4.0.1 +flake8-blind-except==0.2.1 +flake8-debugger==4.1.2 flake8-deprecated==1.3 flake8-docstrings==1.6.0 flake8-isort==4.1.1 flake8-string-format==0.3.0 ipdb==0.13.9 -isort==5.8.0 -pdbpp==0.10.2 -pytest==6.2.4 -pytest-cov==2.12.1 +isort==5.10.1 +pdbpp==0.10.3 +pytest==7.1.2 +pytest-cov==3.0.0 pytest-django==4.5.2 pytest-env==0.6.2 +# needs to stay at 2.1.0 because of wrong interpretation of parameters with "__" pytest-factoryboy==2.1.0 pytest-freezegun==0.4.2 -pytest-mock==3.6.1 -pytest-randomly==3.8.0 +pytest-mock==3.7.0 +pytest-randomly==3.12.0 requests-mock==1.9.3 snapshottest==0.6.0 diff --git a/requirements.txt b/requirements.txt index b3c6860b..456c0f0d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,25 +1,25 @@ -python-dateutil==2.8.1 +python-dateutil==2.8.2 django==3.2.13 # might remove this once we find out how the jsonapi extras_require work -django-cors-headers==3.10.1 -django-filter==2.4.0 +django-cors-headers==3.13.0 +django-filter==21.1 django-multiselectfield==0.1.12 -django-prometheus==2.1.0 -djangorestframework==3.12.4 -djangorestframework-jsonapi[django-filter]==3.1.0 +django-prometheus==2.2.0 +djangorestframework==3.13.1 +djangorestframework-jsonapi[django-filter]==5.0.0 mozilla-django-oidc==2.0.0 -psycopg2==2.8.6 -pytz==2021.1 +psycopg2==2.9.3 +pytz==2022.1 pyexcel-webio==0.1.4 -pyexcel-io==0.6.4 +pyexcel-io==0.6.6 django-excel==0.0.10 -django-nested-inline==0.4.4 -pyexcel-ods3==0.6.0 +django-nested-inline==0.4.5 +pyexcel-ods3==0.6.1 pyexcel-xlsx==0.6.0 pyexcel-ezodf==0.3.4 -django-environ==0.4.5 +django-environ==0.8.1 django-money==2.1.1 python-redmine==2.3.0 -sentry-sdk==1.5.1 +sentry-sdk==1.5.12 gunicorn==20.1.0 -whitenoise==5.3.0 +whitenoise==6.2.0 diff --git a/timed/employment/models.py b/timed/employment/models.py index 02d91139..8e54db54 100644 --- a/timed/employment/models.py +++ b/timed/employment/models.py @@ -112,7 +112,7 @@ def calculate_used_days(self, user, start, end): return None absences = Absence.objects.filter( - user=user, type=self, date__range=[start, end] + user=user, absence_type=self, date__range=[start, end] ) used_days = absences.count() return used_days @@ -301,7 +301,7 @@ def calculate_worktime(self, start, end): absence.calculate_duration(self) for absence in Absence.objects.filter( user=self.user_id, date__gte=start, date__lte=end - ).select_related("type") + ).select_related("absence_type") ], timedelta(), ) diff --git a/timed/employment/serializers.py b/timed/employment/serializers.py index ea1071f2..3f089861 100644 --- a/timed/employment/serializers.py +++ b/timed/employment/serializers.py @@ -114,7 +114,7 @@ class AbsenceBalanceSerializer(Serializer): ) absence_credits = relations.SerializerMethodResourceRelatedField( - source="get_absence_credits", + method_name="get_absence_credits", model=models.AbsenceCredit, many=True, read_only=True, @@ -183,8 +183,8 @@ def get_used_duration(self, instance): for absence in Absence.objects.filter( user=instance.user, date__range=[start, instance.date], - type_id=instance.id, - ).select_related("type") + absence_type_id=instance.id, + ).select_related("absence_type") ], timedelta(), ) diff --git a/timed/employment/tests/test_absence_balance.py b/timed/employment/tests/test_absence_balance.py index 8111777a..df776043 100644 --- a/timed/employment/tests/test_absence_balance.py +++ b/timed/employment/tests/test_absence_balance.py @@ -24,9 +24,11 @@ def test_absence_balance_full_day(auth_client, django_assert_num_queries): # credit on different user, may not show up AbsenceCreditFactory.create(date=date.today(), absence_type=absence_type) - AbsenceFactory.create(date=day, user=user, type=absence_type) + AbsenceFactory.create(date=day, user=user, absence_type=absence_type) - AbsenceFactory.create(date=day - timedelta(days=1), user=user, type=absence_type) + AbsenceFactory.create( + date=day - timedelta(days=1), user=user, absence_type=absence_type + ) url = reverse("absence-balance-list") @@ -68,9 +70,11 @@ def test_absence_balance_fill_worktime(auth_client, django_assert_num_queries): user=user, date=day + timedelta(days=1), duration=timedelta(hours=4) ) - AbsenceFactory.create(date=day + timedelta(days=1), user=user, type=absence_type) + AbsenceFactory.create( + date=day + timedelta(days=1), user=user, absence_type=absence_type + ) - AbsenceFactory.create(date=day, user=user, type=absence_type) + AbsenceFactory.create(date=day, user=user, absence_type=absence_type) url = reverse("absence-balance-list") with django_assert_num_queries(11): diff --git a/timed/employment/tests/test_user.py b/timed/employment/tests/test_user.py index 8fa1a37a..c5a6653d 100644 --- a/timed/employment/tests/test_user.py +++ b/timed/employment/tests/test_user.py @@ -180,7 +180,7 @@ def test_user_transfer(superadmin_client): AbsenceTypeFactory.create(fill_worktime=True) AbsenceTypeFactory.create(fill_worktime=False) absence_type = AbsenceTypeFactory.create(fill_worktime=False) - AbsenceFactory.create(user=user, type=absence_type, date=date(2017, 12, 29)) + AbsenceFactory.create(user=user, absence_type=absence_type, date=date(2017, 12, 29)) url = reverse("user-transfer", args=[user.id]) response = superadmin_client.post(url) diff --git a/timed/mixins.py b/timed/mixins.py index 4ccdbe37..38079632 100644 --- a/timed/mixins.py +++ b/timed/mixins.py @@ -37,10 +37,10 @@ def _is_related_field(self, val): Ignores serializer method fields which define logic separately. """ return isinstance(val, relations.ResourceRelatedField) and not isinstance( - val, relations.SerializerMethodResourceRelatedField + val, relations.ManySerializerMethodResourceRelatedField ) - def get_serializer(self, data, *args, **kwargs): + def get_serializer(self, data=None, *args, **kwargs): # no data no wrapping needed if not data: return super().get_serializer(data, *args, **kwargs) diff --git a/timed/subscription/migrations/0005_alter_package_price_currency.py b/timed/subscription/migrations/0005_alter_package_price_currency.py index c0f1e57c..866a13a7 100644 --- a/timed/subscription/migrations/0005_alter_package_price_currency.py +++ b/timed/subscription/migrations/0005_alter_package_price_currency.py @@ -219,7 +219,7 @@ class Migration(migrations.Migration): ("PEI", "Peruvian Inti"), ("PEN", "Peruvian Sol"), ("PES", "Peruvian Sol (1863–1965)"), - ("PHP", "Philippine Piso"), + ("PHP", "Philippine Peso"), ("XPT", "Platinum"), ("PLN", "Polish Zloty"), ("PLZ", "Polish Zloty (1950–1995)"), diff --git a/timed/tracking/factories.py b/timed/tracking/factories.py index 46e43bbc..7e232d81 100644 --- a/timed/tracking/factories.py +++ b/timed/tracking/factories.py @@ -79,7 +79,7 @@ class AbsenceFactory(DjangoModelFactory): """Absence factory.""" user = SubFactory("timed.employment.factories.UserFactory") - type = SubFactory("timed.employment.factories.AbsenceTypeFactory") + absence_type = SubFactory("timed.employment.factories.AbsenceTypeFactory") date = Faker("date") class Meta: diff --git a/timed/tracking/migrations/0014_rename_type_absence_absence_type.py b/timed/tracking/migrations/0014_rename_type_absence_absence_type.py new file mode 100644 index 00000000..00b9efc4 --- /dev/null +++ b/timed/tracking/migrations/0014_rename_type_absence_absence_type.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.13 on 2022-06-16 13:25 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("tracking", "0013_report_billed"), + ] + + operations = [ + migrations.RenameField( + model_name="absence", + old_name="type", + new_name="absence_type", + ), + ] diff --git a/timed/tracking/models.py b/timed/tracking/models.py index f18ec31a..94ba8d33 100644 --- a/timed/tracking/models.py +++ b/timed/tracking/models.py @@ -151,7 +151,7 @@ class Absence(models.Model): comment = models.TextField(blank=True) date = models.DateField() - type = models.ForeignKey( + absence_type = models.ForeignKey( "employment.AbsenceType", on_delete=models.PROTECT, related_name="absences" ) user = models.ForeignKey( @@ -167,7 +167,7 @@ def calculate_duration(self, employment): for absences which need to fill day calcuation needs to check how much time has been reported on that day. """ - if not self.type.fill_worktime: + if not self.absence_type.fill_worktime: return employment.worktime_per_day reports = Report.objects.filter(date=self.date, user=self.user_id) diff --git a/timed/tracking/serializers.py b/timed/tracking/serializers.py index e2a43d06..ba9ee4ac 100644 --- a/timed/tracking/serializers.py +++ b/timed/tracking/serializers.py @@ -267,16 +267,16 @@ class ReportIntersectionSerializer(Serializer): """ customer = relations.SerializerMethodResourceRelatedField( - source="get_customer", model=Customer, read_only=True + method_name="get_customer", model=Customer, read_only=True ) project = relations.SerializerMethodResourceRelatedField( - source="get_project", model=Project, read_only=True + method_name="get_project", model=Project, read_only=True ) task = relations.SerializerMethodResourceRelatedField( - source="get_task", model=Task, read_only=True + method_name="get_task", model=Task, read_only=True ) user = relations.SerializerMethodResourceRelatedField( - source="get_user", model=User, read_only=True + method_name="get_user", model=User, read_only=True ) comment = SerializerMethodField() review = SerializerMethodField() @@ -356,12 +356,12 @@ class AbsenceSerializer(ModelSerializer): """Absence serializer.""" duration = SerializerMethodField(source="get_duration") - type = ResourceRelatedField(queryset=AbsenceType.objects.all()) + absence_type = ResourceRelatedField(queryset=AbsenceType.objects.all()) user = CurrentUserResourceRelatedField() included_serializers = { "user": "timed.employment.serializers.UserSerializer", - "type": "timed.employment.serializers.AbsenceTypeSerializer", + "absence_type": "timed.employment.serializers.AbsenceTypeSerializer", } def get_duration(self, instance): @@ -383,7 +383,7 @@ def validate_date(self, value): return value - def validate_type(self, value): + def validate_absence_type(self, value): """Only owner is allowed to change type.""" if self.instance is not None: user = self.context["request"].user @@ -425,4 +425,4 @@ class Meta: """Meta information for the absence serializer.""" model = models.Absence - fields = ["comment", "date", "duration", "type", "user"] + fields = ["comment", "date", "duration", "absence_type", "user"] diff --git a/timed/tracking/tests/test_absence.py b/timed/tracking/tests/test_absence.py index 25fa53bc..e5dc07c1 100644 --- a/timed/tracking/tests/test_absence.py +++ b/timed/tracking/tests/test_absence.py @@ -116,7 +116,7 @@ def test_absence_create(auth_client, is_external, expected): employment = EmploymentFactory.create( user=user, start_date=date, worktime_per_day=datetime.timedelta(hours=8) ) - type = AbsenceTypeFactory.create() + absence_type = AbsenceTypeFactory.create() if is_external: employment.is_external = True @@ -128,7 +128,9 @@ def test_absence_create(auth_client, is_external, expected): "id": None, "attributes": {"date": date.strftime("%Y-%m-%d")}, "relationships": { - "type": {"data": {"type": "absence-types", "id": type.id}} + "absence_type": { + "data": {"type": "absence-types", "id": absence_type.id} + } }, } } @@ -200,7 +202,7 @@ def test_absence_update_superadmin_type(superadmin_client): """Test that superadmin may not change type of absence.""" user = UserFactory.create() date = datetime.date(2017, 5, 3) - type = AbsenceTypeFactory.create() + absence_type = AbsenceTypeFactory.create() absence = AbsenceFactory.create(user=user, date=datetime.date(2016, 5, 3)) EmploymentFactory.create( user=user, start_date=date, worktime_per_day=datetime.timedelta(hours=8) @@ -210,8 +212,11 @@ def test_absence_update_superadmin_type(superadmin_client): "data": { "type": "absences", "id": absence.id, + "attributes": {"date": date.strftime("%Y-%m-%d")}, "relationships": { - "type": {"data": {"type": "absence-types", "id": type.id}} + "absence_type": { + "data": {"type": "absence-types", "id": absence_type.id} + } }, } } @@ -249,7 +254,7 @@ def test_absence_fill_worktime(auth_client): EmploymentFactory.create( user=user, start_date=date, worktime_per_day=datetime.timedelta(hours=8) ) - type = AbsenceTypeFactory.create(fill_worktime=True) + absence_type = AbsenceTypeFactory.create(fill_worktime=True) ReportFactory.create(user=user, date=date, duration=datetime.timedelta(hours=5)) @@ -259,7 +264,9 @@ def test_absence_fill_worktime(auth_client): "id": None, "attributes": {"date": date.strftime("%Y-%m-%d")}, "relationships": { - "type": {"data": {"type": "absence-types", "id": type.id}} + "absence_type": { + "data": {"type": "absence-types", "id": absence_type.id} + } }, } } @@ -284,7 +291,7 @@ def test_absence_fill_worktime_reported_time_to_long(auth_client): EmploymentFactory.create( user=user, start_date=date, worktime_per_day=datetime.timedelta(hours=8) ) - type = AbsenceTypeFactory.create(fill_worktime=True) + absence_type = AbsenceTypeFactory.create(fill_worktime=True) ReportFactory.create( user=user, date=date, duration=datetime.timedelta(hours=8, minutes=30) @@ -296,7 +303,9 @@ def test_absence_fill_worktime_reported_time_to_long(auth_client): "id": None, "attributes": {"date": date.strftime("%Y-%m-%d")}, "relationships": { - "type": {"data": {"type": "absence-types", "id": type.id}} + "absence_type": { + "data": {"type": "absence-types", "id": absence_type.id} + } }, } } @@ -314,7 +323,7 @@ def test_absence_weekend(auth_client): """Should not be able to create an absence on a weekend.""" date = datetime.date(2017, 5, 14) user = auth_client.user - type = AbsenceTypeFactory.create() + absence_type = AbsenceTypeFactory.create() EmploymentFactory.create( user=user, start_date=date, worktime_per_day=datetime.timedelta(hours=8) ) @@ -325,7 +334,9 @@ def test_absence_weekend(auth_client): "id": None, "attributes": {"date": date.strftime("%Y-%m-%d")}, "relationships": { - "type": {"data": {"type": "absence-types", "id": type.id}} + "absence_type": { + "data": {"type": "absence-types", "id": absence_type.id} + } }, } } @@ -340,7 +351,7 @@ def test_absence_public_holiday(auth_client): """Should not be able to create an absence on a public holiday.""" date = datetime.date(2017, 5, 16) user = auth_client.user - type = AbsenceTypeFactory.create() + absence_type = AbsenceTypeFactory.create() employment = EmploymentFactory.create( user=user, start_date=date, worktime_per_day=datetime.timedelta(hours=8) ) @@ -352,7 +363,9 @@ def test_absence_public_holiday(auth_client): "id": None, "attributes": {"date": date.strftime("%Y-%m-%d")}, "relationships": { - "type": {"data": {"type": "absence-types", "id": type.id}} + "absence_type": { + "data": {"type": "absence-types", "id": absence_type.id} + } }, } } @@ -365,7 +378,7 @@ def test_absence_public_holiday(auth_client): def test_absence_create_unemployed(auth_client): """Test creation of absence fails on unemployed day.""" - type = AbsenceTypeFactory.create() + absence_type = AbsenceTypeFactory.create() data = { "data": { @@ -373,7 +386,9 @@ def test_absence_create_unemployed(auth_client): "id": None, "attributes": {"date": "2017-05-16"}, "relationships": { - "type": {"data": {"type": "absence-types", "id": type.id}} + "absence_type": { + "data": {"type": "absence-types", "id": absence_type.id} + } }, } } diff --git a/timed/tracking/views.py b/timed/tracking/views.py index 23293617..180306f5 100644 --- a/timed/tracking/views.py +++ b/timed/tracking/views.py @@ -367,10 +367,10 @@ def get_queryset(self): """Get absences only for internal employees.""" user = self.request.user if user.is_superuser: - queryset = models.Absence.objects.select_related("type", "user") + queryset = models.Absence.objects.select_related("absence_type", "user") return queryset - queryset = models.Absence.objects.select_related("type", "user").filter( + queryset = models.Absence.objects.select_related("absence_type", "user").filter( Q(user=user) | Q(user__in=user.supervisees.all()) ) return queryset