numerous improvements

This commit is contained in:
Ross
2024-02-19 11:15:15 +00:00
parent 1e3318dfd6
commit e4c5847fd1
+106 -39
View File
@@ -61,7 +61,6 @@ except ImportError: # if it's not there locally, try the wxPython lib.
import wx.lib.agw.pygauge as PG
import wx.lib.agw.thumbnailctrl as TC
import wx.lib.agw.ultimatelistctrl as ULC
@@ -192,13 +191,12 @@ def annonymise(
anonymize_file(file, new_filepath)
# progress_dialog.Destroy()
return new_filepath
def anonymize_file(file: Path, new_filepath: Path):
def anonymize_file(file: Path, new_filepath: Path, remove_original: bool = False):
logger.debug(f"Annonymise file, in={file}, out={new_filepath}")
# Ensure the output folder exists
new_filepath.parents[0].mkdir(parents=True, exist_ok=True)
@@ -210,6 +208,9 @@ def anonymize_file(file: Path, new_filepath: Path):
with pydicom.dcmread(file) as dataset:
anonymizer.anonymize(dataset)
dataset.save_as(new_filepath)
if remove_original:
os.remove(file)
return dataset, new_filepath
@@ -298,7 +299,7 @@ class LoginDialog(wx.Dialog):
main_sizer.Add(user_sizer, 0, wx.ALL, 5)
main_sizer.Add(p_sizer, 0, wx.ALL, 5)
btn = wx.Button(self, label="Login")
btn = wx.Button(self, id=wx.ID_OK, label="Login")
btn.Bind(wx.EVT_BUTTON, self.on_login)
main_sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5)
@@ -372,7 +373,7 @@ class LoginDialog(wx.Dialog):
print(new.content)
if self.parent.login_success:
self.parent.upload_button.Show()
self.parent.upload_button.Enable()
self.parent.login_button.Hide()
self.parent.panel.Layout()
self.Destroy()
@@ -477,6 +478,7 @@ class AnonGui(wx.App):
clear_files=True,
clear_files_on_close=True,
dev=False,
remove_files_on_annonymisation=False,
):
self.dir = output_dir
self.input_dir = input_dir
@@ -500,6 +502,7 @@ class AnonGui(wx.App):
self.silent_fail = silent_fail
self.clear_files_on_start = clear_files
self.clear_files_on_close = clear_files_on_close
self.remove_files_on_annonymisation = remove_files_on_annonymisation
self.rqst = None
@@ -575,7 +578,7 @@ class AnonGui(wx.App):
self.link_rapid_upload = hl.HyperLinkCtrl(
self.panel,
wx.ID_ANY,
"Rapid Uploader",
"Rapid",
URL=f"{self.base_site_url}/rapids/create/",
)
# self.panel.sizer.Add(self.link_rapid_upload, 0, wx.ALL, 10)
@@ -586,10 +589,17 @@ class AnonGui(wx.App):
"Long Series",
URL=f"{self.base_site_url}/longs/create/series",
)
self.link_atlas_case = hl.HyperLinkCtrl(
self.panel,
wx.ID_ANY,
"Atlas Case",
URL=f"{self.base_site_url}/atlas/case/create/",
)
# self.panel.sizer.Add(self.link_long_upload, 0, wx.ALL, 10)
hoz_links = wx.BoxSizer(wx.HORIZONTAL)
hoz_links.Add(self.link_rapid_upload, 1, wx.EXPAND)
hoz_links.Add(self.link_long_upload, 1, wx.EXPAND)
hoz_links.Add(self.link_atlas_case, 1, wx.EXPAND)
self.panel.sizer.Add(hoz_links, 1, wx.EXPAND)
# self.dirctrl = wx.GenericDirCtrl(self.panel, wx.ID_ANY,
@@ -598,13 +608,18 @@ class AnonGui(wx.App):
# wx.DIRCTRL_3D_INTERNAL |
# wx.DIRCTRL_MULTIPLE,
# filter="Dicom files (*.dcm)|*.dcm")
upload_drag_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.drag_button = wx.Button(
self.panel, id=wx.ID_ANY, label="Click here to drag files"
self.panel, id=wx.ID_ANY, label="Drag"
)
self.drag_button.SetToolTip("Click here to drag files")
self.upload_button = wx.Button(
self.panel, id=wx.ID_ANY, label="Click here to upload files"
)
self.upload_button.Hide()
self.upload_button.SetToolTip("Upload files to the server. You must be logged in.")
self.upload_button.Disable()
upload_drag_sizer.Add(self.upload_button, 4, wx.EXPAND)
upload_drag_sizer.Add(self.drag_button, 1, wx.EXPAND)
# Use some sizers to see layout options
# self.panel.sizer.Add(self.dirctrl, 1, wx.EXPAND)
@@ -630,20 +645,25 @@ class AnonGui(wx.App):
self.list_ctrl.InsertColumn(6, "Hash")
self.list_ctrl.InsertColumn(7, "Series Instance UID")
# self.UpdateListCtrl()
self.login_button = wx.Button(self.panel, id=wx.ID_ANY, label="Login")
self.login_button.SetForegroundColour(wx.BLUE)
self.login_button.SetBackgroundColour(wx.WHITE)
self.login_button.SetToolTip("Click to login to the server")
self.clear_files_button = wx.Button(
self.panel, id=wx.ID_ANY, label="Clear files"
)
self.clear_files_button.SetToolTip("Click to clear all files.")
self.refresh_files_button = wx.Button(self.panel, id=wx.ID_ANY, label="Refresh")
self.reorder_files_button = wx.Button(self.panel, id=wx.ID_ANY, label="Reorder")
self.reorder_files_button.SetToolTip("Click to reorder files as they are shown below. This will sequentially rename the files. Please note this is usually better done once the files have been uploaded.")
self.reannonymise_files_button = wx.Button(
self.panel, id=wx.ID_ANY, label="Reannonymise"
)
self.reannonymise_files_button.SetToolTip("Click to reannonymise the currently loaded files.")
self.manual_load_button = wx.Button(self.panel, id=wx.ID_ANY, label="Load")
self.manual_load_button.SetToolTip("Click to manually load dicom from a directory.")
# wx.Bind(self.clear_files_button, )
self.Bind(wx.EVT_BUTTON, self.Login, self.login_button)
@@ -651,11 +671,12 @@ class AnonGui(wx.App):
self.Bind(wx.EVT_BUTTON, lambda evt: self.LoadDir(), self.refresh_files_button)
self.Bind(wx.EVT_BUTTON, self.ReorderFiles, self.reorder_files_button)
self.Bind(wx.EVT_BUTTON, self.ReAnnonymiseFiles, self.reannonymise_files_button)
self.Bind(
wx.EVT_BUTTON,
lambda evt: self.AnnonymiseFilesInDir(self.input_dir, self.dir),
self.manual_load_button,
)
#self.Bind(
# wx.EVT_BUTTON,
# lambda evt: self.AnnonymiseFilesInDir(self.input_dir, self.dir),
# self.manual_load_button,
#)
self.Bind(wx.EVT_BUTTON, self.import_from_dir, self.manual_load_button)
# Use some sizers to see layout options
# self.panel.sizer.Add(self.dirctrl, 1, wx.EXPAND)
@@ -672,11 +693,14 @@ class AnonGui(wx.App):
self.panel.sizer.Add(self.series_grid, 2, wx.EXPAND)
self.panel.sizer.Add(self.list_ctrl, 8, wx.EXPAND)
self.panel.sizer.Add(self.drag_button, 2, wx.EXPAND)
self.panel.sizer.Add(self.upload_button, 2, wx.EXPAND)
#self.panel.sizer.Add(self.drag_button, 2, wx.EXPAND)
#self.panel.sizer.Add(self.upload_button, 2, wx.EXPAND)
self.panel.sizer.Add(upload_drag_sizer, 2, wx.EXPAND)
self.progress_sizer = wx.BoxSizer(wx.VERTICAL)
self.progress_bar = wx.Gauge(self.panel, style=wx.GA_HORIZONTAL|wx.GA_PROGRESS)
self.progress_bar = wx.Gauge(
self.panel, style=wx.GA_HORIZONTAL | wx.GA_PROGRESS
)
# self.progress_bar = PG.PyGauge(self.panel, style=wx.GA_HORIZONTAL|wx.GA_PROGRESS)
# self.progress_bar.SetDrawValue(draw=True, drawPercent=True, font=None, colour=wx.RED, formatString=None)
self.progress_text = wx.StaticText(self.panel, label="Text")
@@ -691,18 +715,21 @@ class AnonGui(wx.App):
self.panel.sizer.Fit(self.panel)
self.frame.Show()
# self.spinner.Hide()
wx.CallAfter(self.post_init)
return super().OnInit()
def post_init(self):
self.AnnonymiseFilesInDir(self.input_dir, self.dir)
self.Bind(wx.EVT_KEY_DOWN, self.onKeyPress)
self.image_hash_check_in_progress = False
self.duplicate_check_timer = wx.Timer()
self.Bind(wx.EVT_TIMER, self.check_image_hashes, self.duplicate_check_timer)
self.duplicate_check_timer.Start(2000)
return super().OnInit()
def populate_series_grid(self):
self.series_grid.series = []
@@ -717,6 +744,15 @@ class AnonGui(wx.App):
self.series_grid.display_series()
def import_from_dir(self, event):
default_path = str(self.input_dir.resolve())
with wx.DirDialog(
self.frame, "Choose a directory", style=wx.DD_DEFAULT_STYLE, defaultPath=default_path
) as dlg:
if dlg.ShowModal() == wx.ID_OK:
load_path = Path(dlg.GetPath())
self.AnnonymiseFilesInDir(load_path, self.dir)
def AnnonymiseFilesInDir(self, input_dir, output_dir):
logger.debug("AnnonymiseFilesInDir")
logger.debug(f"input: {input_dir} / output {output_dir}")
@@ -726,6 +762,11 @@ class AnonGui(wx.App):
else:
return
if not files:
logger.debug("No files to annonymise")
wx.adv.NotificationMessage("Annonymise files", "No files to annonymise").Show()
return
logger.debug(files)
# files = order_files_by_dicom_attribute(files)
@@ -751,7 +792,7 @@ class AnonGui(wx.App):
i += 1
# new_filepath = Path(output_dir).joinpath(f"IMG_{i:03}.dcm")
dataset, filepath = anonymize_file(file, new_filepath)
dataset, filepath = anonymize_file(file, new_filepath, remove_original=self.remove_files_on_annonymisation)
self.add_datails_to_file_detail_map(dataset, filepath)
# progress_dialog.Destroy()
@@ -764,16 +805,19 @@ class AnonGui(wx.App):
self.progress_bar.SetRange(total)
self.progress_sizer.ShowItems(show=True)
self.panel.Layout()
#self.panel.sizer.Fit(self.panel)
self.panel.sizer.Fit(self.panel)
try:
class P():
class P:
def __init__(self, gui, total, text):
self.gui = gui
self.text = text
self.total = total
def update(self, n: int):
self.gui.progress_bar.SetValue(n)
self.gui.progress_text.SetLabelText(f"{self.text} : {n}")
yield P(self, total, text)
finally:
# self.progress_sizer.Hide()
@@ -822,6 +866,7 @@ class AnonGui(wx.App):
return super().OnExit()
def LoadDir(self, dir=None, refresh=True, clear_file_detail_map=False):
"""Loads data from the output directory (annonymised files)"""
if clear_file_detail_map:
self.file_detail_map = {}
self.file_duplicate_check = {}
@@ -871,12 +916,20 @@ class AnonGui(wx.App):
for i in range(self.list_ctrl.GetItemCount()):
if self.list_ctrl.IsSelected(i):
path = self.list_ctrl.GetItemPyData(i)["file"]
if path in self.file_duplicate_check and self.file_duplicate_check[path]["id"]:
if (
path in self.file_duplicate_check
and self.file_duplicate_check[path]["id"]
):
menu.Append(3, "View on site")
menu.Bind(wx.EVT_MENU, lambda evt: webbrowser.open(f"{self.base_site_url}{self.file_duplicate_check[path]['url']}"), id=3)
menu.Bind(
wx.EVT_MENU,
lambda evt: webbrowser.open(
f"{self.base_site_url}{self.file_duplicate_check[path]['url']}"
),
id=3,
)
break
self.frame.PopupMenu(menu)
def ViewDicom(self, event):
@@ -900,7 +953,9 @@ class AnonGui(wx.App):
tempdir = Path(tempfile.gettempdir())
to_move = []
with self.progress(self.list_ctrl.GetItemCount(), "Reannonymise files") as p:
for i in range(self.list_ctrl.GetItemCount()):
p.update(i)
path = self.list_ctrl.GetItemPyData(i)["file"]
logger.debug("--", path)
# paths.append(path)
@@ -986,7 +1041,6 @@ class AnonGui(wx.App):
except:
slice_location = "..."
series_instances_uid = str(dataset.SeriesInstanceUID)
d2 = {
"series_instances_uid": series_instances_uid,
@@ -1034,7 +1088,7 @@ class AnonGui(wx.App):
series_desc,
slice_location,
hash,
series_instances_uid
series_instances_uid,
)
# hasher = blake3()
@@ -1094,16 +1148,15 @@ class AnonGui(wx.App):
hashes.append(hash)
series_instances_uid = d2["series_instances_uid"]
try:
self.series_map[series_instances_uid].append(f)
self.series_map_details[series_instances_uid][
"series_description"
] = d2["series_description"]
self.series_map_details[series_instances_uid]["study_description"] = d2[
self.series_map_details[series_instances_uid][
"study_description"
]
] = d2["study_description"]
self.series_map_details[series_instances_uid]["name"] = d2["name"]
except:
logger.debug(f"Unable to get Series Instance UID: {f.name}")
@@ -1122,7 +1175,6 @@ class AnonGui(wx.App):
self.list_ctrl.UpdateData(DATA)
self.populate_series_grid()
# progress_dialog.Destroy()
# if hashes:
@@ -1133,15 +1185,12 @@ class AnonGui(wx.App):
self.image_hash_check_in_progress = True
to_check = self.file_detail_map.keys() - self.file_duplicate_check.keys()
print(to_check)
if to_check:
hash_file_map = {}
for file in to_check:
hash_file_map[self.file_detail_map[file][1]] = file
hashes = list(hash_file_map.keys())
self.rqst.headers["X-CSRFToken"] = self.rqst.cookies["csrftoken"]
@@ -1233,6 +1282,8 @@ class AnonGui(wx.App):
print(upload_file_list)
print(duplicate_file_list)
wx.CallAfter(
self.upload_complete_message, upload_file_list, duplicate_file_list
)
@@ -1263,26 +1314,41 @@ class AnonGui(wx.App):
def runGUI(
ctx: typer.Context,
input_dir: Path = "C:\\Temp\\",
#input_dir: Path = "/home/ross/temp/in/",
output_dir: Path = "C:\\temp_upload\\",
#output_dir: Path = "/home/ross/temp/out/",
silent_fail: bool = False,
clear_files: bool = True,
clear_files_on_close: bool = True,
remove_files_on_annonymisation: bool = True,
dev: bool = False,
):
if "linux" in sys.platform:
input_dir = Path("/home/ross/temp/in/")
output_dir = Path("/home/ross/temp/out/")
if ctx.invoked_subcommand is None:
try:
typer.echo("run gui")
except AttributeError: # No console
pass
launchGUI(
input_dir, output_dir, silent_fail, clear_files, clear_files_on_close, dev
input_dir,
output_dir,
silent_fail,
clear_files,
clear_files_on_close,
dev,
remove_files_on_annonymisation,
)
def launchGUI(
input_dir, output_dir, silent_fail, clear_files, clear_files_on_close, dev
input_dir,
output_dir,
silent_fail,
clear_files,
clear_files_on_close,
dev,
remove_files_on_annonymisation,
):
app = AnonGui(
input_dir=input_dir,
@@ -1291,6 +1357,7 @@ def launchGUI(
clear_files=clear_files,
clear_files_on_close=clear_files_on_close,
dev=dev,
remove_files_on_annonymisation=remove_files_on_annonymisation,
)
app.MainLoop()