psdi-data-conversion 0.0.33__py3-none-any.whl → 0.0.36__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- psdi_data_conversion/app.py +23 -3
- psdi_data_conversion/constants.py +2 -0
- psdi_data_conversion/converter.py +16 -6
- psdi_data_conversion/converters/atomsk.py +3 -1
- psdi_data_conversion/converters/base.py +99 -39
- psdi_data_conversion/converters/c2x.py +3 -1
- psdi_data_conversion/converters/openbabel.py +40 -1
- psdi_data_conversion/database.py +5 -0
- psdi_data_conversion/main.py +18 -10
- psdi_data_conversion/static/content/accessibility.htm +5 -5
- psdi_data_conversion/static/content/convert.htm +31 -15
- psdi_data_conversion/static/content/convertato.htm +46 -28
- psdi_data_conversion/static/content/convertc2x.htm +47 -28
- psdi_data_conversion/static/content/documentation.htm +4 -4
- psdi_data_conversion/static/content/download.htm +4 -1
- psdi_data_conversion/static/content/feedback.htm +4 -4
- psdi_data_conversion/static/content/index-versions/psdi-common-header.html +1 -1
- psdi_data_conversion/static/content/psdi-common-header.html +1 -1
- psdi_data_conversion/static/content/report.htm +9 -7
- psdi_data_conversion/static/javascript/common.js +20 -0
- psdi_data_conversion/static/javascript/convert.js +1 -2
- psdi_data_conversion/static/javascript/convert_common.js +100 -7
- psdi_data_conversion/static/javascript/convertato.js +1 -2
- psdi_data_conversion/static/javascript/convertc2x.js +1 -2
- psdi_data_conversion/static/javascript/format.js +12 -0
- psdi_data_conversion/static/javascript/report.js +6 -0
- psdi_data_conversion/static/styles/format.css +13 -0
- psdi_data_conversion/static/styles/psdi-common.css +5 -2
- psdi_data_conversion/templates/index.htm +4 -1
- psdi_data_conversion/testing/conversion_test_specs.py +240 -149
- psdi_data_conversion/testing/utils.py +22 -7
- {psdi_data_conversion-0.0.33.dist-info → psdi_data_conversion-0.0.36.dist-info}/METADATA +29 -6
- {psdi_data_conversion-0.0.33.dist-info → psdi_data_conversion-0.0.36.dist-info}/RECORD +36 -36
- {psdi_data_conversion-0.0.33.dist-info → psdi_data_conversion-0.0.36.dist-info}/WHEEL +0 -0
- {psdi_data_conversion-0.0.33.dist-info → psdi_data_conversion-0.0.36.dist-info}/entry_points.txt +0 -0
- {psdi_data_conversion-0.0.33.dist-info → psdi_data_conversion-0.0.36.dist-info}/licenses/LICENSE +0 -0
psdi_data_conversion/app.py
CHANGED
@@ -102,8 +102,16 @@ def website():
|
|
102
102
|
else:
|
103
103
|
max_file_size = const.DEFAULT_MAX_FILE_SIZE
|
104
104
|
|
105
|
+
# And same for the Open Babel maximum file size
|
106
|
+
ev_max_file_size_ob = os.environ.get(const.MAX_FILESIZE_OB_EV)
|
107
|
+
if ev_max_file_size_ob is not None:
|
108
|
+
max_file_size_ob = float(ev_max_file_size_ob)*const.MEGABYTE
|
109
|
+
else:
|
110
|
+
max_file_size_ob = const.DEFAULT_MAX_FILE_SIZE_OB
|
111
|
+
|
105
112
|
data = [{'token': token,
|
106
113
|
'max_file_size': max_file_size,
|
114
|
+
'max_file_size_ob': max_file_size_ob,
|
107
115
|
'service_mode': service_mode,
|
108
116
|
'production_mode': production_mode,
|
109
117
|
'sha': get_last_sha()}]
|
@@ -150,11 +158,23 @@ def convert():
|
|
150
158
|
fo.write(str(e))
|
151
159
|
abort(const.STATUS_CODE_GENERAL)
|
152
160
|
|
153
|
-
#
|
154
|
-
|
161
|
+
# If the exception provides a status code, get it
|
162
|
+
status_code: int
|
163
|
+
if hasattr(e, "status_code"):
|
164
|
+
status_code = e.status_code
|
165
|
+
else:
|
166
|
+
status_code = const.STATUS_CODE_GENERAL
|
167
|
+
|
168
|
+
# If the exception provides a message, report it
|
169
|
+
if hasattr(e, "message"):
|
170
|
+
msg = f"An unexpected exception was raised by the converter, with error message:\n{e.message}\n"
|
171
|
+
else:
|
172
|
+
# Failsafe exception message
|
173
|
+
msg = ("The following unexpected exception was raised by the converter:\n" +
|
174
|
+
traceback.format_exc()+"\n")
|
155
175
|
with open(qualified_output_log, "w") as fo:
|
156
176
|
fo.write(msg)
|
157
|
-
abort(
|
177
|
+
abort(status_code)
|
158
178
|
|
159
179
|
return repr(conversion_output)
|
160
180
|
else:
|
@@ -40,6 +40,7 @@ CL_SCRIPT_NAME = "psdi-data-convert"
|
|
40
40
|
LOG_MODE_EV = "LOG_MODE"
|
41
41
|
LOG_LEVEL_EV = "LOG_LEVEL"
|
42
42
|
MAX_FILESIZE_EV = "MAX_FILESIZE"
|
43
|
+
MAX_FILESIZE_OB_EV = "MAX_FILESIZE_OB"
|
43
44
|
|
44
45
|
# Files and Folders
|
45
46
|
# -----------------
|
@@ -47,6 +48,7 @@ MAX_FILESIZE_EV = "MAX_FILESIZE"
|
|
47
48
|
# Maximum output file size in bytes
|
48
49
|
MEGABYTE = 1024*1024
|
49
50
|
DEFAULT_MAX_FILE_SIZE = 0*MEGABYTE
|
51
|
+
DEFAULT_MAX_FILE_SIZE_OB = 1*MEGABYTE
|
50
52
|
|
51
53
|
DEFAULT_UPLOAD_DIR = './psdi_data_conversion/static/uploads'
|
52
54
|
DEFAULT_DOWNLOAD_DIR = './psdi_data_conversion/static/downloads'
|
@@ -17,6 +17,7 @@ from psdi_data_conversion.converters import base
|
|
17
17
|
|
18
18
|
import glob
|
19
19
|
|
20
|
+
from psdi_data_conversion.converters.openbabel import CONVERTER_OB
|
20
21
|
from psdi_data_conversion.file_io import (is_archive, is_supported_archive, pack_zip_or_tar, split_archive_ext,
|
21
22
|
unpack_zip_or_tar)
|
22
23
|
|
@@ -105,7 +106,8 @@ def get_converter(*args, name=const.CONVERTER_DEFAULT, **converter_kwargs) -> ba
|
|
105
106
|
name : str
|
106
107
|
The desired converter type, by default 'Open Babel'
|
107
108
|
data : dict[str | Any] | None
|
108
|
-
A dict of any other data needed by a converter or for extra logging information, default empty dict
|
109
|
+
A dict of any other data needed by a converter or for extra logging information, default empty dict. See the
|
110
|
+
docstring of each converter for supported keys and values that can be passed to `data` here
|
109
111
|
abort_callback : Callable[[int], None]
|
110
112
|
Function to be called if the conversion hits an error and must be aborted, default `abort_raise`, which
|
111
113
|
raises an appropriate exception
|
@@ -117,7 +119,9 @@ def get_converter(*args, name=const.CONVERTER_DEFAULT, **converter_kwargs) -> ba
|
|
117
119
|
download_dir : str
|
118
120
|
The location of output files relative to the current directory
|
119
121
|
max_file_size : float
|
120
|
-
The maximum allowed file size for input/output files, in MB, default 1 MB
|
122
|
+
The maximum allowed file size for input/output files, in MB, default 1 MB for Open Babel, unlimited for other
|
123
|
+
converters. If 0, will be unlimited. If an archive of files is provided, this will apply to the total of all
|
124
|
+
files contained in it
|
121
125
|
no_check : bool
|
122
126
|
If False (default), will check at setup whether or not a conversion between the desired file formats is
|
123
127
|
supported with the specified converter
|
@@ -238,7 +242,7 @@ def run_converter(filename: str,
|
|
238
242
|
*args,
|
239
243
|
from_format: str | None = None,
|
240
244
|
download_dir=const.DEFAULT_DOWNLOAD_DIR,
|
241
|
-
max_file_size=
|
245
|
+
max_file_size=None,
|
242
246
|
log_file: str | None = None,
|
243
247
|
log_mode=const.LOG_SIMPLE,
|
244
248
|
strict=False,
|
@@ -261,7 +265,8 @@ def run_converter(filename: str,
|
|
261
265
|
name : str
|
262
266
|
The desired converter type, by default 'Open Babel'
|
263
267
|
data : dict[str | Any] | None
|
264
|
-
A dict of any other data needed by a converter or for extra logging information, default empty dict
|
268
|
+
A dict of any other data needed by a converter or for extra logging information, default empty dict. See the
|
269
|
+
docstring of each converter for supported keys and values that can be passed to `data` here
|
265
270
|
abort_callback : Callable[[int], None]
|
266
271
|
Function to be called if the conversion hits an error and must be aborted, default `abort_raise`, which
|
267
272
|
raises an appropriate exception
|
@@ -280,8 +285,9 @@ def run_converter(filename: str,
|
|
280
285
|
into a file of the same format, their logs will be combined into a single log, and the converted files and
|
281
286
|
individual logs will be deleted
|
282
287
|
max_file_size : float
|
283
|
-
The maximum allowed file size for input/output files, in MB, default 1 MB
|
284
|
-
archive of files is provided, this will apply to the total of all
|
288
|
+
The maximum allowed file size for input/output files, in MB, default 1 MB for Open Babel, unlimited for other
|
289
|
+
converters. If 0, will be unlimited. If an archive of files is provided, this will apply to the total of all
|
290
|
+
files contained in it
|
285
291
|
no_check : bool
|
286
292
|
If False (default), will check at setup whether or not a conversion between the desired file formats is
|
287
293
|
supported with the specified converter
|
@@ -318,6 +324,10 @@ def run_converter(filename: str,
|
|
318
324
|
If something goes wrong during the conversion process
|
319
325
|
"""
|
320
326
|
|
327
|
+
# Set the maximum file size based on which converter is being used if using the default
|
328
|
+
if max_file_size is None:
|
329
|
+
max_file_size = const.DEFAULT_MAX_FILE_SIZE_OB if name == CONVERTER_OB else const.DEFAULT_MAX_FILE_SIZE
|
330
|
+
|
321
331
|
# Set the log file if it was unset - note that in server logging mode, this value won't be used within the
|
322
332
|
# converter class, so it needs to be set up here to match what will be set up there
|
323
333
|
if log_file is None:
|
@@ -11,7 +11,9 @@ CONVERTER_ATO = 'Atomsk'
|
|
11
11
|
|
12
12
|
|
13
13
|
class AtoFileConverter(ScriptFileConverter):
|
14
|
-
"""File Converter specialized to use Atomsk for conversions
|
14
|
+
"""File Converter specialized to use Atomsk for conversions.
|
15
|
+
|
16
|
+
This converter does not yet support any additional configuration options provided at class init to the `data` kwarg.
|
15
17
|
"""
|
16
18
|
|
17
19
|
name = CONVERTER_ATO
|
@@ -34,7 +34,12 @@ except ImportError:
|
|
34
34
|
class FileConverterException(RuntimeError):
|
35
35
|
"""Exception class to represent any runtime error encountered by this package.
|
36
36
|
"""
|
37
|
-
|
37
|
+
|
38
|
+
def __init__(self,
|
39
|
+
*args,
|
40
|
+
logged: bool = False):
|
41
|
+
super().__init__(*args)
|
42
|
+
self.logged = logged
|
38
43
|
|
39
44
|
|
40
45
|
class FileConverterAbortException(FileConverterException):
|
@@ -212,7 +217,7 @@ class FileConverter:
|
|
212
217
|
use_envvars=False,
|
213
218
|
upload_dir=const.DEFAULT_UPLOAD_DIR,
|
214
219
|
download_dir=const.DEFAULT_DOWNLOAD_DIR,
|
215
|
-
max_file_size=
|
220
|
+
max_file_size=None,
|
216
221
|
no_check=False,
|
217
222
|
log_file: str | None = None,
|
218
223
|
log_mode=const.LOG_FULL,
|
@@ -231,7 +236,8 @@ class FileConverter:
|
|
231
236
|
The format to convert from, as the file extension (e.g. "pdb"). If None is provided (default), will be
|
232
237
|
determined from the extension of `filename`
|
233
238
|
data : dict[str | Any] | None
|
234
|
-
A dict of any other data needed by a converter or for extra logging information, default empty dict
|
239
|
+
A dict of any other data needed by a converter or for extra logging information, default empty dict. See the
|
240
|
+
docstring of each converter for supported keys and values that can be passed to `data` here
|
235
241
|
abort_callback : Callable[[int], None]
|
236
242
|
Function to be called if the conversion hits an error and must be aborted, default `abort_raise`, which
|
237
243
|
raises an appropriate exception
|
@@ -275,12 +281,32 @@ class FileConverter:
|
|
275
281
|
|
276
282
|
try:
|
277
283
|
|
284
|
+
if max_file_size is None:
|
285
|
+
from psdi_data_conversion.converters.openbabel import CONVERTER_OB
|
286
|
+
if self.name == CONVERTER_OB:
|
287
|
+
self.max_file_size = const.DEFAULT_MAX_FILE_SIZE_OB*const.MEGABYTE
|
288
|
+
else:
|
289
|
+
self.max_file_size = const.DEFAULT_MAX_FILE_SIZE*const.MEGABYTE
|
290
|
+
else:
|
291
|
+
self.max_file_size = max_file_size*const.MEGABYTE
|
292
|
+
|
293
|
+
# Set values from envvars if desired
|
294
|
+
if use_envvars:
|
295
|
+
# Get the maximum allowed size from the envvar for it
|
296
|
+
from psdi_data_conversion.converters.openbabel import CONVERTER_OB
|
297
|
+
if self.name == CONVERTER_OB:
|
298
|
+
ev_max_file_size = os.environ.get(const.MAX_FILESIZE_OB_EV)
|
299
|
+
else:
|
300
|
+
ev_max_file_size = os.environ.get(const.MAX_FILESIZE_EV)
|
301
|
+
|
302
|
+
if ev_max_file_size is not None:
|
303
|
+
self.max_file_size = float(ev_max_file_size)*const.MEGABYTE
|
304
|
+
|
278
305
|
# Set member variables directly from input
|
279
306
|
self.in_filename = filename
|
280
307
|
self.to_format = to_format
|
281
308
|
self.upload_dir = upload_dir
|
282
309
|
self.download_dir = download_dir
|
283
|
-
self.max_file_size = max_file_size*const.MEGABYTE
|
284
310
|
self.log_file = log_file
|
285
311
|
self.log_mode = log_mode
|
286
312
|
self.log_level = log_level
|
@@ -312,13 +338,6 @@ class FileConverter:
|
|
312
338
|
self.err: str | None = None
|
313
339
|
self.quality: str | None = None
|
314
340
|
|
315
|
-
# Set values from envvars if desired
|
316
|
-
if use_envvars:
|
317
|
-
# Get the maximum allowed size from the envvar for it
|
318
|
-
ev_max_file_size = os.environ.get(const.MAX_FILESIZE_EV)
|
319
|
-
if ev_max_file_size is not None:
|
320
|
-
self.max_file_size = float(ev_max_file_size)*const.MEGABYTE
|
321
|
-
|
322
341
|
# Create directory 'uploads' if not extant.
|
323
342
|
if not os.path.exists(self.upload_dir):
|
324
343
|
os.makedirs(self.upload_dir, exist_ok=True)
|
@@ -351,10 +370,13 @@ class FileConverter:
|
|
351
370
|
self.logger.debug("Finished FileConverter initialisation")
|
352
371
|
|
353
372
|
except Exception as e:
|
373
|
+
# Don't catch a deliberate abort; let it pass through
|
354
374
|
if isinstance(e, l_abort_exceptions):
|
355
|
-
|
356
|
-
|
357
|
-
|
375
|
+
if not hasattr(e, "logged") or e.logged is False:
|
376
|
+
self.logger.error(f"Unexpected exception raised while running the converter, of type '{type(e)}' "
|
377
|
+
f"with message: {str(e)}")
|
378
|
+
if e:
|
379
|
+
e.logged = True
|
358
380
|
raise
|
359
381
|
# Try to run the standard abort method. There's a good chance this will fail though depending on what went
|
360
382
|
# wrong when during init, so we fallback to printing the exception to stderr
|
@@ -362,6 +384,8 @@ class FileConverter:
|
|
362
384
|
if not isinstance(e, FileConverterHelpException):
|
363
385
|
self.logger.error(f"Exception triggering an abort was raised while initializing the converter. "
|
364
386
|
f"Exception was type '{type(e)}', with message: {str(e)}")
|
387
|
+
if e:
|
388
|
+
e.logged = True
|
365
389
|
self._abort(message="The application encountered an error while initializing the converter:\n" +
|
366
390
|
traceback.format_exc(), e=e)
|
367
391
|
except Exception as ee:
|
@@ -445,20 +469,28 @@ class FileConverter:
|
|
445
469
|
"""
|
446
470
|
|
447
471
|
try:
|
472
|
+
self.logger.debug("Checking input file size")
|
473
|
+
self._check_input_file_size_and_status()
|
474
|
+
|
448
475
|
self.logger.debug("Starting file conversion")
|
449
476
|
self._convert()
|
450
477
|
|
451
478
|
self.logger.debug("Finished file conversion; performing cleanup tasks")
|
452
479
|
self._finish_convert()
|
453
480
|
except Exception as e:
|
481
|
+
# Don't catch a deliberate abort; let it pass through
|
454
482
|
if isinstance(e, l_abort_exceptions):
|
455
|
-
#
|
456
|
-
|
457
|
-
|
483
|
+
# Log the error if it hasn't yet been logged
|
484
|
+
if not hasattr(e, "logged") or e.logged is False:
|
485
|
+
self.logger.error(f"Unexpected exception raised while running the converter, of type '{type(e)}' "
|
486
|
+
f"with message: {str(e)}")
|
487
|
+
e.logged = True
|
458
488
|
raise
|
459
489
|
if not isinstance(e, FileConverterHelpException):
|
460
490
|
self.logger.error(f"Exception triggering an abort was raised while running the converter. Exception "
|
461
491
|
f"was type '{type(e)}', with message: {str(e)}")
|
492
|
+
if e:
|
493
|
+
e.logged = True
|
462
494
|
self._abort(message="The application encountered an error while running the converter:\n" +
|
463
495
|
traceback.format_exc(), e=e)
|
464
496
|
|
@@ -522,13 +554,23 @@ class FileConverter:
|
|
522
554
|
# Note this message in the dev logger as well
|
523
555
|
if not isinstance(e, FileConverterHelpException):
|
524
556
|
self.logger.error(message)
|
557
|
+
if e:
|
558
|
+
e.logged = True
|
525
559
|
|
526
|
-
# Call the abort callback function now. We first try
|
527
|
-
# we fall back to just calling it with the status code
|
560
|
+
# Call the abort callback function now. We first try passing information to the callback function
|
528
561
|
try:
|
529
562
|
self.abort_callback(status_code, message, e=e, **kwargs)
|
530
563
|
except TypeError:
|
531
|
-
|
564
|
+
# The callback function doesn't support arguments, so we instead call the callback, catch any exception it
|
565
|
+
# raises, monkey-patch on the extra info, and reraise it
|
566
|
+
try:
|
567
|
+
self.abort_callback(status_code)
|
568
|
+
except Exception as ee:
|
569
|
+
ee.status_code = status_code
|
570
|
+
ee.message = message
|
571
|
+
ee.e = e
|
572
|
+
ee.abort_kwargs = kwargs
|
573
|
+
raise ee
|
532
574
|
|
533
575
|
def _abort_from_err(self):
|
534
576
|
"""Call an abort after a call to the converter has completed, but it's returned an error. Create a message for
|
@@ -538,7 +580,7 @@ class FileConverter:
|
|
538
580
|
self._create_message() +
|
539
581
|
self.out + '\n' +
|
540
582
|
self.err)
|
541
|
-
self._abort(message=self.err)
|
583
|
+
self._abort(message=self.err, logged=True)
|
542
584
|
|
543
585
|
def _create_message(self) -> str:
|
544
586
|
"""Create a log of options passed to the converter - this method should be overloaded to log any information
|
@@ -578,19 +620,38 @@ class FileConverter:
|
|
578
620
|
|
579
621
|
self.logger.info(message)
|
580
622
|
|
581
|
-
def
|
582
|
-
"""Get file
|
623
|
+
def _check_input_file_size_and_status(self):
|
624
|
+
"""Get input file size and status, checking that the file isn't too large
|
625
|
+
"""
|
583
626
|
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
627
|
+
try:
|
628
|
+
self.in_size = os.path.getsize(os.path.realpath(self.in_filename))
|
629
|
+
except FileNotFoundError:
|
630
|
+
# Something went wrong and the output file doesn't exist
|
631
|
+
err_message = f"Expected output file {self.in_filename} does not exist."
|
632
|
+
self.logger.error(err_message)
|
633
|
+
self.err += f"ERROR: {err_message}\n"
|
634
|
+
self._abort_from_err()
|
635
|
+
|
636
|
+
# Check that the input file doesn't exceed the maximum allowed size
|
637
|
+
if self.max_file_size > 0 and self.in_size > self.max_file_size:
|
638
|
+
|
639
|
+
self._abort(const.STATUS_CODE_SIZE,
|
640
|
+
f"ERROR converting {os.path.basename(self.in_filename)} to " +
|
641
|
+
os.path.basename(self.out_filename) + ": "
|
642
|
+
f"Input file exceeds maximum size.\nInput file size is "
|
643
|
+
f"{self.in_size/const.MEGABYTE:.2f} MB; maximum input file size is "
|
644
|
+
f"{self.max_file_size/const.MEGABYTE:.2f} MB.\n",
|
645
|
+
max_file_size=self.max_file_size,
|
646
|
+
in_size=self.in_size,
|
647
|
+
out_size=None)
|
648
|
+
|
649
|
+
def _check_output_file_size_and_status(self):
|
650
|
+
"""Get output file size and status, checking that the file isn't too large
|
590
651
|
"""
|
591
|
-
|
652
|
+
|
592
653
|
try:
|
593
|
-
out_size = os.path.getsize(os.path.realpath(self.out_filename))
|
654
|
+
self.out_size = os.path.getsize(os.path.realpath(self.out_filename))
|
594
655
|
except FileNotFoundError:
|
595
656
|
# Something went wrong and the output file doesn't exist
|
596
657
|
err_message = f"Expected output file {self.out_filename} does not exist."
|
@@ -599,20 +660,19 @@ class FileConverter:
|
|
599
660
|
self._abort_from_err()
|
600
661
|
|
601
662
|
# Check that the output file doesn't exceed the maximum allowed size
|
602
|
-
if self.max_file_size > 0 and out_size > self.max_file_size:
|
663
|
+
if self.max_file_size > 0 and self.out_size > self.max_file_size:
|
603
664
|
|
604
665
|
self._abort(const.STATUS_CODE_SIZE,
|
605
666
|
f"ERROR converting {os.path.basename(self.in_filename)} to " +
|
606
667
|
os.path.basename(self.out_filename) + ": "
|
607
668
|
f"Output file exceeds maximum size.\nInput file size is "
|
608
|
-
f"{in_size/const.MEGABYTE:.2f} MB; Output file size is {out_size/const.MEGABYTE:.2f} "
|
669
|
+
f"{self.in_size/const.MEGABYTE:.2f} MB; Output file size is {self.out_size/const.MEGABYTE:.2f} "
|
609
670
|
f"MB; maximum output file size is {self.max_file_size/const.MEGABYTE:.2f} MB.\n",
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
self.logger.debug(f"Output file found to have size {out_size/const.MEGABYTE:.2f} MB")
|
671
|
+
max_file_size=self.max_file_size,
|
672
|
+
in_size=self.in_size,
|
673
|
+
out_size=self.out_size)
|
614
674
|
|
615
|
-
|
675
|
+
self.logger.debug(f"Output file found to have size {self.out_size/const.MEGABYTE:.2f} MB")
|
616
676
|
|
617
677
|
def get_quality(self) -> str:
|
618
678
|
"""Query the JSON file to obtain conversion quality
|
@@ -630,7 +690,7 @@ class FileConverter:
|
|
630
690
|
"""Run final common steps to clean up a conversion and log success or abort due to an error
|
631
691
|
"""
|
632
692
|
|
633
|
-
self.
|
693
|
+
self._check_output_file_size_and_status()
|
634
694
|
|
635
695
|
if self.delete_input:
|
636
696
|
os.remove(self.in_filename)
|
@@ -11,7 +11,9 @@ CONVERTER_C2X = 'c2x'
|
|
11
11
|
|
12
12
|
|
13
13
|
class C2xFileConverter(ScriptFileConverter):
|
14
|
-
"""File Converter specialized to use c2x for conversions
|
14
|
+
"""File Converter specialized to use c2x for conversions.
|
15
|
+
|
16
|
+
This converter does not yet support any additional configuration options provided at class init to the `data` kwarg.
|
15
17
|
"""
|
16
18
|
|
17
19
|
name = CONVERTER_C2X
|
@@ -76,7 +76,46 @@ def get_coord_gen(l_opts: list[str] | None) -> dict[str, str]:
|
|
76
76
|
|
77
77
|
|
78
78
|
class OBFileConverter(FileConverter):
|
79
|
-
"""File Converter specialized to use Open Babel for conversions
|
79
|
+
"""File Converter specialized to use Open Babel for conversions.
|
80
|
+
|
81
|
+
This converter supports some additional configuration options which can be provided at class init or call to
|
82
|
+
`run_converter()` through providing a dict to the `data` kwarg. The supported keys and values are:
|
83
|
+
|
84
|
+
"from_flags": str
|
85
|
+
String of concatenated one-letter flags for how to read the input file. To list the flags supported for a given
|
86
|
+
input format, call ``psdi-data-convert -l -f <format> -w Open Babel`` at the command-line and look for the
|
87
|
+
"Allowed input flags" section, if one exists, or alternatively call the library function
|
88
|
+
``psdi_data_conversion.database.get_in_format_args("Open Babel", <format>)`` from within Python code.
|
89
|
+
|
90
|
+
"to_flags": str
|
91
|
+
String of concatenated one-letter flags for how to write the output file. To list the flags supported for a
|
92
|
+
given output format, call ``psdi-data-convert -l -f <format> -w Open Babel`` at the command-line and look for
|
93
|
+
the "Allowed output flags" section, if one exists, or alternatively call the library function
|
94
|
+
``psdi_data_conversion.database.get_out_format_args("Open Babel", <format>)`` from within Python code.
|
95
|
+
|
96
|
+
"from_options": str
|
97
|
+
String of space-separated options for how to read the input file. Each option "word" in this string should start
|
98
|
+
with the letter indicating which option is being used, followed by the value for that option. To list the
|
99
|
+
options supported for a given input format, call ``psdi-data-convert -l -f <format> -w Open Babel`` at the
|
100
|
+
command-line and look for the "Allowed input options" section, if one exists, or alternatively call the library
|
101
|
+
function ``psdi_data_conversion.database.get_in_format_args("Open Babel", <format>)`` from within Python code.
|
102
|
+
|
103
|
+
"to_options": str
|
104
|
+
String of space-separated options for how to write the output file. Each option "word" in this string should
|
105
|
+
start with the letter indicating which option is being used, followed by the value for that option. To list the
|
106
|
+
options supported for a given output format, call ``psdi-data-convert -l -t <format> -w Open Babel`` at the
|
107
|
+
command-line and look for the "Allowed output options" section, if one exists, or alternatively call the library
|
108
|
+
function ``psdi_data_conversion.database.get_out_format_args("Open Babel", <format>)`` from within Python code.
|
109
|
+
|
110
|
+
"coordinates": str
|
111
|
+
One of "Gen2D", "Gen3D", or "neither", specifying how positional coordinates should be generated in the output
|
112
|
+
file. Default "neither"
|
113
|
+
|
114
|
+
"coordOption": str
|
115
|
+
One of "fastest", "fast", "medium", "better", or "best", specifying the quality of the calculation of
|
116
|
+
coordinates. Default "medium"
|
117
|
+
|
118
|
+
Note that some other keys are supported for compatibility purposes, but these may be deprecated in the future.
|
80
119
|
"""
|
81
120
|
|
82
121
|
name = CONVERTER_OB
|
psdi_data_conversion/database.py
CHANGED
@@ -284,6 +284,11 @@ class ConverterInfo:
|
|
284
284
|
d_format_args: dict[str | int, set[ArgInfo]] = {}
|
285
285
|
l_parent_format_info = self.parent.l_format_info
|
286
286
|
|
287
|
+
# If the converter doesn't provide argument info, set l_arg_info to an empty list so it can be iterated in
|
288
|
+
# the next step, rather than None
|
289
|
+
if not l_arg_info:
|
290
|
+
l_arg_info = []
|
291
|
+
|
287
292
|
for arg_info in l_arg_info:
|
288
293
|
|
289
294
|
if arg_info is None:
|
psdi_data_conversion/main.py
CHANGED
@@ -371,7 +371,7 @@ def detail_converter_use(args: ConvertArgs):
|
|
371
371
|
if format_name in l_formats:
|
372
372
|
optional_not: str = ""
|
373
373
|
else:
|
374
|
-
optional_not: str = "
|
374
|
+
optional_not: str = "not "
|
375
375
|
print_wrap(f"Conversion {to_or_from} {format_name} is {optional_not}supported by {args.name}.\n")
|
376
376
|
|
377
377
|
# List all possible formats, and which can be used for input and which for output
|
@@ -551,12 +551,12 @@ def detail_possible_converters(from_format: str, to_format: str):
|
|
551
551
|
return
|
552
552
|
|
553
553
|
print_wrap(f"The following registered converters can convert from {from_format} to {to_format}:", newline=True)
|
554
|
-
print("\n ".join(l_possible_registered_converters))
|
554
|
+
print(" " + "\n ".join(l_possible_registered_converters) + "\n")
|
555
555
|
if l_possible_unregistered_converters:
|
556
556
|
print("")
|
557
557
|
print_wrap("Additionally, the following converters are supported by this package on other platforms and can "
|
558
558
|
"perform this conversion:", newline=True)
|
559
|
-
print("\n ".join(
|
559
|
+
print(" " + "\n ".join(l_possible_unregistered_converters) + "\n")
|
560
560
|
|
561
561
|
print_wrap("For details on input/output flags and options allowed by a converter for this conversion, call:")
|
562
562
|
print(f"{CL_SCRIPT_NAME} -l <converter name> -f {from_format} -t {to_format}")
|
@@ -694,26 +694,34 @@ def run_from_args(args: ConvertArgs):
|
|
694
694
|
delete_input=args.delete_input,
|
695
695
|
refresh_local_log=False)
|
696
696
|
except FileConverterHelpException as e:
|
697
|
-
|
697
|
+
if not e.logged:
|
698
|
+
print_wrap(f"ERROR: {e}", err=True)
|
699
|
+
e.logged = True
|
698
700
|
success = False
|
699
701
|
continue
|
700
702
|
except FileConverterAbortException as e:
|
701
|
-
|
702
|
-
|
703
|
+
if not e.logged:
|
704
|
+
print_wrap(f"ERROR: Attempt to convert file {filename} aborted with status code {e.status_code} and "
|
705
|
+
f"message:\n{e}\n", err=True)
|
706
|
+
e.logged = True
|
703
707
|
success = False
|
704
708
|
continue
|
705
709
|
except FileConverterInputException as e:
|
706
710
|
if "Conversion from" in str(e) and "is not supported" in str(e):
|
707
|
-
|
711
|
+
if not e.logged:
|
712
|
+
print_wrap(f"ERROR: {e}", err=True, newline=True)
|
708
713
|
detail_possible_converters(args.from_format, args.to_format)
|
709
|
-
|
714
|
+
elif not e.logged:
|
710
715
|
print_wrap(f"ERROR: Attempt to convert file {filename} failed at converter initialization with "
|
711
716
|
f"exception type {type(e)} and message: \n{e}\n", err=True)
|
717
|
+
e.logged = True
|
712
718
|
success = False
|
713
719
|
continue
|
714
720
|
except Exception as e:
|
715
|
-
|
716
|
-
|
721
|
+
if not hasattr(e, "logged") or e.logged is False:
|
722
|
+
print_wrap(f"ERROR: Attempt to convert file {filename} failed with exception type {type(e)} and "
|
723
|
+
f"message: \n{e}\n", err=True)
|
724
|
+
e.logged = True
|
717
725
|
success = False
|
718
726
|
continue
|
719
727
|
|
@@ -15,10 +15,14 @@
|
|
15
15
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400" rel="stylesheet" type="text/css">
|
16
16
|
<link href="https://fonts.googleapis.com/css?family=Lexend:400" rel="stylesheet" type="text/css">
|
17
17
|
<link rel="stylesheet" href="../styles/format.css">
|
18
|
+
|
19
|
+
<script src="https://code.jquery.com/jquery-3.7.1.js" integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4="
|
20
|
+
crossorigin="anonymous"></script>
|
21
|
+
|
18
22
|
<script src="../javascript/load_accessibility.js"></script>
|
19
|
-
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js"></script>
|
20
23
|
<script src="../javascript/common.js" type="module"></script>
|
21
24
|
<script src="../javascript/psdi-common.js" type="module"></script>
|
25
|
+
<script src="../javascript/accessibility.js" type="module" language="JavaScript"></script>
|
22
26
|
</head>
|
23
27
|
|
24
28
|
<body marginwidth="0">
|
@@ -244,10 +248,6 @@
|
|
244
248
|
</div>
|
245
249
|
</form>
|
246
250
|
|
247
|
-
<script src="https://code.jquery.com/jquery-3.7.1.js" integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4="
|
248
|
-
crossorigin="anonymous"></script>
|
249
|
-
<script src="../javascript/accessibility.js" type="module" language="JavaScript"></script>
|
250
|
-
|
251
251
|
<footer class="footer" id="psdi-footer"></footer>
|
252
252
|
|
253
253
|
</body>
|