Skip to content
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
7b50bb6
Used Optional[] as type hint for Python 3.9 compatibility
tieneupin Oct 2, 2024
e796663
Raise Exception if no TransportManager instance is found when running…
tieneupin Oct 2, 2024
0167f96
Corrected database call in 'validate_and_sanitise' function; simplifi…
tieneupin Oct 2, 2024
6dac704
Added functions to post requests to the server to register LIF and TI…
tieneupin Oct 2, 2024
bbd3e29
Used Path object to resolve file path in 'validate_and_sanitise'; add…
tieneupin Oct 3, 2024
b6a4297
Corrected returned booleans at the end of different workflow branches
tieneupin Oct 9, 2024
9d03fa2
Corrected inconsistent returns and raises in functions
tieneupin Oct 9, 2024
dadb02d
Simplified CLEM BaseModels and updated CLEMContext file handling logi…
tieneupin Oct 9, 2024
c69a22f
Allow forward slashes in series name when validating input
tieneupin Oct 9, 2024
276ee54
Added folder to store module feedback callback workflows in
tieneupin Oct 10, 2024
f6fd56d
Added feedback callback elif-blocks for CLEM workflows
tieneupin Oct 10, 2024
5ef7218
Updated 'lif_to_stack' API endpoint to load and pass on more information
tieneupin Oct 10, 2024
d29b8ed
Updated 'lif_to_stack' workflow to handle additional zocalo wrapper p…
tieneupin Oct 10, 2024
398b09e
Notes on how 'feedback_callback' works
tieneupin Oct 30, 2024
7820501
Constructed parameters from file path; updated parameter names
tieneupin Oct 30, 2024
51c528c
Fixed comments
tieneupin Oct 30, 2024
2d6d367
Removed 'instrument_name' parameter from murfey.server.murfey_db
tieneupin Oct 31, 2024
e8447e0
Refactored CLEM Murfey workflows
tieneupin Nov 1, 2024
c4bdfc7
Missed deleting the old file
tieneupin Nov 1, 2024
a01a786
Updated TIFF Pydantic model
tieneupin Nov 1, 2024
185158f
Added Pydantic models to validate LIF and TIFF preprocessing result m…
tieneupin Nov 1, 2024
ad9a489
Changed 'color' to the more generic 'channel'
tieneupin Nov 1, 2024
c9ffa31
Refactored functions to parse CLEM preprocessing results and started …
tieneupin Nov 1, 2024
c3c78a0
Completed draft of function to register LIF preprocessing results
tieneupin Nov 4, 2024
f9858ee
Added 'number_of_members' as a parameter to register for a series
tieneupin Nov 4, 2024
ecf6d38
Added 'return None' to CLEM results registration code blocks
tieneupin Nov 4, 2024
a132b86
Merged recent changes from 'main' branch
tieneupin Nov 4, 2024
f7681c9
Rewrote 'tiff_to_stack' function to work with Zocalo wrapper
tieneupin Nov 4, 2024
480a7e4
Modified URLs for CLEM preprocesisng API endpoints
tieneupin Nov 4, 2024
09e85cc
Stringified TIFF list when submitting to cluster
tieneupin Nov 4, 2024
3cd3344
Ensured file paths in recipes are represented canonically
tieneupin Nov 4, 2024
816d434
Adjusted order of parameters
tieneupin Nov 4, 2024
8576eb1
Canonicalisation of strings appears to not be needed when submitting …
tieneupin Nov 4, 2024
4e5b202
Fixed outdated parameters to pass over to 'tiff_to_stack' workflow
tieneupin Nov 5, 2024
fbc9fe5
Fixed broken database refreshes and updated logged messages
tieneupin Nov 6, 2024
8bcd8c0
Updated 'TIFFPreprocessingResult' Pydantic model to parse stringified…
tieneupin Nov 6, 2024
2cc5624
Completed function to register TIFF preprocessing results
tieneupin Nov 6, 2024
0811cba
Missed a return statement in 'except' block
tieneupin Nov 6, 2024
3fd77b4
Rewrote CLEM preprocessing results registration workflows to make use…
tieneupin Nov 6, 2024
93c08ea
Adjusted naming convention for LIF and TIFF processing functions and …
tieneupin Nov 6, 2024
9cd58ff
Replaced 'row' with 'session_row' to make variable more explicit
tieneupin Nov 7, 2024
befd420
Rewrote function to iterate over entry point names and run the match
tieneupin Nov 7, 2024
5397c0e
Missed a print statement
tieneupin Nov 7, 2024
fa6c5c8
Reverted entry point group name to 'murfey.workflows' to facilitate g…
tieneupin Nov 7, 2024
1a0057b
Optimised code logic for LIF and TIFF processing API endpoints
tieneupin Nov 7, 2024
feade10
Added 'requeue=False' argument when nacking message
tieneupin Nov 7, 2024
36efb6f
Fixed logic when reading rsync base path from machine config
tieneupin Nov 7, 2024
aafc518
Used 'entry_points' from 'backports.entry_points_selectable' instead
tieneupin Nov 8, 2024
cb1a570
Used 'entry_points' instance from 'backports.entry_points_selectable'…
tieneupin Nov 8, 2024
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
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ murfey = "murfey.client:run"
"password" = "murfey.server.api.auth:password_token_validation"
[project.entry-points."murfey.config.extraction"]
"murfey_machine" = "murfey.util.config:get_extended_machine_config"
[project.entry-points."murfey.workflows"]
"lif_to_stack" = "murfey.workflows.lif_to_stack:zocalo_cluster_request"
"tiff_to_stack" = "murfey.workflows.tiff_to_stack:zocalo_cluster_request"
[project.entry-points."murfey.workflows.clem"]
"process_raw_lifs" = "murfey.workflows.clem.process_raw_lifs:zocalo_cluster_request"
"process_raw_tiffs" = "murfey.workflows.clem.process_raw_tiffs:zocalo_cluster_request"
"register_lif_preprocessing_result" = "murfey.workflows.clem.register_preprocessing_results:register_lif_preprocessing_result"
"register_tiff_preprocessing_result" = "murfey.workflows.clem.register_preprocessing_results:register_tiff_preprocessing_result"

[tool.setuptools]
package-dir = {"" = "src"}
Expand Down
4 changes: 2 additions & 2 deletions src/murfey/client/contexts/clem.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@

try:
# Construct the URL to post the request to
url = f"{str(environment.url.geturl())}/sessions/{environment.murfey_session}/lif_to_stack?lif_file={quote(str(lif_file), safe='')}"
url = f"{str(environment.url.geturl())}/sessions/{environment.murfey_session}/clem/preprocessing/process_raw_lifs?lif_file={quote(str(lif_file), safe='')}"

Check warning on line 389 in src/murfey/client/contexts/clem.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/client/contexts/clem.py#L389

Added line #L389 was not covered by tests
# Validate
if not url:
logger.error(
Expand Down Expand Up @@ -442,7 +442,7 @@

try:
# Construct URL for Murfey server to communicate with
url = f"{str(environment.url.geturl())}/sessions/{environment.murfey_session}/tiff_to_stack"
url = f"{str(environment.url.geturl())}/sessions/{environment.murfey_session}/clem/preprocessing/process_raw_tiffs"

Check warning on line 445 in src/murfey/client/contexts/clem.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/client/contexts/clem.py#L445

Added line #L445 was not covered by tests
if not url:
logger.error(
"URL could not be constructed from the environment and file path"
Expand Down
30 changes: 30 additions & 0 deletions src/murfey/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import math
import os
import subprocess
import sys
import time
from datetime import datetime
from functools import partial, singledispatch
Expand Down Expand Up @@ -61,6 +62,12 @@
from murfey.util.spa_params import default_spa_parameters
from murfey.util.state import global_state

# Import entry_points depending on Python version
if sys.version_info < (3, 10):
from importlib_metadata import EntryPoint, entry_points
else:
from importlib.metadata import EntryPoint, entry_points

try:
from importlib.resources import files # type: ignore
except ImportError:
Expand Down Expand Up @@ -2964,6 +2971,29 @@
if _transport_object:
_transport_object.transport.ack(header)
return None
elif message["register"] in (
"register_lif_preprocessing_result",
"register_tiff_preprocessing_result",
):
murfey_workflows = list(

Check warning on line 2978 in src/murfey/server/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/server/__init__.py#L2978

Added line #L2978 was not covered by tests
entry_points().select(
group="murfey.workflows.clem", name=message["register"]
)
)
# Run the workflow if a match is found
if len(murfey_workflows) > 0:
workflow: EntryPoint = murfey_workflows[0]
workflow.load()(

Check warning on line 2986 in src/murfey/server/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/server/__init__.py#L2985-L2986

Added lines #L2985 - L2986 were not covered by tests
message=message,
db=murfey_db,
)
if _transport_object:
_transport_object.transport.ack(header)

Check warning on line 2991 in src/murfey/server/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/server/__init__.py#L2991

Added line #L2991 was not covered by tests
# Nack message if no workflow found for message
else:
if _transport_object:
_transport_object.transport.nack(header)
return None

Check warning on line 2996 in src/murfey/server/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/server/__init__.py#L2995-L2996

Added lines #L2995 - L2996 were not covered by tests
if _transport_object:
_transport_object.transport.nack(header, requeue=False)
return None
Expand Down
52 changes: 40 additions & 12 deletions src/murfey/server/api/clem.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
CLEMTIFFFile,
)
from murfey.util.db import Session as MurfeySession
from murfey.util.models import TiffSeriesInfo
from murfey.util.models import TIFFSeriesInfo

# Use backport from importlib_metadata for Python <3.10
if sys.version_info.major == 3 and sys.version_info.minor < 10:
Expand Down Expand Up @@ -631,51 +631,79 @@
"""


@router.post("/sessions/{session_id}/lif_to_stack") # API posts to this URL
def lif_to_stack(
@router.post(
"/sessions/{session_id}/clem/preprocessing/process_raw_lifs"
) # API posts to this URL
def process_raw_lifs(
session_id: int, # Used by the decorator
lif_file: Path,
db: Session = murfey_db,
):
# Get command line entry point
murfey_workflows = entry_points().select(
group="murfey.workflows", name="lif_to_stack"
group="murfey.workflows.clem", name="process_raw_lifs"
)

# Use entry point if found
if len(murfey_workflows) == 1:
if murfey_workflows:

# Get instrument name from the database
# This is needed in order to load the correct config file
row: MurfeySession = db.exec(

Check warning on line 652 in src/murfey/server/api/clem.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/server/api/clem.py#L652

Added line #L652 was not covered by tests
select(MurfeySession).where(MurfeySession.id == session_id)
).one()
instrument_name = row.instrument_name

Check warning on line 655 in src/murfey/server/api/clem.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/server/api/clem.py#L655

Added line #L655 was not covered by tests

# Pass arguments along to the correct workflow
workflow: EntryPoint = list(murfey_workflows)[0]
workflow.load()(
# Match the arguments found in murfey.workflows.lif_to_stack
# Match the arguments found in murfey.workflows.process_raw_lifs
file=lif_file,
root_folder="images",
session_id=session_id,
instrument_name=instrument_name,
messenger=_transport_object,
)
return True

# Raise error if Murfey workflow not found
else:
raise RuntimeError("The relevant Murfey workflow was not found")


@router.post("/sessions/{session_id}/tiff_to_stack")
def tiff_to_stack(
@router.post("/sessions/{session_id}/clem/preprocessing/process_raw_tiffs")
def process_raw_tiffs(
session_id: int, # Used by the decorator
tiff_info: TiffSeriesInfo,
tiff_info: TIFFSeriesInfo,
db: Session = murfey_db,
):
# Get command line entry point
murfey_workflows = entry_points().select(
group="murfey.workflows", name="tiff_to_stack"
group="murfey.workflows.clem", name="process_raw_tiffs"
)

# Use entry point if found
if murfey_workflows:

# Load instrument name from database
row: MurfeySession = db.exec(

Check warning on line 689 in src/murfey/server/api/clem.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/server/api/clem.py#L689

Added line #L689 was not covered by tests
select(MurfeySession).where(MurfeySession.id == session_id)
).one()
instrument_name = row.instrument_name

Check warning on line 692 in src/murfey/server/api/clem.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/server/api/clem.py#L692

Added line #L692 was not covered by tests

# Pass arguments to correct workflow
workflow: EntryPoint = list(murfey_workflows)[0]
workflow.load()(
# Match the arguments found in murfey.workflows.tiff_to_stack
file=tiff_info.tiff_files[0], # Pass it only one file from the list
# Match the arguments found in murfey.workflows.process_raw_tiffs
tiff_list=tiff_info.tiff_files,
root_folder="images",
session_id=session_id,
instrument_name=instrument_name,
metadata=tiff_info.series_metadata,
messenger=_transport_object,
)
return True

Check warning on line 705 in src/murfey/server/api/clem.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/server/api/clem.py#L705

Added line #L705 was not covered by tests

# Raise error if Murfey workflow not found
else:
raise RuntimeError("The relevant Murfey workflow was not found")
3 changes: 0 additions & 3 deletions src/murfey/server/murfey_db.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import os
from functools import partial

import yaml
Expand All @@ -11,8 +10,6 @@

from murfey.util.config import Security, get_security_config

instrument_name = os.getenv("BEAMLINE", "")


def url(security_config: Security | None = None) -> str:
security_config = security_config or get_security_config()
Expand Down
9 changes: 6 additions & 3 deletions src/murfey/util/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,12 @@ class CLEMImageSeries(SQLModel, table=True): # type: ignore
) # One to many

# Process checklist for series
images_aligned: bool = False # Image stacks aligned to reference image
rgbs_created: bool = False # Image stacks all colorised
composite_created: bool = False # Composite flattened image created
number_of_members: int = (
0 # Expected number of image stacks belonging to this series
)
images_aligned: bool = False # Have all members been aligned?
rgbs_created: bool = False # Have all members been colourised?
composite_created: bool = False # Has a composite image been created?
composite_image: Optional[str] = None # Full path to composite image


Expand Down
39 changes: 37 additions & 2 deletions src/murfey/util/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations

from ast import literal_eval
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional

from pydantic import BaseModel
from pydantic import BaseModel, validator

"""
General Models
Expand Down Expand Up @@ -154,12 +155,46 @@
"""


class TiffSeriesInfo(BaseModel):
class TIFFSeriesInfo(BaseModel):
series_name: str
tiff_files: List[Path]
series_metadata: Path


class LIFPreprocessingResult(BaseModel):
image_stack: Path
metadata: Path
series_name: str
channel: str
number_of_members: int
parent_lif: Path


class TIFFPreprocessingResult(BaseModel):
image_stack: Path
metadata: Path
series_name: str
channel: str
number_of_members: int
parent_tiffs: list[Path]

@validator(
"parent_tiffs",
pre=True,
)
def parse_stringified_list(cls, value):
if isinstance(value, str):
try:
eval_result = literal_eval(value)

Check warning on line 188 in src/murfey/util/models.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/util/models.py#L187-L188

Added lines #L187 - L188 were not covered by tests
if isinstance(eval_result, list):
parent_tiffs = [Path(p) for p in eval_result]
return parent_tiffs
except (SyntaxError, ValueError):
raise ValueError("Unable to parse input")

Check warning on line 193 in src/murfey/util/models.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/util/models.py#L190-L193

Added lines #L190 - L193 were not covered by tests
# Return value as-is; if it fails, it fails
return value

Check warning on line 195 in src/murfey/util/models.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/util/models.py#L195

Added line #L195 was not covered by tests


"""
FIB
===
Expand Down
Empty file.
70 changes: 70 additions & 0 deletions src/murfey/workflows/clem/process_raw_lifs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""
Script to allow Murfey to submit the LIF-to-STACK job to the cluster.
The recipe referred to here is stored on GitLab.
"""

from pathlib import Path
from typing import Optional

Check warning on line 7 in src/murfey/workflows/clem/process_raw_lifs.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/workflows/clem/process_raw_lifs.py#L6-L7

Added lines #L6 - L7 were not covered by tests

from murfey.util.config import get_machine_config

Check warning on line 9 in src/murfey/workflows/clem/process_raw_lifs.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/workflows/clem/process_raw_lifs.py#L9

Added line #L9 was not covered by tests

try:
from murfey.server.ispyb import TransportManager # Session
except AttributeError:
pass # Ignore if ISPyB credentials environment variable not set

Check warning on line 14 in src/murfey/workflows/clem/process_raw_lifs.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/workflows/clem/process_raw_lifs.py#L11-L14

Added lines #L11 - L14 were not covered by tests


def zocalo_cluster_request(

Check warning on line 17 in src/murfey/workflows/clem/process_raw_lifs.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/workflows/clem/process_raw_lifs.py#L17

Added line #L17 was not covered by tests
file: Path,
root_folder: str,
session_id: int, # Provided by the client via the API endpoint
instrument_name: str, # Acquired by looking up the Session table
messenger: Optional[TransportManager] = None,
):
if messenger:
# Use file path parts to construct parameters
path_parts = list((file.parent / file.stem).parts)

Check warning on line 26 in src/murfey/workflows/clem/process_raw_lifs.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/workflows/clem/process_raw_lifs.py#L26

Added line #L26 was not covered by tests
# Replace leading "/" in Unix paths
path_parts[0] = "" if path_parts[0] == "/" else path_parts[0]
try:

Check warning on line 29 in src/murfey/workflows/clem/process_raw_lifs.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/workflows/clem/process_raw_lifs.py#L28-L29

Added lines #L28 - L29 were not covered by tests
# Find the position of the root folder in the list
root_index = [p.lower() for p in path_parts].index(root_folder.lower())
except ValueError:
raise Exception(

Check warning on line 33 in src/murfey/workflows/clem/process_raw_lifs.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/workflows/clem/process_raw_lifs.py#L31-L33

Added lines #L31 - L33 were not covered by tests
f"Unable to find the root folder {root_folder!r} in the file path {file!r}"
)

# Construct the session and job name
session_dir = "/".join(path_parts[:root_index])
job_name = "--".join(

Check warning on line 39 in src/murfey/workflows/clem/process_raw_lifs.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/workflows/clem/process_raw_lifs.py#L38-L39

Added lines #L38 - L39 were not covered by tests
[p.replace(" ", "_") if " " in p else p for p in path_parts][
root_index + 1 :
]
)

# Load machine config to get the feedback queue
machine_config = get_machine_config()
feedback_queue = machine_config[instrument_name].feedback_queue

Check warning on line 47 in src/murfey/workflows/clem/process_raw_lifs.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/workflows/clem/process_raw_lifs.py#L46-L47

Added lines #L46 - L47 were not covered by tests

# Send the message
# The keys under "parameters" will populate all the matching fields in {}
# in the processing recipe
messenger.send(

Check warning on line 52 in src/murfey/workflows/clem/process_raw_lifs.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/workflows/clem/process_raw_lifs.py#L52

Added line #L52 was not covered by tests
"processing_recipe",
{
"recipes": ["clem-lif-to-stack"],
"parameters": {
# Job parameters
"lif_file": f"{str(file)}",
"root_folder": root_folder,
# Other recipe parameters
"session_dir": f"{str(session_dir)}",
"session_id": session_id,
"job_name": job_name,
"feedback_queue": feedback_queue,
},
},
new_connection=True,
)
else:
raise Exception("Unable to find transport manager")

Check warning on line 70 in src/murfey/workflows/clem/process_raw_lifs.py

View check run for this annotation

Codecov / codecov/patch

src/murfey/workflows/clem/process_raw_lifs.py#L70

Added line #L70 was not covered by tests
Loading