Initial project upload

This commit is contained in:
Mohamed Mathar Irfan
2026-07-28 17:57:02 +05:30
commit ed6610d5d8
23919 changed files with 3003316 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
"""Dashboard + analytics aggregates that feed the client dashboard UI."""
from collections import Counter
from datetime import datetime, timedelta, timezone
from fastapi import APIRouter, Depends
from app.core.deps import require_org, Principal
from app.models import Product, Batch, QRCode, ScanLog, Plan, StorageUsage
router = APIRouter(prefix="/dashboard", tags=["dashboard"])
@router.get("/summary")
async def summary(p: Principal = Depends(require_org)):
oid = str(p.org.id)
products = await Product.find(Product.org_id == oid).to_list()
batches = await Batch.find(Batch.org_id == oid).to_list()
qrs = await QRCode.find(QRCode.org_id == oid).to_list()
scans = await ScanLog.find(ScanLog.org_id == oid).to_list()
plan = await Plan.get(p.org.plan_id) if p.org.plan_id else None
storage = await StorageUsage.find_one(StorageUsage.org_id == oid)
today = datetime.now(timezone.utc).date()
scans_today = sum(1 for s in scans if s.scanned_at.date() == today)
active_qr = sum(1 for q in qrs if q.status == "active")
avg_trust = round(sum(x.trust_score for x in products) / len(products)) if products else 0
return {
"products": len(products),
"product_limit": plan.product_limit if plan else None,
"qr_codes": len(qrs),
"active_qr": active_qr,
"batches": len(batches),
"scans_today": scans_today,
"total_scans": len(scans),
"trust_score_avg": avg_trust,
"storage_used_gb": round((storage.used_bytes if storage else 0) / 1024 ** 3, 1),
"storage_total_gb": round((storage.total_bytes if storage else 0) / 1024 ** 3, 1),
"plan_name": plan.name if plan else "Free",
"user_count": p.org.user_count,
"user_limit": plan.user_limit if plan else None,
}
@router.get("/scan-trend")
async def scan_trend(p: Principal = Depends(require_org), days: int = 7):
oid = str(p.org.id)
scans = await ScanLog.find(ScanLog.org_id == oid).to_list()
start = datetime.now(timezone.utc).date() - timedelta(days=days - 1)
buckets = {start + timedelta(days=i): 0 for i in range(days)}
for s in scans:
d = s.scanned_at.date()
if d in buckets:
buckets[d] += 1
return [{"date": str(d), "scans": c} for d, c in sorted(buckets.items())]
@router.get("/top-products")
async def top_products(
p: Principal = Depends(require_org),
limit: int = 8,
):
scans = await ScanLog.find(
ScanLog.org_id == str(p.org.id)
).to_list()
counter = Counter(s.product_id for s in scans if s.product_id)
products = await Product.find(
Product.org_id == str(p.org.id)
).to_list()
lookup = {str(prod.id): prod.name for prod in products}
result = []
for product_id, count in counter.most_common(limit):
result.append({
"name": lookup.get(product_id, "Unknown Product"),
"scans": count,
})
return result
@router.get("/top-locations")
async def top_locations(p: Principal = Depends(require_org), limit: int = 5):
scans = await ScanLog.find(ScanLog.org_id == str(p.org.id)).to_list()
counter = Counter(s.city for s in scans if s.city)
return [{"city": city, "scans": n} for city, n in counter.most_common(limit)]
@router.get("/recent-activity")
async def recent_activity(p: Principal = Depends(require_org), limit: int = 8):
from app.models import AuditLog
logs = await AuditLog.find(AuditLog.org_id == str(p.org.id)).to_list()
logs.sort(key=lambda x: x.created_at, reverse=True)
return [{"action": l.action, "details": l.details, "user": l.user_name,
"module": l.module, "at": l.created_at} for l in logs[:limit]]
@router.get("/devices")
async def devices(p: Principal = Depends(require_org)):
scans = await ScanLog.find(ScanLog.org_id == str(p.org.id)).to_list()
counter = Counter(s.device or "Other" for s in scans)
return [{"device": d, "scans": n} for d, n in counter.most_common()]