feat: add blog draft generator and fix broken routes
Blog drafting section (dev-only): - BlogDraftGenerator gathers project data (gaps, proposals, stats) as context and calls Claude to produce Medium-style blog posts - DB schema: blog_drafts table with title, content, tags, cost tracking - Web UI: list, generate (async with live preview), detail (rendered + source toggle), edit, and export routes - 6 writing styles: deep-dive, overview, opinion, listicle, comparison, series-post - Nav link added to sidebar under Proposals Bug fixes found via route testing (scripts/test_all_routes.py): - /authors/<id>: Draft.status → Draft.states (correct attribute name) - /false-positives: add missing `import re` in ratings.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -98,3 +98,9 @@ from webui.data.proposals import ( # noqa: F401
|
||||
get_proposal_detail,
|
||||
get_proposals_for_gap,
|
||||
)
|
||||
|
||||
# Blog Drafts
|
||||
from webui.data.blog import ( # noqa: F401
|
||||
get_all_blog_drafts,
|
||||
get_blog_draft_detail,
|
||||
)
|
||||
|
||||
@@ -117,7 +117,7 @@ def get_author_detail(db: Database, person_id: int) -> dict | None:
|
||||
"name": d.name,
|
||||
"title": d.title,
|
||||
"date": d.date,
|
||||
"status": d.status,
|
||||
"states": d.states,
|
||||
"categories": r.categories if r else [],
|
||||
"score": round(r.composite_score, 2) if r else None,
|
||||
"novelty": r.novelty if r else None,
|
||||
|
||||
30
src/webui/data/blog.py
Normal file
30
src/webui/data/blog.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""Blog draft data access for the web dashboard."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from ietf_analyzer.db import Database
|
||||
|
||||
|
||||
def get_all_blog_drafts(db: Database) -> list[dict]:
|
||||
"""Return all blog drafts, newest first."""
|
||||
drafts = db.all_blog_drafts()
|
||||
for d in drafts:
|
||||
try:
|
||||
d["tags"] = json.loads(d.get("tags") or "[]")
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
d["tags"] = []
|
||||
return drafts
|
||||
|
||||
|
||||
def get_blog_draft_detail(db: Database, draft_id: int) -> dict | None:
|
||||
"""Return a single blog draft with parsed tags."""
|
||||
d = db.get_blog_draft(draft_id)
|
||||
if not d:
|
||||
return None
|
||||
try:
|
||||
d["tags"] = json.loads(d.get("tags") or "[]")
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
d["tags"] = []
|
||||
return d
|
||||
@@ -2,6 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from collections import Counter, defaultdict
|
||||
|
||||
from ietf_analyzer.db import Database
|
||||
|
||||
Reference in New Issue
Block a user