From 4b4485c778fe9a9715d58fecfbb110539f35b115 Mon Sep 17 00:00:00 2001 From: Karol Beker Date: Thu, 30 May 2019 19:06:30 +0200 Subject: [PATCH 1/3] Add FurationFieldForm --- employees/forms.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/employees/forms.py b/employees/forms.py index bf385179c..a1e4878af 100644 --- a/employees/forms.py +++ b/employees/forms.py @@ -2,8 +2,13 @@ from bootstrap_datepicker_plus import DatePickerInput from django import forms +from django.core.exceptions import ValidationError from django.db.models import QuerySet +from django.forms import TextInput +from django.utils.dateparse import parse_duration +from common.convert import timedelta_to_string +from employees.common.strings import ReportValidationStrings from employees.models import Report @@ -17,7 +22,27 @@ def __init__(self, queryset: QuerySet, *args: Any, **kwargs: Any) -> None: self.fields["projects"].choices = [(project.id, project.name) for project in queryset] +class DurationInput(TextInput): + def format_value(self, value: str) -> str: + return timedelta_to_string(parse_duration(value)) + + +class DurationFieldForm(forms.DurationField): + widget = DurationInput + + def clean(self, value: str) -> str: + if value.count(":") != 1: + raise ValidationError(message=ReportValidationStrings.WORK_HOURS_WRONG_FORMAT.value) + hours, minutes = value.split(":") + if not hours.isdigit() or not minutes.isdigit(): + raise ValidationError(message=ReportValidationStrings.WORK_HOURS_WRONG_FORMAT.value) + return f"{hours}:{minutes}:00" + + class AdminReportForm(forms.ModelForm): + + work_hours = DurationFieldForm() + class Meta: model = Report fields = ("date", "description", "task_activities", "project", "work_hours") From 6a54c46f5ff4cda6daca1807716d56d53982b929 Mon Sep 17 00:00:00 2001 From: Karol Beker Date: Fri, 31 May 2019 11:32:00 +0200 Subject: [PATCH 2/3] Add tests for DurationFieldForm --- employees/tests/test_unit_custom_forms.py | 51 +++++++++++++++++++ .../tests/test_unit_project_join_form.py | 25 --------- 2 files changed, 51 insertions(+), 25 deletions(-) create mode 100644 employees/tests/test_unit_custom_forms.py delete mode 100644 employees/tests/test_unit_project_join_form.py diff --git a/employees/tests/test_unit_custom_forms.py b/employees/tests/test_unit_custom_forms.py new file mode 100644 index 000000000..bff9c7d30 --- /dev/null +++ b/employees/tests/test_unit_custom_forms.py @@ -0,0 +1,51 @@ +import datetime + +import assertpy +import pytest +from django.core.exceptions import ValidationError +from django.test import TestCase + +from employees.common.strings import ReportValidationStrings +from employees.forms import DurationFieldForm +from employees.forms import ProjectJoinForm +from managers.models import Project + + +class ProjectJoinFormTests(TestCase): + def test_project_join_form_should_create_choice_field_with_project_name_and_id_based_on_queryset_provided_in_constructor( + self + ): + queryset_length = 10 + for i in range(queryset_length): + project = Project(name=f"Test Project {i}", start_date=datetime.datetime.now()) + project.full_clean() + project.save() + queryset = Project.objects.all() + form = ProjectJoinForm(queryset) + choices = form.fields["projects"].choices + self.assertIsNotNone(choices) + self.assertEqual(len(choices), queryset_length) + for i in range(queryset_length): + self.assertEqual(choices[i][0], queryset[i].id) + self.assertEqual(choices[i][1], queryset[i].name) + + +class TestDurationFieldForm: + def _test_duration_field_form(self, initial_value: str, input_value: str) -> str: # pylint: disable=no-self-use + duration_field_form = DurationFieldForm(initial=initial_value) + return duration_field_form.clean(input_value) + + @pytest.mark.parametrize( + ("initial_value", "input_value", "expected_value"), [("08:00", "8:00", "8:00:00"), ("08:00", "8:0", "8:0:00")] + ) + def test_that_correct_work_hours_is_same_as_assumpted(self, initial_value, input_value, expected_value): + assertpy.assert_that(self._test_duration_field_form(initial_value, input_value)).is_equal_to(expected_value) + + @pytest.mark.parametrize( + ("initial_value", "input_value"), + [("08:00", ":00"), ("08:00", "8:"), ("08:00", ":"), ("08:00", ""), ("08:00", "four:zero"), ("08:00", "8:zero")], + ) + def test_that_incorrect_work_hours_will_raise_exception(self, initial_value, input_value): + with pytest.raises(ValidationError) as exception: + self._test_duration_field_form(initial_value, input_value) + assertpy.assert_that(exception.value.message).is_equal_to(ReportValidationStrings.WORK_HOURS_WRONG_FORMAT.value) diff --git a/employees/tests/test_unit_project_join_form.py b/employees/tests/test_unit_project_join_form.py deleted file mode 100644 index 2add55ea6..000000000 --- a/employees/tests/test_unit_project_join_form.py +++ /dev/null @@ -1,25 +0,0 @@ -import datetime - -from django.test import TestCase - -from employees.forms import ProjectJoinForm -from managers.models import Project - - -class ProjectJoinFormTests(TestCase): - def test_project_join_form_should_create_choice_field_with_project_name_and_id_based_on_queryset_provided_in_constructor( - self - ): - queryset_length = 10 - for i in range(queryset_length): - project = Project(name=f"Test Project {i}", start_date=datetime.datetime.now()) - project.full_clean() - project.save() - queryset = Project.objects.all() - form = ProjectJoinForm(queryset) - choices = form.fields["projects"].choices - self.assertIsNotNone(choices) - self.assertEqual(len(choices), queryset_length) - for i in range(queryset_length): - self.assertEqual(choices[i][0], queryset[i].id) - self.assertEqual(choices[i][1], queryset[i].name) From 8a2cb0b8696c289b4b263f35018ad5cdc69a5a55 Mon Sep 17 00:00:00 2001 From: Karol Beker Date: Fri, 31 May 2019 11:33:17 +0200 Subject: [PATCH 3/3] Update test because of new function in DurationFieldForm --- employees/tests/test_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/employees/tests/test_views.py b/employees/tests/test_views.py index 49b20b5a5..30cbd7df2 100644 --- a/employees/tests/test_views.py +++ b/employees/tests/test_views.py @@ -89,7 +89,7 @@ def setUp(self): "project": self.report.project.pk, "author": self.report.author.pk, "task_activities": self.report.task_activities.pk, - "work_hours": self.report.work_hours, + "work_hours": self.report.work_hours_str, } def test_project_report_detail_view_should_display_report_details(self):