feat: Ipad app features and Dream Weaver for Velocity WebOS
Some checks failed
Production Readiness / backend-contracts (pull_request) Has been cancelled
Production Readiness / webos-typecheck (pull_request) Has been cancelled
Production Readiness / ipad-parse (pull_request) Has been cancelled

This commit is contained in:
Sayan Datta
2026-04-28 10:59:07 +05:30
parent 184bfa77f8
commit fefe8373ec
117 changed files with 19510 additions and 6383 deletions

View File

@@ -21,7 +21,7 @@ from __future__ import annotations
import json
import logging
from datetime import UTC, datetime
from datetime import datetime, timezone
from typing import Any, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
@@ -43,6 +43,10 @@ def _pool(request: Request):
return pool
def _tenant_scope(user) -> str:
return user.tenant_id
# ── Pydantic Models ───────────────────────────────────────────────────────────
VALID_SOURCE_TYPES = {"csv", "json", "api_push", "manual"}
@@ -111,7 +115,7 @@ async def create_import_batch(
VALUES ($1, $2, $3, $4, $5)
RETURNING batch_id, status, created_at
""",
user.role, body.source_type, user.user_id, body.total_rows, body.source_file_ref,
_tenant_scope(user), body.source_type, user.user_id, body.total_rows, body.source_file_ref,
)
return dict(row)
@@ -134,10 +138,10 @@ async def list_import_batches(
ORDER BY created_at DESC
LIMIT $2 OFFSET $3
""",
user.role, limit, offset,
_tenant_scope(user), limit, offset,
)
total = await conn.fetchval(
"SELECT COUNT(*) FROM inventory_import_batches WHERE tenant_id=$1", user.role,
"SELECT COUNT(*) FROM inventory_import_batches WHERE tenant_id=$1", _tenant_scope(user),
)
return {"total": total, "limit": limit, "offset": offset, "batches": [dict(r) for r in rows]}
@@ -154,7 +158,7 @@ async def get_import_batch(
"""
SELECT * FROM inventory_import_batches WHERE batch_id=$1 AND tenant_id=$2
""",
batch_id, user.role,
batch_id, _tenant_scope(user),
)
if not row:
raise HTTPException(404, "Batch not found")
@@ -187,7 +191,7 @@ async def create_property(
)
RETURNING property_id, created_at
""",
user.role, body.batch_id, body.source_id, body.project_name, body.developer_name,
_tenant_scope(user), body.batch_id, body.source_id, body.project_name, body.developer_name,
json.dumps(body.location), body.property_type, json.dumps(body.price_bands),
json.dumps(body.unit_mix), body.amenities,
body.status, json.dumps(body.validation_state),
@@ -207,7 +211,7 @@ async def list_properties(
pool = _pool(request)
async with pool.acquire() as conn:
where_clause = "WHERE tenant_id = $1"
params: list[Any] = [user.role]
params: list[Any] = [_tenant_scope(user)]
idx = 2
if status_filter:
@@ -246,7 +250,7 @@ async def get_property(
async with pool.acquire() as conn:
row = await conn.fetchrow(
"SELECT * FROM inventory_properties WHERE property_id=$1 AND tenant_id=$2",
property_id, user.role,
property_id, _tenant_scope(user),
)
if not row:
raise HTTPException(404, "Property not found")
@@ -287,8 +291,8 @@ async def update_property(
if not updates:
raise HTTPException(400, "No fields to update")
_add("updated_at", datetime.now(UTC))
values.extend([property_id, user.role])
_add("updated_at", datetime.now(timezone.utc))
values.extend([property_id, _tenant_scope(user)])
pool = _pool(request)
async with pool.acquire() as conn:
@@ -319,7 +323,7 @@ async def archive_property(
SET status='archived', updated_at=NOW()
WHERE property_id=$1 AND tenant_id=$2
""",
property_id, user.role,
property_id, _tenant_scope(user),
)
if result == "UPDATE 0":
raise HTTPException(404, "Property not found")
@@ -344,7 +348,7 @@ async def add_media(
# Verify property belongs to tenant
exists = await conn.fetchval(
"SELECT 1 FROM inventory_properties WHERE property_id=$1 AND tenant_id=$2",
property_id, user.role,
property_id, _tenant_scope(user),
)
if not exists:
raise HTTPException(404, "Property not found")
@@ -356,7 +360,7 @@ async def add_media(
VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8)
RETURNING media_asset_id, created_at
""",
property_id, user.role, body.media_type, body.url, body.thumbnail_url,
property_id, _tenant_scope(user), body.media_type, body.url, body.thumbnail_url,
body.sort_order, json.dumps(body.metadata), user.user_id,
)
return {"media_asset_id": str(row["media_asset_id"]), "created_at": str(row["created_at"])}
@@ -377,7 +381,7 @@ async def list_media(
WHERE property_id=$1 AND tenant_id=$2
ORDER BY sort_order ASC, created_at ASC
""",
property_id, user.role,
property_id, _tenant_scope(user),
)
return {"media": [dict(r) for r in rows]}
@@ -392,7 +396,7 @@ async def delete_media(
async with pool.acquire() as conn:
result = await conn.execute(
"DELETE FROM inventory_media_assets WHERE media_asset_id=$1 AND tenant_id=$2",
media_asset_id, user.role,
media_asset_id, _tenant_scope(user),
)
if result == "DELETE 0":
raise HTTPException(404, "Media asset not found")