73 lines
2.3 KiB
Python
73 lines
2.3 KiB
Python
"""
|
|
Sidebar navigation data builder.
|
|
Loads domains > areas > projects hierarchy for the sidebar tree.
|
|
"""
|
|
|
|
from sqlalchemy import text
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
async def get_sidebar_data(db: AsyncSession) -> dict:
|
|
"""Build full sidebar navigation data."""
|
|
|
|
# Domains
|
|
result = await db.execute(text("""
|
|
SELECT id, name, color FROM domains
|
|
WHERE is_deleted = false ORDER BY sort_order, name
|
|
"""))
|
|
domains = [dict(r._mapping) for r in result]
|
|
|
|
# Areas grouped by domain
|
|
result = await db.execute(text("""
|
|
SELECT id, domain_id, name FROM areas
|
|
WHERE is_deleted = false ORDER BY sort_order, name
|
|
"""))
|
|
areas = [dict(r._mapping) for r in result]
|
|
|
|
# Projects grouped by domain/area
|
|
result = await db.execute(text("""
|
|
SELECT id, domain_id, area_id, name, status FROM projects
|
|
WHERE is_deleted = false AND status != 'archived'
|
|
ORDER BY sort_order, name
|
|
"""))
|
|
projects = [dict(r._mapping) for r in result]
|
|
|
|
# Counts for badges
|
|
result = await db.execute(text("""
|
|
SELECT count(*) FROM capture WHERE is_deleted = false AND processed = false
|
|
"""))
|
|
capture_count = result.scalar() or 0
|
|
|
|
result = await db.execute(text("""
|
|
SELECT count(*) FROM daily_focus
|
|
WHERE is_deleted = false AND focus_date = CURRENT_DATE AND completed = false
|
|
"""))
|
|
focus_count = result.scalar() or 0
|
|
|
|
# Build tree structure
|
|
domain_tree = []
|
|
for d in domains:
|
|
d_areas = [a for a in areas if str(a["domain_id"]) == str(d["id"])]
|
|
d_projects = [p for p in projects if str(p["domain_id"]) == str(d["id"])]
|
|
|
|
# Projects under areas
|
|
for a in d_areas:
|
|
a["projects"] = [p for p in d_projects if str(p.get("area_id", "")) == str(a["id"])]
|
|
|
|
# Projects directly under domain (no area)
|
|
standalone_projects = [p for p in d_projects if p.get("area_id") is None]
|
|
|
|
domain_tree.append({
|
|
"id": d["id"],
|
|
"name": d["name"],
|
|
"color": d.get("color", "#4F6EF7"),
|
|
"areas": d_areas,
|
|
"standalone_projects": standalone_projects,
|
|
})
|
|
|
|
return {
|
|
"domain_tree": domain_tree,
|
|
"capture_count": capture_count,
|
|
"focus_count": focus_count,
|
|
}
|