Skip to content

Commit e4d2dd7

Browse files
committed
Move correlative workflow related endpoints
1 parent 23119cb commit e4d2dd7

File tree

4 files changed

+162
-172
lines changed

4 files changed

+162
-172
lines changed

src/murfey/server/api/__init__.py

Lines changed: 1 addition & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
from fastapi import APIRouter, Depends, Request
88
from fastapi.responses import FileResponse, HTMLResponse
9-
from ispyb.sqlalchemy import BLSample, BLSampleGroup, BLSampleImage, BLSubSample
10-
from PIL import Image
119
from prometheus_client import Counter, Gauge
1210
from pydantic import BaseModel
1311
from sqlmodel import select
@@ -37,17 +35,7 @@
3735
SPAFeedbackParameters,
3836
SPARelionParameters,
3937
)
40-
from murfey.util.models import (
41-
BLSampleImageParameters,
42-
BLSampleParameters,
43-
BLSubSampleParameters,
44-
ClientInfo,
45-
MillingParameters,
46-
RegistrationMessage,
47-
RsyncerInfo,
48-
RsyncerSource,
49-
Sample,
50-
)
38+
from murfey.util.models import ClientInfo, RsyncerInfo, RsyncerSource
5139

5240
log = logging.getLogger("murfey.server.api")
5341

@@ -234,104 +222,6 @@ class ProcessingDetails(BaseModel):
234222
feedback_params: SPAFeedbackParameters
235223

236224

237-
@router.get("/visit/{visit_name}/samples")
238-
def get_samples(visit_name: str, db=murfey.server.ispyb.DB) -> List[Sample]:
239-
return murfey.server.ispyb.get_sub_samples_from_visit(visit_name, db=db)
240-
241-
242-
@router.post("/visit/{visit_name}/sample_group")
243-
def register_sample_group(visit_name: str, db=murfey.server.ispyb.DB) -> dict:
244-
proposal_id = murfey.server.ispyb.get_proposal_id(
245-
visit_name[:2], visit_name.split("-")[0][2:], db=db
246-
)
247-
record = BLSampleGroup(proposalId=proposal_id)
248-
if _transport_object:
249-
return _transport_object.do_insert_sample_group(record)
250-
return {"success": False}
251-
252-
253-
@router.post("/visit/{visit_name}/sample")
254-
def register_sample(visit_name: str, sample_params: BLSampleParameters) -> dict:
255-
record = BLSample()
256-
if _transport_object:
257-
return _transport_object.do_insert_sample(record, sample_params.sample_group_id)
258-
return {"success": False}
259-
260-
261-
@router.post("/visit/{visit_name}/subsample")
262-
def register_subsample(
263-
visit_name: str, subsample_params: BLSubSampleParameters
264-
) -> dict:
265-
record = BLSubSample(
266-
blSampleId=subsample_params.sample_id, imgFilePath=subsample_params.image_path
267-
)
268-
if _transport_object:
269-
return _transport_object.do_insert_subsample(record)
270-
return {"success": False}
271-
272-
273-
@router.post("/visit/{visit_name}/sample_image")
274-
def register_sample_image(
275-
visit_name: str, sample_image_params: BLSampleImageParameters
276-
) -> dict:
277-
record = BLSampleImage(
278-
blSampleId=sample_image_params.sample_id,
279-
imageFullPath=sample_image_params.image_path,
280-
)
281-
if _transport_object:
282-
return _transport_object.do_insert_sample_image(record)
283-
return {"success": False}
284-
285-
286-
@router.post("/instruments/{instrument_name}/feedback")
287-
async def send_murfey_message(instrument_name: str, msg: RegistrationMessage):
288-
if _transport_object:
289-
_transport_object.send(
290-
_transport_object.feedback_queue, {"register": msg.registration}
291-
)
292-
293-
294-
@router.post("/visits/{year}/{visit_name}/{session_id}/make_milling_gif")
295-
async def make_gif(
296-
year: int,
297-
visit_name: str,
298-
session_id: int,
299-
gif_params: MillingParameters,
300-
db=murfey_db,
301-
):
302-
instrument_name = (
303-
db.exec(select(Session).where(Session.id == session_id)).one().instrument_name
304-
)
305-
machine_config = get_machine_config(instrument_name=instrument_name)[
306-
instrument_name
307-
]
308-
output_dir = (
309-
Path(machine_config.rsync_basepath)
310-
/ secure_filename(year)
311-
/ secure_filename(visit_name)
312-
/ "processed"
313-
)
314-
output_dir.mkdir(exist_ok=True)
315-
output_dir = output_dir / secure_filename(gif_params.raw_directory)
316-
output_dir.mkdir(exist_ok=True)
317-
output_path = output_dir / f"lamella_{gif_params.lamella_number}_milling.gif"
318-
image_full_paths = [
319-
output_dir.parent / gif_params.raw_directory / i for i in gif_params.images
320-
]
321-
images = [Image.open(f) for f in image_full_paths]
322-
for im in images:
323-
im.thumbnail((512, 512))
324-
images[0].save(
325-
output_path,
326-
format="GIF",
327-
append_images=images[1:],
328-
save_all=True,
329-
duration=30,
330-
loop=0,
331-
)
332-
return {"output_gif": str(output_path)}
333-
334-
335225
@router.post("/visits/{visit_name}/monitoring/{on}")
336226
def change_monitoring_status(visit_name: str, on: int):
337227
prom.monitoring_switch.labels(visit=visit_name)

src/murfey/server/api/workflow.py

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,24 @@
66

77
import sqlalchemy
88
from fastapi import APIRouter, Depends
9-
from ispyb.sqlalchemy import Atlas
9+
from ispyb.sqlalchemy import (
10+
Atlas,
11+
BLSample,
12+
BLSampleGroup,
13+
BLSampleGroupHasBLSample,
14+
BLSampleImage,
15+
BLSubSample,
16+
)
1017
from pydantic import BaseModel
1118
from sqlalchemy.exc import OperationalError
1219
from sqlmodel import col, select
1320
from werkzeug.utils import secure_filename
1421

22+
try:
23+
from PIL import Image
24+
except ImportError:
25+
Image = None
26+
1527
import murfey.server.prometheus as prom
1628
from murfey.server import (
1729
_murfey_id,
@@ -25,6 +37,7 @@
2537
)
2638
from murfey.server.api.auth import MurfeySessionID, validate_token
2739
from murfey.server.api.spa import _cryolo_model_path
40+
from murfey.server.ispyb import DB, get_proposal_id
2841
from murfey.server.murfey_db import murfey_db
2942
from murfey.util.config import get_machine_config
3043
from murfey.util.db import (
@@ -892,3 +905,149 @@ def _add_tilt():
892905
except OperationalError:
893906
await asyncio.sleep(30)
894907
_add_tilt()
908+
909+
910+
correlative_router = APIRouter(
911+
prefix="/workflow/correlative",
912+
dependencies=[Depends(validate_token)],
913+
tags=["workflow correlative imaging"],
914+
)
915+
916+
917+
class Sample(BaseModel):
918+
sample_group_id: int
919+
sample_id: int
920+
subsample_id: int
921+
image_path: Optional[Path]
922+
923+
924+
@correlative_router.get("/visit/{visit_name}/samples")
925+
def get_samples(visit_name: str, db=DB) -> List[Sample]:
926+
proposal_id = get_proposal_id(visit_name[:2], visit_name.split("-")[0][2:], db)
927+
samples = (
928+
db.query(BLSampleGroup, BLSampleGroupHasBLSample, BLSample, BLSubSample)
929+
.join(BLSample, BLSample.blSampleId == BLSampleGroupHasBLSample.blSampleId)
930+
.join(
931+
BLSampleGroup,
932+
BLSampleGroup.blSampleGroupId == BLSampleGroupHasBLSample.blSampleGroupId,
933+
)
934+
.join(BLSubSample, BLSubSample.blSampleId == BLSample.blSampleId)
935+
.filter(BLSampleGroup.proposalId == proposal_id)
936+
.all()
937+
)
938+
res = [
939+
Sample(
940+
sample_group_id=s[1].blSampleGroupId,
941+
sample_id=s[2].blSampleId,
942+
subsample_id=s[3].blSubSampleId,
943+
image_path=s[3].imgFilePath,
944+
)
945+
for s in samples
946+
]
947+
return res
948+
949+
950+
@correlative_router.post("/visit/{visit_name}/sample_group")
951+
def register_sample_group(visit_name: str, db=DB) -> dict:
952+
proposal_id = get_proposal_id(visit_name[:2], visit_name.split("-")[0][2:], db=db)
953+
record = BLSampleGroup(proposalId=proposal_id)
954+
if _transport_object:
955+
return _transport_object.do_insert_sample_group(record)
956+
return {"success": False}
957+
958+
959+
class BLSampleParameters(BaseModel):
960+
sample_group_id: int
961+
962+
963+
@correlative_router.post("/visit/{visit_name}/sample")
964+
def register_sample(visit_name: str, sample_params: BLSampleParameters) -> dict:
965+
record = BLSample()
966+
if _transport_object:
967+
return _transport_object.do_insert_sample(record, sample_params.sample_group_id)
968+
return {"success": False}
969+
970+
971+
class BLSubSampleParameters(BaseModel):
972+
sample_id: int
973+
image_path: Optional[Path] = None
974+
975+
976+
@correlative_router.post("/visit/{visit_name}/subsample")
977+
def register_subsample(
978+
visit_name: str, subsample_params: BLSubSampleParameters
979+
) -> dict:
980+
record = BLSubSample(
981+
blSampleId=subsample_params.sample_id, imgFilePath=subsample_params.image_path
982+
)
983+
if _transport_object:
984+
return _transport_object.do_insert_subsample(record)
985+
return {"success": False}
986+
987+
988+
class BLSampleImageParameters(BaseModel):
989+
sample_id: int
990+
sample_path: Path
991+
992+
993+
@correlative_router.post("/visit/{visit_name}/sample_image")
994+
def register_sample_image(
995+
visit_name: str, sample_image_params: BLSampleImageParameters
996+
) -> dict:
997+
record = BLSampleImage(
998+
blSampleId=sample_image_params.sample_id,
999+
imageFullPath=sample_image_params.image_path,
1000+
)
1001+
if _transport_object:
1002+
return _transport_object.do_insert_sample_image(record)
1003+
return {"success": False}
1004+
1005+
1006+
class MillingParameters(BaseModel):
1007+
lamella_number: int
1008+
images: List[str]
1009+
raw_directory: str
1010+
1011+
1012+
@correlative_router.post("/visits/{year}/{visit_name}/{session_id}/make_milling_gif")
1013+
async def make_gif(
1014+
year: int,
1015+
visit_name: str,
1016+
session_id: int,
1017+
gif_params: MillingParameters,
1018+
db=murfey_db,
1019+
):
1020+
instrument_name = (
1021+
db.exec(select(Session).where(Session.id == session_id)).one().instrument_name
1022+
)
1023+
machine_config = get_machine_config(instrument_name=instrument_name)[
1024+
instrument_name
1025+
]
1026+
output_dir = (
1027+
Path(machine_config.rsync_basepath)
1028+
/ secure_filename(year)
1029+
/ secure_filename(visit_name)
1030+
/ "processed"
1031+
)
1032+
output_dir.mkdir(exist_ok=True)
1033+
output_dir = output_dir / secure_filename(gif_params.raw_directory)
1034+
output_dir.mkdir(exist_ok=True)
1035+
output_path = output_dir / f"lamella_{gif_params.lamella_number}_milling.gif"
1036+
image_full_paths = [
1037+
output_dir.parent / gif_params.raw_directory / i for i in gif_params.images
1038+
]
1039+
if Image is not None:
1040+
images = [Image.open(f) for f in image_full_paths]
1041+
else:
1042+
images = []
1043+
for im in images:
1044+
im.thumbnail((512, 512))
1045+
images[0].save(
1046+
output_path,
1047+
format="GIF",
1048+
append_images=images[1:],
1049+
save_all=True,
1050+
duration=30,
1051+
loop=0,
1052+
)
1053+
return {"output_gif": str(output_path)}

src/murfey/server/ispyb.py

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
from murfey.util import sanitise
3535
from murfey.util.config import get_security_config
36-
from murfey.util.models import FoilHoleParameters, GridSquareParameters, Sample
36+
from murfey.util.models import FoilHoleParameters, GridSquareParameters
3737

3838
log = logging.getLogger("murfey.server.ispyb")
3939
security_config = get_security_config()
@@ -625,31 +625,6 @@ def get_proposal_id(
625625
return query[0].proposalId
626626

627627

628-
def get_sub_samples_from_visit(visit: str, db: sqlalchemy.orm.Session) -> List[Sample]:
629-
proposal_id = get_proposal_id(visit[:2], visit.split("-")[0][2:], db)
630-
samples = (
631-
db.query(BLSampleGroup, BLSampleGroupHasBLSample, BLSample, BLSubSample)
632-
.join(BLSample, BLSample.blSampleId == BLSampleGroupHasBLSample.blSampleId)
633-
.join(
634-
BLSampleGroup,
635-
BLSampleGroup.blSampleGroupId == BLSampleGroupHasBLSample.blSampleGroupId,
636-
)
637-
.join(BLSubSample, BLSubSample.blSampleId == BLSample.blSampleId)
638-
.filter(BLSampleGroup.proposalId == proposal_id)
639-
.all()
640-
)
641-
res = [
642-
Sample(
643-
sample_group_id=s[1].blSampleGroupId,
644-
sample_id=s[2].blSampleId,
645-
subsample_id=s[3].blSubSampleId,
646-
image_path=s[3].imgFilePath,
647-
)
648-
for s in samples
649-
]
650-
return res
651-
652-
653628
def get_all_ongoing_visits(
654629
microscope: str, db: sqlalchemy.orm.Session | None
655630
) -> list[Visit]:

src/murfey/util/models.py

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -72,40 +72,6 @@ class RsyncerInfo(BaseModel):
7272
tag: str = ""
7373

7474

75-
"""
76-
FIB
77-
===
78-
Models related to FIB, as part of correlative workflow with TEM.
79-
"""
80-
81-
82-
class Sample(BaseModel):
83-
sample_group_id: int
84-
sample_id: int
85-
subsample_id: int
86-
image_path: Optional[Path]
87-
88-
89-
class BLSampleImageParameters(BaseModel):
90-
sample_id: int
91-
sample_path: Path
92-
93-
94-
class BLSampleParameters(BaseModel):
95-
sample_group_id: int
96-
97-
98-
class BLSubSampleParameters(BaseModel):
99-
sample_id: int
100-
image_path: Optional[Path] = None
101-
102-
103-
class MillingParameters(BaseModel):
104-
lamella_number: int
105-
images: List[str]
106-
raw_directory: str
107-
108-
10975
"""
11076
Single Particle Analysis
11177
========================

0 commit comments

Comments
 (0)