diff --git a/app/modules/bank_statement_analyzer/ui.py b/app/modules/bank_statement_analyzer/ui.py index e52badd..3805de8 100644 --- a/app/modules/bank_statement_analyzer/ui.py +++ b/app/modules/bank_statement_analyzer/ui.py @@ -1,9 +1,12 @@ from __future__ import annotations +from datetime import datetime, timezone from pathlib import Path +from zoneinfo import ZoneInfo, ZoneInfoNotFoundError from fastapi import APIRouter, File, Form, Request, UploadFile from fastapi.responses import FileResponse, JSONResponse, RedirectResponse +from sqlalchemy import select from app.core.db.common import CommonSessionLocal from app.core.http_responses import ui_access_denied, not_found_response @@ -18,6 +21,47 @@ from .service import can_use, create_job_folder, delete_owned_job, enqueue_job, router = APIRouter(prefix="/tools/bank-statement-analyzer", tags=["bank-statement-analyzer-ui"]) +def _display_timezone_name(request: Request, db, user) -> str: + """Resolve the active branch timezone, falling back to tenant and India defaults.""" + from app.modules.core.tenancy.models import Branch, Tenant + + branch_id = request.session.get("active_branch_id") or getattr(user, "branch_id", None) + tenant_id = request.session.get("active_tenant_id") or getattr(user, "tenant_id", None) + + if branch_id not in (None, "", 0, "0"): + branch = db.scalar(select(Branch).where(Branch.id == int(branch_id))) + branch_timezone = (getattr(branch, "timezone", None) or "").strip() if branch else "" + if branch_timezone: + return branch_timezone + + if tenant_id not in (None, "", 0, "0"): + tenant = db.scalar(select(Tenant).where(Tenant.id == int(tenant_id))) + tenant_timezone = (getattr(tenant, "default_timezone", None) or "").strip() if tenant else "" + if tenant_timezone: + return tenant_timezone + + return "Asia/Kolkata" + + +def _format_local_datetime(value: datetime | None, timezone_name: str) -> str | None: + if value is None: + return None + try: + target_timezone = ZoneInfo(timezone_name) + except ZoneInfoNotFoundError: + target_timezone = ZoneInfo("Asia/Kolkata") + aware_value = value if value.tzinfo is not None else value.replace(tzinfo=timezone.utc) + return aware_value.astimezone(target_timezone).strftime("%d-%b-%Y %I:%M %p %Z") + + +def _localised_job_view(job, timezone_name: str) -> dict: + view = job_view(job) + view["submitted_at"] = _format_local_datetime(view.get("submitted_at"), timezone_name) + view["completed_at"] = _format_local_datetime(view.get("completed_at"), timezone_name) + view["expires_at"] = _format_local_datetime(view.get("expires_at"), timezone_name) + return view + + def _ctx(request, db, user, **extra): data = { "request": request, @@ -53,9 +97,11 @@ def index(request: Request, job: str | None = None): user, roles, denied = _auth(request, db) if denied: return denied + timezone_name = _display_timezone_name(request, db, user) selected_job = get_owned_job(user.id, job) if job else None - recent = [job_view(item) for item in list_user_jobs(user.id, limit=8)] - return templates.TemplateResponse("modules/bank_statement_analyzer/templates/bank_statement_analyzer/index.html", _ctx(request, db, user, error="", active_job=job_view(selected_job) if selected_job else None, recent_jobs=recent)) + recent = [_localised_job_view(item, timezone_name) for item in list_user_jobs(user.id, limit=8)] + active_job = _localised_job_view(selected_job, timezone_name) if selected_job else None + return templates.TemplateResponse("modules/bank_statement_analyzer/templates/bank_statement_analyzer/index.html", _ctx(request, db, user, error="", active_job=active_job, recent_jobs=recent, display_timezone=timezone_name)) finally: db.close() @@ -83,8 +129,9 @@ async def analyze(request: Request, csrf_token: str = Form(...), bank_selection: user = get_current_user(request, db=db) if not user: return RedirectResponse("/login", status_code=303) - recent = [job_view(item) for item in list_user_jobs(user.id, limit=8)] - return templates.TemplateResponse("modules/bank_statement_analyzer/templates/bank_statement_analyzer/index.html", _ctx(request, db, user, error=str(exc), active_job=None, recent_jobs=recent, selected_bank=selected_bank, financial_year=financial_year, classification_enabled=classification_enabled), status_code=400) + timezone_name = _display_timezone_name(request, db, user) + recent = [_localised_job_view(item, timezone_name) for item in list_user_jobs(user.id, limit=8)] + return templates.TemplateResponse("modules/bank_statement_analyzer/templates/bank_statement_analyzer/index.html", _ctx(request, db, user, error=str(exc), active_job=None, recent_jobs=recent, selected_bank=selected_bank, financial_year=financial_year, classification_enabled=classification_enabled, display_timezone=timezone_name), status_code=400) finally: db.close() @@ -97,8 +144,9 @@ def jobs(request: Request): user, roles, denied = _auth(request, db) if denied: return denied - items = [job_view(item) for item in list_user_jobs(user.id, limit=100)] - return templates.TemplateResponse("modules/bank_statement_analyzer/templates/bank_statement_analyzer/jobs.html", _ctx(request, db, user, jobs=items)) + timezone_name = _display_timezone_name(request, db, user) + items = [_localised_job_view(item, timezone_name) for item in list_user_jobs(user.id, limit=100)] + return templates.TemplateResponse("modules/bank_statement_analyzer/templates/bank_statement_analyzer/jobs.html", _ctx(request, db, user, jobs=items, display_timezone=timezone_name)) finally: db.close()