From aa2591d9100c9062a41fdc4ff6463ea770b08689 Mon Sep 17 00:00:00 2001 From: Felipe Alvarado Date: Thu, 16 Jan 2025 11:29:35 +0100 Subject: [PATCH] Add timezone to datetime fields --- app/datasources/db/models.py | 10 ++- app/tests/routers/test_contracts.py | 5 +- app/utils.py | 9 ++ ...e94343c151_add_timezone_datetime_fields.py | 85 +++++++++++++++++++ 4 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 app/utils.py create mode 100644 migrations/versions/e5e94343c151_add_timezone_datetime_fields.py diff --git a/app/datasources/db/models.py b/app/datasources/db/models.py index 7570e8e..1f45c36 100644 --- a/app/datasources/db/models.py +++ b/app/datasources/db/models.py @@ -1,7 +1,7 @@ from datetime import datetime, timezone from typing import AsyncIterator, Self, cast -from sqlalchemy import Row +from sqlalchemy import Row, DateTime from sqlmodel import ( JSON, Column, @@ -46,15 +46,17 @@ class TimeStampedSQLModel(SQLModel): """ created: datetime = Field( - default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None), + default_factory=lambda: datetime.now(timezone.utc), nullable=False, + sa_type=DateTime(timezone=True), # type: ignore ) modified: datetime = Field( - default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None), + default_factory=lambda: datetime.now(timezone.utc), nullable=False, + sa_type=DateTime(timezone=True), # type: ignore sa_column_kwargs={ - "onupdate": lambda: datetime.now(timezone.utc).replace(tzinfo=None), + "onupdate": lambda: datetime.now(timezone.utc), }, ) diff --git a/app/tests/routers/test_contracts.py b/app/tests/routers/test_contracts.py index fca7195..bfbaa06 100644 --- a/app/tests/routers/test_contracts.py +++ b/app/tests/routers/test_contracts.py @@ -6,6 +6,7 @@ from ...datasources.db.database import database_session from ...datasources.db.models import Abi, AbiSource, Contract from ...main import app +from ...utils import datetime_to_str from ..datasources.db.db_async_conn import DbAsyncConn from ..mocks.abi_mock import mock_abi_json @@ -42,11 +43,11 @@ async def test_view_contracts(self, session: AsyncSession): self.assertEqual(results[0]["address"], address_expected) self.assertEqual(results[0]["abi"]["abi_json"], mock_abi_json) self.assertEqual(results[0]["abi"]["abi_hash"], "0xb4b61541") - self.assertEqual(results[0]["abi"]["modified"], abi.modified.isoformat()) + self.assertEqual(results[0]["abi"]["modified"], datetime_to_str(abi.modified)) self.assertEqual(results[0]["display_name"], None) self.assertEqual(results[0]["chain_id"], 1) self.assertEqual(results[0]["project"], None) - self.assertEqual(results[0]["modified"], contract.modified.isoformat()) + self.assertEqual(results[0]["modified"], datetime_to_str(contract.modified)) # Test filter by chain_id contract = Contract( address=address, name="A Test Contracts", chain_id=5, abi=abi diff --git a/app/utils.py b/app/utils.py new file mode 100644 index 0000000..693c34b --- /dev/null +++ b/app/utils.py @@ -0,0 +1,9 @@ +from datetime import datetime + + +def datetime_to_str(value: datetime) -> str: + """ + :param value: `datetime.datetime` value + :return: ``ISO 8601`` date with ``Z`` format + """ + return value.isoformat().replace("+00:00", "Z") diff --git a/migrations/versions/e5e94343c151_add_timezone_datetime_fields.py b/migrations/versions/e5e94343c151_add_timezone_datetime_fields.py new file mode 100644 index 0000000..c356593 --- /dev/null +++ b/migrations/versions/e5e94343c151_add_timezone_datetime_fields.py @@ -0,0 +1,85 @@ +"""add_timezone_datetime_fields + +Revision ID: e5e94343c151 +Revises: 8d2eacb17707 +Create Date: 2025-01-16 10:14:21.505781 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = "e5e94343c151" +down_revision: Union[str, None] = "8d2eacb17707" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "abi", + "created", + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False, + ) + op.alter_column( + "abi", + "modified", + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False, + ) + op.alter_column( + "contract", + "created", + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False, + ) + op.alter_column( + "contract", + "modified", + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False, + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "contract", + "modified", + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False, + ) + op.alter_column( + "contract", + "created", + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False, + ) + op.alter_column( + "abi", + "modified", + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False, + ) + op.alter_column( + "abi", + "created", + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False, + ) + # ### end Alembic commands ###