Skip to content

Commit

Permalink
Merge pull request #4350 from unicef/218202-pdu-import-validation-dat…
Browse files Browse the repository at this point in the history
…e-format

[218202] PDU enhance validation for date fields
  • Loading branch information
pkujawa authored Oct 18, 2024
2 parents b6982ca + 5cbf5a8 commit 6db5d6d
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import json
from typing import Any, Optional, Union

Expand Down Expand Up @@ -38,6 +39,22 @@ def to_python(self, value: Union[Optional[str], bool]) -> Optional[bool]:
raise ValidationError("Invalid boolean value", code="invalid")


class StrictDateField(forms.DateField):
def to_python(self, value: Any) -> Optional[datetime.date]:
"""
Override to cover other cases, i.e. integer value provided.
"""
if value in self.empty_values:
return None
if isinstance(value, datetime.datetime):
return value.date()
if isinstance(value, datetime.date): # pragma: no cover
return value
if isinstance(value, str):
return super().to_python(value)
raise ValidationError("Invalid date value", code="invalid")


class RowValidationError(ValidationError):
pass

Expand Down Expand Up @@ -278,7 +295,7 @@ def _build_form(self) -> type[forms.Form]:
form_fields_dict[f"{round['field']}__round_number"] = forms.IntegerField()
form_fields_dict[f"{round['field']}__round_name"] = forms.CharField(required=False)
form_fields_dict[f"{round['field']}__round_value"] = self._get_form_field_for_value(flexible_attribute)
form_fields_dict[f"{round['field']}__collection_date"] = forms.DateField(required=False)
form_fields_dict[f"{round['field']}__collection_date"] = StrictDateField(required=False)

return type("PeriodicDataUpdateForm", (PeriodicDataUpdateBaseForm,), form_fields_dict)

Expand All @@ -290,5 +307,5 @@ def _get_form_field_for_value(self, flexible_attribute: FlexibleAttribute) -> fo
elif flexible_attribute.pdu_data.subtype == PeriodicFieldData.BOOL:
return StrictBooleanField(required=False)
elif flexible_attribute.pdu_data.subtype == PeriodicFieldData.DATE:
return forms.DateField(required=False)
return StrictDateField(required=False)
raise ValidationError(f"Invalid subtype for field {flexible_attribute.name}")
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,59 @@ def test_import_data_boolean(self) -> None:
self.assertEqual(self.individual.flex_fields[flexible_attribute.name]["1"]["value"], True)
self.assertEqual(self.individual.flex_fields[flexible_attribute.name]["1"]["collection_date"], "2021-05-02")

def test_import_data_boolean_1(self) -> None:
flexible_attribute = self.boolean_attribute
periodic_data_update_template, periodic_data_update_upload = self.prepare_test_data(
[
{
"field": flexible_attribute.name,
"round": 1,
"round_name": flexible_attribute.pdu_data.rounds_names[0],
"number_of_records": 0,
}
],
[[False, "2021-05-02"]],
)
service = PeriodicDataUpdateImportService(periodic_data_update_upload)
service.import_data()
self.assertEqual(periodic_data_update_upload.status, PeriodicDataUpdateUpload.Status.SUCCESSFUL)
self.assertEqual(periodic_data_update_upload.error_message, None)
self.individual.refresh_from_db()
self.assertEqual(self.individual.flex_fields[flexible_attribute.name]["1"]["value"], False)
self.assertEqual(self.individual.flex_fields[flexible_attribute.name]["1"]["collection_date"], "2021-05-02")

def test_import_data_boolean_fail(self) -> None:
flexible_attribute = self.boolean_attribute
periodic_data_update_template, periodic_data_update_upload = self.prepare_test_data(
[
{
"field": flexible_attribute.name,
"round": 1,
"round_name": flexible_attribute.pdu_data.rounds_names[0],
"number_of_records": 0,
}
],
[["Yes", "2021-05-02"]],
)
service = PeriodicDataUpdateImportService(periodic_data_update_upload)
service.import_data()
self.assertEqual(periodic_data_update_upload.status, PeriodicDataUpdateUpload.Status.FAILED)
errors = {
"form_errors": [
{
"row": 2,
"errors": {
"boolean_attribute__round_value": [{"message": "Invalid boolean value", "code": "invalid"}]
},
}
],
"non_form_errors": None,
}
self.assertEqual(
json.loads(periodic_data_update_upload.error_message),
errors,
)

def test_import_data_date(self) -> None:
flexible_attribute = self.date_attribute
periodic_data_update_template, periodic_data_update_upload = self.prepare_test_data(
Expand Down Expand Up @@ -233,7 +286,7 @@ def test_import_data_date_no_collection_date(self) -> None:
self.assertEqual(self.individual.flex_fields[flexible_attribute.name]["1"]["value"], "1996-06-21")
self.assertEqual(self.individual.flex_fields[flexible_attribute.name]["1"]["collection_date"], "2021-03-07")

def test_import_data_date_fail(self) -> None:
def test_import_data_date_fail_string(self) -> None:
flexible_attribute = self.date_attribute
periodic_data_update_template, periodic_data_update_upload = self.prepare_test_data(
[
Expand Down Expand Up @@ -263,6 +316,36 @@ def test_import_data_date_fail(self) -> None:
errors,
)

def test_import_data_date_fail_int(self) -> None:
flexible_attribute = self.date_attribute
periodic_data_update_template, periodic_data_update_upload = self.prepare_test_data(
[
{
"field": flexible_attribute.name,
"round": 1,
"round_name": flexible_attribute.pdu_data.rounds_names[0],
"number_of_records": 0,
}
],
[[58, "2021-05-02"]],
)
service = PeriodicDataUpdateImportService(periodic_data_update_upload)
service.import_data()
self.assertEqual(periodic_data_update_upload.status, PeriodicDataUpdateUpload.Status.FAILED)
errors = {
"form_errors": [
{
"row": 2,
"errors": {"date_attribute__round_value": [{"message": "Invalid date value", "code": "invalid"}]},
}
],
"non_form_errors": None,
}
self.assertEqual(
json.loads(periodic_data_update_upload.error_message),
errors,
)

def test_read_periodic_data_update_non_form_errors(self) -> None:
flexible_attribute = self.date_attribute
periodic_data_update_template, periodic_data_update_upload = self.prepare_test_data(
Expand Down Expand Up @@ -292,6 +375,29 @@ def test_read_periodic_data_update_non_form_errors(self) -> None:
errors,
)

def test_import_data_date_format_correct(self) -> None:
flexible_attribute = self.date_attribute
periodic_data_update_template, periodic_data_update_upload = self.prepare_test_data(
[
{
"field": flexible_attribute.name,
"round": 1,
"round_name": flexible_attribute.pdu_data.rounds_names[0],
"number_of_records": 0,
}
],
[
[datetime.datetime(2021, 5, 2), datetime.datetime(2021, 5, 2)],
],
)
service = PeriodicDataUpdateImportService(periodic_data_update_upload)
service.import_data()
self.assertEqual(periodic_data_update_upload.status, PeriodicDataUpdateUpload.Status.SUCCESSFUL)
self.assertEqual(periodic_data_update_upload.error_message, None)
self.individual.refresh_from_db()
self.assertEqual(self.individual.flex_fields[flexible_attribute.name]["1"]["value"], "2021-05-02")
self.assertEqual(self.individual.flex_fields[flexible_attribute.name]["1"]["collection_date"], "2021-05-02")

def test_read_periodic_data_update_template_object(self) -> None:
periodic_data_update_template = PeriodicDataUpdateTemplate.objects.create(
program=self.program,
Expand Down

0 comments on commit 6db5d6d

Please sign in to comment.