From dec145ada57735b8ab54f0222fe944dd7565b005 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 6 Jan 2026 19:34:51 -0600 Subject: [PATCH] fix: add date type coercion logic for CSV importer --- src/wuttasync/importing/csv.py | 11 +++++++++++ tests/importing/test_csv.py | 22 +++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/wuttasync/importing/csv.py b/src/wuttasync/importing/csv.py index 9190099..60c51eb 100644 --- a/src/wuttasync/importing/csv.py +++ b/src/wuttasync/importing/csv.py @@ -391,6 +391,10 @@ def make_coercer(attr): # pylint: disable=too-many-return-statements ): return coerce_datetime + # Date + if isinstance(attr.type, sa.Date): + return coerce_date + # Float # nb. check this before decimal, since Numeric inherits from Float if isinstance(attr.type, sa.Float): @@ -423,6 +427,13 @@ def coerce_boolean_nullable(value): # pylint: disable=missing-function-docstrin return coerce_boolean(value) +def coerce_date(value): # pylint: disable=missing-function-docstring + if value == "": + return None + + return datetime.datetime.strptime(value, "%Y-%m-%d").date() + + def coerce_datetime(value): # pylint: disable=missing-function-docstring if value == "": return None diff --git a/tests/importing/test_csv.py b/tests/importing/test_csv.py index b3f0fad..b36bee9 100644 --- a/tests/importing/test_csv.py +++ b/tests/importing/test_csv.py @@ -271,6 +271,9 @@ class Example(Base): flag = sa.Column(sa.Boolean(), nullable=False) optional_flag = sa.Column(sa.Boolean(), nullable=True) + date = sa.Column(sa.Date(), nullable=False) + optional_date = sa.Column(sa.Date(), nullable=True) + dt = sa.Column(sa.DateTime(), nullable=False) optional_dt = sa.Column(sa.DateTime(), nullable=True) @@ -285,7 +288,7 @@ class TestMakeCoercers(TestCase): def test_basic(self): coercers = mod.make_coercers(Example) - self.assertEqual(len(coercers), 12) + self.assertEqual(len(coercers), 14) self.assertIs(coercers["id"], mod.coerce_integer) self.assertIs(coercers["optional_id"], mod.coerce_integer) @@ -293,6 +296,8 @@ class TestMakeCoercers(TestCase): self.assertIs(coercers["optional_name"], mod.coerce_string_nullable) self.assertIs(coercers["flag"], mod.coerce_boolean) self.assertIs(coercers["optional_flag"], mod.coerce_boolean_nullable) + self.assertIs(coercers["date"], mod.coerce_date) + self.assertIs(coercers["optional_date"], mod.coerce_date) self.assertIs(coercers["dt"], mod.coerce_datetime) self.assertIs(coercers["optional_dt"], mod.coerce_datetime) self.assertIs(coercers["dec"], mod.coerce_decimal) @@ -322,6 +327,12 @@ class TestMakeCoercer(TestCase): func = mod.make_coercer(Example.optional_flag) self.assertIs(func, mod.coerce_boolean_nullable) + func = mod.make_coercer(Example.date) + self.assertIs(func, mod.coerce_date) + + func = mod.make_coercer(Example.optional_date) + self.assertIs(func, mod.coerce_date) + func = mod.make_coercer(Example.dt) self.assertIs(func, mod.coerce_datetime) @@ -365,6 +376,15 @@ class TestCoercers(TestCase): self.assertIsNone(mod.coerce_boolean_nullable("")) + def test_coerce_date(self): + self.assertIsNone(mod.coerce_date("")) + + value = mod.coerce_date("2025-10-19") + self.assertIsInstance(value, datetime.date) + self.assertEqual(value, datetime.date(2025, 10, 19)) + + self.assertRaises(ValueError, mod.coerce_date, "XXX") + def test_coerce_datetime(self): self.assertIsNone(mod.coerce_datetime(""))