Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add endpoint and function to set the current operating cycle #65

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
23 changes: 22 additions & 1 deletion src/nsls2api/api/v1/facility_api.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
from typing import Annotated
import fastapi

from fastapi import Depends

from nsls2api.api.models.facility_model import (
FacilityName,
FacilityCyclesResponseModel,
FacilityCurrentOperatingCycleResponseModel,
)
from nsls2api.infrastructure.security import validate_admin_role
from nsls2api.services import proposal_service, facility_service

router = fastapi.APIRouter()


# TODO: Add back into schema when implemented.
@router.get(
"/facility/{facility}/cycles/current",
response_model=FacilityCurrentOperatingCycleResponseModel,
Expand All @@ -36,6 +39,24 @@ async def get_current_operating_cycle(facility: FacilityName):

return response_model

@router.post("/facility/{facility}/cycles/current",
response_model=FacilityCurrentOperatingCycleResponseModel, dependencies=[Depends(validate_admin_role)],)
async def set_current_operating_cycle(facility: FacilityName, cycle: str,):
current_cycle = await facility_service.set_current_operating_cycle(facility.name, cycle)
if current_cycle is None:
return fastapi.responses.JSONResponse(
{
"error": f"Failed to set the current operating cycle for facility {facility.name}"
},
status_code=404,
)

response_model = FacilityCurrentOperatingCycleResponseModel(
facility=facility.name, cycle=current_cycle
)

return response_model


@router.get(
"/facility/{facility}/cycles",
Expand Down
37 changes: 37 additions & 0 deletions src/nsls2api/services/facility_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,43 @@ async def current_operating_cycle(facility: str) -> Optional[str]:

return cycle.name

async def set_current_operating_cycle(facility: str, cycle: str) -> Optional[str]:
"""
Set Current Operating Cycle

This method sets the current operating cycle for a given facility.

:param facility: The facility name (str).
:param cycle: The cycle name (str).
:return: The current operating cycle (str) or None if no current operating cycle is found.
"""
new_current_cycle = await Cycle.find_one(
Cycle.facility == facility,
Cycle.name == cycle,
)

if new_current_cycle is None:
return None

# Now find previous current operating cycle.
stuartcampbell marked this conversation as resolved.
Show resolved Hide resolved
previous_cycle = await Cycle.find_one(
Cycle.facility == facility,
Cycle.is_current_operating_cycle == True, # noqa: E712
stuartcampbell marked this conversation as resolved.
Show resolved Hide resolved
)

if previous_cycle is not None:
await previous_cycle.set({Cycle.is_current_operating_cycle: False})

await new_current_cycle.set({Cycle.is_current_operating_cycle: True})

# Let's now check all is well with the world
expected_current_cycle = await current_operating_cycle(facility)
if str(expected_current_cycle) != cycle:
logger.error(f"Failed to set the current operating cycle for {facility} to be {cycle}.")
return None
Comment on lines +165 to +174
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably packaged into a transaction (or at least use some context manager) to rollback on failure and avoid an inconsistent state.

The test at the end could be removed at that point.


return expected_current_cycle


async def is_healthy(facility: str) -> bool:
"""
Expand Down
14 changes: 10 additions & 4 deletions src/nsls2api/services/proposal_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ async def fetch_proposals_for_cycle(cycle: str) -> list[str]:
cycle = await Cycle.find_one(Cycle.name == cycle)
if cycle is None:
raise LookupError(f"Cycle {cycle} not found")

# In case a 'None' has crept into the database
if cycle.proposals is None:
return []

return cycle.proposals
stuartcampbell marked this conversation as resolved.
Show resolved Hide resolved


Expand Down Expand Up @@ -549,7 +554,7 @@ async def synchronize_proposal_from_pass(proposal_id: int) -> None:
user_list.append(pi_info)

data_session = generate_data_session_for_proposal(proposal_id)

proposal = Proposal(
proposal_id=str(pass_proposal.Proposal_ID),
title=pass_proposal.Title,
Expand Down Expand Up @@ -643,12 +648,13 @@ async def worker_update_cycle_information(
) -> None:
start_time = datetime.datetime.now()

#TODO: Add test that cycle and facility combination is valid

# TODO: Add test that cycle and facility combination is valid

if cycle:
# If we've specified a cycle then only sync that one
cycles = await Cycle.find(Cycle.name == str(cycle), Cycle.facility == facility).to_list()
cycles = await Cycle.find(
Cycle.name == str(cycle), Cycle.facility == facility
).to_list()
else:
cycles = await Cycle.find(Cycle.facility == facility).to_list()

Expand Down
Loading