diff --git a/app/api/subscriptions.py b/app/api/subscriptions.py index 572ba48b..ce695240 100644 --- a/app/api/subscriptions.py +++ b/app/api/subscriptions.py @@ -9,7 +9,7 @@ Public endpoints: """ import logging -from datetime import datetime, timezone +from datetime import datetime, time, timedelta, timezone from typing import Annotated, Any from fastapi import APIRouter, Depends, HTTPException, Request, status @@ -207,19 +207,29 @@ def platform_stats(request: Request, db: DbSession, _admin: AdminUser) -> dict[s from app.models import FileRecord, UserProfile today = datetime.now(timezone.utc).date() + day_start = datetime.combine(today, time.min, tzinfo=timezone.utc) + day_end = day_start + timedelta(days=1) + month_start = day_start.replace(day=1) + if month_start.month == 12: + month_end = month_start.replace(year=month_start.year + 1, month=1) + else: + month_end = month_start.replace(month=month_start.month + 1) # Total files total_files: int = db.query(func.count(FileRecord.id)).scalar() or 0 # Files today files_today: int = ( - db.query(func.count(FileRecord.id)).filter(func.date(FileRecord.created_at) == today).scalar() or 0 + db.query(func.count(FileRecord.id)) + .filter(FileRecord.created_at >= day_start, FileRecord.created_at < day_end) + .scalar() + or 0 ) # Files this month files_this_month: int = ( db.query(func.count(FileRecord.id)) - .filter(func.strftime("%Y-%m", FileRecord.created_at) == today.strftime("%Y-%m")) + .filter(FileRecord.created_at >= month_start, FileRecord.created_at < month_end) .scalar() or 0 ) diff --git a/app/utils/subscription.py b/app/utils/subscription.py index f0d1bd3e..7d98b773 100644 --- a/app/utils/subscription.py +++ b/app/utils/subscription.py @@ -29,7 +29,7 @@ At average usage (~40 % of quota) margins improve to 55-65 % after tax. from __future__ import annotations import logging -from datetime import date, datetime, timezone +from datetime import date, datetime, time, timedelta, timezone from typing import Any from sqlalchemy import func @@ -317,6 +317,20 @@ def _today_utc() -> date: return datetime.now(timezone.utc).date() +def _day_bounds_utc(day: date) -> tuple[datetime, datetime]: + start = datetime.combine(day, time.min, tzinfo=timezone.utc) + return start, start + timedelta(days=1) + + +def _month_bounds_utc(day: date) -> tuple[datetime, datetime]: + start = datetime.combine(day.replace(day=1), time.min, tzinfo=timezone.utc) + if start.month == 12: + end = start.replace(year=start.year + 1, month=1) + else: + end = start.replace(month=start.month + 1) + return start, end + + def _scalar_count(query: Any) -> int: """Execute a count query and return an int, defaulting to 0 for NULL.""" return query.scalar() or 0 @@ -335,12 +349,13 @@ def get_today_file_count(db: Session, owner_id: str) -> int: """Files processed by this user today (UTC, not counting duplicates).""" from app.models import FileRecord - today = _today_utc() + day_start, day_end = _day_bounds_utc(_today_utc()) return _scalar_count( db.query(func.count(FileRecord.id)).filter( FileRecord.owner_id == owner_id, FileRecord.is_duplicate.is_(False), - func.date(FileRecord.created_at) == today, + FileRecord.created_at >= day_start, + FileRecord.created_at < day_end, ) ) @@ -349,12 +364,13 @@ def get_month_file_count(db: Session, owner_id: str) -> int: """Files processed by this user this calendar month (UTC, not counting duplicates).""" from app.models import FileRecord - today = _today_utc() + month_start, month_end = _month_bounds_utc(_today_utc()) return _scalar_count( db.query(func.count(FileRecord.id)).filter( FileRecord.owner_id == owner_id, FileRecord.is_duplicate.is_(False), - func.strftime("%Y-%m", FileRecord.created_at) == today.strftime("%Y-%m"), + FileRecord.created_at >= month_start, + FileRecord.created_at < month_end, ) ) diff --git a/app/views/general.py b/app/views/general.py index 6efb2a7f..fe7851f3 100644 --- a/app/views/general.py +++ b/app/views/general.py @@ -2,7 +2,7 @@ General routes for the application homepage and basic pages. """ -from datetime import date, datetime, timezone +from datetime import date, datetime, time, timedelta, timezone from pathlib import Path from fastapi import Depends, HTTPException, Request @@ -64,17 +64,27 @@ async def serve_index(request: Request, db: Session = Depends(get_db)): user = request.session.get("user") or {} is_admin = user.get("is_admin", False) + day_start = datetime.combine(today, time.min, tzinfo=timezone.utc) + day_end = day_start + timedelta(days=1) + month_start = day_start.replace(day=1) + if month_start.month == 12: + month_end = month_start.replace(year=month_start.year + 1, month=1) + else: + month_end = month_start.replace(month=month_start.month + 1) try: total_files: int = db.query(func.count(FileRecord.id)).scalar() or 0 files_today: int = ( - db.query(func.count(FileRecord.id)).filter(func.date(FileRecord.created_at) == today).scalar() or 0 + db.query(func.count(FileRecord.id)) + .filter(FileRecord.created_at >= day_start, FileRecord.created_at < day_end) + .scalar() + or 0 ) files_month: int = ( db.query(func.count(FileRecord.id)) - .filter(func.strftime("%Y-%m", FileRecord.created_at) == today.strftime("%Y-%m")) + .filter(FileRecord.created_at >= month_start, FileRecord.created_at < month_end) .scalar() or 0 )