3
0
Fork 0

fix: add DateTimeAlchemyFilter for datetime columns

This commit is contained in:
Lance Edgar 2026-03-21 18:58:53 -05:00
parent f8cb97ce63
commit 20a586e950
2 changed files with 397 additions and 7 deletions

View file

@ -9,6 +9,7 @@ from unittest.mock import patch
import sqlalchemy as sa
from wuttjamaican.db.model import Base
from wuttjamaican.util import get_timezone_by_name
from wuttaweb.grids import filters as mod
from wuttaweb.testing import WebTestCase
@ -569,7 +570,8 @@ class TestBooleanAlchemyFilter(WebTestCase):
class TheLocalThing(Base):
__tablename__ = "the_local_thing"
id = sa.Column(sa.Integer(), primary_key=True, autoincrement=False)
date = sa.Column(sa.DateTime(timezone=True), nullable=True)
date = sa.Column(sa.Date(), nullable=True)
timestamp = sa.Column(sa.DateTime(), nullable=True)
class TestDateAlchemyFilter(WebTestCase):
@ -604,7 +606,7 @@ class TestDateAlchemyFilter(WebTestCase):
# null value
self.assertIsNone(filtr.coerce_value(None))
# value as datetime
# value as date
value = datetime.date(2024, 1, 1)
result = filtr.coerce_value(value)
self.assertIs(value, result)
@ -717,7 +719,7 @@ class TestDateAlchemyFilter(WebTestCase):
self.sample_query, datetime.date(2024, 3, 1)
)
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 2)
self.assertEqual(filtered_query.count(), 4)
# value as string
filtered_query = filtr.filter_less_equal(self.sample_query, "2024-04-01")
@ -725,6 +727,238 @@ class TestDateAlchemyFilter(WebTestCase):
self.assertEqual(filtered_query.count(), 4)
class TestDateTimeAlchemyFilter(WebTestCase):
def setUp(self):
self.setup_web()
self.tzlocal = get_timezone_by_name("America/Los_Angeles")
self.sample_data = [
{
"id": 1,
"timestamp": self.app.make_utc(
datetime.datetime(2024, 1, 1, 1, 20, tzinfo=self.tzlocal)
),
},
{
"id": 2,
"timestamp": self.app.make_utc(
datetime.datetime(2024, 1, 1, 23, 40, tzinfo=self.tzlocal)
),
},
{
"id": 3,
"timestamp": self.app.make_utc(
datetime.datetime(2024, 3, 1, 22, 10, tzinfo=self.tzlocal)
),
},
{
"id": 4,
"timestamp": self.app.make_utc(
datetime.datetime(2024, 3, 1, 2, 0, tzinfo=self.tzlocal)
),
},
{"id": 5, "timestamp": None},
{"id": 6, "timestamp": None},
]
for thing in self.sample_data:
thing = TheLocalThing(**thing)
self.session.add(thing)
self.session.commit()
self.sample_query = self.session.query(TheLocalThing)
def make_filter(self, model_property, **kwargs):
factory = kwargs.pop("factory", mod.DateTimeAlchemyFilter)
kwargs["model_property"] = model_property
return factory(self.request, model_property.key, **kwargs)
def test_coerce_value(self):
filtr = self.make_filter(TheLocalThing.timestamp)
# null value
self.assertIsNone(filtr.coerce_value(None))
# value as date
value = datetime.date(2024, 1, 1)
result = filtr.coerce_value(value)
self.assertIs(value, result)
# value as string
result = filtr.coerce_value("2024-04-01")
self.assertIsInstance(result, datetime.date)
self.assertEqual(result, datetime.date(2024, 4, 1))
# invalid
result = filtr.coerce_value("thisinputisbad")
self.assertIsNone(result)
def test_equal(self):
model = self.app.model
with patch.object(self.app, "get_timezone", return_value=self.tzlocal):
filtr = self.make_filter(TheLocalThing.timestamp)
self.assertEqual(self.sample_query.count(), 6)
# null value ignored
filtered_query = filtr.filter_equal(self.sample_query, None)
self.assertIs(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 6)
# value as date
filtered_query = filtr.filter_equal(
self.sample_query, datetime.date(2024, 3, 1)
)
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 2)
# value as string
filtered_query = filtr.filter_equal(self.sample_query, "2024-02-01")
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 0)
filtered_query = filtr.filter_equal(self.sample_query, "2024-01-01")
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 2)
def test_not_equal(self):
model = self.app.model
with patch.object(self.app, "get_timezone", return_value=self.tzlocal):
filtr = self.make_filter(TheLocalThing.timestamp)
self.assertEqual(self.sample_query.count(), 6)
# null value ignored
filtered_query = filtr.filter_not_equal(self.sample_query, None)
self.assertIs(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 6)
# value as date
filtered_query = filtr.filter_not_equal(
self.sample_query, datetime.date(2024, 2, 1)
)
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 4)
# value as string
filtered_query = filtr.filter_not_equal(self.sample_query, "2024-02-01")
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 4)
filtered_query = filtr.filter_not_equal(self.sample_query, "2024-01-01")
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 2)
def test_greater_than(self):
model = self.app.model
with patch.object(self.app, "get_timezone", return_value=self.tzlocal):
filtr = self.make_filter(TheLocalThing.timestamp)
self.assertEqual(self.sample_query.count(), 6)
# null value ignored
filtered_query = filtr.filter_greater_than(self.sample_query, None)
self.assertIs(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 6)
# value as date
filtered_query = filtr.filter_greater_than(
self.sample_query, datetime.date(2024, 2, 1)
)
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 2)
# value as string
filtered_query = filtr.filter_greater_than(self.sample_query, "2024-02-01")
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 2)
def test_greater_equal(self):
model = self.app.model
with patch.object(self.app, "get_timezone", return_value=self.tzlocal):
filtr = self.make_filter(TheLocalThing.timestamp)
self.assertEqual(self.sample_query.count(), 6)
# null value ignored
filtered_query = filtr.filter_greater_equal(self.sample_query, None)
self.assertIs(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 6)
# value as date (clear of boundary)
filtered_query = filtr.filter_greater_equal(
self.sample_query, datetime.date(2024, 2, 1)
)
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 2)
# value as date (at boundary)
filtered_query = filtr.filter_greater_equal(
self.sample_query, datetime.date(2024, 3, 1)
)
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 2)
# value as string
filtered_query = filtr.filter_greater_equal(self.sample_query, "2024-01-01")
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 4)
def test_less_than(self):
model = self.app.model
with patch.object(self.app, "get_timezone", return_value=self.tzlocal):
filtr = self.make_filter(TheLocalThing.timestamp)
self.assertEqual(self.sample_query.count(), 6)
# null value ignored
filtered_query = filtr.filter_less_than(self.sample_query, None)
self.assertIs(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 6)
# value as date
filtered_query = filtr.filter_less_than(
self.sample_query, datetime.date(2024, 2, 1)
)
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 2)
# value as string
filtered_query = filtr.filter_less_than(self.sample_query, "2024-04-01")
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 4)
def test_less_equal(self):
model = self.app.model
with patch.object(self.app, "get_timezone", return_value=self.tzlocal):
filtr = self.make_filter(TheLocalThing.timestamp)
self.assertEqual(self.sample_query.count(), 6)
# null value ignored
filtered_query = filtr.filter_less_equal(self.sample_query, None)
self.assertIs(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 6)
# value as date (clear of boundary)
filtered_query = filtr.filter_less_equal(
self.sample_query, datetime.date(2024, 2, 1)
)
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 2)
# value as date (at boundary)
filtered_query = filtr.filter_less_equal(
self.sample_query, datetime.date(2024, 3, 1)
)
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 4)
# value as string
filtered_query = filtr.filter_less_equal(self.sample_query, "2024-04-01")
self.assertIsNot(filtered_query, self.sample_query)
self.assertEqual(filtered_query.count(), 4)
class TestVerbNotSupported(TestCase):
def test_basic(self):