psdi-data-conversion 0.0.35__py3-none-any.whl → 0.0.37__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 (39) hide show
  1. psdi_data_conversion/app.py +110 -10
  2. psdi_data_conversion/constants.py +2 -0
  3. psdi_data_conversion/converter.py +16 -6
  4. psdi_data_conversion/converters/atomsk.py +3 -1
  5. psdi_data_conversion/converters/base.py +99 -39
  6. psdi_data_conversion/converters/c2x.py +3 -1
  7. psdi_data_conversion/converters/openbabel.py +40 -1
  8. psdi_data_conversion/database.py +5 -0
  9. psdi_data_conversion/main.py +18 -10
  10. psdi_data_conversion/static/content/accessibility.htm +5 -5
  11. psdi_data_conversion/static/content/convert.htm +18 -13
  12. psdi_data_conversion/static/content/convertato.htm +40 -33
  13. psdi_data_conversion/static/content/convertc2x.htm +40 -33
  14. psdi_data_conversion/static/content/documentation.htm +4 -4
  15. psdi_data_conversion/static/content/download.htm +26 -10
  16. psdi_data_conversion/static/content/feedback.htm +4 -4
  17. psdi_data_conversion/static/content/index-versions/psdi-common-header.html +1 -1
  18. psdi_data_conversion/static/content/psdi-common-header.html +1 -1
  19. psdi_data_conversion/static/content/report.htm +9 -7
  20. psdi_data_conversion/static/javascript/common.js +20 -0
  21. psdi_data_conversion/static/javascript/convert.js +1 -2
  22. psdi_data_conversion/static/javascript/convert_common.js +80 -7
  23. psdi_data_conversion/static/javascript/convertato.js +1 -2
  24. psdi_data_conversion/static/javascript/convertc2x.js +1 -2
  25. psdi_data_conversion/static/javascript/format.js +12 -0
  26. psdi_data_conversion/static/javascript/report.js +6 -0
  27. psdi_data_conversion/static/styles/format.css +0 -6
  28. psdi_data_conversion/static/styles/psdi-common.css +10 -6
  29. psdi_data_conversion/templates/index.htm +10 -2
  30. psdi_data_conversion/testing/constants.py +6 -3
  31. psdi_data_conversion/testing/conversion_callbacks.py +7 -6
  32. psdi_data_conversion/testing/conversion_test_specs.py +333 -153
  33. psdi_data_conversion/testing/gui.py +366 -0
  34. psdi_data_conversion/testing/utils.py +108 -51
  35. {psdi_data_conversion-0.0.35.dist-info → psdi_data_conversion-0.0.37.dist-info}/METADATA +90 -51
  36. {psdi_data_conversion-0.0.35.dist-info → psdi_data_conversion-0.0.37.dist-info}/RECORD +39 -38
  37. {psdi_data_conversion-0.0.35.dist-info → psdi_data_conversion-0.0.37.dist-info}/WHEEL +1 -1
  38. {psdi_data_conversion-0.0.35.dist-info → psdi_data_conversion-0.0.37.dist-info}/entry_points.txt +1 -0
  39. {psdi_data_conversion-0.0.35.dist-info → psdi_data_conversion-0.0.37.dist-info}/licenses/LICENSE +0 -0
@@ -3,6 +3,8 @@
3
3
  Version 1.0, 17th December 2024
4
4
  */
5
5
 
6
+ import { disableDirtyForms, enableDirtyForms, initDirtyForms } from "./common.js";
7
+
6
8
  const SECOND = 1000; // Milliseconds
7
9
  const CONVERT_TIMEOUT = 60 * SECOND;
8
10
  const MEGABYTE = 1024 * 1024;
@@ -31,7 +33,18 @@ var token = "",
31
33
 
32
34
  export function commonConvertReady(converter) {
33
35
  token = sessionStorage.getItem("token");
34
- max_file_size = sessionStorage.getItem("max_file_size");
36
+
37
+ // Open Babel uniquely has its own maximum file size
38
+ if (converter == "Open Babel") {
39
+ max_file_size = sessionStorage.getItem("max_file_size_ob");
40
+ } else {
41
+ max_file_size = sessionStorage.getItem("max_file_size");
42
+ }
43
+
44
+ // Set the text for displaying the maximum size
45
+ if (max_file_size > 0) {
46
+ $(".max-file-size").text(" (max size " + (max_file_size / MEGABYTE).toFixed(2) + " MB)");
47
+ }
35
48
 
36
49
  in_str = sessionStorage.getItem("in_str");
37
50
  out_str = sessionStorage.getItem("out_str");
@@ -48,17 +61,25 @@ export function commonConvertReady(converter) {
48
61
  $("#heading").html("Convert from \'" + in_ext + "\' (" + in_note + ") to \'" + out_ext + "\' (" + out_note +
49
62
  ") using " + converter);
50
63
 
64
+ // Connect the buttons to events
51
65
  $("#extCheck").click(setExtCheck);
52
66
  $("#requestLog").click(setRequestLog);
67
+ $("#clearUpload").click(clearUploadedFile);
68
+
69
+ // Connect the file upload to event and limit the types it can accept
53
70
  $("#fileToUpload").change(checkFile);
71
+ limitFileType();
72
+
73
+ initDirtyForms();
54
74
 
55
- return [token, max_file_size, in_str, in_ext, out_str, out_ext];
75
+ return [token, in_str, in_ext, out_str, out_ext];
56
76
  }
57
77
 
58
78
  // Converts user-supplied file to another format and downloads the resulting file
59
79
  export function convertFile(form_data, download_fname, fname) {
60
80
 
61
81
  showSpinner();
82
+ disableConvertButton();
62
83
 
63
84
  let convertTimedOut = false;
64
85
 
@@ -70,7 +91,12 @@ export function convertFile(form_data, download_fname, fname) {
70
91
  contentType: false,
71
92
  timeout: CONVERT_TIMEOUT,
72
93
  success: async function () {
94
+
73
95
  hideSpinner();
96
+ enableConvertButton();
97
+ clearUploadedFile();
98
+ disableDirtyForms();
99
+
74
100
  if (!convertTimedOut) {
75
101
  await downloadFile(`../downloads/${download_fname}`, download_fname)
76
102
 
@@ -109,6 +135,7 @@ export function convertFile(form_data, download_fname, fname) {
109
135
  },
110
136
  error: function (xmlhttprequest, textstatus, message) {
111
137
  hideSpinner();
138
+ enableConvertButton();
112
139
  if (textstatus === "timeout") {
113
140
  convertTimedOut = true;
114
141
  alert("ERROR: Conversion attempt timed out. This may be because the conversion is too complicated, " +
@@ -149,6 +176,13 @@ export function convertFile(form_data, download_fname, fname) {
149
176
 
150
177
  function setExtCheck(event) {
151
178
  extCheck = this.checked;
179
+
180
+ // Toggle whether or not the file upload limits uploaded type based on whether or not this box is ticked
181
+ if (extCheck) {
182
+ limitFileType();
183
+ } else {
184
+ unlimitFileType();
185
+ }
152
186
  }
153
187
 
154
188
  export function getExtCheck() {
@@ -195,6 +229,9 @@ export function isArchiveExt(ext) {
195
229
  // Check that the file meets requirements for upload
196
230
  function checkFile(event) {
197
231
 
232
+ // Enable dirty form checking whenever a file is uploaded
233
+ enableDirtyForms();
234
+
198
235
  let allGood = true;
199
236
  let file = this.files[0];
200
237
  let message = "";
@@ -226,15 +263,52 @@ function checkFile(event) {
226
263
  }
227
264
 
228
265
  if (allGood) {
229
- $("#uploadButton").css({ "background-color": "var(--ifm-color-primary)", "color": "var(--ifm-hero-text-color)" });
230
- $("#uploadButton").prop({ disabled: false });
266
+ enableConvertButton();
231
267
  } else {
232
- $("#uploadButton").css({ "background-color": "var(--psdi-bg-color-secondary)", "color": "gray" });
233
- $("#uploadButton").prop({ disabled: true });
268
+ disableConvertButton();
234
269
  alert(message);
235
270
  }
236
271
  }
237
272
 
273
+ /**
274
+ * Allow the file upload to only accept the expected type of file
275
+ */
276
+ function limitFileType() {
277
+ $("#fileToUpload")[0].accept = "." + in_ext;
278
+ }
279
+
280
+ /**
281
+ * Allow the file upload to accept any type of file
282
+ */
283
+ function unlimitFileType() {
284
+ $("#fileToUpload")[0].accept = "*";
285
+ }
286
+
287
+ /**
288
+ * Clear any uploaded file
289
+ */
290
+ function clearUploadedFile() {
291
+ $("#fileToUpload").val('');
292
+ disableConvertButton();
293
+ }
294
+
295
+ /**
296
+ * Enable the "Convert" button
297
+ */
298
+ function enableConvertButton() {
299
+ $("#uploadButton").css({ "background-color": "var(--ifm-color-primary)", "color": "var(--ifm-hero-text-color)" });
300
+ $("#uploadButton").prop({ disabled: false });
301
+ }
302
+
303
+
304
+ /**
305
+ * Disable the "Convert" button
306
+ */
307
+ function disableConvertButton() {
308
+ $("#uploadButton").css({ "background-color": "var(--psdi-bg-color-secondary)", "color": "gray" });
309
+ $("#uploadButton").prop({ disabled: true });
310
+ }
311
+
238
312
  /**
239
313
  * Start a download of a file
240
314
  *
@@ -256,7 +330,6 @@ async function downloadFile(path, filename) {
256
330
  });
257
331
  }
258
332
 
259
-
260
333
  /**
261
334
  * Show the loading spinner
262
335
  */
@@ -8,14 +8,13 @@
8
8
  import { commonConvertReady, convertFile, getExtCheck, splitArchiveExt, isArchiveExt } from "./convert_common.js"
9
9
 
10
10
  var token = "",
11
- max_file_size = 0,
12
11
  in_ext = "",
13
12
  out_ext = "",
14
13
  in_str = "",
15
14
  out_str = "";
16
15
 
17
16
  $(document).ready(function () {
18
- [token, max_file_size, in_str, in_ext, out_str, out_ext] = commonConvertReady("Atomsk");
17
+ [token, in_str, in_ext, out_str, out_ext] = commonConvertReady("Atomsk");
19
18
  $("#uploadButton").click(submitFile);
20
19
  });
21
20
 
@@ -8,14 +8,13 @@
8
8
  import { commonConvertReady, convertFile, getExtCheck, splitArchiveExt, isArchiveExt } from "./convert_common.js"
9
9
 
10
10
  var token = "",
11
- max_file_size = 0,
12
11
  in_ext = "",
13
12
  out_ext = "",
14
13
  in_str = "",
15
14
  out_str = "";
16
15
 
17
16
  $(document).ready(function () {
18
- [token, max_file_size, in_str, in_ext, out_str, out_ext] = commonConvertReady("c2x");
17
+ [token, in_str, in_ext, out_str, out_ext] = commonConvertReady("c2x");
19
18
  $("#uploadButton").click(submitFile);
20
19
  });
21
20
 
@@ -5,6 +5,7 @@
5
5
  This is the JavaScript which makes the Format and Converter Selection gui work.
6
6
  */
7
7
 
8
+ import { disableDirtyForms, cleanDirtyForms, initDirtyForms, loadServiceMode, loadProductionMode } from "./common.js";
8
9
  import {
9
10
  getInputFormats, getOutputFormats, getOutputFormatsForInputFormat,
10
11
  getInputFormatsForOutputFormat, getConverters, getConverterByName, getLevelChemInfo
@@ -29,12 +30,16 @@ $(document).ready(function () {
29
30
 
30
31
  sessionStorage.setItem("token", token);
31
32
  sessionStorage.setItem("max_file_size", max_file_size);
33
+ sessionStorage.setItem("max_file_size_ob", max_file_size_ob);
32
34
  sessionStorage.setItem("service_mode", service_mode);
33
35
  sessionStorage.setItem("production_mode", production_mode);
34
36
  sessionStorage.setItem("in_str", "");
35
37
  sessionStorage.setItem("out_str", "");
36
38
  sessionStorage.setItem("success", "");
37
39
 
40
+ loadServiceMode();
41
+ loadProductionMode();
42
+
38
43
  $("#fromList").click(populateConversionSuccess);
39
44
  $("#toList").click(populateConversionSuccess);
40
45
  $("#searchTo").keyup(filterOptions);
@@ -43,6 +48,8 @@ $(document).ready(function () {
43
48
  $("#success").click(showConverterDetails);
44
49
  $("#resetButton").click(resetAll);
45
50
  $("#showButton").click(showQualityDetails);
51
+
52
+ initDirtyForms();
46
53
  });
47
54
 
48
55
  /**
@@ -512,6 +519,9 @@ function getFormat(str) {
512
519
 
513
520
  // Stores chosen formats and switches to the Conversion page
514
521
  function goToConversionPage(event) {
522
+
523
+ disableDirtyForms();
524
+
515
525
  var path = ``;
516
526
 
517
527
  if ($("#name").html() == "Open Babel") {
@@ -604,4 +614,6 @@ function resetAll() {
604
614
 
605
615
  // Populates the "Convert to" selection list
606
616
  getOutputFormats().then(formats => populateList(formats, "to"));
617
+
618
+ cleanDirtyForms();
607
619
  }
@@ -5,6 +5,7 @@
5
5
  This is the JavaScript which makes the report.htm gui work.
6
6
  */
7
7
 
8
+ import { disableDirtyForms, initDirtyForms } from "./common.js";
8
9
  import { getAllFormats, getConverters } from "./data.js";
9
10
 
10
11
  var token = "",
@@ -53,6 +54,8 @@ $(document).ready(function () {
53
54
  $("#resetButton").click(resetAll);
54
55
  $("#resetButton2").click(resetAll);
55
56
  $("#reportButton").click(submitUserInput);
57
+
58
+ initDirtyForms();
56
59
  });
57
60
 
58
61
  // Included in this file for convenience. When the 'Report' button is clicked, a user's missing conversion report
@@ -131,6 +134,7 @@ function hideConverterDetails() {
131
134
 
132
135
  // Submits user input
133
136
  function submitUserInput() {
137
+
134
138
  const from = $("#searchFrom").val(),
135
139
  to = $("#searchTo").val();
136
140
 
@@ -205,6 +209,8 @@ function hideOffer() { }
205
209
 
206
210
  // Submit feedback
207
211
  function submitFeedback(data) {
212
+ disableDirtyForms();
213
+
208
214
  $.post(`/feedback/`, {
209
215
  'token': token,
210
216
  'data': JSON.stringify(data)
@@ -146,12 +146,6 @@ select#dark-background option {
146
146
  display: none;
147
147
  }
148
148
 
149
- .footer__hline hr {
150
- border-top-style : none;
151
- border-bottom-style: inset;
152
- border-bottom-width: 1px;
153
- }
154
-
155
149
  /* Convert button and loading spinner */
156
150
  .convert-button-and-spinner {
157
151
  display : flex;
@@ -228,7 +228,7 @@ body {
228
228
  top : 0;
229
229
  left : 0;
230
230
  background: var(--ifm-background-color);
231
- z-index : 9999;
231
+ z-index : 99999;
232
232
  }
233
233
 
234
234
  select,
@@ -412,7 +412,8 @@ h6 {
412
412
  }
413
413
 
414
414
  p {
415
- margin: 0 0 1.25rem 0;
415
+ margin : 0 0 1.25rem 0;
416
+ padding-bottom: 0;
416
417
  }
417
418
 
418
419
  .text--center {
@@ -546,9 +547,9 @@ a svg {
546
547
  }
547
548
 
548
549
  .navbar__title h5 {
549
- display: unset;
550
+ display: flex;
550
551
  color : var(--ifm-heading-color);
551
- margin : 0 0 0.5rem 0.5rem;
552
+ margin : 0 0 0.5rem 1.5rem;
552
553
  }
553
554
 
554
555
  .navbar__logo img {
@@ -640,8 +641,11 @@ a svg {
640
641
  }
641
642
 
642
643
  .footer__hline hr {
643
- margin: 0.5rem 0;
644
- height: 2px;
644
+ margin : 0.5rem 0;
645
+ height : 2px;
646
+ border-top-style : none;
647
+ border-bottom-style: inset;
648
+ border-bottom-width: 1px;
645
649
  }
646
650
 
647
651
  .max-width-box .footer__col {
@@ -18,10 +18,13 @@
18
18
 
19
19
  <script src="https://code.jquery.com/jquery-3.7.1.js" integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4="
20
20
  crossorigin="anonymous"></script>
21
+ <script src="https://cdn.jsdelivr.net/jquery.dirtyforms/2.0.0/jquery.dirtyforms.min.js"></script>
22
+
21
23
  {% for item in data %}
22
24
  <script>
23
25
  const token = "{{item.token}}";
24
26
  const max_file_size = "{{item.max_file_size}}";
27
+ const max_file_size_ob = "{{item.max_file_size_ob}}";
25
28
  const service_mode = "{{item.service_mode}}";
26
29
  const production_mode = "{{item.production_mode}}";
27
30
  document.documentElement.setAttribute("service-mode", service_mode);
@@ -52,8 +55,13 @@
52
55
  </div>
53
56
  </div>
54
57
 
55
- <form name="gui">
58
+ <form name="gui" class="gui">
56
59
  <div class="max-width-box">
60
+ <p>Your one-stop shop for file format conversion, PSDI's Data Conversion Service enables users to seamlessly
61
+ convert between different file formats and assess the quality of proposed conversions. This is the web version
62
+ of our service. To find out more about Data Conversion and our other tools, click
63
+ <a href="https://resources.psdi.ac.uk/resource-themes/7bcc430a-fdc9-413e-bf32-bf163236430b"
64
+ id="more" target="_blank">here.</a></p>
57
65
  <p>Select 'from' and 'to' file formats in the 'Convert from/to' boxes, in either order. Typing where indicated
58
66
  filters the options (case insensitive); for example, typing 'can' or 'NON' reduces the number of options to one:
59
67
  'can: Canonical SMILES.' If you change your mind about a conversion, it is advisable to click on the 'Reset'
@@ -115,4 +123,4 @@
115
123
 
116
124
  </body>
117
125
 
118
- </html>
126
+ </html>
@@ -6,7 +6,10 @@ Constants related to unit testing
6
6
 
7
7
  import os
8
8
 
9
- TEST_DATA_LOC = os.path.abspath("./test_data")
9
+ # Locations relative to the root directory of the project - to ensure the files are found, tests should chdir to this
10
+ # directory before searching for files
10
11
 
11
- INPUT_TEST_DATA_LOC = TEST_DATA_LOC
12
- OUTPUT_TEST_DATA_LOC = os.path.join(TEST_DATA_LOC, "output")
12
+ TEST_DATA_LOC_IN_PROJECT = "./test_data"
13
+
14
+ INPUT_TEST_DATA_LOC_IN_PROJECT = TEST_DATA_LOC_IN_PROJECT
15
+ OUTPUT_TEST_DATA_LOC_IN_PROJECT = os.path.join(TEST_DATA_LOC_IN_PROJECT, "output")
@@ -15,8 +15,8 @@ from tempfile import TemporaryDirectory
15
15
  from psdi_data_conversion.constants import DATETIME_RE_RAW
16
16
  from psdi_data_conversion.file_io import unpack_zip_or_tar
17
17
  from psdi_data_conversion.log_utility import string_with_placeholders_matches
18
- from psdi_data_conversion.testing.constants import OUTPUT_TEST_DATA_LOC
19
- from psdi_data_conversion.testing.utils import ConversionTestInfo, LibraryConversionTestInfo, check_file_match
18
+ from psdi_data_conversion.testing.constants import OUTPUT_TEST_DATA_LOC_IN_PROJECT
19
+ from psdi_data_conversion.testing.utils import ConversionTestInfo, check_file_match
20
20
 
21
21
 
22
22
  class MultiCallback:
@@ -326,7 +326,7 @@ class CheckException:
326
326
  """Callable class which checks an exception raised for its type, status code, and message. Tests will only be
327
327
  run on tests with the python library, as that's the only route that provides exceptions."""
328
328
 
329
- ex_type: type[Exception]
329
+ ex_type: type[Exception] | None = None
330
330
  """The expected type of the raised exception (subclasses of it will also be allowed)"""
331
331
 
332
332
  ex_message: str | None = None
@@ -339,7 +339,8 @@ class CheckException:
339
339
  def __call__(self, test_info: ConversionTestInfo) -> str:
340
340
  """Perform the check on the exception"""
341
341
 
342
- if not isinstance(test_info, LibraryConversionTestInfo):
342
+ # Skip check on CLA, since this won't catch any exceptions
343
+ if test_info.run_type == "cla":
343
344
  return ""
344
345
 
345
346
  # Confirm that an exception was indeed raised
@@ -350,7 +351,7 @@ class CheckException:
350
351
  l_errors: list[str] = []
351
352
 
352
353
  # Check the exception type
353
- if not issubclass(exc_info.type, self.ex_type):
354
+ if self.ex_type and not issubclass(exc_info.type, self.ex_type):
354
355
  l_errors.append(f"ERROR: Raised exception is of type '{exc_info.type}', but expected '{self.ex_type}'")
355
356
 
356
357
  exc = exc_info.value
@@ -389,6 +390,6 @@ class MatchOutputFile:
389
390
  def __call__(self, test_info: ConversionTestInfo):
390
391
  """Run the check comparing the two files"""
391
392
 
392
- qualified_ex_output_filename = os.path.join(OUTPUT_TEST_DATA_LOC, self.ex_output_filename)
393
+ qualified_ex_output_filename = os.path.join(OUTPUT_TEST_DATA_LOC_IN_PROJECT, self.ex_output_filename)
393
394
 
394
395
  return check_file_match(test_info.qualified_out_filename, qualified_ex_output_filename)