psdi-data-conversion 0.0.23__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/__init__.py +11 -0
- psdi_data_conversion/app.py +242 -0
- psdi_data_conversion/bin/linux/atomsk +0 -0
- psdi_data_conversion/bin/linux/c2x +0 -0
- psdi_data_conversion/bin/mac/atomsk +0 -0
- psdi_data_conversion/bin/mac/c2x +0 -0
- psdi_data_conversion/constants.py +185 -0
- psdi_data_conversion/converter.py +459 -0
- psdi_data_conversion/converters/__init__.py +6 -0
- psdi_data_conversion/converters/atomsk.py +32 -0
- psdi_data_conversion/converters/base.py +702 -0
- psdi_data_conversion/converters/c2x.py +32 -0
- psdi_data_conversion/converters/openbabel.py +239 -0
- psdi_data_conversion/database.py +1064 -0
- psdi_data_conversion/dist.py +87 -0
- psdi_data_conversion/file_io.py +216 -0
- psdi_data_conversion/log_utility.py +241 -0
- psdi_data_conversion/main.py +776 -0
- psdi_data_conversion/scripts/atomsk.sh +32 -0
- psdi_data_conversion/scripts/c2x.sh +26 -0
- psdi_data_conversion/security.py +38 -0
- psdi_data_conversion/static/content/accessibility.htm +254 -0
- psdi_data_conversion/static/content/convert.htm +121 -0
- psdi_data_conversion/static/content/convertato.htm +65 -0
- psdi_data_conversion/static/content/convertc2x.htm +65 -0
- psdi_data_conversion/static/content/documentation.htm +94 -0
- psdi_data_conversion/static/content/feedback.htm +53 -0
- psdi_data_conversion/static/content/header-links.html +8 -0
- psdi_data_conversion/static/content/index-versions/header-links.html +8 -0
- psdi_data_conversion/static/content/index-versions/psdi-common-footer.html +99 -0
- psdi_data_conversion/static/content/index-versions/psdi-common-header.html +28 -0
- psdi_data_conversion/static/content/psdi-common-footer.html +99 -0
- psdi_data_conversion/static/content/psdi-common-header.html +28 -0
- psdi_data_conversion/static/content/report.htm +103 -0
- psdi_data_conversion/static/data/data.json +143940 -0
- psdi_data_conversion/static/img/colormode-toggle-dm.svg +3 -0
- psdi_data_conversion/static/img/colormode-toggle-lm.svg +3 -0
- psdi_data_conversion/static/img/psdi-icon-dark.svg +136 -0
- psdi_data_conversion/static/img/psdi-icon-light.svg +208 -0
- psdi_data_conversion/static/img/psdi-logo-darktext.png +0 -0
- psdi_data_conversion/static/img/psdi-logo-lighttext.png +0 -0
- psdi_data_conversion/static/img/social-logo-bluesky-black.svg +4 -0
- psdi_data_conversion/static/img/social-logo-bluesky-white.svg +4 -0
- psdi_data_conversion/static/img/social-logo-instagram-black.svg +1 -0
- psdi_data_conversion/static/img/social-logo-instagram-white.svg +1 -0
- psdi_data_conversion/static/img/social-logo-linkedin-black.png +0 -0
- psdi_data_conversion/static/img/social-logo-linkedin-white.png +0 -0
- psdi_data_conversion/static/img/social-logo-mastodon-black.svg +4 -0
- psdi_data_conversion/static/img/social-logo-mastodon-white.svg +4 -0
- psdi_data_conversion/static/img/social-logo-x-black.svg +3 -0
- psdi_data_conversion/static/img/social-logo-x-white.svg +3 -0
- psdi_data_conversion/static/img/social-logo-youtube-black.png +0 -0
- psdi_data_conversion/static/img/social-logo-youtube-white.png +0 -0
- psdi_data_conversion/static/img/ukri-epsr-logo-darktext.png +0 -0
- psdi_data_conversion/static/img/ukri-epsr-logo-lighttext.png +0 -0
- psdi_data_conversion/static/img/ukri-logo-darktext.png +0 -0
- psdi_data_conversion/static/img/ukri-logo-lighttext.png +0 -0
- psdi_data_conversion/static/javascript/accessibility.js +196 -0
- psdi_data_conversion/static/javascript/common.js +42 -0
- psdi_data_conversion/static/javascript/convert.js +296 -0
- psdi_data_conversion/static/javascript/convert_common.js +252 -0
- psdi_data_conversion/static/javascript/convertato.js +107 -0
- psdi_data_conversion/static/javascript/convertc2x.js +107 -0
- psdi_data_conversion/static/javascript/data.js +176 -0
- psdi_data_conversion/static/javascript/format.js +611 -0
- psdi_data_conversion/static/javascript/load_accessibility.js +89 -0
- psdi_data_conversion/static/javascript/psdi-common.js +177 -0
- psdi_data_conversion/static/javascript/report.js +381 -0
- psdi_data_conversion/static/styles/format.css +147 -0
- psdi_data_conversion/static/styles/psdi-common.css +705 -0
- psdi_data_conversion/templates/index.htm +114 -0
- psdi_data_conversion/testing/__init__.py +5 -0
- psdi_data_conversion/testing/constants.py +12 -0
- psdi_data_conversion/testing/conversion_callbacks.py +394 -0
- psdi_data_conversion/testing/conversion_test_specs.py +208 -0
- psdi_data_conversion/testing/utils.py +522 -0
- psdi_data_conversion-0.0.23.dist-info/METADATA +663 -0
- psdi_data_conversion-0.0.23.dist-info/RECORD +81 -0
- psdi_data_conversion-0.0.23.dist-info/WHEEL +4 -0
- psdi_data_conversion-0.0.23.dist-info/entry_points.txt +2 -0
- psdi_data_conversion-0.0.23.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
"""@file psdi_data_conversion/converters/c2x.py
|
2
|
+
|
3
|
+
Created 2025-01-23 by Bryan Gillis.
|
4
|
+
|
5
|
+
c2x FileConverter
|
6
|
+
"""
|
7
|
+
|
8
|
+
from psdi_data_conversion.converters.base import ScriptFileConverter
|
9
|
+
|
10
|
+
CONVERTER_C2X = 'c2x'
|
11
|
+
|
12
|
+
|
13
|
+
class C2xFileConverter(ScriptFileConverter):
|
14
|
+
"""File Converter specialized to use c2x for conversions
|
15
|
+
"""
|
16
|
+
|
17
|
+
name = CONVERTER_C2X
|
18
|
+
script = "c2x.sh"
|
19
|
+
required_bin = "c2x"
|
20
|
+
info = ("c2x binaries compiled for 64-bit Linux and MacOS systems are distributed with this package. It may be "
|
21
|
+
"registered on other systems by compiling it locally and adding the compiled 'c2x' binary (with this "
|
22
|
+
"exact name - rename it or make a symbolic link to it if necessary) to your $PATH.\n"
|
23
|
+
"\n"
|
24
|
+
"c2x is licensed under GPLv3, the full text of which may be found at "
|
25
|
+
"https://www.gnu.org/licenses/gpl-3.0.en.html. Its binaries are redistributed here under the terms of this "
|
26
|
+
"license, and any further redistribution must also follow these terms. Its corresponding source code "
|
27
|
+
"may be downloaded from https://www.c2x.org.uk/downloads/")
|
28
|
+
|
29
|
+
|
30
|
+
# Assign this converter to the `converter` variable - this lets the psdi_data_conversion.converter module detect and
|
31
|
+
# register it, making it available for use by the command-line script, python library, and web app
|
32
|
+
converter = C2xFileConverter
|
@@ -0,0 +1,239 @@
|
|
1
|
+
"""@file psdi_data_conversion/converters/obenbabel.py
|
2
|
+
|
3
|
+
Created 2025-01-23 by Bryan Gillis.
|
4
|
+
|
5
|
+
Open Babel FileConverter
|
6
|
+
"""
|
7
|
+
|
8
|
+
from copy import deepcopy
|
9
|
+
from openbabel import openbabel
|
10
|
+
import py
|
11
|
+
|
12
|
+
from psdi_data_conversion.converters.base import FileConverter, FileConverterHelpException
|
13
|
+
from psdi_data_conversion.security import SAFE_STRING_RE, string_is_safe
|
14
|
+
|
15
|
+
CONVERTER_OB = 'Open Babel'
|
16
|
+
|
17
|
+
# Constants related to command-line and library arguments unique to this converter
|
18
|
+
L_ALLOWED_COORD_GENS = ["Gen2D", "Gen3D", "neither"]
|
19
|
+
DEFAULT_COORD_GEN = "neither"
|
20
|
+
COORD_GEN_KEY = "coordinates"
|
21
|
+
L_ALLOWED_COORD_GEN_QUALS = ["fastest", "fast", "medium", "better", "best"]
|
22
|
+
DEFAULT_COORD_GEN_QUAL = "medium"
|
23
|
+
COORD_GEN_QUAL_KEY = "coordOption"
|
24
|
+
|
25
|
+
D_DEFAULT_OB_DATA = {COORD_GEN_KEY: DEFAULT_COORD_GEN,
|
26
|
+
COORD_GEN_QUAL_KEY: DEFAULT_COORD_GEN_QUAL}
|
27
|
+
|
28
|
+
|
29
|
+
def check_string_security(s: str):
|
30
|
+
"""Checks that a string is secure and raises an exception if it isn't.
|
31
|
+
"""
|
32
|
+
if not string_is_safe(s):
|
33
|
+
raise FileConverterHelpException(f"Format option '{s}' does not pass security checks. It must pass the regex "
|
34
|
+
f"/{SAFE_STRING_RE.pattern}/.")
|
35
|
+
|
36
|
+
|
37
|
+
def get_option_and_value(s: str):
|
38
|
+
"""Splits an option into the option character and value for it, checking for security
|
39
|
+
"""
|
40
|
+
check_string_security(s)
|
41
|
+
if len(s) == 0:
|
42
|
+
return "", ""
|
43
|
+
return s[0], s[1:]
|
44
|
+
|
45
|
+
|
46
|
+
def get_coord_gen(l_opts: list[str] | None) -> dict[str, str]:
|
47
|
+
|
48
|
+
# Keyword arguments specific to OpenBabel conversion
|
49
|
+
coord_gen: str
|
50
|
+
if l_opts is None:
|
51
|
+
coord_gen = DEFAULT_COORD_GEN
|
52
|
+
else:
|
53
|
+
coord_gen = l_opts[0]
|
54
|
+
|
55
|
+
coord_gen_qual: str
|
56
|
+
if l_opts is None or len(l_opts) == 1:
|
57
|
+
coord_gen_qual = DEFAULT_COORD_GEN_QUAL
|
58
|
+
else:
|
59
|
+
coord_gen_qual = l_opts[1]
|
60
|
+
|
61
|
+
# No more than two arguments supplied to --coord-gen
|
62
|
+
if l_opts is not None and len(l_opts) > 2:
|
63
|
+
raise FileConverterHelpException("At most two arguments may be provided to --coord-gen, the mode and "
|
64
|
+
"quality, e.g. '--coord-gen Gen3D best'")
|
65
|
+
|
66
|
+
# Coordinate generation options are valid
|
67
|
+
if coord_gen not in L_ALLOWED_COORD_GENS:
|
68
|
+
raise FileConverterHelpException(f"Coordinate generation type '{coord_gen}' not recognised. Allowed "
|
69
|
+
f"types are: {L_ALLOWED_COORD_GENS}")
|
70
|
+
if coord_gen_qual not in L_ALLOWED_COORD_GEN_QUALS:
|
71
|
+
raise FileConverterHelpException(f"Coordinate generation quality '{coord_gen_qual}' not recognised. "
|
72
|
+
f"Allowed qualities are: {L_ALLOWED_COORD_GEN_QUALS}")
|
73
|
+
|
74
|
+
return {COORD_GEN_KEY: coord_gen,
|
75
|
+
COORD_GEN_QUAL_KEY: coord_gen_qual}
|
76
|
+
|
77
|
+
|
78
|
+
class OBFileConverter(FileConverter):
|
79
|
+
"""File Converter specialized to use Open Babel for conversions
|
80
|
+
"""
|
81
|
+
|
82
|
+
name = CONVERTER_OB
|
83
|
+
has_in_format_flags_or_options = True
|
84
|
+
has_out_format_flags_or_options = True
|
85
|
+
database_key_prefix = "ob"
|
86
|
+
|
87
|
+
allowed_flags = ()
|
88
|
+
allowed_options = (("--coord-gen",
|
89
|
+
{"help": "(Open Babel converter only). The mode to be used for Open Babel calculation of "
|
90
|
+
"atomic coordinates, and optionally the quality of the conversion. The mode should be one of "
|
91
|
+
"'Gen2D', 'Gen3D', or 'neither' (default 'neither'). The quality, if supplied, should be "
|
92
|
+
"one of 'fastest', 'fast', 'medium', 'better' or 'best' (default 'medium'). E.g. "
|
93
|
+
"'--coord-gen Gen2D' (quality defaults to 'medium'), '--coord-gen Gen3D best'",
|
94
|
+
"type": str,
|
95
|
+
"default": None,
|
96
|
+
"nargs": "+"},
|
97
|
+
get_coord_gen),)
|
98
|
+
|
99
|
+
def _convert(self):
|
100
|
+
|
101
|
+
self.logger.debug("Using OpenBabel's Python library to perform file conversion")
|
102
|
+
|
103
|
+
# Apply default values to the data dict
|
104
|
+
_data = deepcopy(D_DEFAULT_OB_DATA)
|
105
|
+
_data.update(self.data)
|
106
|
+
self.data = _data
|
107
|
+
|
108
|
+
try:
|
109
|
+
stdouterr_ob = py.io.StdCaptureFD(in_=False)
|
110
|
+
|
111
|
+
ob_conversion = openbabel.OBConversion()
|
112
|
+
ob_conversion.SetInAndOutFormats(self.from_format, self.to_format)
|
113
|
+
|
114
|
+
# Retrieve 'from' and 'to' option flags and arguments
|
115
|
+
from_flags = self.data.get("from_flags", "")
|
116
|
+
to_flags = self.data.get("to_flags", "")
|
117
|
+
from_arg_flags = self.data.get("from_arg_flags", "")
|
118
|
+
to_arg_flags = self.data.get("to_arg_flags", "")
|
119
|
+
from_args = self.data.get("from_args", "")
|
120
|
+
to_args = self.data.get("to_args", "")
|
121
|
+
|
122
|
+
# Add option flags and arguments as appropriate
|
123
|
+
for char in from_flags:
|
124
|
+
check_string_security(char)
|
125
|
+
ob_conversion.AddOption(char, ob_conversion.INOPTIONS)
|
126
|
+
|
127
|
+
for char in to_flags:
|
128
|
+
check_string_security(char)
|
129
|
+
ob_conversion.AddOption(char, ob_conversion.OUTOPTIONS)
|
130
|
+
|
131
|
+
self.data["read_flags_args"] = []
|
132
|
+
self.data["write_flags_args"] = []
|
133
|
+
|
134
|
+
# Check if we were provided options by the command-line script/library or the web app, and handle them
|
135
|
+
# appropriately
|
136
|
+
if "from_options" in self.data:
|
137
|
+
# From options were provided by the command-line script or library
|
138
|
+
l_from_options = self.data["from_options"].split()
|
139
|
+
for opt in l_from_options:
|
140
|
+
option, value = get_option_and_value(opt)
|
141
|
+
ob_conversion.AddOption(option, ob_conversion.INOPTIONS, value)
|
142
|
+
self.logger.debug(f"Set Open Babel read flags arguments to: {self.data['from_options']}")
|
143
|
+
# Store the options in the "read_flags_args" entry for the later logging
|
144
|
+
self.data["read_flags_args"] = l_from_options
|
145
|
+
else:
|
146
|
+
# From options were provided by the command-line script or library
|
147
|
+
for char in from_arg_flags:
|
148
|
+
index = from_args.find('£')
|
149
|
+
arg, from_args = from_args[0:index], from_args[index + 1:len(from_args)]
|
150
|
+
check_string_security(char), check_string_security(arg)
|
151
|
+
ob_conversion.AddOption(char, ob_conversion.INOPTIONS, arg)
|
152
|
+
self.data["read_flags_args"].append(char + " " + arg)
|
153
|
+
self.logger.debug(f"Set Open Babel read flags arguments to: {self.data['read_flags_args']}")
|
154
|
+
|
155
|
+
if "to_options" in self.data:
|
156
|
+
# From options were provided by the command-line script or library
|
157
|
+
l_to_options = self.data["to_options"].split()
|
158
|
+
for opt in l_to_options:
|
159
|
+
option, value = get_option_and_value(opt)
|
160
|
+
ob_conversion.AddOption(option, ob_conversion.OUTOPTIONS, value)
|
161
|
+
self.logger.debug(f"Set Open Babel write flags arguments to: {self.data['to_options']}")
|
162
|
+
# Store the options in the "write_flags_args" entry for the later logging
|
163
|
+
self.data["write_flags_args"] = l_to_options
|
164
|
+
else:
|
165
|
+
# From options were provided by the command-line script or library
|
166
|
+
for char in to_arg_flags:
|
167
|
+
index = to_args.find('£')
|
168
|
+
arg, to_args = to_args[0:index], to_args[index + 1:len(to_args)]
|
169
|
+
check_string_security(char), check_string_security(arg)
|
170
|
+
ob_conversion.AddOption(char, ob_conversion.OUTOPTIONS, arg)
|
171
|
+
self.data["write_flags_args"].append(char + " " + arg)
|
172
|
+
self.logger.debug(f"Set Open Babel write flags arguments to: {self.data['read_flags_args']}")
|
173
|
+
|
174
|
+
# Read the file to be converted
|
175
|
+
mol = openbabel.OBMol()
|
176
|
+
ob_conversion.ReadFile(mol, self.in_filename)
|
177
|
+
|
178
|
+
# Calculate atomic coordinates
|
179
|
+
if self.data[COORD_GEN_KEY] == 'neither':
|
180
|
+
self.data[COORD_GEN_QUAL_KEY] = 'N/A'
|
181
|
+
else:
|
182
|
+
# Retrieve coordinate calculation option (fastest, fast, medium, better, best)
|
183
|
+
self.option = self.data[COORD_GEN_QUAL_KEY]
|
184
|
+
|
185
|
+
gen = openbabel.OBOp.FindType(self.data[COORD_GEN_KEY])
|
186
|
+
self.logger.debug(f"Performing Open Babel {self.data[COORD_GEN_KEY]} coordinate conversion with option "
|
187
|
+
f"'{self.option}'")
|
188
|
+
gen.Do(mol, self.data[COORD_GEN_QUAL_KEY])
|
189
|
+
|
190
|
+
# Write the converted file
|
191
|
+
ob_conversion.WriteFile(mol, self.out_filename)
|
192
|
+
|
193
|
+
self.out, self.err = stdouterr_ob.reset() # Grab stdout and stderr
|
194
|
+
|
195
|
+
finally:
|
196
|
+
# Reset stdout and stderr capture
|
197
|
+
stdouterr_ob.done()
|
198
|
+
|
199
|
+
if "Open Babel Error" in self.err:
|
200
|
+
self._abort_from_err()
|
201
|
+
|
202
|
+
def _create_message(self) -> str:
|
203
|
+
"""Overload method to create a log of options passed to the converter
|
204
|
+
"""
|
205
|
+
|
206
|
+
message = ""
|
207
|
+
|
208
|
+
label_length = 19
|
209
|
+
|
210
|
+
for (label, key, multi) in (("Coord. gen.:", COORD_GEN_KEY, False),
|
211
|
+
("Coord. option:", "coord_option", False),
|
212
|
+
("Read options:", "from_flags", False),
|
213
|
+
("Write options:", "to_flags", False),
|
214
|
+
("Read opts + args:", "read_flags_args", True),
|
215
|
+
("Write opts + args:", "write_flags_args", True)):
|
216
|
+
val = self.data.get(key)
|
217
|
+
|
218
|
+
if not val:
|
219
|
+
message += f"{label:<{label_length}}none\n"
|
220
|
+
continue
|
221
|
+
|
222
|
+
if multi:
|
223
|
+
l_items = val
|
224
|
+
else:
|
225
|
+
l_items = (val,)
|
226
|
+
|
227
|
+
for i, item in enumerate(l_items):
|
228
|
+
if i == 0:
|
229
|
+
line_label = label
|
230
|
+
else:
|
231
|
+
line_label = ""
|
232
|
+
message += f"{line_label:<{label_length}}{item}\n"
|
233
|
+
|
234
|
+
return message
|
235
|
+
|
236
|
+
|
237
|
+
# Assign this converter to the `converter` variable - this lets the psdi_data_conversion.converter module detect and
|
238
|
+
# register it, making it available for use by the CLI and web app
|
239
|
+
converter = OBFileConverter
|