Add author detail, idea detail, and gap-draft reverse link pages
- Author detail page (/authors/<person_id>): shows author info, all drafts with ratings, and co-authors with shared draft counts. Public route. - Idea detail page (/ideas/<idea_id>): shows idea metadata, source draft, and top-5 most similar ideas via embedding cosine similarity. Admin route. - Gap detail page: added "Related Drafts" section that finds drafts by extracting draft names from evidence text and searching by topic keywords. - Updated author links across templates to use /authors/<person_id> URLs. - Added DB methods: get_author_by_id, get_author_drafts, get_coauthors. - Extended top_authors to include person_id (5th tuple element). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -639,10 +639,10 @@ class Database:
|
||||
def author_count(self) -> int:
|
||||
return self.conn.execute("SELECT COUNT(*) FROM authors").fetchone()[0]
|
||||
|
||||
def top_authors(self, limit: int = 20) -> list[tuple[str, str, int, list[str]]]:
|
||||
"""Return (name, affiliation, draft_count, [draft_names])."""
|
||||
def top_authors(self, limit: int = 20) -> list[tuple[str, str, int, list[str], int]]:
|
||||
"""Return (name, affiliation, draft_count, [draft_names], person_id)."""
|
||||
rows = self.conn.execute(
|
||||
"""SELECT a.name, a.affiliation, COUNT(da.draft_name) as cnt,
|
||||
"""SELECT a.person_id, a.name, a.affiliation, COUNT(da.draft_name) as cnt,
|
||||
GROUP_CONCAT(da.draft_name, '||') as drafts
|
||||
FROM authors a
|
||||
JOIN draft_authors da ON a.person_id = da.person_id
|
||||
@@ -653,10 +653,50 @@ class Database:
|
||||
).fetchall()
|
||||
return [
|
||||
(r["name"], r["affiliation"], r["cnt"],
|
||||
r["drafts"].split("||") if r["drafts"] else [])
|
||||
r["drafts"].split("||") if r["drafts"] else [],
|
||||
r["person_id"])
|
||||
for r in rows
|
||||
]
|
||||
|
||||
def get_author_by_id(self, person_id: int) -> dict | None:
|
||||
"""Return author info by person_id, or None if not found."""
|
||||
row = self.conn.execute(
|
||||
"SELECT * FROM authors WHERE person_id = ?", (person_id,)
|
||||
).fetchone()
|
||||
if not row:
|
||||
return None
|
||||
return {
|
||||
"person_id": row["person_id"],
|
||||
"name": row["name"],
|
||||
"ascii_name": row["ascii_name"],
|
||||
"affiliation": row["affiliation"],
|
||||
"resource_uri": row["resource_uri"],
|
||||
}
|
||||
|
||||
def get_author_drafts(self, person_id: int) -> list[str]:
|
||||
"""Return draft names for a given author."""
|
||||
rows = self.conn.execute(
|
||||
"SELECT draft_name FROM draft_authors WHERE person_id = ? ORDER BY draft_name",
|
||||
(person_id,),
|
||||
).fetchall()
|
||||
return [r["draft_name"] for r in rows]
|
||||
|
||||
def get_coauthors(self, person_id: int) -> list[dict]:
|
||||
"""Return co-authors for a given person (authors on the same drafts)."""
|
||||
rows = self.conn.execute(
|
||||
"""SELECT DISTINCT a.person_id, a.name, a.affiliation, COUNT(DISTINCT da2.draft_name) as shared
|
||||
FROM draft_authors da1
|
||||
JOIN draft_authors da2 ON da1.draft_name = da2.draft_name AND da2.person_id != da1.person_id
|
||||
JOIN authors a ON da2.person_id = a.person_id
|
||||
WHERE da1.person_id = ?
|
||||
GROUP BY a.person_id
|
||||
ORDER BY shared DESC""",
|
||||
(person_id,),
|
||||
).fetchall()
|
||||
return [{"person_id": r["person_id"], "name": r["name"],
|
||||
"affiliation": r["affiliation"], "shared_drafts": r["shared"]}
|
||||
for r in rows]
|
||||
|
||||
def top_orgs(self, limit: int = 20) -> list[tuple[str, int, int]]:
|
||||
"""Return (org, author_count, draft_count)."""
|
||||
rows = self.conn.execute(
|
||||
|
||||
Reference in New Issue
Block a user