This commit is contained in:
Justin Visser 2026-02-19 22:50:22 +01:00
parent 710f7675c3
commit ba7976d935
10 changed files with 271 additions and 356 deletions

View file

@ -31,13 +31,13 @@ router = APIRouter(prefix="/publications", tags=["api-publications"])
)
async def list_publications(
request: Request,
mode: Literal["all", "new"] | None = Query(default=None),
mode: Literal["all", "unread", "latest", "new"] | None = Query(default=None),
scholar_profile_id: int | None = Query(default=None, ge=1),
limit: int = Query(default=300, ge=1, le=1000),
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_api_current_user),
):
resolved_mode = publication_service.resolve_mode(mode)
resolved_mode = publication_service.resolve_publication_view_mode(mode)
selected_scholar_id = scholar_profile_id
if selected_scholar_id is not None:
selected_profile = await scholar_service.get_user_scholar_by_id(
@ -59,10 +59,14 @@ async def list_publications(
scholar_profile_id=selected_scholar_id,
limit=limit,
)
new_count = await publication_service.count_for_user(
unread_count = await publication_service.count_unread_for_user(
db_session,
user_id=current_user.id,
scholar_profile_id=selected_scholar_id,
)
latest_count = await publication_service.count_latest_for_user(
db_session,
user_id=current_user.id,
mode=publication_service.MODE_NEW,
scholar_profile_id=selected_scholar_id,
)
total_count = await publication_service.count_for_user(
@ -76,7 +80,9 @@ async def list_publications(
data={
"mode": resolved_mode,
"selected_scholar_profile_id": selected_scholar_id,
"new_count": new_count,
"unread_count": unread_count,
"latest_count": latest_count,
"new_count": latest_count,
"total_count": total_count,
"publications": [
{

View file

@ -515,6 +515,8 @@ class PublicationItemData(BaseModel):
class PublicationsListData(BaseModel):
mode: str
selected_scholar_profile_id: int | None
unread_count: int
latest_count: int
new_count: int
total_count: int
publications: list[PublicationItemData]

View file

@ -14,8 +14,10 @@ from app.db.models import (
ScholarPublication,
)
MODE_NEW = "new"
MODE_ALL = "all"
MODE_UNREAD = "unread"
MODE_LATEST = "latest"
MODE_NEW = "new" # compatibility alias for MODE_LATEST
@dataclass(frozen=True)
@ -45,10 +47,16 @@ class UnreadPublicationItem:
pub_url: str | None
def resolve_publication_view_mode(value: str | None) -> str:
if value == MODE_UNREAD:
return MODE_UNREAD
if value in {MODE_LATEST, MODE_NEW}:
return MODE_LATEST
return MODE_ALL
def resolve_mode(value: str | None) -> str:
if value == MODE_ALL:
return MODE_ALL
return MODE_NEW
return resolve_publication_view_mode(value)
async def get_latest_completed_run_id_for_user(
@ -99,8 +107,10 @@ def publications_query(
)
if scholar_profile_id is not None:
stmt = stmt.where(ScholarProfile.id == scholar_profile_id)
if mode == MODE_NEW:
# "New" means discovered in the latest completed run.
if mode == MODE_UNREAD:
stmt = stmt.where(ScholarPublication.is_read.is_(False))
if mode == MODE_LATEST:
# "Latest" means discovered in the latest completed run.
if latest_run_id is None:
stmt = stmt.where(False)
else:
@ -116,7 +126,7 @@ async def list_for_user(
scholar_profile_id: int | None = None,
limit: int = 300,
) -> list[PublicationListItem]:
resolved_mode = resolve_mode(mode)
resolved_mode = resolve_publication_view_mode(mode)
latest_run_id = await get_latest_completed_run_id_for_user(
db_session,
user_id=user_id,
@ -177,11 +187,11 @@ async def list_unread_for_user(
result = await db_session.execute(
publications_query(
user_id=user_id,
mode=MODE_ALL,
mode=MODE_UNREAD,
latest_run_id=None,
scholar_profile_id=None,
limit=limit,
).where(ScholarPublication.is_read.is_(False))
)
)
rows = result.all()
items: list[UnreadPublicationItem] = []
@ -224,7 +234,7 @@ async def list_new_for_latest_run_for_user(
rows = await list_for_user(
db_session,
user_id=user_id,
mode=MODE_NEW,
mode=MODE_LATEST,
scholar_profile_id=None,
limit=limit,
)
@ -250,7 +260,7 @@ async def count_for_user(
mode: str = MODE_ALL,
scholar_profile_id: int | None = None,
) -> int:
resolved_mode = resolve_mode(mode)
resolved_mode = resolve_publication_view_mode(mode)
latest_run_id = await get_latest_completed_run_id_for_user(
db_session,
user_id=user_id,
@ -263,7 +273,9 @@ async def count_for_user(
)
if scholar_profile_id is not None:
stmt = stmt.where(ScholarProfile.id == scholar_profile_id)
if resolved_mode == MODE_NEW:
if resolved_mode == MODE_UNREAD:
stmt = stmt.where(ScholarPublication.is_read.is_(False))
if resolved_mode == MODE_LATEST:
if latest_run_id is None:
return 0
stmt = stmt.where(ScholarPublication.first_seen_run_id == latest_run_id)
@ -271,6 +283,34 @@ async def count_for_user(
return int(result.scalar_one() or 0)
async def count_unread_for_user(
db_session: AsyncSession,
*,
user_id: int,
scholar_profile_id: int | None = None,
) -> int:
return await count_for_user(
db_session,
user_id=user_id,
mode=MODE_UNREAD,
scholar_profile_id=scholar_profile_id,
)
async def count_latest_for_user(
db_session: AsyncSession,
*,
user_id: int,
scholar_profile_id: int | None = None,
) -> int:
return await count_for_user(
db_session,
user_id=user_id,
mode=MODE_LATEST,
scholar_profile_id=scholar_profile_id,
)
async def mark_all_unread_as_read_for_user(
db_session: AsyncSession,
*,