From 0543eb74541882837678cb93d290b49fb5c53bc9 Mon Sep 17 00:00:00 2001
From: Benoit Crickboom <pro@sleig.be>
Date: Fri, 23 Feb 2024 18:14:57 +0100
Subject: [PATCH] clean up

---
 orthanc_tools/orthanc_uploader.py | 131 ++++++++++++++++++++++++++++++
 requirements.txt                  |   6 +-
 tests/test_3_orthancs.py          |   1 -
 3 files changed, 135 insertions(+), 3 deletions(-)
 create mode 100644 orthanc_tools/orthanc_uploader.py

diff --git a/orthanc_tools/orthanc_uploader.py b/orthanc_tools/orthanc_uploader.py
new file mode 100644
index 0000000..2fc325f
--- /dev/null
+++ b/orthanc_tools/orthanc_uploader.py
@@ -0,0 +1,131 @@
+import inquirer
+from pathlib import Path
+import logging
+import argparse
+from orthanc_api_client import OrthancApiClient
+
+logger = logging.getLogger(__name__)
+
+'''
+This script is interactive!
+So, run it from the console, follow instructions and enjoy...
+'''
+
+class OrthancUploader:
+    def __init__(self,
+                 api_client: OrthancApiClient,
+                 path: str
+                 ):
+        self._api_client = api_client
+        self._path = path
+
+    def get_folder(self, current_path = Path('.')):
+        """ Print a numbered list of the subfolders in the working directory
+        (i.e. the directory the script is run from), and returns the directory
+        the user chooses.
+        """
+        answers = {'studies_path': 'None'}
+
+        while answers['studies_path'] != '.':
+            print("Browse to the folder you want to upload to Orthanc, then select the . to validate.")
+            questions = [
+                inquirer.List(
+                    "studies_path",
+                    message="Curent folder: " + str(current_path.absolute()),
+                    choices=['.'] + ['..'] + [str(d) for d in current_path.iterdir() if d.is_dir()]
+                )
+            ]
+            answers = inquirer.prompt(questions)
+            if answers['studies_path'] == '..':
+                current_path = current_path.absolute().parent
+            elif answers['studies_path'] != '.':
+                current_path = Path(answers['studies_path'])
+        return str(current_path.absolute())
+
+    def execute(self):
+
+        # let the user choose the folder to upload
+        path = self.get_folder(Path(self._path))
+
+        # let's get the existing labels
+        labels_list = orthanc_client.get_all_labels()
+        orthanc_labels_chosen_list = []
+
+        # let the user choose the labels to apply among the existing ones
+        if len(labels_list)>0:
+            questions_labels = [
+                inquirer.Checkbox(
+                    "orthanc_labels_chosen_list",
+                    message="Choose the existing labels to apply",
+                    choices=labels_list,
+                )
+            ]
+
+            answers = inquirer.prompt(questions_labels)
+            orthanc_labels_chosen_list = answers['orthanc_labels_chosen_list']
+
+        # let the user manually add extra labels
+        answers = {'orthanc_extra_label': 'None'}
+        while answers['orthanc_extra_label'] != '':
+            print(f"Current labels list: {orthanc_labels_chosen_list}")
+            questions_labels_manual = [
+                inquirer.Text(
+                    "orthanc_extra_label",
+                    message="Type an extra label to apply, leave it empty and press ENTER if any"
+                ),
+            ]
+
+            answers = inquirer.prompt(questions_labels_manual)
+            orthanc_extra_label = answers['orthanc_extra_label']
+            if orthanc_extra_label != "":
+                orthanc_labels_chosen_list.append(orthanc_extra_label)
+
+        print("-----------------------------------------------------------------------------------")
+        print(f"Folder to import: {path}")
+        print(f"Labels to apply: {orthanc_labels_chosen_list}")
+        questions_continue = [
+            inquirer.Confirm("continue", message="Ready to start?", default=True)
+        ]
+
+        answers = inquirer.prompt(questions_continue)
+        if answers['continue'] == False:
+            print("Aborted!")
+            exit(-1)
+
+        logger.info(f"starting import of folder {path} ...")
+        dicom_ids_set, orthanc_ids_set, rejected_files_list = orthanc_client.upload_folder_return_details(path)
+
+        logger.info(f"starting labeling...")
+        for id in orthanc_ids_set:
+            orthanc_client.studies.add_labels(orthanc_id=id, labels=orthanc_labels_chosen_list)
+
+        if len(rejected_files_list)>0:
+            logger.info("End of upload, here are files in error:")
+            for rejected_file in rejected_files_list:
+                logger.info(f"{rejected_file[0]} --- {rejected_file[1]}")
+        logger.info("End of upload, no errors :-)")
+
+
+if __name__ == '__main__':
+    level = logging.INFO
+
+    logging.basicConfig(level=level, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+
+    parser = argparse.ArgumentParser(description="Upload studies from a selected folder and recursively in all subfolders to an Orthanc and apply labels.")
+    parser.add_argument('--url', help="The url of the Orthanc to upload studies to.", default="http://localhost:8042")
+    parser.add_argument('--user', help="The user of the Orthanc to upload studies to.", default="demo")
+    parser.add_argument('--password', help="The password of the Orthanc to upload studies to.", default="demo")
+    parser.add_argument('--start_path', help="The path used as start path for the selection of the folder containing studies to upload. Default value: script execution path", default=".")
+
+    args = parser.parse_args()
+
+    orthanc_url = args.url
+    orthanc_user = args.user
+    orthanc_password = args.password
+    start_path = args.start_path
+
+    orthanc_client = OrthancApiClient(orthanc_url, user=orthanc_user, pwd=orthanc_password)
+
+    uploader = OrthancUploader(api_client=orthanc_client, path=start_path)
+
+    uploader.execute()
diff --git a/requirements.txt b/requirements.txt
index 894503a..75a3ef3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,9 @@
 requests
-orthanc-api-client>=0.14.2
+orthanc-api-client>=0.14.11
 pydicom>=2.3.1
 hl7==0.4.2
 six
 schedule==1.2.0
-pika
\ No newline at end of file
+pika
+pathlib
+inquirer
\ No newline at end of file
diff --git a/tests/test_3_orthancs.py b/tests/test_3_orthancs.py
index 3b143bb..4bbd8ae 100644
--- a/tests/test_3_orthancs.py
+++ b/tests/test_3_orthancs.py
@@ -643,7 +643,6 @@ def test_orthanc_cleaner_with_future_studies(self):
         self.assertEqual(len(self.oa.studies.get_all_ids()), 1)
         self.assertNotEqual(old_study_id, self.oa.studies.get_all_ids()[0])
 
-
 if __name__ == '__main__':
     logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
     unittest.main()