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.
- psdi_data_conversion/app.py +110 -10
- 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 +18 -13
- psdi_data_conversion/static/content/convertato.htm +40 -33
- psdi_data_conversion/static/content/convertc2x.htm +40 -33
- psdi_data_conversion/static/content/documentation.htm +4 -4
- psdi_data_conversion/static/content/download.htm +26 -10
- 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 +80 -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 +0 -6
- psdi_data_conversion/static/styles/psdi-common.css +10 -6
- psdi_data_conversion/templates/index.htm +10 -2
- psdi_data_conversion/testing/constants.py +6 -3
- psdi_data_conversion/testing/conversion_callbacks.py +7 -6
- psdi_data_conversion/testing/conversion_test_specs.py +333 -153
- psdi_data_conversion/testing/gui.py +366 -0
- psdi_data_conversion/testing/utils.py +108 -51
- {psdi_data_conversion-0.0.35.dist-info → psdi_data_conversion-0.0.37.dist-info}/METADATA +90 -51
- {psdi_data_conversion-0.0.35.dist-info → psdi_data_conversion-0.0.37.dist-info}/RECORD +39 -38
- {psdi_data_conversion-0.0.35.dist-info → psdi_data_conversion-0.0.37.dist-info}/WHEEL +1 -1
- {psdi_data_conversion-0.0.35.dist-info → psdi_data_conversion-0.0.37.dist-info}/entry_points.txt +1 -0
- {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
|
-
|
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,
|
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
|
-
|
230
|
-
$("#uploadButton").prop({ disabled: false });
|
266
|
+
enableConvertButton();
|
231
267
|
} else {
|
232
|
-
|
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,
|
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,
|
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 :
|
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:
|
550
|
+
display: flex;
|
550
551
|
color : var(--ifm-heading-color);
|
551
|
-
margin : 0 0 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
|
-
|
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
|
-
|
12
|
-
|
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
|
19
|
-
from psdi_data_conversion.testing.utils import ConversionTestInfo,
|
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
|
-
|
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(
|
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)
|