Fix PostgreSQL timestamp handling in gauntletruns.py

- Convert milliseconds to datetime for GET filters (created_after/before, ended_after/before)
- Fix is_active filter to use is_null() instead of comparing to 0
- Fix PATCH to use datetime.now() instead of int timestamps
- Fix POST to convert timestamps and use None for nullable ended field
- Update Pydantic model defaults to None instead of int

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-01-30 22:39:45 -06:00
parent e1c39cfb17
commit f3b0b90860
2 changed files with 33 additions and 17 deletions

View File

@ -8,7 +8,7 @@
"branch": "postgres-migration",
"totalEstimatedHours": 26,
"totalTasks": 28,
"completedTasks": 20
"completedTasks": 24
},
"context": {
"sourceDatabase": {
@ -532,7 +532,7 @@
"id": "TS-008",
"name": "Fix gauntletruns.py GET timestamp filters (4 fields)",
"category": "high",
"completed": false,
"completed": true,
"file": "app/routers_v2/gauntletruns.py",
"lines": [36, 37, 58, 60, 62, 64],
"notes": "created_after, created_before, ended_after, ended_before"
@ -541,7 +541,7 @@
"id": "TS-009",
"name": "Fix gauntletruns.py GET is_active filter (NULL vs 0)",
"category": "high",
"completed": false,
"completed": true,
"file": "app/routers_v2/gauntletruns.py",
"lines": [67, 69],
"notes": "Should use is_null() not == 0"
@ -550,7 +550,7 @@
"id": "TS-010",
"name": "Fix gauntletruns.py PATCH timestamp fields",
"category": "high",
"completed": false,
"completed": true,
"file": "app/routers_v2/gauntletruns.py",
"lines": [122, 124, 129, 131],
"notes": "Assigns int to DateTimeField, uses 0 instead of None"
@ -559,7 +559,7 @@
"id": "TS-011",
"name": "Fix gauntletruns.py POST timestamp fields",
"category": "high",
"completed": false,
"completed": true,
"file": "app/routers_v2/gauntletruns.py",
"lines": [28, 29, 152],
"notes": "Pydantic model uses int with default 0"

View File

@ -25,8 +25,8 @@ class GauntletRunModel(pydantic.BaseModel):
wins: Optional[int] = 0
losses: Optional[int] = 0
gsheet: Optional[str] = None
created: Optional[int] = int(datetime.timestamp(datetime.now())*1000)
ended: Optional[int] = 0
created: Optional[int] = None
ended: Optional[int] = None
@router.get('')
@ -55,18 +55,24 @@ async def get_gauntletruns(
if gsheet is not None:
all_gauntlets = all_gauntlets.where(GauntletRun.gsheet == gsheet)
if created_after is not None:
all_gauntlets = all_gauntlets.where(GauntletRun.created >= created_after)
# Convert milliseconds timestamp to datetime for PostgreSQL comparison
created_after_dt = datetime.fromtimestamp(created_after / 1000)
all_gauntlets = all_gauntlets.where(GauntletRun.created >= created_after_dt)
if created_before is not None:
all_gauntlets = all_gauntlets.where(GauntletRun.created <= created_before)
created_before_dt = datetime.fromtimestamp(created_before / 1000)
all_gauntlets = all_gauntlets.where(GauntletRun.created <= created_before_dt)
if ended_after is not None:
all_gauntlets = all_gauntlets.where(GauntletRun.ended >= ended_after)
ended_after_dt = datetime.fromtimestamp(ended_after / 1000)
all_gauntlets = all_gauntlets.where(GauntletRun.ended >= ended_after_dt)
if ended_before is not None:
all_gauntlets = all_gauntlets.where(GauntletRun.ended <= ended_before)
ended_before_dt = datetime.fromtimestamp(ended_before / 1000)
all_gauntlets = all_gauntlets.where(GauntletRun.ended <= ended_before_dt)
if is_active is not None:
if is_active is True:
all_gauntlets = all_gauntlets.where(GauntletRun.ended == 0)
# Active runs have NULL ended, not 0
all_gauntlets = all_gauntlets.where(GauntletRun.ended.is_null())
else:
all_gauntlets = all_gauntlets.where(GauntletRun.ended != 0)
all_gauntlets = all_gauntlets.where(GauntletRun.ended.is_null(False))
if gauntlet_id is not None:
all_gauntlets = all_gauntlets.where(GauntletRun.gauntlet_id << gauntlet_id)
if season is not None:
@ -121,14 +127,14 @@ async def patch_gauntletrun(
this_run.gsheet = gsheet
if created is not None:
if created is True:
this_run.created = int(datetime.timestamp(datetime.now())*1000)
this_run.created = datetime.now()
else:
this_run.created = None
if ended is not None:
if ended is True:
this_run.ended = int(datetime.timestamp(datetime.now())*1000)
this_run.ended = datetime.now()
else:
this_run.ended = 0
this_run.ended = None
if this_run.save():
r_curr = model_to_dict(this_run)
@ -149,7 +155,17 @@ async def post_gauntletrun(gauntletrun: GauntletRunModel, token: str = Depends(o
detail='You are not authorized to post gauntlets. This event has been logged.'
)
this_run = GauntletRun(**gauntletrun.dict())
run_data = gauntletrun.dict()
# Convert milliseconds timestamps to datetime for PostgreSQL
if run_data.get('created'):
run_data['created'] = datetime.fromtimestamp(run_data['created'] / 1000)
else:
run_data['created'] = datetime.now()
if run_data.get('ended'):
run_data['ended'] = datetime.fromtimestamp(run_data['ended'] / 1000)
else:
run_data['ended'] = None
this_run = GauntletRun(**run_data)
if this_run.save():
r_run = model_to_dict(this_run)