numerous improvements
This commit is contained in:
+160
-93
@@ -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.pygauge as PG
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import wx.lib.agw.thumbnailctrl as TC
|
import wx.lib.agw.thumbnailctrl as TC
|
||||||
import wx.lib.agw.ultimatelistctrl as ULC
|
import wx.lib.agw.ultimatelistctrl as ULC
|
||||||
|
|
||||||
@@ -192,13 +191,12 @@ def annonymise(
|
|||||||
|
|
||||||
anonymize_file(file, new_filepath)
|
anonymize_file(file, new_filepath)
|
||||||
|
|
||||||
|
|
||||||
# progress_dialog.Destroy()
|
# progress_dialog.Destroy()
|
||||||
|
|
||||||
return new_filepath
|
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}")
|
logger.debug(f"Annonymise file, in={file}, out={new_filepath}")
|
||||||
# Ensure the output folder exists
|
# Ensure the output folder exists
|
||||||
new_filepath.parents[0].mkdir(parents=True, exist_ok=True)
|
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:
|
with pydicom.dcmread(file) as dataset:
|
||||||
anonymizer.anonymize(dataset)
|
anonymizer.anonymize(dataset)
|
||||||
dataset.save_as(new_filepath)
|
dataset.save_as(new_filepath)
|
||||||
|
|
||||||
|
if remove_original:
|
||||||
|
os.remove(file)
|
||||||
return dataset, new_filepath
|
return dataset, new_filepath
|
||||||
|
|
||||||
|
|
||||||
@@ -298,7 +299,7 @@ class LoginDialog(wx.Dialog):
|
|||||||
main_sizer.Add(user_sizer, 0, wx.ALL, 5)
|
main_sizer.Add(user_sizer, 0, wx.ALL, 5)
|
||||||
main_sizer.Add(p_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)
|
btn.Bind(wx.EVT_BUTTON, self.on_login)
|
||||||
main_sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5)
|
main_sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5)
|
||||||
|
|
||||||
@@ -372,7 +373,7 @@ class LoginDialog(wx.Dialog):
|
|||||||
print(new.content)
|
print(new.content)
|
||||||
|
|
||||||
if self.parent.login_success:
|
if self.parent.login_success:
|
||||||
self.parent.upload_button.Show()
|
self.parent.upload_button.Enable()
|
||||||
self.parent.login_button.Hide()
|
self.parent.login_button.Hide()
|
||||||
self.parent.panel.Layout()
|
self.parent.panel.Layout()
|
||||||
self.Destroy()
|
self.Destroy()
|
||||||
@@ -477,6 +478,7 @@ class AnonGui(wx.App):
|
|||||||
clear_files=True,
|
clear_files=True,
|
||||||
clear_files_on_close=True,
|
clear_files_on_close=True,
|
||||||
dev=False,
|
dev=False,
|
||||||
|
remove_files_on_annonymisation=False,
|
||||||
):
|
):
|
||||||
self.dir = output_dir
|
self.dir = output_dir
|
||||||
self.input_dir = input_dir
|
self.input_dir = input_dir
|
||||||
@@ -500,6 +502,7 @@ class AnonGui(wx.App):
|
|||||||
self.silent_fail = silent_fail
|
self.silent_fail = silent_fail
|
||||||
self.clear_files_on_start = clear_files
|
self.clear_files_on_start = clear_files
|
||||||
self.clear_files_on_close = clear_files_on_close
|
self.clear_files_on_close = clear_files_on_close
|
||||||
|
self.remove_files_on_annonymisation = remove_files_on_annonymisation
|
||||||
|
|
||||||
self.rqst = None
|
self.rqst = None
|
||||||
|
|
||||||
@@ -575,7 +578,7 @@ class AnonGui(wx.App):
|
|||||||
self.link_rapid_upload = hl.HyperLinkCtrl(
|
self.link_rapid_upload = hl.HyperLinkCtrl(
|
||||||
self.panel,
|
self.panel,
|
||||||
wx.ID_ANY,
|
wx.ID_ANY,
|
||||||
"Rapid Uploader",
|
"Rapid",
|
||||||
URL=f"{self.base_site_url}/rapids/create/",
|
URL=f"{self.base_site_url}/rapids/create/",
|
||||||
)
|
)
|
||||||
# self.panel.sizer.Add(self.link_rapid_upload, 0, wx.ALL, 10)
|
# self.panel.sizer.Add(self.link_rapid_upload, 0, wx.ALL, 10)
|
||||||
@@ -586,10 +589,17 @@ class AnonGui(wx.App):
|
|||||||
"Long Series",
|
"Long Series",
|
||||||
URL=f"{self.base_site_url}/longs/create/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)
|
# self.panel.sizer.Add(self.link_long_upload, 0, wx.ALL, 10)
|
||||||
hoz_links = wx.BoxSizer(wx.HORIZONTAL)
|
hoz_links = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
hoz_links.Add(self.link_rapid_upload, 1, wx.EXPAND)
|
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_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.panel.sizer.Add(hoz_links, 1, wx.EXPAND)
|
||||||
|
|
||||||
# self.dirctrl = wx.GenericDirCtrl(self.panel, wx.ID_ANY,
|
# self.dirctrl = wx.GenericDirCtrl(self.panel, wx.ID_ANY,
|
||||||
@@ -598,13 +608,18 @@ class AnonGui(wx.App):
|
|||||||
# wx.DIRCTRL_3D_INTERNAL |
|
# wx.DIRCTRL_3D_INTERNAL |
|
||||||
# wx.DIRCTRL_MULTIPLE,
|
# wx.DIRCTRL_MULTIPLE,
|
||||||
# filter="Dicom files (*.dcm)|*.dcm")
|
# filter="Dicom files (*.dcm)|*.dcm")
|
||||||
|
upload_drag_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
self.drag_button = wx.Button(
|
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.upload_button = wx.Button(
|
||||||
self.panel, id=wx.ID_ANY, label="Click here to upload files"
|
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
|
# Use some sizers to see layout options
|
||||||
# self.panel.sizer.Add(self.dirctrl, 1, wx.EXPAND)
|
# 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(6, "Hash")
|
||||||
self.list_ctrl.InsertColumn(7, "Series Instance UID")
|
self.list_ctrl.InsertColumn(7, "Series Instance UID")
|
||||||
|
|
||||||
|
|
||||||
# self.UpdateListCtrl()
|
# self.UpdateListCtrl()
|
||||||
|
|
||||||
self.login_button = wx.Button(self.panel, id=wx.ID_ANY, label="Login")
|
self.login_button = wx.Button(self.panel, id=wx.ID_ANY, label="Login")
|
||||||
self.login_button.SetForegroundColour(wx.BLUE)
|
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.clear_files_button = wx.Button(
|
||||||
self.panel, id=wx.ID_ANY, label="Clear files"
|
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.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 = 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.reannonymise_files_button = wx.Button(
|
||||||
self.panel, id=wx.ID_ANY, label="Reannonymise"
|
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 = 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, )
|
# wx.Bind(self.clear_files_button, )
|
||||||
self.Bind(wx.EVT_BUTTON, self.Login, self.login_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, 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.ReorderFiles, self.reorder_files_button)
|
||||||
self.Bind(wx.EVT_BUTTON, self.ReAnnonymiseFiles, self.reannonymise_files_button)
|
self.Bind(wx.EVT_BUTTON, self.ReAnnonymiseFiles, self.reannonymise_files_button)
|
||||||
self.Bind(
|
#self.Bind(
|
||||||
wx.EVT_BUTTON,
|
# wx.EVT_BUTTON,
|
||||||
lambda evt: self.AnnonymiseFilesInDir(self.input_dir, self.dir),
|
# lambda evt: self.AnnonymiseFilesInDir(self.input_dir, self.dir),
|
||||||
self.manual_load_button,
|
# self.manual_load_button,
|
||||||
)
|
#)
|
||||||
|
self.Bind(wx.EVT_BUTTON, self.import_from_dir, self.manual_load_button)
|
||||||
|
|
||||||
# Use some sizers to see layout options
|
# Use some sizers to see layout options
|
||||||
# self.panel.sizer.Add(self.dirctrl, 1, wx.EXPAND)
|
# self.panel.sizer.Add(self.dirctrl, 1, wx.EXPAND)
|
||||||
@@ -672,13 +693,16 @@ class AnonGui(wx.App):
|
|||||||
|
|
||||||
self.panel.sizer.Add(self.series_grid, 2, wx.EXPAND)
|
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.list_ctrl, 8, wx.EXPAND)
|
||||||
self.panel.sizer.Add(self.drag_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(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_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.progress_bar = PG.PyGauge(self.panel, style=wx.GA_HORIZONTAL|wx.GA_PROGRESS)
|
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_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")
|
self.progress_text = wx.StaticText(self.panel, label="Text")
|
||||||
self.progress_sizer.Add(self.progress_bar, 2, wx.EXPAND)
|
self.progress_sizer.Add(self.progress_bar, 2, wx.EXPAND)
|
||||||
self.progress_sizer.Add(self.progress_text, 2, wx.EXPAND)
|
self.progress_sizer.Add(self.progress_text, 2, wx.EXPAND)
|
||||||
@@ -691,18 +715,21 @@ class AnonGui(wx.App):
|
|||||||
self.panel.sizer.Fit(self.panel)
|
self.panel.sizer.Fit(self.panel)
|
||||||
self.frame.Show()
|
self.frame.Show()
|
||||||
# self.spinner.Hide()
|
# self.spinner.Hide()
|
||||||
|
|
||||||
|
wx.CallAfter(self.post_init)
|
||||||
|
|
||||||
|
return super().OnInit()
|
||||||
|
|
||||||
|
def post_init(self):
|
||||||
self.AnnonymiseFilesInDir(self.input_dir, self.dir)
|
self.AnnonymiseFilesInDir(self.input_dir, self.dir)
|
||||||
|
|
||||||
self.Bind(wx.EVT_KEY_DOWN, self.onKeyPress)
|
self.Bind(wx.EVT_KEY_DOWN, self.onKeyPress)
|
||||||
|
|
||||||
|
|
||||||
self.image_hash_check_in_progress = False
|
self.image_hash_check_in_progress = False
|
||||||
self.duplicate_check_timer = wx.Timer()
|
self.duplicate_check_timer = wx.Timer()
|
||||||
self.Bind(wx.EVT_TIMER, self.check_image_hashes, self.duplicate_check_timer)
|
self.Bind(wx.EVT_TIMER, self.check_image_hashes, self.duplicate_check_timer)
|
||||||
self.duplicate_check_timer.Start(2000)
|
self.duplicate_check_timer.Start(2000)
|
||||||
|
|
||||||
return super().OnInit()
|
|
||||||
|
|
||||||
def populate_series_grid(self):
|
def populate_series_grid(self):
|
||||||
self.series_grid.series = []
|
self.series_grid.series = []
|
||||||
|
|
||||||
@@ -717,6 +744,15 @@ class AnonGui(wx.App):
|
|||||||
|
|
||||||
self.series_grid.display_series()
|
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):
|
def AnnonymiseFilesInDir(self, input_dir, output_dir):
|
||||||
logger.debug("AnnonymiseFilesInDir")
|
logger.debug("AnnonymiseFilesInDir")
|
||||||
logger.debug(f"input: {input_dir} / output {output_dir}")
|
logger.debug(f"input: {input_dir} / output {output_dir}")
|
||||||
@@ -726,21 +762,26 @@ class AnonGui(wx.App):
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not files:
|
||||||
|
logger.debug("No files to annonymise")
|
||||||
|
wx.adv.NotificationMessage("Annonymise files", "No files to annonymise").Show()
|
||||||
|
return
|
||||||
|
|
||||||
logger.debug(files)
|
logger.debug(files)
|
||||||
|
|
||||||
#files = order_files_by_dicom_attribute(files)
|
# files = order_files_by_dicom_attribute(files)
|
||||||
|
|
||||||
#progress_dialog = wx.ProgressDialog(
|
# progress_dialog = wx.ProgressDialog(
|
||||||
# "Annonymise files",
|
# "Annonymise files",
|
||||||
# "Removing data...",
|
# "Removing data...",
|
||||||
# maximum=len(files),
|
# maximum=len(files),
|
||||||
# style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME,
|
# style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME,
|
||||||
#)
|
# )
|
||||||
with self.progress(len(files), "Annonymise files") as p:
|
with self.progress(len(files), "Annonymise files") as p:
|
||||||
#p.SetRange(len(files))
|
# p.SetRange(len(files))
|
||||||
|
|
||||||
for n, file in enumerate(files):
|
for n, file in enumerate(files):
|
||||||
#progress_dialog.Update(n)
|
# progress_dialog.Update(n)
|
||||||
p.update(n)
|
p.update(n)
|
||||||
new_filepath: Path
|
new_filepath: Path
|
||||||
# Get next available filename
|
# Get next available filename
|
||||||
@@ -751,36 +792,39 @@ class AnonGui(wx.App):
|
|||||||
i += 1
|
i += 1
|
||||||
# new_filepath = Path(output_dir).joinpath(f"IMG_{i:03}.dcm")
|
# 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)
|
self.add_datails_to_file_detail_map(dataset, filepath)
|
||||||
#progress_dialog.Destroy()
|
# progress_dialog.Destroy()
|
||||||
|
|
||||||
self.LoadDir(self.dir)
|
self.LoadDir(self.dir)
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def progress(self, total: int, text: str="Working"):
|
def progress(self, total: int, text: str = "Working"):
|
||||||
#self.progress_sizer.Show()
|
# self.progress_sizer.Show()
|
||||||
self.progress_text.SetLabelText(text)
|
self.progress_text.SetLabelText(text)
|
||||||
self.progress_bar.SetRange(total)
|
self.progress_bar.SetRange(total)
|
||||||
self.progress_sizer.ShowItems(show=True)
|
self.progress_sizer.ShowItems(show=True)
|
||||||
self.panel.Layout()
|
self.panel.Layout()
|
||||||
#self.panel.sizer.Fit(self.panel)
|
self.panel.sizer.Fit(self.panel)
|
||||||
try:
|
try:
|
||||||
class P():
|
|
||||||
|
class P:
|
||||||
def __init__(self, gui, total, text):
|
def __init__(self, gui, total, text):
|
||||||
self.gui = gui
|
self.gui = gui
|
||||||
self.text = text
|
self.text = text
|
||||||
self.total = total
|
self.total = total
|
||||||
|
|
||||||
def update(self, n: int):
|
def update(self, n: int):
|
||||||
self.gui.progress_bar.SetValue(n)
|
self.gui.progress_bar.SetValue(n)
|
||||||
self.gui.progress_text.SetLabelText(f"{self.text} : {n}")
|
self.gui.progress_text.SetLabelText(f"{self.text} : {n}")
|
||||||
|
|
||||||
yield P(self, total, text)
|
yield P(self, total, text)
|
||||||
finally:
|
finally:
|
||||||
#self.progress_sizer.Hide()
|
# self.progress_sizer.Hide()
|
||||||
self.progress_sizer.ShowItems(show=False)
|
self.progress_sizer.ShowItems(show=False)
|
||||||
self.panel.Layout()
|
self.panel.Layout()
|
||||||
#self.panel.SetAutoLayout(1)
|
# self.panel.SetAutoLayout(1)
|
||||||
#self.panel.sizer.Fit(self.panel)
|
# self.panel.sizer.Fit(self.panel)
|
||||||
|
|
||||||
def onKeyPress(self, event):
|
def onKeyPress(self, event):
|
||||||
keycode = event.GetKeyCode()
|
keycode = event.GetKeyCode()
|
||||||
@@ -822,6 +866,7 @@ class AnonGui(wx.App):
|
|||||||
return super().OnExit()
|
return super().OnExit()
|
||||||
|
|
||||||
def LoadDir(self, dir=None, refresh=True, clear_file_detail_map=False):
|
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:
|
if clear_file_detail_map:
|
||||||
self.file_detail_map = {}
|
self.file_detail_map = {}
|
||||||
self.file_duplicate_check = {}
|
self.file_duplicate_check = {}
|
||||||
@@ -871,12 +916,20 @@ class AnonGui(wx.App):
|
|||||||
for i in range(self.list_ctrl.GetItemCount()):
|
for i in range(self.list_ctrl.GetItemCount()):
|
||||||
if self.list_ctrl.IsSelected(i):
|
if self.list_ctrl.IsSelected(i):
|
||||||
path = self.list_ctrl.GetItemPyData(i)["file"]
|
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.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
|
break
|
||||||
|
|
||||||
|
|
||||||
self.frame.PopupMenu(menu)
|
self.frame.PopupMenu(menu)
|
||||||
|
|
||||||
def ViewDicom(self, event):
|
def ViewDicom(self, event):
|
||||||
@@ -900,13 +953,15 @@ class AnonGui(wx.App):
|
|||||||
|
|
||||||
tempdir = Path(tempfile.gettempdir())
|
tempdir = Path(tempfile.gettempdir())
|
||||||
to_move = []
|
to_move = []
|
||||||
for i in range(self.list_ctrl.GetItemCount()):
|
with self.progress(self.list_ctrl.GetItemCount(), "Reannonymise files") as p:
|
||||||
path = self.list_ctrl.GetItemPyData(i)["file"]
|
for i in range(self.list_ctrl.GetItemCount()):
|
||||||
logger.debug("--", path)
|
p.update(i)
|
||||||
# paths.append(path)
|
path = self.list_ctrl.GetItemPyData(i)["file"]
|
||||||
new_path = annonymise(path, tempdir, run_gui=False)
|
logger.debug("--", path)
|
||||||
os.remove(path)
|
# paths.append(path)
|
||||||
to_move.append((new_path, Path(self.dir, new_path.name)))
|
new_path = annonymise(path, tempdir, run_gui=False)
|
||||||
|
os.remove(path)
|
||||||
|
to_move.append((new_path, Path(self.dir, new_path.name)))
|
||||||
|
|
||||||
for path, new_path in to_move:
|
for path, new_path in to_move:
|
||||||
logger.debug("**", path, new_path)
|
logger.debug("**", path, new_path)
|
||||||
@@ -956,7 +1011,7 @@ class AnonGui(wx.App):
|
|||||||
|
|
||||||
dlg.ShowModal()
|
dlg.ShowModal()
|
||||||
|
|
||||||
def ClearFiles(self, event=None, load_dir: bool=True):
|
def ClearFiles(self, event=None, load_dir: bool = True):
|
||||||
files = Path(self.dir).glob("*")
|
files = Path(self.dir).glob("*")
|
||||||
for f in files:
|
for f in files:
|
||||||
os.remove(f)
|
os.remove(f)
|
||||||
@@ -986,7 +1041,6 @@ class AnonGui(wx.App):
|
|||||||
except:
|
except:
|
||||||
slice_location = "..."
|
slice_location = "..."
|
||||||
|
|
||||||
|
|
||||||
series_instances_uid = str(dataset.SeriesInstanceUID)
|
series_instances_uid = str(dataset.SeriesInstanceUID)
|
||||||
d2 = {
|
d2 = {
|
||||||
"series_instances_uid": series_instances_uid,
|
"series_instances_uid": series_instances_uid,
|
||||||
@@ -995,36 +1049,36 @@ class AnonGui(wx.App):
|
|||||||
"name": patient_name,
|
"name": patient_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
#md5 = hashlib.md5()
|
# md5 = hashlib.md5()
|
||||||
#first = True
|
# first = True
|
||||||
#for i in dataset.pixel_array.astype(str).flatten():
|
# for i in dataset.pixel_array.astype(str).flatten():
|
||||||
# if first:
|
# if first:
|
||||||
# first = False
|
# first = False
|
||||||
# md5.update(f"{i}".encode())
|
# md5.update(f"{i}".encode())
|
||||||
# else:
|
# else:
|
||||||
# md5.update(f",{i}".encode())
|
# md5.update(f",{i}".encode())
|
||||||
#md5.update(dataset.PixelData)
|
# md5.update(dataset.PixelData)
|
||||||
#md5.update("".join(dataset.pixel_array.astype(str).flatten()).encode())
|
# md5.update("".join(dataset.pixel_array.astype(str).flatten()).encode())
|
||||||
#md5.update(np.char.join("", dataset.pixel_array.astype(str).flatten()).encode())
|
# md5.update(np.char.join("", dataset.pixel_array.astype(str).flatten()).encode())
|
||||||
#hash = md5.hexdigest()
|
# hash = md5.hexdigest()
|
||||||
#print(hash)
|
# print(hash)
|
||||||
|
|
||||||
#md5 = hashlib.md5()
|
# md5 = hashlib.md5()
|
||||||
##hash = md5.hexdigest()
|
##hash = md5.hexdigest()
|
||||||
##md5 = hashlib.md5()
|
##md5 = hashlib.md5()
|
||||||
##first = True
|
##first = True
|
||||||
##s = ",".join([f"{i}" for i in dataset.pixel_array.astype(str).flatten()]).encode()
|
##s = ",".join([f"{i}" for i in dataset.pixel_array.astype(str).flatten()]).encode()
|
||||||
##print(s)
|
##print(s)
|
||||||
#print(dataset.PixelData[:20])
|
# print(dataset.PixelData[:20])
|
||||||
#print(len(dataset.PixelData))
|
# print(len(dataset.PixelData))
|
||||||
#md5.update(dataset.PixelData)
|
# md5.update(dataset.PixelData)
|
||||||
|
|
||||||
#hash = md5.hexdigest()
|
# hash = md5.hexdigest()
|
||||||
|
|
||||||
hasher = blake3()
|
hasher = blake3()
|
||||||
hasher.update(dataset.PixelData)
|
hasher.update(dataset.PixelData)
|
||||||
hash = hasher.hexdigest()
|
hash = hasher.hexdigest()
|
||||||
#dataset.PixelData
|
# dataset.PixelData
|
||||||
|
|
||||||
d = (
|
d = (
|
||||||
file.name,
|
file.name,
|
||||||
@@ -1034,23 +1088,23 @@ class AnonGui(wx.App):
|
|||||||
series_desc,
|
series_desc,
|
||||||
slice_location,
|
slice_location,
|
||||||
hash,
|
hash,
|
||||||
series_instances_uid
|
series_instances_uid,
|
||||||
)
|
)
|
||||||
|
|
||||||
#hasher = blake3()
|
# hasher = blake3()
|
||||||
#first = True
|
# first = True
|
||||||
#for i in dataset.pixel_array.astype(str).flatten():
|
# for i in dataset.pixel_array.astype(str).flatten():
|
||||||
# if first:
|
# if first:
|
||||||
# first = False
|
# first = False
|
||||||
# hasher.update(f"{i}".encode())
|
# hasher.update(f"{i}".encode())
|
||||||
# else:
|
# else:
|
||||||
# hasher.update(f",{i}".encode())
|
# hasher.update(f",{i}".encode())
|
||||||
|
|
||||||
#hash = hasher.digest()
|
# hash = hasher.digest()
|
||||||
|
|
||||||
#hash = get_file_hash(file, hasher="md5")
|
# hash = get_file_hash(file, hasher="md5")
|
||||||
|
|
||||||
#hash = None # If we don't use the hash...
|
# hash = None # If we don't use the hash...
|
||||||
self.file_detail_map[file] = d, hash, d2
|
self.file_detail_map[file] = d, hash, d2
|
||||||
|
|
||||||
return d, hash, d2
|
return d, hash, d2
|
||||||
@@ -1061,14 +1115,14 @@ class AnonGui(wx.App):
|
|||||||
n = 0
|
n = 0
|
||||||
DATA = {}
|
DATA = {}
|
||||||
|
|
||||||
#progress_dialog = wx.ProgressDialog(
|
# progress_dialog = wx.ProgressDialog(
|
||||||
# "Loading files",
|
# "Loading files",
|
||||||
# "Extracting data from files",
|
# "Extracting data from files",
|
||||||
# maximum=len(self.files),
|
# maximum=len(self.files),
|
||||||
# style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME,
|
# style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME,
|
||||||
#)
|
# )
|
||||||
with self.progress((len(self.files)), "Loading files") as p:
|
with self.progress((len(self.files)), "Loading files") as p:
|
||||||
#p.SetRange(len(self.files))
|
# p.SetRange(len(self.files))
|
||||||
|
|
||||||
self.series_map = defaultdict(list)
|
self.series_map = defaultdict(list)
|
||||||
self.series_map_details = defaultdict(dict)
|
self.series_map_details = defaultdict(dict)
|
||||||
@@ -1076,7 +1130,7 @@ class AnonGui(wx.App):
|
|||||||
hashes = []
|
hashes = []
|
||||||
|
|
||||||
for n, f in enumerate(self.files):
|
for n, f in enumerate(self.files):
|
||||||
#progress_dialog.Update(n)
|
# progress_dialog.Update(n)
|
||||||
p.update(n)
|
p.update(n)
|
||||||
# dataset = pydicom.read_file(f)
|
# dataset = pydicom.read_file(f)
|
||||||
|
|
||||||
@@ -1094,16 +1148,15 @@ class AnonGui(wx.App):
|
|||||||
|
|
||||||
hashes.append(hash)
|
hashes.append(hash)
|
||||||
|
|
||||||
|
|
||||||
series_instances_uid = d2["series_instances_uid"]
|
series_instances_uid = d2["series_instances_uid"]
|
||||||
try:
|
try:
|
||||||
self.series_map[series_instances_uid].append(f)
|
self.series_map[series_instances_uid].append(f)
|
||||||
self.series_map_details[series_instances_uid][
|
self.series_map_details[series_instances_uid][
|
||||||
"series_description"
|
"series_description"
|
||||||
] = d2["series_description"]
|
] = d2["series_description"]
|
||||||
self.series_map_details[series_instances_uid]["study_description"] = d2[
|
self.series_map_details[series_instances_uid][
|
||||||
"study_description"
|
"study_description"
|
||||||
]
|
] = d2["study_description"]
|
||||||
self.series_map_details[series_instances_uid]["name"] = d2["name"]
|
self.series_map_details[series_instances_uid]["name"] = d2["name"]
|
||||||
except:
|
except:
|
||||||
logger.debug(f"Unable to get Series Instance UID: {f.name}")
|
logger.debug(f"Unable to get Series Instance UID: {f.name}")
|
||||||
@@ -1122,10 +1175,9 @@ class AnonGui(wx.App):
|
|||||||
self.list_ctrl.UpdateData(DATA)
|
self.list_ctrl.UpdateData(DATA)
|
||||||
self.populate_series_grid()
|
self.populate_series_grid()
|
||||||
|
|
||||||
|
# progress_dialog.Destroy()
|
||||||
|
|
||||||
#progress_dialog.Destroy()
|
# if hashes:
|
||||||
|
|
||||||
#if hashes:
|
|
||||||
# self.check_image_hashes(hashes)
|
# self.check_image_hashes(hashes)
|
||||||
|
|
||||||
def check_image_hashes(self, evt):
|
def check_image_hashes(self, evt):
|
||||||
@@ -1133,15 +1185,12 @@ class AnonGui(wx.App):
|
|||||||
self.image_hash_check_in_progress = True
|
self.image_hash_check_in_progress = True
|
||||||
|
|
||||||
to_check = self.file_detail_map.keys() - self.file_duplicate_check.keys()
|
to_check = self.file_detail_map.keys() - self.file_duplicate_check.keys()
|
||||||
print(to_check)
|
|
||||||
|
|
||||||
if to_check:
|
if to_check:
|
||||||
hash_file_map = {}
|
hash_file_map = {}
|
||||||
for file in to_check:
|
for file in to_check:
|
||||||
hash_file_map[self.file_detail_map[file][1]] = file
|
hash_file_map[self.file_detail_map[file][1]] = file
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
hashes = list(hash_file_map.keys())
|
hashes = list(hash_file_map.keys())
|
||||||
|
|
||||||
self.rqst.headers["X-CSRFToken"] = self.rqst.cookies["csrftoken"]
|
self.rqst.headers["X-CSRFToken"] = self.rqst.cookies["csrftoken"]
|
||||||
@@ -1150,7 +1199,7 @@ class AnonGui(wx.App):
|
|||||||
resp = self.rqst.post(
|
resp = self.rqst.post(
|
||||||
f"{self.base_site_url}/api/atlas/check_image_hashes/",
|
f"{self.base_site_url}/api/atlas/check_image_hashes/",
|
||||||
data=json.dumps(hashes),
|
data=json.dumps(hashes),
|
||||||
#files=files,
|
# files=files,
|
||||||
)
|
)
|
||||||
|
|
||||||
for hash, dup in resp.json().items():
|
for hash, dup in resp.json().items():
|
||||||
@@ -1180,14 +1229,14 @@ class AnonGui(wx.App):
|
|||||||
files_to_upload[i : i + n] for i in range(0, len(files_to_upload), n)
|
files_to_upload[i : i + n] for i in range(0, len(files_to_upload), n)
|
||||||
]
|
]
|
||||||
|
|
||||||
#progress_dialog = wx.ProgressDialog(
|
# progress_dialog = wx.ProgressDialog(
|
||||||
# "Uploading files",
|
# "Uploading files",
|
||||||
# "Uploading files. This is done in batches...",
|
# "Uploading files. This is done in batches...",
|
||||||
# maximum=len(chunked_files),
|
# maximum=len(chunked_files),
|
||||||
# style=wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME,
|
# style=wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME,
|
||||||
#)
|
# )
|
||||||
with self.progress(len(chunked_files), "Uploading files (in batches)") as p:
|
with self.progress(len(chunked_files), "Uploading files (in batches)") as p:
|
||||||
#p.SetRange(len(chunked_files))
|
# p.SetRange(len(chunked_files))
|
||||||
|
|
||||||
upload_file_list = []
|
upload_file_list = []
|
||||||
duplicate_file_list = []
|
duplicate_file_list = []
|
||||||
@@ -1209,7 +1258,7 @@ class AnonGui(wx.App):
|
|||||||
return resp
|
return resp
|
||||||
|
|
||||||
p.update(n)
|
p.update(n)
|
||||||
#progress_dialog.Update(n, f"Uploading batch {n}/{len(chunked_files)}")
|
# progress_dialog.Update(n, f"Uploading batch {n}/{len(chunked_files)}")
|
||||||
|
|
||||||
logger.debug(f"n: {n}")
|
logger.debug(f"n: {n}")
|
||||||
# try to upload the files
|
# try to upload the files
|
||||||
@@ -1228,11 +1277,13 @@ class AnonGui(wx.App):
|
|||||||
duplicate_file_list.extend(resp.json()["duplicates"])
|
duplicate_file_list.extend(resp.json()["duplicates"])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
#progress_dialog.Destroy()
|
# progress_dialog.Destroy()
|
||||||
|
|
||||||
print(upload_file_list)
|
print(upload_file_list)
|
||||||
print(duplicate_file_list)
|
print(duplicate_file_list)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
wx.CallAfter(
|
wx.CallAfter(
|
||||||
self.upload_complete_message, upload_file_list, duplicate_file_list
|
self.upload_complete_message, upload_file_list, duplicate_file_list
|
||||||
)
|
)
|
||||||
@@ -1263,26 +1314,41 @@ class AnonGui(wx.App):
|
|||||||
def runGUI(
|
def runGUI(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
input_dir: Path = "C:\\Temp\\",
|
input_dir: Path = "C:\\Temp\\",
|
||||||
#input_dir: Path = "/home/ross/temp/in/",
|
|
||||||
output_dir: Path = "C:\\temp_upload\\",
|
output_dir: Path = "C:\\temp_upload\\",
|
||||||
#output_dir: Path = "/home/ross/temp/out/",
|
|
||||||
silent_fail: bool = False,
|
silent_fail: bool = False,
|
||||||
clear_files: bool = True,
|
clear_files: bool = True,
|
||||||
clear_files_on_close: bool = True,
|
clear_files_on_close: bool = True,
|
||||||
|
remove_files_on_annonymisation: bool = True,
|
||||||
dev: bool = False,
|
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:
|
if ctx.invoked_subcommand is None:
|
||||||
try:
|
try:
|
||||||
typer.echo("run gui")
|
typer.echo("run gui")
|
||||||
except AttributeError: # No console
|
except AttributeError: # No console
|
||||||
pass
|
pass
|
||||||
launchGUI(
|
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(
|
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(
|
app = AnonGui(
|
||||||
input_dir=input_dir,
|
input_dir=input_dir,
|
||||||
@@ -1291,6 +1357,7 @@ def launchGUI(
|
|||||||
clear_files=clear_files,
|
clear_files=clear_files,
|
||||||
clear_files_on_close=clear_files_on_close,
|
clear_files_on_close=clear_files_on_close,
|
||||||
dev=dev,
|
dev=dev,
|
||||||
|
remove_files_on_annonymisation=remove_files_on_annonymisation,
|
||||||
)
|
)
|
||||||
app.MainLoop()
|
app.MainLoop()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user