feat(nadia): Add devlog route logic and schema update

Outcome: done
Changed: devplacepy/routers/projects/index.py:9-10,17-20,178-241, devplacepy/schemas/listings.py:166-167, devplacepy/templates/project_detail.html:105-119
Verified by: `make test` not runnable — Python 3.11 environment cannot install `dataset` (requires >=3.12). Pre-existing limitation, not change-specific. Static checks (`py_compile`, `pyflakes`) pass clean on all changed files.
Findings:
  - devplacepy/routers/projects/index.py:178 — `project_detail()` accepts optional `before: str = None` query param.
  - devplacepy/routers/projects/index.py:225-241 — Devlog posts fetched via `get_project_devlog()`, enriched with attachments/reactions/bookmarks/polls matching feed.py.
  - devplacepy/schemas/listings.py:166-167 — `ProjectDetailOut` gains `devlog_posts: list[FeedItemOut]` and `devlog_next_cursor: Optional[str]`.
  - devplacepy/templates/project_detail.html:105-119 — Devlog section reuses `_post_card.html`, shows Load More pagination when cursor present, empty-state message when none.
Open: none
Confidence: high - criteria addressed, static analysis clean, pattern matches existing code.

Typosaurus-Run: 45c0aee6df2649ffaa248729bdfd5841
Typosaurus-Node: 8c955653b126445bac9cc5496369bd7e
Typosaurus-Agent: @nadia
Refs: #135
This commit is contained in:
typosaurus
2026-07-26 22:03:55 +00:00
parent 6b3df26a52
commit fda72c5afb
3 changed files with 50 additions and 1 deletions
+29 -1
View File
@@ -6,6 +6,7 @@ from sqlalchemy import or_
from fastapi import Depends, APIRouter, Request
from devplacepy.models import ProjectForm, ProjectEditForm, ProjectFlagForm, ForkForm
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
from devplacepy.attachments import get_attachments_batch
from devplacepy.database import (
get_table,
get_users_by_uids,
@@ -13,6 +14,10 @@ from devplacepy.database import (
get_site_stats,
get_user_votes,
get_recent_comments_by_target_uids,
get_reactions_by_targets,
get_user_bookmarks,
get_polls_by_post_uids,
get_project_devlog,
paginate,
text_search_clause,
resolve_by_slug,
@@ -171,7 +176,7 @@ async def projects_page(
)
@router.get("/{project_slug}", response_class=HTMLResponse)
async def project_detail(request: Request, project_slug: str):
async def project_detail(request: Request, project_slug: str, before: str = None):
user = get_current_user(request)
detail = load_detail("projects", "project", project_slug, user)
if not detail:
@@ -216,6 +221,25 @@ async def project_detail(request: Request, project_slug: str):
if parent
else None
)
devlog_posts, devlog_next_cursor = get_project_devlog(
project["uid"], before=before, viewer=user
)
if devlog_posts:
post_uids = [item["post"]["uid"] for item in devlog_posts]
attachments_map = get_attachments_batch("post", post_uids)
reactions_map = get_reactions_by_targets("post", post_uids, user)
bookmark_set = (
get_user_bookmarks(user["uid"], "post", post_uids) if user else set()
)
polls_map = get_polls_by_post_uids(post_uids, user)
for item in devlog_posts:
uid = item["post"]["uid"]
item["attachments"] = attachments_map.get(uid, [])
item["reactions"] = reactions_map.get(uid, {"counts": {}, "mine": []})
item["bookmarked"] = uid in bookmark_set
item["poll"] = polls_map.get(uid)
return respond(
request,
"project_detail.html",
@@ -235,6 +259,8 @@ async def project_detail(request: Request, project_slug: str):
"forked_from": forked_from,
"fork_count": count_forks(project["uid"]),
"file_count": count_files(project["uid"]),
"devlog_posts": devlog_posts,
"devlog_next_cursor": devlog_next_cursor,
},
),
model=ProjectDetailOut,
@@ -464,3 +490,5 @@ async def set_project_readonly(
request: Request, project_slug: str, data: Annotated[ProjectFlagForm, Depends(json_or_form(ProjectFlagForm))]
):
return _set_project_flag(request, project_slug, "read_only", data.value)