Skip to content

Commit

Permalink
Fix datetimeComponent: always stored as ISO with date, time, timezone.
Browse files Browse the repository at this point in the history
Fix datetimeComponent which is always stored as ISO with combined
date, time and timezone.
The previous implementation was wrong and now backwards imcompatible.

Warning:
Test all `datetimeComponent` setters and their impact on implementations.
  • Loading branch information
bobslee committed May 6, 2024
1 parent b9f1a43 commit 17f2e98
Show file tree
Hide file tree
Showing 5 changed files with 16 additions and 100 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 2.0.0

Fix `datetimeComponent` which is always stored as ISO with combined date, time and timezone.\
The previous implementation was wrong and now backwards imcompatible.\

**Warning**:

Test all `datetimeComponent` setters and their impact on implementations.

## 1.2.7

Fix ValueError in `datetimeComponent` value setter exception handler:\
Expand Down
95 changes: 1 addition & 94 deletions formiodata/components/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,103 +63,10 @@ def value(self, value):
- https://gist.github.com/Susensio/979259559e2bebcd0273f1a95d7c1e79
- https://stackoverflow.com/questions/35290540/understanding-property-decorator-and-inheritance
"""
# TODO: to improve these transformations (mappings and loops)

if not value:
return value

component = self.component_owner.input_components.get(self.key)

# if not component.raw.get('enableTime'):
if not self.enableTime:
# OMG some parsing to deal with the ISO format (storage).
try:
dt = datetime.fromisoformat(value)
dt_format = self.raw.get('format')
py_format = copy(dt_format)
for date_part, mapping in self._format_mappings().items():
done_date_part = False
for fm_formio, fm_py in mapping.items():
# fm_formio are (JS) uibDateParser codes, see comment
# in _format_mappings
if not done_date_part and fm_formio in dt_format:
py_format = py_format.replace(fm_formio, fm_py)
done_date_part = True
super(self.__class__, self.__class__).value.fset(
self,
dt.strftime(py_format)
)
except ValueError:
dt_format = self.raw.get('format')
py_format = copy(dt_format)
for date_part, mapping in self._format_mappings().items():
done_date_part = False
for fm_formio, fm_py in mapping.items():
# fm_formio are (JS) uibDateParser codes, see comment
# in _format_mappings
if not done_date_part and fm_formio in dt_format:
py_format = py_format.replace(fm_formio, fm_py)
done_date_part = True
py_dt = datetime.strptime(value, py_format)
val = py_dt.isoformat()
super(self.__class__, self.__class__).value.fset(self, val)
return
else:
dt = self._fromisoformat(value)
py_dt_format = formio_dt_format = component.raw.get('format')
mapping = self._format_mappings()

# year
done = False
for formio, py in mapping['year'].items():
if not done and formio in formio_dt_format:
py_dt_format = py_dt_format.replace(formio, py)
done = True

# month
done = False
for formio, py in mapping['month'].items():
if not done and formio in formio_dt_format:
py_dt_format = py_dt_format.replace(formio, py)
done = True

# day
done = False
for formio, py in mapping['day'].items():
if not done and formio in formio_dt_format:
py_dt_format = py_dt_format.replace(formio, py)
done = True

# hour
done = False
for formio, py in mapping['hour'].items():
if not done and formio in formio_dt_format:
py_dt_format = py_dt_format.replace(formio, py)
done = True

# minute
done = False
for formio, py in mapping['minute'].items():
if not done and formio in formio_dt_format:
py_dt_format = py_dt_format.replace(formio, py)
done = True

# second
done = False
for formio, py in mapping['second'].items():
if not done and formio in formio_dt_format:
py_dt_format = py_dt_format.replace(formio, py)
done = True

# 12 hours AM/PM
done = False
for formio, py in mapping['am_pm'].items():
if not done and formio in formio_dt_format:
py_dt_format = py_dt_format.replace(formio, py)
done = True

val = dt.strftime(py_dt_format)
super(self.__class__, self.__class__).value.fset(self, val)
return super(self.__class__, self.__class__).value.fset(self, value)

def to_datetime(self):
if not self.raw_value:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "formio-data"
version = "1.2.7"
version = "2.0.0"
homepage = "https://github.com/novacode-nl/python-formio-data"
description = "formio.js JSON-data API"
readme = "README.md"
Expand Down
8 changes: 4 additions & 4 deletions tests/test_component_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,23 @@ def test_set_label(self):
def test_get_form(self):
birthdate = self.form.input_components['birthdate']
self.assertEqual(birthdate.label, 'Birthdate')
self.assertEqual(birthdate.value, '1999-12-31')
self.assertEqual(birthdate.value, '1999-12-31T12:00:00+01:00')
self.assertEqual(birthdate.type, 'datetime')

appointmentDateTime = self.form.input_components['appointmentDateTime']
self.assertEqual(appointmentDateTime.label, 'Appointment Date / Time')
self.assertEqual(appointmentDateTime.value, '2021-02-26 12:30 PM')
self.assertEqual(appointmentDateTime.value, '2021-02-26T12:30:00+01:00')
self.assertEqual(appointmentDateTime.type, 'datetime')

def test_get_form_data(self):
birthdate = self.form.input.birthdate
self.assertEqual(birthdate.label, 'Birthdate')
self.assertEqual(birthdate.value, '1999-12-31')
self.assertEqual(birthdate.value, '1999-12-31T12:00:00+01:00')
self.assertEqual(birthdate.type, 'datetime')

appointmentDateTime = self.form.input.appointmentDateTime
self.assertEqual(appointmentDateTime.label, 'Appointment Date / Time')
self.assertEqual(appointmentDateTime.value, '2021-02-26 12:30 PM')
self.assertEqual(appointmentDateTime.value, '2021-02-26T12:30:00+01:00')
self.assertEqual(appointmentDateTime.type, 'datetime')

def test_to_datetime(self):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_nested_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ def test_Form_input_components_not_datagrid(self):
birthdate = form.input_components['birthdate']
self.assertIsInstance(birthdate, datetimeComponent)
self.assertEqual(birthdate.label, 'Birthdate')
self.assertEqual(birthdate.value, '1999-12-31')
self.assertEqual(birthdate.value, '1999-12-31T12:00:00+01:00')
self.assertEqual(birthdate.type, 'datetime')
self.assertIsInstance(birthdate.to_datetime().date(), date)

Expand Down

0 comments on commit 17f2e98

Please sign in to comment.