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.
Files changed (81) hide show
  1. psdi_data_conversion/__init__.py +11 -0
  2. psdi_data_conversion/app.py +242 -0
  3. psdi_data_conversion/bin/linux/atomsk +0 -0
  4. psdi_data_conversion/bin/linux/c2x +0 -0
  5. psdi_data_conversion/bin/mac/atomsk +0 -0
  6. psdi_data_conversion/bin/mac/c2x +0 -0
  7. psdi_data_conversion/constants.py +185 -0
  8. psdi_data_conversion/converter.py +459 -0
  9. psdi_data_conversion/converters/__init__.py +6 -0
  10. psdi_data_conversion/converters/atomsk.py +32 -0
  11. psdi_data_conversion/converters/base.py +702 -0
  12. psdi_data_conversion/converters/c2x.py +32 -0
  13. psdi_data_conversion/converters/openbabel.py +239 -0
  14. psdi_data_conversion/database.py +1064 -0
  15. psdi_data_conversion/dist.py +87 -0
  16. psdi_data_conversion/file_io.py +216 -0
  17. psdi_data_conversion/log_utility.py +241 -0
  18. psdi_data_conversion/main.py +776 -0
  19. psdi_data_conversion/scripts/atomsk.sh +32 -0
  20. psdi_data_conversion/scripts/c2x.sh +26 -0
  21. psdi_data_conversion/security.py +38 -0
  22. psdi_data_conversion/static/content/accessibility.htm +254 -0
  23. psdi_data_conversion/static/content/convert.htm +121 -0
  24. psdi_data_conversion/static/content/convertato.htm +65 -0
  25. psdi_data_conversion/static/content/convertc2x.htm +65 -0
  26. psdi_data_conversion/static/content/documentation.htm +94 -0
  27. psdi_data_conversion/static/content/feedback.htm +53 -0
  28. psdi_data_conversion/static/content/header-links.html +8 -0
  29. psdi_data_conversion/static/content/index-versions/header-links.html +8 -0
  30. psdi_data_conversion/static/content/index-versions/psdi-common-footer.html +99 -0
  31. psdi_data_conversion/static/content/index-versions/psdi-common-header.html +28 -0
  32. psdi_data_conversion/static/content/psdi-common-footer.html +99 -0
  33. psdi_data_conversion/static/content/psdi-common-header.html +28 -0
  34. psdi_data_conversion/static/content/report.htm +103 -0
  35. psdi_data_conversion/static/data/data.json +143940 -0
  36. psdi_data_conversion/static/img/colormode-toggle-dm.svg +3 -0
  37. psdi_data_conversion/static/img/colormode-toggle-lm.svg +3 -0
  38. psdi_data_conversion/static/img/psdi-icon-dark.svg +136 -0
  39. psdi_data_conversion/static/img/psdi-icon-light.svg +208 -0
  40. psdi_data_conversion/static/img/psdi-logo-darktext.png +0 -0
  41. psdi_data_conversion/static/img/psdi-logo-lighttext.png +0 -0
  42. psdi_data_conversion/static/img/social-logo-bluesky-black.svg +4 -0
  43. psdi_data_conversion/static/img/social-logo-bluesky-white.svg +4 -0
  44. psdi_data_conversion/static/img/social-logo-instagram-black.svg +1 -0
  45. psdi_data_conversion/static/img/social-logo-instagram-white.svg +1 -0
  46. psdi_data_conversion/static/img/social-logo-linkedin-black.png +0 -0
  47. psdi_data_conversion/static/img/social-logo-linkedin-white.png +0 -0
  48. psdi_data_conversion/static/img/social-logo-mastodon-black.svg +4 -0
  49. psdi_data_conversion/static/img/social-logo-mastodon-white.svg +4 -0
  50. psdi_data_conversion/static/img/social-logo-x-black.svg +3 -0
  51. psdi_data_conversion/static/img/social-logo-x-white.svg +3 -0
  52. psdi_data_conversion/static/img/social-logo-youtube-black.png +0 -0
  53. psdi_data_conversion/static/img/social-logo-youtube-white.png +0 -0
  54. psdi_data_conversion/static/img/ukri-epsr-logo-darktext.png +0 -0
  55. psdi_data_conversion/static/img/ukri-epsr-logo-lighttext.png +0 -0
  56. psdi_data_conversion/static/img/ukri-logo-darktext.png +0 -0
  57. psdi_data_conversion/static/img/ukri-logo-lighttext.png +0 -0
  58. psdi_data_conversion/static/javascript/accessibility.js +196 -0
  59. psdi_data_conversion/static/javascript/common.js +42 -0
  60. psdi_data_conversion/static/javascript/convert.js +296 -0
  61. psdi_data_conversion/static/javascript/convert_common.js +252 -0
  62. psdi_data_conversion/static/javascript/convertato.js +107 -0
  63. psdi_data_conversion/static/javascript/convertc2x.js +107 -0
  64. psdi_data_conversion/static/javascript/data.js +176 -0
  65. psdi_data_conversion/static/javascript/format.js +611 -0
  66. psdi_data_conversion/static/javascript/load_accessibility.js +89 -0
  67. psdi_data_conversion/static/javascript/psdi-common.js +177 -0
  68. psdi_data_conversion/static/javascript/report.js +381 -0
  69. psdi_data_conversion/static/styles/format.css +147 -0
  70. psdi_data_conversion/static/styles/psdi-common.css +705 -0
  71. psdi_data_conversion/templates/index.htm +114 -0
  72. psdi_data_conversion/testing/__init__.py +5 -0
  73. psdi_data_conversion/testing/constants.py +12 -0
  74. psdi_data_conversion/testing/conversion_callbacks.py +394 -0
  75. psdi_data_conversion/testing/conversion_test_specs.py +208 -0
  76. psdi_data_conversion/testing/utils.py +522 -0
  77. psdi_data_conversion-0.0.23.dist-info/METADATA +663 -0
  78. psdi_data_conversion-0.0.23.dist-info/RECORD +81 -0
  79. psdi_data_conversion-0.0.23.dist-info/WHEEL +4 -0
  80. psdi_data_conversion-0.0.23.dist-info/entry_points.txt +2 -0
  81. 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