diff --git a/monailabel/datastore/local.py b/monailabel/datastore/local.py
index 2b3350025..3412853e0 100644
--- a/monailabel/datastore/local.py
+++ b/monailabel/datastore/local.py
@@ -218,6 +218,15 @@ def set_description(self, description: str):
self._update_datastore_file()
def _to_id(self, file: str) -> Tuple[str, str]:
+ """
+ Derive an identifier and extension from a filename using the configured file extensions.
+
+ Parameters:
+ file (str): Filename to parse.
+
+ Returns:
+ Tuple[str, str]: The identifier and matched extension.
+ """
ext = file_ext(file)
extensions = [e.replace("*", "") for e in self._extensions]
for e in extensions:
@@ -227,11 +236,28 @@ def _to_id(self, file: str) -> Tuple[str, str]:
return id, ext
def _to_label_id(self, file: str) -> Tuple[str, str]:
+ """Convert a label filename to its identifier and extension.
+
+ Parameters:
+ file (str): Label filename to parse.
+
+ Returns:
+ Tuple[str, str]: The label identifier and file extension.
+ """
if file.lower().endswith(".seg.nrrd"):
return file[: -len(".seg.nrrd")], ".seg.nrrd"
return self._to_id(file)
def _filename(self, id: str, ext: str) -> str:
+ """Construct a filename by concatenating an identifier with its extension.
+
+ Parameters:
+ id (str): The file identifier.
+ ext (str): The file extension.
+
+ Returns:
+ str: The resulting filename.
+ """
return id + ext
def _to_bytes(self, file):
@@ -633,6 +659,15 @@ def _add_non_existing_images(self) -> int:
return invalidate
def _add_non_existing_labels(self, tag) -> int:
+ """
+ Add label metadata for label files that exist on disk but are missing from the datastore.
+
+ Parameters:
+ tag (str): Label tag identifying the directory containing the label files.
+
+ Returns:
+ int: Number of labels added to the datastore.
+ """
invalidate = 0
self._init_from_datastore_file()
diff --git a/monailabel/endpoints/datastore_review.py b/monailabel/endpoints/datastore_review.py
index 696463dfe..5edce123d 100644
--- a/monailabel/endpoints/datastore_review.py
+++ b/monailabel/endpoints/datastore_review.py
@@ -38,6 +38,16 @@
def _safe_label_info(datastore, image_id: str, tag: str) -> Dict[str, Any]:
+ """
+ Retrieve label metadata for an image and tag.
+
+ Parameters:
+ image_id (str): Identifier of the image.
+ tag (str): Label tag used to locate the metadata.
+
+ Returns:
+ Dict[str, Any]: The label metadata when it is a dictionary; otherwise, an empty dictionary.
+ """
try:
info = datastore.get_label_info(image_id, tag)
return info if isinstance(info, dict) else {}
@@ -49,6 +59,18 @@ def _safe_label_info(datastore, image_id: str, tag: str) -> Dict[str, Any]:
def _parse_date_range(date_range: Optional[str]) -> Optional[Tuple[datetime, datetime]]:
+ """
+ Parse an optional comma-separated ISO timestamp range.
+
+ Parameters:
+ date_range (Optional[str]): Range in the form ``start,end``.
+
+ Returns:
+ Optional[Tuple[datetime, datetime]]: The parsed start and end timestamps, or ``None`` when no range is provided.
+
+ Raises:
+ HTTPException: If the range is incomplete or contains invalid ISO timestamps.
+ """
if not date_range:
return None
@@ -63,6 +85,16 @@ def _parse_date_range(date_range: Optional[str]) -> Optional[Tuple[datetime, dat
def _matches_date_range(value: Optional[str], parsed: Optional[Tuple[datetime, datetime]]) -> bool:
+ """
+ Determine whether a timestamp falls within an optional inclusive date range.
+
+ Parameters:
+ value (Optional[str]): ISO-formatted timestamp to evaluate.
+ parsed (Optional[Tuple[datetime, datetime]]): Inclusive start and end timestamps, or `None` to disable filtering.
+
+ Returns:
+ bool: `true` if filtering is disabled or the timestamp falls within the range, `false` otherwise.
+ """
if not parsed:
return True
if not value:
@@ -78,6 +110,17 @@ def _matches_date_range(value: Optional[str], parsed: Optional[Tuple[datetime, d
def _review_case(datastore, image_id: str, tag: str) -> Dict[str, Any]:
+ """
+ Build a review record containing image metadata, review details, and label status.
+
+ Parameters:
+ datastore: Datastore used to retrieve image, label, and review information.
+ image_id (str): Identifier of the image.
+ tag (str): Label tag associated with the review.
+
+ Returns:
+ Dict[str, Any]: Review record with derived status, reviewer information, and label presence.
+ """
image_info = datastore.get_image_info(image_id) or {}
label_info = _safe_label_info(datastore, image_id, tag)
labels = datastore.get_labels_by_image_id(image_id) or {}
@@ -108,6 +151,22 @@ def _list_review_cases(
date_range: Optional[str],
tag: str,
) -> List[Dict[str, Any]]:
+ """
+ Collect review cases that match the specified status, image, reviewer, date, and label tag filters.
+
+ Parameters:
+ status_filter (Optional[str]): Review status to match.
+ search (Optional[str]): Comma-separated image IDs to include.
+ reviewer (Optional[str]): Reviewer name to match.
+ date_range (Optional[str]): ISO timestamp range in the format ``start,end``.
+ tag (str): Label tag used to build each review case.
+
+ Returns:
+ List[Dict[str, Any]]: Review cases that satisfy all provided filters.
+
+ Raises:
+ HTTPException: If ``date_range`` is not a valid ISO timestamp range.
+ """
datastore = app_instance().datastore()
image_ids = datastore.list_images()
parsed_range = _parse_date_range(date_range)
@@ -134,6 +193,15 @@ def _list_review_cases(
def _summary(items: List[Dict[str, Any]]) -> Dict[str, int]:
+ """
+ Count review cases by overall total and status.
+
+ Parameters:
+ items (List[Dict[str, Any]]): Review case records containing a status field.
+
+ Returns:
+ Dict[str, int]: Counts for total, approved, flagged, pending, and unlabeled cases.
+ """
return {
"total": len(items),
"approved": sum(1 for item in items if item.get("status") == "approved"),
@@ -144,6 +212,15 @@ def _summary(items: List[Dict[str, Any]]) -> Dict[str, int]:
def _report_stats(items: List[Dict[str, Any]]) -> Dict[str, Any]:
+ """
+ Summarize review cases by status and difficulty level.
+
+ Parameters:
+ items (List[Dict[str, Any]]): Review case records to summarize.
+
+ Returns:
+ Dict[str, Any]: Status and difficulty counts with the current recording timestamp.
+ """
return {
**_summary(items),
"easy": sum(1 for item in items if item.get("level") == "easy"),
@@ -154,14 +231,15 @@ def _report_stats(items: List[Dict[str, Any]]) -> Dict[str, Any]:
def _render_csv(items: List[Dict[str, Any]]) -> str:
- def sanitize_csv_value(value: Any) -> str:
- """Sanitize CSV values to prevent formula injection."""
- text = str(value) if value else ""
- # Prefix values that start with formula-like characters
- if text and text[0] in ("=", "+", "-", "@"):
- return "'" + text
- return text
+ """
+ Render review case records as CSV text.
+
+ Parameters:
+ items (List[Dict[str, Any]]): Review case records to include in the CSV output.
+ Returns:
+ str: CSV text containing a header row and one row for each review case.
+ """
handle = io.StringIO()
writer = csv.writer(handle)
writer.writerow(["image_id", "status", "level", "reviewer", "comment", "last_reviewed", "tag"])
@@ -181,9 +259,27 @@ def sanitize_csv_value(value: Any) -> str:
def _render_html(stats: Dict[str, Any]) -> str:
+ """
+ Render review statistics as an HTML report.
+
+ Parameters:
+ stats (Dict[str, Any]): Statistics and generation timestamp to include in the report.
+
+ Returns:
+ str: An HTML document containing review status and difficulty counts with percentages.
+ """
total = stats["total"] or 1
def row(name: str, value: int) -> str:
+ """Render an HTML table row containing a label, count, and percentage of the total.
+
+ Parameters:
+ name (str): The label displayed in the row.
+ value (int): The count used to calculate the percentage.
+
+ Returns:
+ str: An escaped HTML table row with the label, count, and percentage.
+ """
return f"
| {escape(name)} | {value} | " f"{(100.0 * value / total):.1f}% |
"
return (
@@ -215,6 +311,21 @@ async def api_review_cases(
tag: str = DefaultLabelTag.FINAL.value,
user: User = Depends(RBAC(settings.MONAI_LABEL_AUTH_ROLE_USER)),
):
+ """
+ List filtered review cases with pagination and aggregate status counts.
+
+ Parameters:
+ offset (int): Number of matching cases to skip.
+ limit (int): Maximum number of cases to include in the results.
+ status_filter (Optional[str]): Status used to filter cases.
+ search (Optional[str]): Comma-separated image identifiers used to filter cases.
+ reviewer (Optional[str]): Reviewer name used to filter cases.
+ date_range (Optional[str]): ISO timestamp range in the format ``start,end``.
+ tag (str): Label tag used to build the review cases.
+
+ Returns:
+ Dict[str, Any]: A response containing summary counts, paginated results, and filter metadata.
+ """
items = _list_review_cases(status_filter, search, reviewer, date_range, tag)
results = items[offset : offset + limit]
@@ -236,6 +347,18 @@ async def api_review_versions(
image: str,
user: User = Depends(RBAC(settings.MONAI_LABEL_AUTH_ROLE_USER)),
):
+ """
+ Retrieve the available label versions for an image.
+
+ Parameters:
+ image (str): Identifier of the image whose label versions are requested.
+
+ Returns:
+ dict: A success response containing the image identifier and version metadata.
+
+ Raises:
+ HTTPException: If no labels are found for the image.
+ """
datastore = app_instance().datastore()
labels = datastore.get_labels_by_image_id(image)
if not labels:
@@ -266,6 +389,22 @@ async def api_review_report(
tag: str = DefaultLabelTag.FINAL.value,
user: User = Depends(RBAC(settings.MONAI_LABEL_AUTH_ROLE_USER)),
):
+ """
+ Generate a review report in JSON, CSV, or HTML format.
+
+ Parameters:
+ fmt (str): Output format: `"json"`, `"csv"`, or `"html"`.
+ reviewer (Optional[str]): Limits results to a specific reviewer.
+ date_range (Optional[str]): ISO timestamp range in `start,end` format.
+ tag (str): Label tag used to select review cases.
+
+ Returns:
+ dict: A formatted report containing review statistics and, for JSON output,
+ the applied filters and matching review cases.
+
+ Raises:
+ HTTPException: If `fmt` is not `"json"`, `"csv"`, or `"html"`.
+ """
items = _list_review_cases(None, None, reviewer, date_range, tag)
stats = _report_stats(items)
fmt = fmt.lower()
diff --git a/plugins/slicer/MONAILabelReviewer/MONAILabelReviewer.py b/plugins/slicer/MONAILabelReviewer/MONAILabelReviewer.py
index 109662c38..d93d581f0 100644
--- a/plugins/slicer/MONAILabelReviewer/MONAILabelReviewer.py
+++ b/plugins/slicer/MONAILabelReviewer/MONAILabelReviewer.py
@@ -426,6 +426,9 @@ def cleanCache(self):
# Section: Server
def loadServerSelection(self):
+ """
+ Populate the server URL selector with normalized addresses stored in application settings.
+ """
settings = qt.QSettings()
serverUrlHistory = settings.value("MONAILabel/serverUrlHistory")
@@ -437,14 +440,25 @@ def loadServerSelection(self):
self.ui.comboBox_server_url.addItems(server_urls)
def normalizeServerUrl(self, serverUrl: str) -> str:
+ """
+ Normalize a server URL for consistent use by removing surrounding whitespace and trailing slashes.
+
+ Parameters:
+ serverUrl (str): The server URL to normalize.
+
+ Returns:
+ str: The normalized server URL, or an empty string when no URL is provided.
+ """
if not serverUrl:
return ""
return serverUrl.strip().rstrip("/")
def init_dicom_stream(self):
"""
- initiates connection to monai server
- Default: client listens on "http://127.0.0.1:8000"
+ Connect to the configured MONAI Label server and initialize the reviewer interface.
+
+ The selected server URL is normalized before connection. Displays a warning and leaves
+ the interface uninitialized when the connection fails.
"""
# Check Connection
self.cleanCache()
@@ -1497,6 +1511,11 @@ def updateAfterEditingSegmentation(self):
self.setVersionTagInComboBox(versionTag=newLabelNameCreated)
def reloadImageAfterEditingLabel(self):
+ """
+ Reload the current image using its latest label version after an edit.
+
+ The image display and label version selector are refreshed to reflect the updated segmentation.
+ """
imageId = self.currentImageData.getFileName()
latestVersion = self.currentImageData.getLatestVersionTag()
logging.info(f"{self.getCurrentTime()}: Loading image (id='{imageId}') with version tag = '{latestVersion}'")
@@ -1504,6 +1523,11 @@ def reloadImageAfterEditingLabel(self):
self.fillComboBoxLabelVersions(self.currentImageData)
def processDataStoreRecords(self):
+ """
+ Process datastore records and display a warning when processing fails.
+
+ The selected server address is normalized for inclusion in failure messages.
+ """
serverUrl: str = self.normalizeServerUrl(self.ui.comboBox_server_url.currentText)
result: bool = self.logic.initMetaDataProcessing()
if result is False:
@@ -1668,11 +1692,11 @@ def updateLabelInfo(
def loadDicomAndSegmentation(self, imageData: ImageData, tag: str):
"""
- Loads original Dicom image and Segmentation into slicer window
+ Load an image and, when available, its segmentation into the Slicer scene.
+
Parameters:
- imageData (ImageData): Contains meta data (of dicom and segmenation)
- which is required for rest request to monai server
- in order to get dicom and segmenation (.nrrd).
+ imageData (ImageData): Image metadata used to retrieve the DICOM image and segmentation.
+ tag (str): Version tag identifying the segmentation to load.
"""
# Request dicom
image_name = imageData.getFileName()
@@ -1727,11 +1751,25 @@ def getPathToStore(self, segmentationFileName: str, tempDirectory: str) -> str:
def displaySegmention(self, destination: str):
"""
- Displays the segmentation in slicer window
+ Display a segmentation file in the Slicer window.
+
+ Parameters:
+ destination (str): Path to the segmentation file.
"""
segmentation = slicer.util.loadSegmentation(destination)
def requestDicomImage(self, image_id: str, image_name: str, node_name: str):
+ """
+ Download an image from the MONAI Label server and load it into Slicer.
+
+ Parameters:
+ image_id (str): Identifier of the image to download.
+ image_name (str): File name used for temporary storage.
+ node_name (str): Name associated with the image node.
+
+ Raises:
+ RuntimeError: If the server does not return the image.
+ """
response = self.imageDataController.requestImage(image_id)
if response is None:
raise RuntimeError(f"Failed to download image '{image_id}' from MONAI Label server")
@@ -1745,7 +1783,9 @@ def requestDicomImage(self, image_id: str, image_name: str, node_name: str):
def setTempFolderDir(self):
"""
- Create temporary dirctory to store the downloaded segmentation (.nrrd)
+ Create the temporary directory used to store downloaded segmentation files.
+
+ The directory is created only once and its path is logged.
"""
if self.temp_dir is None:
self.temp_dir = tempfile.TemporaryDirectory()
diff --git a/plugins/slicer/MONAILabelReviewer/MONAILabelReviewerLib/ImageDataController.py b/plugins/slicer/MONAILabelReviewer/MONAILabelReviewerLib/ImageDataController.py
index 670beb303..71f799712 100644
--- a/plugins/slicer/MONAILabelReviewer/MONAILabelReviewerLib/ImageDataController.py
+++ b/plugins/slicer/MONAILabelReviewer/MONAILabelReviewerLib/ImageDataController.py
@@ -235,9 +235,26 @@ def reuqestSegmentation(self, image_id: str, tag: str) -> requests.models.Respon
return img_blob
def getDicomDownloadUri(self, image_id: str) -> str:
+ """Return the download URI for an image's DICOM data.
+
+ Parameters:
+ image_id (str): Identifier of the image.
+
+ Returns:
+ str: DICOM download URI.
+ """
return self.monaiServerREST.getDicomDownloadUri(image_id)
def requestImage(self, image_id: str) -> requests.models.Response:
+ """
+ Request an image from the MONAI server.
+
+ Parameters:
+ image_id (str): Identifier of the image to request.
+
+ Returns:
+ requests.models.Response: The image response, or `None` if the request did not return data.
+ """
img_blob = self.monaiServerREST.requestImage(image_id)
if img_blob is not None:
logging.info(
@@ -248,6 +265,15 @@ def requestImage(self, image_id: str) -> requests.models.Response:
return img_blob
def saveLabelInMonaiServer(self, image_in: str, label_in: str, tag: str, params: Dict):
+ """
+ Save a label and its metadata to the MONAI server.
+
+ Parameters:
+ image_in (str): Identifier or path of the source image.
+ label_in (str): Identifier or path of the label.
+ tag (str): Version tag for the label.
+ params (Dict): Label metadata and update parameters.
+ """
self.monaiServerREST.saveLabel(image_in, label_in, tag, params)
def deleteLabelByVersionTag(self, imageId: str, versionTag: str) -> bool:
diff --git a/plugins/slicer/MONAILabelReviewer/MONAILabelReviewerLib/MonaiServerREST.py b/plugins/slicer/MONAILabelReviewer/MONAILabelReviewerLib/MonaiServerREST.py
index 42c3ceb01..c4dbbb0dc 100644
--- a/plugins/slicer/MONAILabelReviewer/MONAILabelReviewerLib/MonaiServerREST.py
+++ b/plugins/slicer/MONAILabelReviewer/MONAILabelReviewerLib/MonaiServerREST.py
@@ -25,10 +25,21 @@
class MonaiServerREST:
def __init__(self, serverUrl: str):
+ """
+ Initialize the REST client with a normalized server URL.
+
+ Parameters:
+ serverUrl (str): Base URL of the MONAI server. Trailing slashes are removed when provided.
+ """
self.PARAMS_PREFIX_REST_REQUEST = "params"
self.serverUrl = serverUrl.rstrip("/") if serverUrl else serverUrl
def getServerUrl(self) -> str:
+ """Return the configured MONAI server URL.
+
+ Returns:
+ str: The server URL.
+ """
return self.serverUrl
def getCurrentTime(self) -> datetime:
@@ -53,11 +64,28 @@ def requestDataStoreInfo(self) -> dict:
return response.json()
def getDicomDownloadUri(self, image_id: str) -> str:
+ """Build the download URI for a DICOM image.
+
+ Parameters:
+ image_id (str): Identifier of the image to retrieve.
+
+ Returns:
+ str: The encoded DICOM image download URI.
+ """
download_uri = f"{self.serverUrl}/datastore/image?image={quote_plus(image_id)}"
logging.info(f"{self.getCurrentTime()}: REST: request dicom image '{download_uri}'")
return download_uri
def requestImage(self, image_id: str) -> requests.models.Response:
+ """
+ Request an image from the MONAI server.
+
+ Parameters:
+ image_id (str): Identifier of the image to request.
+
+ Returns:
+ requests.models.Response: The successful image response, or None if the request fails or returns a non-200 status.
+ """
download_uri = self.getDicomDownloadUri(image_id)
try:
@@ -81,6 +109,16 @@ def requestImage(self, image_id: str) -> requests.models.Response:
return response
def requestSegmentation(self, image_id: str, tag: str) -> requests.models.Response:
+ """
+ Request a segmentation for an image using the specified version tag.
+
+ Parameters:
+ image_id (str): Identifier of the image whose segmentation is requested.
+ tag (str): Segmentation version tag; an empty string uses the ``final`` tag.
+
+ Returns:
+ requests.models.Response: The successful HTTP response, or ``None`` if the request fails or returns a non-200 status code.
+ """
if tag == "":
tag = "final"
download_uri = f"{self.serverUrl}/datastore/label?label={quote_plus(image_id)}&tag={quote_plus(tag)}"
diff --git a/sample-apps/reviewer/app.py b/sample-apps/reviewer/app.py
index 9757fcd1f..89ef049a4 100644
--- a/sample-apps/reviewer/app.py
+++ b/sample-apps/reviewer/app.py
@@ -65,7 +65,17 @@ def __init__(self, app_dir: str, studies: str, conf: Dict):
logger.info(f"Studies: {self.review_config['studies']}")
def _load_review_config(self, studies: str, conf: Dict) -> Dict[str, Any]:
- """Load review-specific configuration."""
+ """
+ Load review configuration from supplied values and environment variables.
+
+ Parameters:
+ studies (str): Path or identifier for the studies datastore.
+ conf (Dict): Optional configuration values.
+
+ Returns:
+ Dict[str, Any]: Resolved review configuration, including server, datastore,
+ reviewer, and mode settings.
+ """
conf_dict = conf or {}
config = {
@@ -86,29 +96,46 @@ def _load_review_config(self, studies: str, conf: Dict) -> Dict[str, Any]:
return config
def init_infers(self) -> Dict[str, Any]:
+ """Indicate that this application provides no inference components.
+
+ Returns:
+ Dict[str, Any]: An empty dictionary.
+ """
return {}
def init_trainers(self) -> Dict[str, Any]:
+ """
+ Indicate that the application has no training components.
+
+ Returns:
+ Dict[str, Any]: An empty dictionary.
+ """
return {}
def init_strategies(self) -> Dict[str, Any]:
+ """
+ Indicate that the application provides no strategy components.
+
+ Returns:
+ Dict[str, Any]: An empty dictionary.
+ """
return {}
def init_scoring_methods(self) -> Dict[str, Any]:
+ """Indicate that no scoring methods are configured.
+
+ Returns:
+ Dict[str, Any]: An empty dictionary.
+ """
return {}
def info(self) -> Dict[str, Any]:
"""
- Get application information.
+ Provide application metadata and review workflow capabilities.
Returns:
- {
- "name": "MONAILabel Reviewer",
- "description": "Lightweight review app",
- "version": "1.0.0",
- "studies": "/path/to/images",
- "config": {...}
- }
+ Dict[str, Any]: Application metadata including the resolved studies path,
+ review configuration, supported features, and review-only mode.
"""
meta = super().info()
meta.update(
diff --git a/sample-apps/reviewer/client.py b/sample-apps/reviewer/client.py
index 1cca648b7..9a4190d64 100644
--- a/sample-apps/reviewer/client.py
+++ b/sample-apps/reviewer/client.py
@@ -48,11 +48,11 @@ class LightweightReviewClient:
def __init__(self, server_url: Optional[str] = None, timeout: int = 30):
"""
- Initialize lightweight review client.
+ Initialize a review client for the specified MONAI Label server.
Parameters:
- - server_url: MONAI Label server URL
- - timeout: Request timeout in seconds
+ server_url (str): MONAI Label server URL.
+ timeout (int): Request timeout in seconds.
"""
resolved_server_url = server_url or "http://localhost:8000"
self.server_url = resolved_server_url.rstrip("/")
@@ -65,7 +65,12 @@ def __init__(self, server_url: Optional[str] = None, timeout: int = 30):
logger.info(f"ReviewClient initialized: {self.server_url}")
def ping(self) -> bool:
- """Check if server is reachable."""
+ """
+ Check whether the server responds successfully.
+
+ Returns:
+ bool: `True` if the server responds with HTTP status 200, `False` otherwise.
+ """
try:
response = requests.get(f"{self.server_url}", timeout=5)
status = response.status_code == 200
@@ -80,24 +85,15 @@ def ping(self) -> bool:
def list_images(self, offset: int = 0, limit: int = 100, status_filter: Optional[str] = None) -> Dict[str, Any]:
"""
- List all images available for review.
+ List reviewable images with optional pagination and status filtering.
- Query Parameters:
- - offset: Pagination offset (default: 0)
- - limit: Pagination limit (default: 100)
- - status_filter: Filter by status (approved/flagged/unapproved)
+ Parameters:
+ offset (int): Number of images to skip.
+ limit (int): Maximum number of images to return.
+ status_filter (Optional[str]): Status by which to filter images.
Returns:
- {
- "summary": {
- "total": 100,
- "approved": 45,
- "flagged": 5,
- "pending": 50
- },
- "results": [...list of image metadata...],
- "metadata": {...}
- }
+ Dict[str, Any]: Review case data on success, or an error dictionary if the request fails.
"""
try:
params = {"limit": str(limit), "offset": str(offset)}
@@ -122,13 +118,13 @@ def list_images(self, offset: int = 0, limit: int = 100, status_filter: Optional
def download_image(self, image_id: str) -> bytes:
"""
- Download DICOM image data.
+ Download the image data identified by `image_id`.
Parameters:
- - image_id: Unique identifier for the image
+ image_id (str): Unique identifier of the image.
Returns:
- Image file bytes in original format
+ bytes: Image data in its original format, or `None` if the download fails.
"""
try:
response = requests.get(
@@ -149,11 +145,11 @@ def download_image(self, image_id: str) -> bytes:
def download_label(self, label_id: str, tag: str = "final") -> Optional[bytes]:
"""
- Download segmentation label or mask.
+ Download a segmentation label for the specified version.
Parameters:
- - label_id: Label/segmentation ID
- - tag: Label version/tag (default: final)
+ label_id (str): Identifier of the label to download.
+ tag (str): Version or tag of the label.
Returns:
Binary label file bytes in original format
@@ -176,17 +172,13 @@ def download_label(self, label_id: str, tag: str = "final") -> Optional[bytes]:
def download_labelinfo(self, label_id: str) -> Dict[str, Any]:
"""
- Download label metadata.
+ Download metadata for a label using its final version.
Parameters:
- - label_id: Label/segmentation ID
+ label_id (str): Identifier of the label whose metadata to retrieve.
Returns:
- {
- "status": "success",
- "label_id": "label_001",
- "info": {...metadata...}
- }
+ Dict[str, Any] | None: The label metadata, or None if the request fails.
"""
try:
response = requests.get(
@@ -217,19 +209,19 @@ def update_labelinfo(
workflow_id: Optional[str] = None,
) -> bool:
"""
- Update label metadata for review.
+ Update review metadata for a label.
Parameters:
- - label_id: Label/segmentation ID
- - status: "approved" | "flagged" | "unapproved"
- - level: "easy" | "medium" | "hard"
- - comment: Review comment text
- - reviewer_name: Name of reviewer
- - reviewer_email: Email of reviewer
- - workflow_id: Optional workflow identifier
+ label_id (str): Label or segmentation identifier.
+ status (str): Review status to assign.
+ level (Optional[str]): Review difficulty level.
+ comment (Optional[str]): Review comment.
+ reviewer_name (Optional[str]): Name of the reviewer.
+ reviewer_email (Optional[str]): Email address of the reviewer.
+ workflow_id (Optional[str]): Workflow identifier.
Returns:
- True on success, False on failure
+ bool: True if the metadata update succeeds; False otherwise.
"""
try:
review_info = {"status": status, "reviewer_name": reviewer_name, "workflow_id": workflow_id}
@@ -274,18 +266,18 @@ def save_label(
version_note: Optional[str] = None,
) -> bool:
"""
- Save a new or updated segmentation label.
+ Upload a segmentation label for an image.
Parameters:
- - image_id: Image ID
- - label_file: Path to segmentation file
- - tag: Label version/tag
- - reviewer_name: Name of reviewer/saver
- - comment: Optional comment about this version
- - version_note: Reason for this version
+ image_id (str): Identifier of the image associated with the label.
+ label_file (Path): Path to the label file to upload.
+ tag (str): Version tag for the label.
+ reviewer_name (str): Name of the reviewer submitting the label.
+ comment (Optional[str]): Comment associated with the label.
+ version_note (Optional[str]): Note describing the label version.
Returns:
- True on success, False on failure
+ bool: True if the label is saved successfully, False otherwise.
"""
try:
# Prepare approvals data
@@ -322,17 +314,13 @@ def save_label(
def get_versions(self, image_id: str) -> Dict[str, Any]:
"""
- List all available versions for an image.
+ List the available label versions for an image.
Parameters:
- - image_id: Image ID
+ image_id (str): Identifier of the image whose label versions to retrieve.
Returns:
- {
- "status": "success",
- "image_id": "CT_abdomen_001",
- "versions": [...]
- }
+ Dict[str, Any]: Version information on success, or an error dictionary if the request fails.
"""
try:
response = requests.get(
@@ -353,14 +341,14 @@ def get_versions(self, image_id: str) -> Dict[str, Any]:
def generate_report(self, fmt: str = "json", reviewer: str = None) -> Dict[str, Any]:
"""
- Generate a review summary report.
+ Generate a review summary report, optionally filtered by reviewer.
Parameters:
- - fmt: Report format ("json", "csv", "html")
- - reviewer: Optional reviewer filter
+ fmt (str): Requested report format, such as "json", "csv", or "html".
+ reviewer (str): Optional reviewer name used to filter the report.
Returns:
- Report data
+ Dict[str, Any]: Report data on success, or an error dictionary when the request fails.
"""
try:
response = requests.get(
diff --git a/sample-apps/reviewer/lib/config.py b/sample-apps/reviewer/lib/config.py
index 8fa98271e..4a0098e0c 100644
--- a/sample-apps/reviewer/lib/config.py
+++ b/sample-apps/reviewer/lib/config.py
@@ -33,15 +33,14 @@ def __init__(
mode: str = "review",
):
"""
- Initialize reviewer configuration.
+ Initialize reviewer configuration from arguments and environment variables.
Parameters:
- - server_url: MONAI Label server URL (e.g., http://localhost:8000)
- If None, runs in standalone mode using local cache
- - cache_dir: Directory for cache storage
- - reviewer_name: Name of the reviewer
- - reviewer_email: Email of the reviewer
- - mode: Operating mode - "review" (requires server) or "standalone" (local cache only)
+ server_url (Optional[str]): URL of the MONAI Label server.
+ cache_dir (Optional[str]): Directory used for review data caching.
+ reviewer_name (Optional[str]): Display name of the reviewer.
+ reviewer_email (Optional[str]): Email address of the reviewer.
+ mode (str): Operating mode, such as ``"review"`` or ``"standalone"``.
"""
# Resolve server URL
self.server_url = server_url
@@ -92,14 +91,25 @@ def anaconda_channel(self) -> str:
@property
def server_mode(self) -> str:
- """Return the server mode description."""
+ """Describe whether the reviewer operates in standalone or server-synchronized mode.
+
+ Returns:
+ str: The current review mode description.
+ """
if self.review_only:
return "Lightweight Review Mode (Standalone)"
else:
return "Full Review Mode with Server Sync"
def dict(self) -> dict:
- """Return configuration as dictionary."""
+ """
+ Export the review configuration and its derived operating settings.
+
+ Returns:
+ dict: Configuration values including server details, reviewer identity,
+ review mode, workspace settings, history and cache limits, server mode,
+ and synchronization status.
+ """
return {
"server_url": self.server_url,
"cache_dir": self.cache_dir,