diff --git a/src/murfey/server/api/session_info.py b/src/murfey/server/api/session_info.py index 33c872144..3867d35ae 100644 --- a/src/murfey/server/api/session_info.py +++ b/src/murfey/server/api/session_info.py @@ -7,6 +7,7 @@ from fastapi import APIRouter, Depends, Request from fastapi.responses import FileResponse, JSONResponse from pydantic import BaseModel +from sqlalchemy import func from sqlmodel import Session as SQLModelSession, select import murfey.server @@ -286,6 +287,44 @@ def get_data_collections( return data_collections +@router.get("/sessions/{session_id}/movies/preprocessed") +async def get_preprocessed_movie_count( + session_id: MurfeySessionID, + db: SQLModelSession = murfey_db, +): + """ + Helper function that counts the number of movies for the current session that have + been preprocessed or not, and returns the result as a dictionary. + """ + grouped_movies = db.exec( + select(MurfeyDB.Movie.preprocessed, func.count().label("number")) + .join( + MurfeyDB.DataCollection, + MurfeyDB.Movie.data_collection_id == MurfeyDB.DataCollection.id, + ) + .join( + MurfeyDB.DataCollectionGroup, + MurfeyDB.DataCollection.dcg_id == MurfeyDB.DataCollectionGroup.id, + ) + .join( + MurfeyDB.Session, + MurfeyDB.DataCollectionGroup.session_id == MurfeyDB.Session.id, + ) + .where(MurfeyDB.Session.id == session_id) + .group_by(MurfeyDB.Movie.preprocessed) + ).all() + preprocessed_movies = { + "complete": 0, + "incomplete": 0, + } + for preprocessed, number in grouped_movies: + if preprocessed: + preprocessed_movies["complete"] = number + else: + preprocessed_movies["incomplete"] = number + return preprocessed_movies + + @router.get("/clients") async def get_clients(db: SQLModelSession = murfey_db): clients = db.exec(select(MurfeyDB.ClientEnvironment)).all() diff --git a/src/murfey/server/feedback.py b/src/murfey/server/feedback.py index f4c4ddd98..666ac66d6 100644 --- a/src/murfey/server/feedback.py +++ b/src/murfey/server/feedback.py @@ -2051,6 +2051,7 @@ def feedback_callback( .where(db.ProcessingJob.recipe == "em-tomo-align") ).one() + # Update the tilt series and its corresponding tilts relevant_tilt_and_series = _db.exec( select(db.Tilt, db.TiltSeries) .where(db.Tilt.movie_path == message.get("movie")) @@ -2061,7 +2062,17 @@ def feedback_callback( relevant_tilt_series = relevant_tilt_and_series[1] relevant_tilt.motion_corrected = True _db.add(relevant_tilt) + + # Update all related Movie rows and mark as preprocessed + relevant_movies = _db.exec( + select(db.Movie).where(db.Movie.path == message["movie"]) + ).all() + for movie in relevant_movies: + movie.preprocessed = True + _db.add(movie) + _db.commit() + if ( check_tilt_series_mc(relevant_tilt_series.id, _db) and not relevant_tilt_series.processing_requested diff --git a/src/murfey/util/route_manifest.yaml b/src/murfey/util/route_manifest.yaml index db529858e..0cb6d7352 100644 --- a/src/murfey/util/route_manifest.yaml +++ b/src/murfey/util/route_manifest.yaml @@ -1178,6 +1178,13 @@ murfey.server.api.session_info.router: type: int methods: - GET + - path: /session_info/sessions/{session_id}/movies/preprocessed + function: get_preprocessed_movie_count + path_params: + - name: session_id + type: int + methods: + - GET - path: /session_info/clients function: get_clients path_params: [] diff --git a/tests/server/api/test_session_info.py b/tests/server/api/test_session_info.py index efeaf268c..56f43b271 100644 --- a/tests/server/api/test_session_info.py +++ b/tests/server/api/test_session_info.py @@ -114,6 +114,101 @@ def mock_get_db_session(): assert murfey_session.visit_end_time is None +def test_get_preprocessed_movie_count( + tmp_path: Path, + murfey_db_session: SQLModelSession, +): + # Set up parameters + session_id = ExampleVisit.murfey_session_id + + # Create some DataCollectionGroups, DataCollections, and Movies + num_dcgs = 3 + num_dcs = 3 + num_movies = 4 + for i in range(num_dcgs): + dcg_id = i + 1 + dcg_name = f"dcg_{dcg_id}" + dcg_entry = MurfeyDB.DataCollectionGroup( + dataCollectionGroupId=dcg_id, + session_id=session_id, + tag=dcg_name, + ) + murfey_db_session.add(dcg_entry) + for j in range(num_dcs): + dc_id = (i * num_dcs) + j + 1 + dc_entry = MurfeyDB.DataCollection( + dataCollectionId=dc_id, + dataCollectionGroupId=dcg_id, + tag=dcg_name, + ) + murfey_db_session.add(dc_entry) + for k in range(num_movies): + # Add prerequisite table entriees + # ProcessingJob + pj_id = (i * num_dcs * num_movies) + (j * num_movies) + k + 1 + pj_entry = MurfeyDB.ProcessingJob( + processingJobId=pj_id, + recipe="dummy", + dataCollectionId=dc_id, + ) + murfey_db_session.add(pj_entry) + + # AutoProcProgram + app_entry = MurfeyDB.AutoProcProgram( + autoProcProgramId=pj_entry.id, + pj_id=pj_entry.id, + ) + murfey_db_session.add(app_entry) + + # MurfeyLedger + ledger_entry = MurfeyDB.MurfeyLedger( + id=app_entry.id, + app_id=app_entry.id, + ) + murfey_db_session.add(ledger_entry) + + # Add test Movie entry + movie_entry = MurfeyDB.Movie( + movieId=ledger_entry.id, + movieNumber=ledger_entry.id, + dataCollectionId=dc_id, + path=str( + tmp_path / dcg_name / f"movie_{(j * num_movies) + k + 1}.mrc" + ), + tag=dcg_name, + preprocessed=bool(j % 2), + ) + murfey_db_session.add(movie_entry) + # Commit all at once + murfey_db_session.commit() + + # Set up a mock Murfey database session + def mock_get_db_session(): + yield murfey_db_session + + # Set up the backend server + backend_server = set_up_test_backend_client( + router=router, + instrument_name=instrument_name, + session_id=session_id, + mock_db_session=mock_get_db_session, + ) + # Construct the URL path to poke + backend_url_path = url_path_for( + "api.session_info.router", + "get_preprocessed_movie_count", + session_id=session_id, + ) + # Poke the backend + response = backend_server.get( + backend_url_path, + ) + assert response.status_code == 200 + data: dict[str, int] = response.json() + assert data["complete"] == 12 + assert data["incomplete"] == 24 + + @pytest.mark.parametrize( "search_strings", (