Skip to content

Commit

Permalink
Merge branch 'main' into monitor-ux-improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
aristizabal95 authored Jul 10, 2024
2 parents a05895d + 0bed05f commit e364e52
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 28 deletions.
16 changes: 11 additions & 5 deletions scripts/monitor/rano_monitor/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
STAGES_HELP,
DSET_LOC_HELP,
OUT_HELP,
REVIEW_COMMAND,
REVIEW_CMD_HELP,
)
from rano_monitor.dataset_browser import DatasetBrowser
from rano_monitor.handlers import InvalidHandler
Expand All @@ -21,7 +23,7 @@
app = typer.Typer()


def run_dset_app(dset_path, stages_path, output_path):
def run_dset_app(dset_path, stages_path, output_path, review_cmd):
report_path = os.path.join(dset_path, "report.yaml")
dset_data_path = os.path.join(dset_path, "data")
invalid_path = os.path.join(dset_path, "metadata/.invalid.txt")
Expand All @@ -48,6 +50,7 @@ def run_dset_app(dset_path, stages_path, output_path):
invalid_path,
invalid_watchdog,
prompt_watchdog,
review_cmd,
)

observer = Observer()
Expand All @@ -60,7 +63,7 @@ def run_dset_app(dset_path, stages_path, output_path):
observer.stop()


def run_tarball_app(tarball_path):
def run_tarball_app(tarball_path, review_cmd):
folder_name = f".{os.path.basename(tarball_path).split('.')[0]}"
contents_path = os.path.join(os.path.dirname(tarball_path), folder_name)
if not os.path.exists(contents_path):
Expand All @@ -72,7 +75,7 @@ def run_tarball_app(tarball_path):
contents_path = os.path.join(contents_path, "review_cases")
reviewed_watchdog = TarballReviewedHandler(contents_path, t_app)

t_app.set_vars(contents_path)
t_app.set_vars(contents_path, review_cmd)

observer = Observer()
observer.schedule(reviewed_watchdog, path=contents_path, recursive=True)
Expand All @@ -94,10 +97,13 @@ def main(
help=DSET_LOC_HELP,
),
output_path: str = Option(None, "-o", "--out", help=OUT_HELP),
itksnap_executable: str = Option(
REVIEW_COMMAND, "-i", "--itksnap", help=REVIEW_CMD_HELP
),
):
if dataset_uid.endswith(".tar.gz"):
# TODO: implement tarball_app
run_tarball_app(dataset_uid)
run_tarball_app(dataset_uid, itksnap_executable)
return
elif dataset_uid.isdigit():
# Only import medperf dependencies if the user intends to use medperf
Expand All @@ -115,7 +121,7 @@ def main(
"Please ensure the passed dataset UID/path is correct"
)

run_dset_app(dset_path, stages_path, output_path)
run_dset_app(dset_path, stages_path, output_path, itksnap_executable)


if __name__ == "__main__":
Expand Down
1 change: 1 addition & 0 deletions scripts/monitor/rano_monitor/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
REVIEW_FILENAME = "review_cases.tar.gz"
REVIEWED_FILENAME = "reviewed_cases.tar.gz"
REVIEW_COMMAND = "itksnap"
REVIEW_CMD_HELP = f"path or name of the command that launches itksnap. Defaults to '{REVIEW_COMMAND}'"
MANUAL_REVIEW_STAGE = 5
DONE_STAGE = 8
LISTITEM_MAX_LEN = 30
Expand Down
3 changes: 3 additions & 0 deletions scripts/monitor/rano_monitor/dataset_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ def set_vars(
invalid_path,
invalid_watchdog,
prompt_watchdog,
review_cmd,
):
self.dset_data_path = dset_data_path
self.stages_path = stages_path
self.output_path = output_path
self.invalid_path = invalid_path
self.invalid_watchdog = invalid_watchdog
self.prompt_watchdog = prompt_watchdog
self.review_cmd = review_cmd

def update_invalid(self, invalid_subjects):
self.invalid_subjects = invalid_subjects
Expand Down Expand Up @@ -107,6 +109,7 @@ def on_mount(self):
# Set invalid path for subject view
subject_details = self.query_one("#details", SubjectDetails)
subject_details.set_invalid_path(self.invalid_path)
subject_details.review_cmd = self.review_cmd

# Set dataset path to listview
listview = self.query_one("#subjects-list", ListView)
Expand Down
10 changes: 7 additions & 3 deletions scripts/monitor/rano_monitor/tarball_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ class TarballBrowser(App):

subjects = var([])

def set_vars(self, contents_path):
def set_vars(self, contents_path, review_cmd):
self.contents_path = contents_path
self.review_cmd = review_cmd
self.subjects_list = self.__get_subjects()

def __get_subjects(self):
Expand All @@ -55,7 +56,9 @@ def __get_subjects(self):
for subject in subjects:
subject_path = os.path.join(self.contents_path, subject)
timepoints = os.listdir(subject_path)
timepoints = [timepoint for timepoint in timepoints if not timepoint.startswith(".")]
timepoints = [
timepoint for timepoint in timepoints if not timepoint.startswith(".")
]
subject_timepoint_list += [(subject, tp) for tp in timepoints]

return subject_timepoint_list
Expand All @@ -76,6 +79,7 @@ def compose(self) -> ComposeResult:
subject_view = TarballSubjectView()
subject_view.subject = f"{id}|{tp}"
subject_view.contents_path = self.contents_path
subject_view.review_cmd = self.review_cmd
yield subject_view

def on_mount(self):
Expand All @@ -84,7 +88,7 @@ def on_mount(self):
def update_subjects_status(self):
subject_views = self.query(TarballSubjectView)
editor_msg = self.query_one("#review-msg", Static)
editor_msg.display = "none" if is_editor_installed() else "block"
editor_msg.display = "none" if is_editor_installed(self.review_cmd) else "block"

for subject_view in subject_views:
subject_view.update_status()
Expand Down
29 changes: 20 additions & 9 deletions scripts/monitor/rano_monitor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import pandas as pd
import yaml
from rano_monitor.constants import (
REVIEW_COMMAND,
BRAINMASK_BAK,
DEFAULT_SEGMENTATION,
BRAINMASK,
Expand All @@ -22,8 +21,8 @@
)


def is_editor_installed():
review_command_path = shutil.which(REVIEW_COMMAND)
def is_editor_installed(review_command):
review_command_path = shutil.which(review_command)
return review_command_path is not None


Expand All @@ -34,10 +33,10 @@ def get_hash(filepath: str):
return file_hash


def run_editor(t1c, flair, t2, t1, seg, label, cmd=REVIEW_COMMAND):
def run_editor(t1c, flair, t2, t1, seg, label, cmd):
review_cmd = "{cmd} -g {t1c} -o {flair} {t2} {t1} -s {seg} -l {label}"
review_cmd = review_cmd.format(
cmd=REVIEW_COMMAND,
cmd=cmd,
t1c=t1c,
flair=flair,
t2=t2,
Expand All @@ -48,7 +47,9 @@ def run_editor(t1c, flair, t2, t1, seg, label, cmd=REVIEW_COMMAND):
Popen(review_cmd.split(), shell=False, stdout=DEVNULL, stderr=DEVNULL)


def review_tumor(subject: str, data_path: str, labels_path: str):
def review_tumor(
subject: str, data_path: str, labels_path: str, review_cmd: str
):
(
t1c_file,
t1n_file,
Expand All @@ -65,10 +66,18 @@ def review_tumor(subject: str, data_path: str, labels_path: str):
if not is_nifti and not is_under_review:
shutil.copyfile(seg_file, under_review_file)

run_editor(t1c_file, t2f_file, t2w_file, t1n_file, under_review_file, label_file)
run_editor(
t1c_file,
t2f_file,
t2w_file,
t1n_file,
under_review_file,
label_file,
cmd=review_cmd,
)


def review_brain(subject, labels_path, data_path=None):
def review_brain(subject, labels_path, review_cmd, data_path=None):
(
t1c_file,
t1n_file,
Expand All @@ -82,7 +91,9 @@ def review_brain(subject, labels_path, data_path=None):
if not os.path.exists(backup_path):
shutil.copyfile(seg_file, backup_path)

run_editor(t1c_file, t2f_file, t2w_file, t1n_file, seg_file, label_file)
run_editor(
t1c_file, t2f_file, t2w_file, t1n_file, seg_file, label_file, cmd=review_cmd
)


def finalize(subject: str, labels_path: str):
Expand Down
12 changes: 5 additions & 7 deletions scripts/monitor/rano_monitor/widgets/subject_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class SubjectDetails(Static):
invalid_subjects = set()
subject = pd.Series()
dset_path = ""
review_cmd = None # This will be assigned after initialized

def compose(self) -> ComposeResult:
with Center(id="subject-title"):
Expand All @@ -51,10 +52,7 @@ def compose(self) -> ComposeResult:
id="reviewed-button",
disabled=True,
)
yield Static(
"If brain mask is not correct",
id="brianmask-review-header"
)
yield Static("If brain mask is not correct", id="brianmask-review-header")
yield Button(
"Brain mask not available",
disabled=True,
Expand Down Expand Up @@ -134,7 +132,7 @@ def __update_buttons(self):
brainmask_button = self.query_one("#brainmask-review-button", Button)
valid_btn = self.query_one("#valid-btn", Button)

if is_editor_installed():
if is_editor_installed(self.review_cmd):
review_msg.display = "none"
review_button.disabled = False
if self.__can_finalize():
Expand Down Expand Up @@ -175,15 +173,15 @@ def __review_tumor(self):
data_path = to_local_path(data_path, self.dset_path)
labels_path = self.subject["labels_path"]
labels_path = to_local_path(labels_path, self.dset_path)
review_tumor(subject, data_path, labels_path)
review_tumor(subject, data_path, labels_path, review_cmd=self.review_cmd)
self.__update_buttons()
self.notify("This subject can be finalized now")

def __review_brainmask(self):
subject = self.subject.name
labels_path = self.subject["labels_path"]
labels_path = to_local_path(labels_path, self.dset_path)
review_brain(subject, labels_path)
review_brain(subject, labels_path, review_cmd=self.review_cmd)
self.__update_buttons()

def __finalize(self):
Expand Down
29 changes: 25 additions & 4 deletions scripts/monitor/rano_monitor/widgets/tarball_subject_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
class TarballSubjectView(Static):
subject = reactive("")
contents_path = reactive("")
review_cmd = None # This will be assigned after initialized

def compose(self) -> ComposeResult:
with Horizontal(classes="subject-item"):
Expand Down Expand Up @@ -64,7 +65,7 @@ def __update_buttons(self):
tumor_btn = self.query_one(".tumor-btn", Button)
finalize_btn = self.query_one(".finalize-btn", Button)
brain_btn = self.query_one(".brain-btn", Button)
if is_editor_installed():
if is_editor_installed(self.review_cmd):
tumor_btn.disabled = False
if self.__can_finalize():
finalize_btn.disabled = False
Expand All @@ -87,19 +88,19 @@ def __can_review_brain(self):
id, tp = self.subject.split("|")
filepath = os.path.join(self.contents_path, id, tp, BRAINMASK)

return os.path.exists(filepath) and is_editor_installed()
return os.path.exists(filepath) and is_editor_installed(self.review_cmd)

def __review_tumor(self):
id, tp = self.subject.split("|")
data_path = os.path.join(self.contents_path, id, tp, "brain_scans")
labels_path = os.path.join(self.contents_path, id, tp)
review_tumor(self.subject, data_path, labels_path)
review_tumor(self.subject, data_path, labels_path, review_cmd=self.review_cmd)

def __review_brainmask(self):
id, tp = self.subject.split("|")
data_path = os.path.join(self.contents_path, id, tp, "raw_scans")
labels_path = os.path.join(self.contents_path, id, tp)
review_brain(self.subject, labels_path, data_path)
review_brain(self.subject, labels_path, self.review_cmd, data_path)

def __finalize(self):
id, tp = self.subject.split("|")
Expand All @@ -119,3 +120,23 @@ def on_button_pressed(self, event: Button.Pressed) -> None:
self.__finalize()

self.__update_buttons()

def __brain_has_been_reviewed(self):
id, tp = self.subject.split("|")
brainpath = os.path.join(self.contents_path, id, tp, BRAINMASK)
backup_brainpath = os.path.join(self.contents_path, id, tp, BRAINMASK_BAK)

if not os.path.exists(backup_brainpath):
return False

brain_hash = get_hash(brainpath)
backup_hash = get_hash(backup_brainpath)
return brain_hash != backup_hash

def __tumor_has_been_finalized(self):
id, tp = self.subject.split("|")
finalized_tumor_path = os.path.join(self.contents_path, id, tp, "finalized")
finalized_files = os.listdir(finalized_tumor_path)
finalized_files = [file for file in finalized_files if not file.startswith(".")]

return len(finalized_files) > 0

0 comments on commit e364e52

Please sign in to comment.