From 964d69bea5ee096d37541fe014a750cbd1c5a837 Mon Sep 17 00:00:00 2001 From: HafeezOJ <32125238+HafeezOJ@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:54:22 -0700 Subject: [PATCH 1/8] Add a fucntion to inspect dart-runner command --- app.py | 10 +++++++++- figshare/Utils.py | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index 9202787..6335c0d 100644 --- a/app.py +++ b/app.py @@ -2,7 +2,7 @@ import argparse from version import __version__, __commit__ from Log import Log -from figshare.Utils import upload_to_remote, get_archival_staging_storage +from figshare.Utils import inspect_dart, upload_to_remote, get_archival_staging_storage from figshare.Article import Article from datetime import datetime from Config import Config @@ -144,6 +144,14 @@ def main(): + " not be reached or read.", True, False) + if inspect_dart() is None: + log.write_log_in_file('warning', + "dart-runner executable not found. Bagging will not occur.",True) + elif inspect_dart() is False: + log.write_log_in_file('warning', + "dart-runner is not executable or version is lower than 1.0. Bagging will not occur.", + True) + return config_obj, log diff --git a/figshare/Utils.py b/figshare/Utils.py index c2dad8b..f673171 100644 --- a/figshare/Utils.py +++ b/figshare/Utils.py @@ -2,11 +2,44 @@ import json import os import re +import tempfile +import configparser from typing import Any from time import sleep -import tempfile +from subprocess import Popen, PIPE from bagger.wasabi import Wasabi -import configparser + + +def inspect_dart() -> Any: + """ + Checks if dart-runner executable exists and it's usable + + :return: Return True if dart-runner executable exists and it's usable + :rtype: Any + """ + config = configparser.ConfigParser() + config.read('bagger/config/default.toml') + default_config = config['Defaults'] + dart_cmd = default_config['dart_command'].replace('\"', '') + + try: + dart_cmd = f'{dart_cmd} --version' + dart = Popen(dart_cmd, + shell=True, + stdin=PIPE, + stdout=PIPE, + stderr=PIPE, + text=True, + close_fds=True + ) + dart_version, _ = dart.communicate() + if int(dart_version.split(" ")[2].replace('v', '')[0]) < 1: + return False + return True + except FileNotFoundError as e: + return None + except PermissionError as e: + return False def standardize_api_result(api_result) -> dict: From 9d103c805ef6d50beb9e9ab89fce3efe5fa40163 Mon Sep 17 00:00:00 2001 From: HafeezOJ <32125238+HafeezOJ@users.noreply.github.com> Date: Mon, 20 Jul 2026 16:25:44 -0700 Subject: [PATCH 2/8] Prevent bagging attempt if dart-runner is not available --- figshare/Article.py | 75 ++++++++++++++++++++++++++---------------- figshare/Collection.py | 14 +++++--- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/figshare/Article.py b/figshare/Article.py index e7bb33a..3118f0c 100644 --- a/figshare/Article.py +++ b/figshare/Article.py @@ -10,7 +10,7 @@ from figshare.Integration import Integration from figshare.Utils import standardize_api_result, sorter_api_result, get_preserved_version_hash_and_size, metadata_to_hash, check_local_path from figshare.Utils import compare_hash, check_wasabi, calculate_payload_size, get_article_id_and_version_from_path, stringify_metadata -from figshare.Utils import format_version, get_folder_name_in_local_storage, upload_to_remote +from figshare.Utils import format_version, get_folder_name_in_local_storage, upload_to_remote, inspect_dart from slugify import slugify from requests.adapters import HTTPAdapter, Retry @@ -1048,21 +1048,28 @@ def __final_process(self, check_files, copy_files, check_dir, version_data, fold self.logs.write_log_in_file("info", "Saving json in metadata folder for each version.", True) success = success & self.__save_json_in_metadata(version_data, folder_name) - # only run the postprocessor if all above steps succeeded - if success: - value_post_process = self.processor.post_process_script_function("Article", check_dir, value_pre_process) - if (value_post_process != 0): - self.logs.write_log_in_file("error", - f"{version_data['id']} version {version_data['version']} - Post-processing script failed.", + # only run the postprocessor if all above steps succeeded and dart-runner is available + if inspect_dart(): + if success: + value_post_process = self.processor.post_process_script_function("Article", check_dir, value_pre_process) + if (value_post_process != 0): + self.logs.write_log_in_file("error", + f"{version_data['id']} version {version_data['version']} - Post-processing script failed.", + True) + success = False + else: + success = True + else: + self.logs.write_log_in_file("info", + f"No further processing for {version_data['id']} version {version_data['version']} due to errors.", True) success = False - else: - success = True else: - self.logs.write_log_in_file("info", - f"No further processing for {version_data['id']} version {version_data['version']} due to errors.", - True) success = False + self.logs.write_log_in_file("Warning", + f"dart-runner not available. No bagging for {version_data['id']} version {version_data['version']}", + True) + else: # if download process has any errors then delete complete folder self.logs.write_log_in_file("info", "Download process had an error so complete folder is being deleted.", True) @@ -1076,23 +1083,29 @@ def __final_process(self, check_files, copy_files, check_dir, version_data, fold success = False else: if check_files or copy_files: - if success: - # call post process script function for each matched item. - value_post_process = self.processor.post_process_script_function("Article", check_dir, value_pre_process) - if (value_post_process != 0): - self.logs.write_log_in_file("error", - f"{version_data['id']} version {version_data['version']} - Post-processing script failed.", + if inspect_dart(): + if success: + # call post process script function for each matched item. + value_post_process = self.processor.post_process_script_function("Article", check_dir, value_pre_process) + if (value_post_process != 0): + self.logs.write_log_in_file("error", + f"{version_data['id']} version {version_data['version']} - Post-processing script failed.", + True) + success = False + else: + success = True + else: + self.logs.write_log_in_file("info", + f"No further processing for {version_data['id']} version {version_data['version']} due to errors.", True) success = False - else: - success = True else: - self.logs.write_log_in_file("info", - f"No further processing for {version_data['id']} version {version_data['version']} due to errors.", - True) success = False + self.logs.write_log_in_file("Warning", + f"dart-runner not available. No bagging for {version_data['id']} version {version_data['version']}", + True) else: - self.logs.write_log_in_file("error", "Unexpected condidion in final processing. No further actions taken.", True) + self.logs.write_log_in_file("error", "Unexpected condition in final processing. No further actions taken.", True) success = False return success @@ -1237,10 +1250,16 @@ def process_articles(self, articles): else: self.logs.write_log_in_file("error", "Pre-processing script failed. Running post-processing script.", True) # call post process script function for each matched item. - value_post_process = self.processor.post_process_script_function("Article", check_dir, value_pre_process) - if (value_post_process != 0): - self.logs.write_log_in_file("error", f"{version_data['id']} version {version_data['version']} - " - + "Post-processing script failed.", True) + if inspect_dart(): + value_post_process = self.processor.post_process_script_function("Article", check_dir, value_pre_process) + if (value_post_process != 0): + self.logs.write_log_in_file("error", f"{version_data['id']} version {version_data['version']} - " + + "Post-processing script failed.", True) + else: + self.logs.write_log_in_file("Warning", + f"dart-runner not available. No bagging for {version_data['id']} " + + f"version {version_data['version']}", + True) return processed_count, self.skipped_items_counts_dict['ap_trust_preserved_versions'], \ self.skipped_items_counts_dict['wasabi_preserved_versions'], len(self.skipped_items_counts_dict['articles_with_processing_error']), \ self.skipped_items_counts_dict['articles_versions_with_processing_error'] diff --git a/figshare/Collection.py b/figshare/Collection.py index fc4dc44..d7442ef 100644 --- a/figshare/Collection.py +++ b/figshare/Collection.py @@ -8,6 +8,7 @@ from figshare.Integration import Integration from figshare.Utils import standardize_api_result, sorter_api_result, get_preserved_version_hash_and_size, format_version, metadata_to_hash from figshare.Utils import compare_hash, check_wasabi, check_local_path, get_folder_name_in_local_storage, upload_to_remote, stringify_metadata +from figshare.Utils import inspect_dart class Collection: @@ -370,11 +371,16 @@ def process_collections(self, collections): self.__save_json_in_metadata(collection, version, folder_name) collection_preservation_path = self.ingest_staging_storage + \ os.path.basename(os.path.dirname(os.path.dirname(folder_name))) - value_post_process = self.processor.post_process_script_function("Collection", collection_preservation_path) - if (value_post_process != 0): - self.logs.write_log_in_file("error", f"collection {collection} - post-processing script failed.", True) + if inspect_dart(): + value_post_process = self.processor.post_process_script_function("Collection", collection_preservation_path) + if (value_post_process != 0): + self.logs.write_log_in_file("error", f"collection {collection} - post-processing script failed.", True) + else: + processed_count += 1 else: - processed_count += 1 + self.logs.write_log_in_file("Warning", + f"dart-runner not available. No bagging for collection {collection}", + True) else: self.logs.write_log_in_file("info", "*Dry Run* File download and post-processing with " + f"{self.system_config['post_process_script_command']} skipped.", True) From 13cb3a2f35f97fa1ceeb46a50fdffd4a7d4d8d19 Mon Sep 17 00:00:00 2001 From: HafeezOJ <32125238+HafeezOJ@users.noreply.github.com> Date: Mon, 20 Jul 2026 16:29:14 -0700 Subject: [PATCH 3/8] Upgrade waring to error if dart-runner is not available --- figshare/Article.py | 6 +++--- figshare/Collection.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/figshare/Article.py b/figshare/Article.py index 3118f0c..67a642d 100644 --- a/figshare/Article.py +++ b/figshare/Article.py @@ -1066,7 +1066,7 @@ def __final_process(self, check_files, copy_files, check_dir, version_data, fold success = False else: success = False - self.logs.write_log_in_file("Warning", + self.logs.write_log_in_file("error", f"dart-runner not available. No bagging for {version_data['id']} version {version_data['version']}", True) @@ -1101,7 +1101,7 @@ def __final_process(self, check_files, copy_files, check_dir, version_data, fold success = False else: success = False - self.logs.write_log_in_file("Warning", + self.logs.write_log_in_file("error", f"dart-runner not available. No bagging for {version_data['id']} version {version_data['version']}", True) else: @@ -1256,7 +1256,7 @@ def process_articles(self, articles): self.logs.write_log_in_file("error", f"{version_data['id']} version {version_data['version']} - " + "Post-processing script failed.", True) else: - self.logs.write_log_in_file("Warning", + self.logs.write_log_in_file("error", f"dart-runner not available. No bagging for {version_data['id']} " + f"version {version_data['version']}", True) diff --git a/figshare/Collection.py b/figshare/Collection.py index d7442ef..48819cb 100644 --- a/figshare/Collection.py +++ b/figshare/Collection.py @@ -378,7 +378,7 @@ def process_collections(self, collections): else: processed_count += 1 else: - self.logs.write_log_in_file("Warning", + self.logs.write_log_in_file("error", f"dart-runner not available. No bagging for collection {collection}", True) else: From bc4088edc200a7a3293d8212475fd7648c5d563a Mon Sep 17 00:00:00 2001 From: HafeezOJ <32125238+HafeezOJ@users.noreply.github.com> Date: Mon, 20 Jul 2026 17:17:14 -0700 Subject: [PATCH 4/8] Lint --- app.py | 2 +- figshare/Article.py | 6 ++++-- figshare/Utils.py | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index 6335c0d..7db4fb1 100644 --- a/app.py +++ b/app.py @@ -146,7 +146,7 @@ def main(): if inspect_dart() is None: log.write_log_in_file('warning', - "dart-runner executable not found. Bagging will not occur.",True) + "dart-runner executable not found. Bagging will not occur.", True) elif inspect_dart() is False: log.write_log_in_file('warning', "dart-runner is not executable or version is lower than 1.0. Bagging will not occur.", diff --git a/figshare/Article.py b/figshare/Article.py index 67a642d..84865f9 100644 --- a/figshare/Article.py +++ b/figshare/Article.py @@ -1061,7 +1061,8 @@ def __final_process(self, check_files, copy_files, check_dir, version_data, fold success = True else: self.logs.write_log_in_file("info", - f"No further processing for {version_data['id']} version {version_data['version']} due to errors.", + f"No further processing for {version_data['id']} version " + + f"{version_data['version']} due to errors.", True) success = False else: @@ -1096,7 +1097,8 @@ def __final_process(self, check_files, copy_files, check_dir, version_data, fold success = True else: self.logs.write_log_in_file("info", - f"No further processing for {version_data['id']} version {version_data['version']} due to errors.", + f"No further processing for {version_data['id']} version " + + f"{version_data['version']} due to errors.", True) success = False else: diff --git a/figshare/Utils.py b/figshare/Utils.py index f673171..598cce6 100644 --- a/figshare/Utils.py +++ b/figshare/Utils.py @@ -36,9 +36,9 @@ def inspect_dart() -> Any: if int(dart_version.split(" ")[2].replace('v', '')[0]) < 1: return False return True - except FileNotFoundError as e: + except FileNotFoundError: return None - except PermissionError as e: + except PermissionError: return False From 0b7efdfb1100afc81e6581999345234766017530 Mon Sep 17 00:00:00 2001 From: HafeezOJ <32125238+HafeezOJ@users.noreply.github.com> Date: Mon, 20 Jul 2026 17:30:18 -0700 Subject: [PATCH 5/8] Use regex to extract dart-runner version --- figshare/Utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/figshare/Utils.py b/figshare/Utils.py index 598cce6..952131b 100644 --- a/figshare/Utils.py +++ b/figshare/Utils.py @@ -33,7 +33,9 @@ def inspect_dart() -> Any: close_fds=True ) dart_version, _ = dart.communicate() - if int(dart_version.split(" ")[2].replace('v', '')[0]) < 1: + dart_version_re = re.compile("v[0-9]*.") + dart_major_version = re.findall(dart_version_re, dart_version)[0].replace('v','').replace(".", "") + if int(dart_major_version) < 1: return False return True except FileNotFoundError: From c70607367c0410cc701414335a96a6f20e731383 Mon Sep 17 00:00:00 2001 From: HafeezOJ <32125238+HafeezOJ@users.noreply.github.com> Date: Mon, 20 Jul 2026 17:31:52 -0700 Subject: [PATCH 6/8] Lint --- figshare/Utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/figshare/Utils.py b/figshare/Utils.py index 952131b..29cbe86 100644 --- a/figshare/Utils.py +++ b/figshare/Utils.py @@ -34,7 +34,7 @@ def inspect_dart() -> Any: ) dart_version, _ = dart.communicate() dart_version_re = re.compile("v[0-9]*.") - dart_major_version = re.findall(dart_version_re, dart_version)[0].replace('v','').replace(".", "") + dart_major_version = re.findall(dart_version_re, dart_version)[0].replace('v', '').replace('.', '') if int(dart_major_version) < 1: return False return True From 876798f78e221515b4fd45c5681b5b1750f3f5d4 Mon Sep 17 00:00:00 2001 From: HafeezOJ <32125238+HafeezOJ@users.noreply.github.com> Date: Tue, 21 Jul 2026 08:43:01 -0700 Subject: [PATCH 7/8] Stop execution if -continue-on-error is not set --- figshare/Article.py | 6 ++++++ figshare/Collection.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/figshare/Article.py b/figshare/Article.py index 84865f9..119196c 100644 --- a/figshare/Article.py +++ b/figshare/Article.py @@ -1070,6 +1070,8 @@ def __final_process(self, check_files, copy_files, check_dir, version_data, fold self.logs.write_log_in_file("error", f"dart-runner not available. No bagging for {version_data['id']} version {version_data['version']}", True) + if self.system_config['continue-on-error'] == "False": + self.logs.write_log_in_file("info", "Aborting execution.", True, True) else: # if download process has any errors then delete complete folder @@ -1106,6 +1108,8 @@ def __final_process(self, check_files, copy_files, check_dir, version_data, fold self.logs.write_log_in_file("error", f"dart-runner not available. No bagging for {version_data['id']} version {version_data['version']}", True) + if self.system_config['continue-on-error'] == "False": + self.logs.write_log_in_file("info", "Aborting execution.", True, True) else: self.logs.write_log_in_file("error", "Unexpected condition in final processing. No further actions taken.", True) success = False @@ -1262,6 +1266,8 @@ def process_articles(self, articles): f"dart-runner not available. No bagging for {version_data['id']} " + f"version {version_data['version']}", True) + if self.system_config['continue-on-error'] == "False": + self.logs.write_log_in_file("info", "Aborting execution.", True, True) return processed_count, self.skipped_items_counts_dict['ap_trust_preserved_versions'], \ self.skipped_items_counts_dict['wasabi_preserved_versions'], len(self.skipped_items_counts_dict['articles_with_processing_error']), \ self.skipped_items_counts_dict['articles_versions_with_processing_error'] diff --git a/figshare/Collection.py b/figshare/Collection.py index 48819cb..11af1c9 100644 --- a/figshare/Collection.py +++ b/figshare/Collection.py @@ -381,6 +381,8 @@ def process_collections(self, collections): self.logs.write_log_in_file("error", f"dart-runner not available. No bagging for collection {collection}", True) + if self.system_config['continue-on-error'] == "False": + self.logs.write_log_in_file("info", "Aborting execution.", True, True) else: self.logs.write_log_in_file("info", "*Dry Run* File download and post-processing with " + f"{self.system_config['post_process_script_command']} skipped.", True) From 8b0c13ee3af8785abdcc6f54e89d92410871569f Mon Sep 17 00:00:00 2001 From: HafeezOJ <32125238+HafeezOJ@users.noreply.github.com> Date: Tue, 21 Jul 2026 08:54:13 -0700 Subject: [PATCH 8/8] Reduce calls to inspect_dart function --- app.py | 5 +++-- figshare/Article.py | 7 ++++--- figshare/Collection.py | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app.py b/app.py index 7db4fb1..7424685 100644 --- a/app.py +++ b/app.py @@ -82,6 +82,7 @@ def main(): figshare_config = config_obj.figshare_config() system_config = config_obj.system_config() figshare_api_url = figshare_config["url"] + check_dart = inspect_dart() log = Log(env_file) log_location = system_config["logs_location"] ingest_staging_storage = system_config["ingest_staging_storage"] @@ -144,10 +145,10 @@ def main(): + " not be reached or read.", True, False) - if inspect_dart() is None: + if check_dart is None: log.write_log_in_file('warning', "dart-runner executable not found. Bagging will not occur.", True) - elif inspect_dart() is False: + elif check_dart is False: log.write_log_in_file('warning', "dart-runner is not executable or version is lower than 1.0. Bagging will not occur.", True) diff --git a/figshare/Article.py b/figshare/Article.py index 119196c..b1896a7 100644 --- a/figshare/Article.py +++ b/figshare/Article.py @@ -64,6 +64,7 @@ def __init__(self, config, log, ids): 'articles_locally_preserved': 0, 'articles_versions_with_processing_error': 0 } self.skipped_article_versions = {} + self.check_dart = inspect_dart() self.processor = Integration(self.config_obj, self.logs) """ @@ -1049,7 +1050,7 @@ def __final_process(self, check_files, copy_files, check_dir, version_data, fold success = success & self.__save_json_in_metadata(version_data, folder_name) # only run the postprocessor if all above steps succeeded and dart-runner is available - if inspect_dart(): + if self.check_dart: if success: value_post_process = self.processor.post_process_script_function("Article", check_dir, value_pre_process) if (value_post_process != 0): @@ -1086,7 +1087,7 @@ def __final_process(self, check_files, copy_files, check_dir, version_data, fold success = False else: if check_files or copy_files: - if inspect_dart(): + if self.check_dart: if success: # call post process script function for each matched item. value_post_process = self.processor.post_process_script_function("Article", check_dir, value_pre_process) @@ -1256,7 +1257,7 @@ def process_articles(self, articles): else: self.logs.write_log_in_file("error", "Pre-processing script failed. Running post-processing script.", True) # call post process script function for each matched item. - if inspect_dart(): + if self.check_dart: value_post_process = self.processor.post_process_script_function("Article", check_dir, value_pre_process) if (value_post_process != 0): self.logs.write_log_in_file("error", f"{version_data['id']} version {version_data['version']} - " diff --git a/figshare/Collection.py b/figshare/Collection.py index 11af1c9..100244a 100644 --- a/figshare/Collection.py +++ b/figshare/Collection.py @@ -43,6 +43,7 @@ def __init__(self, config, log, ids): 'already_preserved_versions': 0, 'wasabi_preserved_versions': 0, 'ap_trust_preserved_versions': 0 } + self.check_dart = inspect_dart() self.processor = Integration(self.config_obj, self.logs) """ @@ -371,7 +372,7 @@ def process_collections(self, collections): self.__save_json_in_metadata(collection, version, folder_name) collection_preservation_path = self.ingest_staging_storage + \ os.path.basename(os.path.dirname(os.path.dirname(folder_name))) - if inspect_dart(): + if self.check_dart: value_post_process = self.processor.post_process_script_function("Collection", collection_preservation_path) if (value_post_process != 0): self.logs.write_log_in_file("error", f"collection {collection} - post-processing script failed.", True)