Skip to content

Commit c48ef00

Browse files
committed
Move tomogram motio correction endpoint
1 parent 1514bc7 commit c48ef00

File tree

5 files changed

+63
-65
lines changed

5 files changed

+63
-65
lines changed

src/pato/crud/tomograms.py

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import re
22
from typing import Literal, Optional
33

4-
from fastapi import HTTPException, status
4+
from fastapi import HTTPException
55
from lims_utils.tables import (
66
CTF,
77
MotionCorrection,
@@ -10,7 +10,7 @@
1010
TiltImageAlignment,
1111
Tomogram,
1212
)
13-
from sqlalchemy import Column, func, literal_column, select
13+
from sqlalchemy import Column, func, select
1414
from sqlalchemy import func as f
1515

1616
from ..models.response import (
@@ -87,48 +87,46 @@ def get_shift_plot(tomogramId: int):
8787

8888

8989
def get_motion_correction(
90-
limit: int, page: int, tomogramId: int, getMiddle: bool
90+
limit: int, page: int, data_collection_id: int, getMiddle: bool
9191
) -> FullMovieWithTilt:
92-
if getMiddle:
93-
total = db.session.scalar(
94-
select(f.count(literal_column("1")))
95-
.filter(TiltImageAlignment.tomogramId == tomogramId)
96-
.order_by(TiltImageAlignment.refinedTiltAngle)
97-
)
98-
99-
if not total:
100-
raise HTTPException(
101-
status_code=status.HTTP_404_NOT_FOUND,
102-
detail="No items found",
103-
)
104-
105-
limit = 1
106-
page = (total // 2) - 1
107-
10892
query = (
10993
select(MotionCorrection, TiltImageAlignment, CTF, Movie)
110-
.filter(TiltImageAlignment.tomogramId == tomogramId)
94+
.select_from(Movie)
95+
.filter(Movie.dataCollectionId == data_collection_id)
11196
.join(
11297
MotionCorrection,
113-
MotionCorrection.movieId == TiltImageAlignment.movieId,
98+
MotionCorrection.movieId == Movie.movieId,
11499
)
115-
.join(Movie, Movie.movieId == TiltImageAlignment.movieId)
116100
.join(CTF, CTF.motionCorrectionId == MotionCorrection.motionCorrectionId)
117-
.order_by(TiltImageAlignment.refinedTiltAngle)
101+
.outerjoin(TiltImageAlignment, TiltImageAlignment.movieId == Movie.movieId)
102+
.group_by(Movie.movieId)
103+
# Display movies without alignments last
104+
.order_by(
105+
TiltImageAlignment.refinedTiltAngle.is_(None), TiltImageAlignment.refinedTiltAngle, Movie.movieId.desc()
106+
)
118107
)
119108

120-
motion = dict(db.paginate(query, limit, page))
121-
122109
raw_total = db.session.execute(
123110
select(
124111
f.count(Movie.movieId).label("total"),
125-
)
126-
.select_from(Tomogram)
127-
.filter(Tomogram.tomogramId == tomogramId)
128-
.join(Movie, Movie.dataCollectionId == Tomogram.dataCollectionId)
112+
).filter(Movie.dataCollectionId == data_collection_id)
129113
).scalar_one()
130114

131-
return FullMovieWithTilt(**motion, rawTotal=raw_total)
115+
aligned_total = db.session.scalar(
116+
select(f.count(Movie.movieId))
117+
.select_from(Movie)
118+
.filter(Movie.dataCollectionId == data_collection_id)
119+
.join(TiltImageAlignment, TiltImageAlignment.movieId == Movie.movieId)
120+
)
121+
122+
if getMiddle:
123+
# Get central slice, ignore dark images
124+
page = aligned_total // 2 - 1
125+
limit = 1
126+
127+
motion = dict(db.paginate(query, limit, page, precounted_total=raw_total))
128+
129+
return FullMovieWithTilt(**motion, alignedTotal=aligned_total)
132130

133131

134132
def get_ctf(tomogramId: int):
@@ -157,6 +155,4 @@ def get_movie_path(tomogramId: int, movie_type: MovieType):
157155

158156

159157
def get_projection_path(tomogramId: int, axis: Literal["xy", "xz"]):
160-
return _get_generic_tomogram_file(
161-
tomogramId, Tomogram.projXZ if axis == "xz" else Tomogram.projXY
162-
)
158+
return _get_generic_tomogram_file(tomogramId, Tomogram.projXZ if axis == "xz" else Tomogram.projXY)

src/pato/models/response.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ class FullMovie(OrmBaseModel):
194194

195195

196196
class FullMovieWithTilt(Paged[FullMovie]):
197-
rawTotal: int
197+
alignedTotal: int
198198

199199

200200
class CtfBase(OrmBaseModel):

src/pato/routes/collections.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
from fastapi import APIRouter, Body, Depends, status
1+
from fastapi import APIRouter, Body, Depends, Query, status
22
from fastapi.responses import FileResponse
33
from lims_utils.models import Paged, pagination
44

55
from ..auth import Permissions
66
from ..crud import collection_report as report_crud
77
from ..crud import collections as crud
88
from ..crud import generic
9+
from ..crud import tomograms as tomograms_crud
910
from ..models.collections import BaseDataCollectionOut, DataCollectionFileAttachmentOut
1011
from ..models.reprocessing import (
1112
SPAReprocessingParameters,
@@ -14,6 +15,7 @@
1415
from ..models.response import (
1516
DataPoint,
1617
FullMovie,
18+
FullMovieWithTilt,
1719
ItemList,
1820
ProcessingJobResponse,
1921
ReprocessingResponse,
@@ -89,6 +91,19 @@ def get_motion_correction(
8991
return crud.get_motion_correction(collectionId=collectionId, **page)
9092

9193

94+
@router.get("/{collectionId}/tomogram-motion", response_model=FullMovieWithTilt)
95+
def get_motion_correction_for_tomogram(
96+
collectionId: int = Depends(auth),
97+
page: dict[str, int] = Depends(pagination),
98+
getMiddle=Query(
99+
False,
100+
description="Get index closest to the middle. Limit is set to 1, page is ignored",
101+
),
102+
):
103+
"""Get motion correction and tilt alignment data for tomogram"""
104+
return tomograms_crud.get_motion_correction(data_collection_id=collectionId, getMiddle=getMiddle, **page)
105+
106+
92107
@router.get(
93108
"/{collectionId}/iceThickness",
94109
)

src/pato/routes/tomograms.py

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
from typing import Literal
22

3-
from fastapi import APIRouter, Depends, Query
3+
from fastapi import APIRouter, Depends
44
from fastapi.responses import FileResponse
5-
from lims_utils.models import pagination
65

76
from ..auth import Permissions
87
from ..crud import tomograms as crud
98
from ..models.response import (
109
CtfTiltAlign,
1110
DataPoint,
12-
FullMovieWithTilt,
1311
ItemList,
1412
)
1513
from ..utils.generic import MovieType
@@ -28,21 +26,6 @@ def get_shift_plot(tomogramId: int = Depends(auth)):
2826
return crud.get_shift_plot(tomogramId)
2927

3028

31-
@router.get("/{tomogramId}/motion", response_model=FullMovieWithTilt)
32-
def get_motion_correction(
33-
tomogramId: int = Depends(auth),
34-
page: dict[str, int] = Depends(pagination),
35-
getMiddle=Query(
36-
False,
37-
description="Get index closest to the middle. Limit is set to 1, page is ignored",
38-
),
39-
):
40-
"""Get motion correction data for the given tomogram"""
41-
return crud.get_motion_correction(
42-
tomogramId=tomogramId, getMiddle=getMiddle, **page
43-
)
44-
45-
4629
@router.get("/{tomogramId}/centralSlice", response_class=FileResponse)
4730
def get_slice(tomogramId: int = Depends(auth), movieType: MovieType = None):
4831
"""Get tomogram central slice image"""
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,38 @@
11
def test_get(mock_permissions, client):
22
"""Get motion correction in a tomogram (request for admin)"""
3-
resp = client.get("/tomograms/1/motion")
3+
resp = client.get("/dataCollections/6017406/tomogram-motion")
44
resp_json = resp.json()
55

66
assert resp.status_code == 200
7-
assert resp_json["total"] == 4
8-
assert resp_json["rawTotal"] == 5
7+
assert resp_json["total"] == 5
8+
assert resp_json["alignedTotal"] == 5
99

1010

1111
def test_get_middle(mock_permissions, client):
1212
"""Get motion correction without tilt alignment data"""
13-
resp = client.get("/tomograms/1/motion?getMiddle=true")
13+
resp = client.get("/dataCollections/6017406/tomogram-motion?getMiddle=true")
1414
assert resp.status_code == 200
15-
assert resp.json()["items"][0]["TiltImageAlignment"]["refinedTiltAngle"] == 17
15+
assert resp.json()["items"][0]["TiltImageAlignment"]["refinedTiltAngle"] == 16
1616

1717

1818
def test_no_tilt_alignment(mock_permissions, client):
1919
"""Get motion correction without tilt alignment data"""
20-
resp = client.get("/tomograms/3/motion")
21-
motion_correction = resp.json()
20+
resp = client.get("/dataCollections/6017411/tomogram-motion")
2221

2322
assert resp.status_code == 200
24-
assert motion_correction["total"] == 0
23+
24+
motion_correction = resp.json()
25+
assert motion_correction["total"] == 5
26+
assert motion_correction["alignedTotal"] == 0
2527

2628

2729
def test_nth_motion(mock_permissions, client):
2830
"""Get specific motion correction"""
29-
resp = client.get("/tomograms/2/motion?page=1&limit=1")
30-
resp_json = resp.json()
31+
resp = client.get("/dataCollections/6017408/tomogram-motion?page=1&limit=1")
3132

3233
assert resp.status_code == 200
33-
assert resp_json["total"] == 4
34-
assert resp_json["items"][0]["TiltImageAlignment"]["refinedTiltAngle"] == 17
34+
35+
motion_correction = resp.json()
36+
37+
assert motion_correction["total"] == 6
38+
assert motion_correction["items"][0]["TiltImageAlignment"]["refinedTiltAngle"] == 18

0 commit comments

Comments
 (0)