Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/nsls2api/api/models/proposal_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,20 @@ class ProposalIdDataSessionList(pydantic.BaseModel):
proposals: list[ProposalIdDataSession]
count: int
page_size: int
page: int
page: int

class ProposalSummaryForUser(pydantic.BaseModel):
proposal_id: str
title: str
saf_ids: list[str]
principal_investigator: Optional[User]
instruments: Optional[list[str]]
cycles: Optional[list[str]]
data_session: Optional[str]

class UserProposalsList(pydantic.BaseModel):
username: str
count: int
page: int
page_size: int
proposals: list[ProposalSummaryForUser]
52 changes: 50 additions & 2 deletions src/nsls2api/api/v1/user_api.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import asyncio

import fastapi
from fastapi import HTTPException, Request, Header

from typing import Annotated

from fastapi import Depends,Header, HTTPException, Request, Query

from nsls2api.api.models.person_model import DataSessionAccess, LDAPUserResponse, Person
from nsls2api.api.models.proposal_model import Proposal, ProposalSummaryForUser, UserProposalsList
from nsls2api.infrastructure.security import (
get_current_user,
)
from nsls2api.infrastructure.security import get_settings
from nsls2api.services import (
bnlpeople_service,
person_service,
proposal_service,
)
from nsls2api.services.ldap_service import get_user_info, shape_ldap_response

Expand Down Expand Up @@ -106,3 +113,44 @@ async def get_myself(upn: str = Header(...)):
async def get_data_sessions_by_username(username: str):
data_access = await person_service.data_sessions_by_username(username)
return data_access


@router.get("/person/proposals", response_model=UserProposalsList, summary="Fetch proposals for a user including, SAF ID's, PI details")
async def get_proposals_for_username(
username: str = Header(..., description="Username to fetch proposals for"),
page_size: int = Query(10, ge=1, le=200),
page: int = Query(1, ge=1),
):
if not username:
raise HTTPException(status_code=400, detail="Username header is required")

current_cycle, proposals = await proposal_service.fetch_proposals_for_username(
username, page_size=page_size, page=page
)

if current_cycle is None:
raise HTTPException(status_code=404, detail="No current operating cycle found")

proposal_summaries = []
for proposal in proposals:
pi = next((u for u in proposal.users if u.is_pi), None)
saf_ids = [s.saf_id for s in (proposal.safs or []) if s.saf_id]
proposal_summaries.append(
ProposalSummaryForUser(
proposal_id=proposal.proposal_id,
title=proposal.title,
saf_ids=saf_ids,
principal_investigator=pi,
instruments=proposal.instruments,
cycles=proposal.cycles,
data_session=proposal.data_session,
)
)

return UserProposalsList(
username=username,
count=len(proposal_summaries),
page=page,
page_size=page_size,
proposals=proposal_summaries,
)
21 changes: 21 additions & 0 deletions src/nsls2api/services/proposal_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,3 +807,24 @@ async def generate_fake_test_proposal(
await Proposal.insert_one(proposal)

return proposal

async def fetch_proposals_for_username(username: str, page_size: int = 10,
page: int = 1) -> tuple[str | None, list[Proposal]]:
"""Retrieve all proposals associated with given username for current operating cycle"""

current_cycle = await facility_service.current_operating_cycle(FacilityName.nsls2)

if not current_cycle: return None, []

proposals = (
await Proposal.find(
And(
ElemMatch(Proposal.users, {"username": username}),
In(Proposal.cycles, [current_cycle]),
)
)
.limit(page_size)
.skip(page_size * (page - 1))
.to_list()
)
return current_cycle, proposals