psdi-data-conversion 0.1.6__tar.gz → 0.2.0__tar.gz

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 (155) hide show
  1. psdi_data_conversion-0.2.0/CHANGELOG.md +70 -0
  2. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/CONTRIBUTING.md +76 -14
  3. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/PKG-INFO +9 -2
  4. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/README.md +3 -1
  5. psdi_data_conversion-0.2.0/psdi_data_conversion/app.py +77 -0
  6. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/constants.py +11 -7
  7. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/converter.py +41 -28
  8. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/converters/base.py +18 -13
  9. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/database.py +284 -88
  10. psdi_data_conversion-0.2.0/psdi_data_conversion/gui/__init__.py +5 -0
  11. psdi_data_conversion-0.2.0/psdi_data_conversion/gui/accessibility.py +51 -0
  12. psdi_data_conversion-0.2.0/psdi_data_conversion/gui/env.py +239 -0
  13. psdi_data_conversion-0.2.0/psdi_data_conversion/gui/get.py +53 -0
  14. psdi_data_conversion-0.2.0/psdi_data_conversion/gui/post.py +176 -0
  15. psdi_data_conversion-0.2.0/psdi_data_conversion/gui/setup.py +102 -0
  16. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/main.py +70 -13
  17. psdi_data_conversion-0.2.0/psdi_data_conversion/static/content/convert.htm +172 -0
  18. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/content/convertato.htm +36 -26
  19. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/content/convertc2x.htm +39 -26
  20. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/content/download.htm +5 -5
  21. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/content/feedback.htm +2 -2
  22. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/content/header-links.html +3 -3
  23. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/content/index-versions/header-links.html +3 -3
  24. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/content/index-versions/psdi-common-header.html +1 -1
  25. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/content/psdi-common-header.html +1 -1
  26. psdi_data_conversion-0.2.0/psdi_data_conversion/static/javascript/accessibility.js +223 -0
  27. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/javascript/data.js +1 -3
  28. psdi_data_conversion-0.2.0/psdi_data_conversion/static/javascript/load_accessibility.js +106 -0
  29. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/styles/format.css +74 -20
  30. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/styles/psdi-common.css +5 -0
  31. psdi_data_conversion-0.2.0/psdi_data_conversion/templates/accessibility.htm +274 -0
  32. {psdi_data_conversion-0.1.6/psdi_data_conversion/static/content → psdi_data_conversion-0.2.0/psdi_data_conversion/templates}/documentation.htm +25 -5
  33. psdi_data_conversion-0.2.0/psdi_data_conversion/templates/index.htm +151 -0
  34. {psdi_data_conversion-0.1.6/psdi_data_conversion/static/content → psdi_data_conversion-0.2.0/psdi_data_conversion/templates}/report.htm +28 -10
  35. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/testing/conversion_test_specs.py +26 -6
  36. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/testing/utils.py +6 -6
  37. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/pyproject.toml +12 -4
  38. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/tests/gui/gui_test.py +8 -3
  39. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/tests/python/cli_test.py +48 -9
  40. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/tests/python/database_test.py +35 -4
  41. psdi_data_conversion-0.1.6/CHANGELOG.md +0 -20
  42. psdi_data_conversion-0.1.6/psdi_data_conversion/app.py +0 -470
  43. psdi_data_conversion-0.1.6/psdi_data_conversion/static/content/accessibility.htm +0 -255
  44. psdi_data_conversion-0.1.6/psdi_data_conversion/static/content/convert.htm +0 -141
  45. psdi_data_conversion-0.1.6/psdi_data_conversion/static/javascript/accessibility.js +0 -196
  46. psdi_data_conversion-0.1.6/psdi_data_conversion/static/javascript/load_accessibility.js +0 -89
  47. psdi_data_conversion-0.1.6/psdi_data_conversion/templates/index.htm +0 -134
  48. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/.gitignore +0 -0
  49. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/LICENSE +0 -0
  50. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/__init__.py +0 -0
  51. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/bin/LICENSE_ATOMSK +0 -0
  52. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/bin/LICENSE_C2X +0 -0
  53. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/bin/linux/atomsk +0 -0
  54. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/bin/linux/c2x +0 -0
  55. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/bin/mac/atomsk +0 -0
  56. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/bin/mac/c2x +0 -0
  57. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/converters/__init__.py +0 -0
  58. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/converters/atomsk.py +0 -0
  59. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/converters/c2x.py +0 -0
  60. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/converters/openbabel.py +0 -0
  61. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/dist.py +0 -0
  62. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/file_io.py +0 -0
  63. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/log_utility.py +0 -0
  64. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/scripts/atomsk.sh +0 -0
  65. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/scripts/c2x.sh +0 -0
  66. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/security.py +0 -0
  67. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/content/index-versions/psdi-common-footer.html +0 -0
  68. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/content/psdi-common-footer.html +0 -0
  69. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/data/data.json +0 -0
  70. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/colormode-toggle-dm.svg +0 -0
  71. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/colormode-toggle-lm.svg +0 -0
  72. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/psdi-icon-dark.svg +0 -0
  73. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/psdi-icon-light.svg +0 -0
  74. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/psdi-logo-darktext-simple.png +0 -0
  75. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/psdi-logo-darktext.png +0 -0
  76. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/psdi-logo-lighttext-simple.png +0 -0
  77. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/psdi-logo-lighttext.png +0 -0
  78. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-bluesky-black.svg +0 -0
  79. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-bluesky-white.svg +0 -0
  80. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-instagram-black.svg +0 -0
  81. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-instagram-white.svg +0 -0
  82. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-linkedin-black.png +0 -0
  83. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-linkedin-white.png +0 -0
  84. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-mastodon-black.svg +0 -0
  85. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-mastodon-white.svg +0 -0
  86. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-x-black.svg +0 -0
  87. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-x-white.svg +0 -0
  88. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-youtube-black.png +0 -0
  89. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/social-logo-youtube-white.png +0 -0
  90. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/ukri-epsr-logo-darktext.png +0 -0
  91. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/ukri-epsr-logo-lighttext.png +0 -0
  92. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/ukri-logo-darktext.png +0 -0
  93. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/img/ukri-logo-lighttext.png +0 -0
  94. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/javascript/common.js +0 -0
  95. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/javascript/convert.js +0 -0
  96. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/javascript/convert_common.js +0 -0
  97. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/javascript/convertato.js +0 -0
  98. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/javascript/convertc2x.js +0 -0
  99. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/javascript/format.js +0 -0
  100. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/javascript/psdi-common.js +0 -0
  101. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/static/javascript/report.js +0 -0
  102. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/testing/__init__.py +0 -0
  103. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/testing/constants.py +0 -0
  104. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/testing/conversion_callbacks.py +0 -0
  105. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/testing/gui.py +0 -0
  106. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/psdi_data_conversion/utils.py +0 -0
  107. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/scripts/setup_bin.py +0 -0
  108. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/1ARJ.mmcif +0 -0
  109. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/1NE6.mmcif +0 -0
  110. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/5a9z-assembly1.cif +0 -0
  111. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/Fapatite.ins +0 -0
  112. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/aceticacid.mol +0 -0
  113. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/benzyne.molden +0 -0
  114. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/caffeine-smi.tar +0 -0
  115. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/caffeine-smi.tar.gz +0 -0
  116. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/caffeine-smi.zip +0 -0
  117. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/caffeine.inchi +0 -0
  118. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/ch3cl-esp.cub +0 -0
  119. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/cyclopropane_err.mol +0 -0
  120. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/ethanol.xyz +0 -0
  121. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/fullRhinovirus.pdb +0 -0
  122. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/hemoglobin.pdb +0 -0
  123. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/nacl.cif +0 -0
  124. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/nacl.mol +0 -0
  125. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/aceticacid.log.txt +0 -0
  126. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/aceticacid.mol2 +0 -0
  127. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/caffeine-2D-fastest.xyz +0 -0
  128. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/caffeine-3D-best.xyz +0 -0
  129. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/caffeine.smi +0 -0
  130. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/caffeine.xyz +0 -0
  131. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/caffeine_a_in.smi +0 -0
  132. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/caffeine_a_in_kx_f4_l5_out.smi +0 -0
  133. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/caffeine_a_in_kx_f4_out.smi +0 -0
  134. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/caffeine_a_in_kx_out.smi +0 -0
  135. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/caffeine_a_in_x_out.smi +0 -0
  136. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/hemoglobin_Atomsk.xyz +0 -0
  137. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/hemoglobin_c2x.xyz +0 -0
  138. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/nacl.log +0 -0
  139. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/nacl.mol +0 -0
  140. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/quartz_OB.cif +0 -0
  141. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/quartz_OB.log.txt +0 -0
  142. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/quartz_atomsk.cif +0 -0
  143. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/quartz_atomsk.log.txt +0 -0
  144. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/standard_test.inchi +0 -0
  145. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/xyz_files-mol.zip +0 -0
  146. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/output/xyz_files.log.txt +0 -0
  147. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/periodic_dmol3.outmol +0 -0
  148. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/quartz.xyz +0 -0
  149. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/quartz_err.xyz +0 -0
  150. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/test_data/standard_test.cdxml +0 -0
  151. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/tests/python/converter_test.py +0 -0
  152. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/tests/python/dist_test.py +0 -0
  153. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/tests/python/file_io_test.py +0 -0
  154. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/tests/python/logging_test.py +0 -0
  155. {psdi_data_conversion-0.1.6 → psdi_data_conversion-0.2.0}/tests/python/security_test.py +0 -0
@@ -0,0 +1,70 @@
1
+ # Changelog for PSDI Data Conversion
2
+
3
+ ## v0.2.0
4
+
5
+ ### New and Changed Functionality
6
+
7
+ - Changed the keyword arguments `upload_dir` and `download_dir` to `input_dir` and `output_dir` respectively
8
+ - Formats can now be specified case-insensitively
9
+ - When requesting details on a format through the command-line interface, details will be provided on which molecular properties it supports (e.g. whether or not it supports connections information)
10
+ - Added function `database.get_conversion_pathway` which can be used to get possible conversion routes between formats a direct conversion isn't possible with any converter
11
+ - When requesting details on two formats through the command-line interface and a direct conversion between them is not possible, a possible chain conversion will now be recommended
12
+
13
+ ### Bugfixes
14
+
15
+ - Fixed bug where the `input_dir` keyword argument for `run_converter` was being ignored
16
+ - Fixed bug where the local-mode-only text was incorrectly appearing on the report page in service mode
17
+
18
+ ### Testing Changes
19
+
20
+ - Excluded GUI modules from the calculating unit test coverage which can't be measured by the tool
21
+ - Added automated test that the production deployment is working on a schedule and after deploying to it
22
+
23
+ ### Documentation Changes
24
+
25
+ - The Documentation page of the GUI now shows the mode that's being run, the most recent tag, and the SHA of the most recent commit (if this isn't the latest tagged commit)
26
+ - Updated release procedure and checklist in `CONTRIBUTING.md` to reflect current procedure
27
+
28
+ ### Formatting and Refactoring Changes
29
+
30
+ - Changed Documentation and Accessibility pages of the GUI to work as Flask templates
31
+ - Cleaned up Flask files to not be all in one module
32
+ - Changed the database functionality to store possible conversions as a graph instead of a table
33
+ - Dockerfile now builds from `pyproject.toml`, with the now-unused `requirements.txt` removed
34
+
35
+ ### Stylistic Changes
36
+
37
+ - Reformatted pages of the GUI/web app to use a two panel display, with instructions for components in boxes alongside them
38
+
39
+ ## v0.1.7
40
+
41
+ ### New and Changed Functionality
42
+
43
+ - Version, SHA, and service/prod modes now always shown in new About section on the Documentation page
44
+
45
+ ### Documentation Changes
46
+
47
+ - Added information about deployment to CONTRIBUTING.md
48
+
49
+ ### Bugfixes
50
+
51
+ - Environmental variable indicating dev or production mode should now be properly set for the deployed service
52
+
53
+ ## v0.1.6
54
+
55
+ ### New and Changed Functionality
56
+
57
+ - SHA banner at the bottom of home page now preferentially shows the version, only showing the SHA if the current version doesn't match the last tag
58
+
59
+ ### Bugfixes
60
+
61
+ - Fixed bug which was blocking deployment to production
62
+
63
+ ## v0.1.0
64
+
65
+ Initial public release. Features included:
66
+
67
+ - Online server functionality
68
+ - Locally-hosted server
69
+ - Command-line interface
70
+ - Python library
@@ -15,13 +15,13 @@ This project uses a version of [GitLab Flow](https://about.gitlab.com/topics/ver
15
15
 
16
16
  The following tasks should be completed before merging a release candidate branch to `release`:
17
17
 
18
- - Detemine the target version based on the changes made:
18
+ - Determine the target version based on the changes made:
19
19
 
20
20
  - If any breaking changes have been made (after version 1.0.0), the version will advance to the next major version - `X.Y.Z` to `(X+1).0.0`
21
21
  - Otherwise, if any features are added, or any breaking changes are made before version 1.0.0, the version will advance to the next minor version - `X.Y.Z` to `X.(Y+1).0`.
22
22
  - Otherwise, the version will advance to the next bugfix version - `X.Y.Z` to `X.Y.(Z+1)`.
23
23
 
24
- - Create a release candidate branch with the name `rc-<target-version>` (e.g. `rc-1.2.3`). This should trigger an automated workflow to create a Pull Request from this branch to`release`. You may wish to edit the PR's name and/or description.
24
+ - Create a release candidate branch with the name `rc-<target-version>` (e.g. `rc-1.2.3`), branched off of `main`. This should trigger an automated workflow to create a Pull Request from this branch to `release`. You may wish to edit the PR's name and/or description.
25
25
 
26
26
  - Tagging of the release is handled by an automated workflow which determines the new version based on the previous version and the commit history, looking for any commits which indicate a feature addition or breaking change using [Angular convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular#commit-message-format). Since we don't practice this regularly, you'll need to make a commit with this style to indicate any feature additions or breaking changes (this can be done when updating the version in the next step):
27
27
 
@@ -53,7 +53,7 @@ The following tasks should be completed before merging a release candidate branc
53
53
 
54
54
  - Ensure that all automated tests and checks pass - these should be run automatically on the PR opened above
55
55
 
56
- - Manually test the local web interface
56
+ - Manually test the web interface. At this stage, it should be deployed to dev at https://data-conversion-dev.psdi.ac.uk/ (requires VPN to access), and it can be run locally as well
57
57
 
58
58
  - If there have been any changes to the Python backend, run a test that a file can be converted successfully and produces a proper log
59
59
  - If there have been any changes to the web frontend, check the appearance of the site to ensure that it looks as desired. Test the Accessibility page to ensure that changes there work properly, are saved when requested and apply to other pages
@@ -65,7 +65,10 @@ If any of these tasks fail and require changes, make the needed changes and then
65
65
  Then, follow the following steps to make the release:
66
66
 
67
67
  1. Merge the pull request to `release`. The release candidate branch can be safely deleted. This should trigger an automated pipeline to tag, publish, and deploy and the new code.
68
- 2. Merge `release` into `main` via PR (obviously don't delete `release` - if it even gives you the option to, something has gone wrong in the project rulesets, so report this).
68
+ 2. After the above pipeline finishes, confirm that the changes are shown live on the staging site at https://data-conversion-staging.psdi.ac.uk/ by checking the version shown at the bottom of the Documentation page. If necessary, double-check that nothing has broken due to the slight changes in appearance between the dev and staging sites
69
+ 3. Manually trigger the `CI - Deploy to production cluster` workflow on the `release` branch to deploy the site from the staging to release environment, which will make the changes visible to users
70
+ 4. After completion of the workflow, confirm that the changes are live on the production site at https://data-conversion.psdi.ac.uk/ by checking the version shown at the bottom of the Documentation page
71
+ 5. Merge `release` into `main` via PR (obviously don't delete `release` - if it even gives you the option to, something has gone wrong in the project rulesets, so report this).
69
72
 
70
73
  ## Changelog
71
74
 
@@ -193,13 +196,13 @@ class MyFileConverter(FileConverter):
193
196
  converter = MyFileConverter
194
197
  ```
195
198
 
196
- That's all you need to do! The `psdi_data_conversion.converter` module parses all modules in the `converters` package to find converters, so if you've done everything correctly, it will find the new converter and register it for you. You can test that it is properly registered by using the CLA to run:
199
+ That's all you need to do! The `psdi_data_conversion.converter` module parses all modules in the `converters` package to find converters, so if you've done everything correctly, it will find the new converter and register it for you. You can test that it is properly registered by using the CLI to run:
197
200
 
198
201
  ```bash
199
202
  psdi-data-convert -l
200
203
  ```
201
204
 
202
- Your new converter should appear, or else you will probably see an error message which will detail an exception raised when trying to register it. Note that until the converter's information is added to the database (the file `psdi_data_conversion/static/data/data.json`), the CLA will show that it is unable to perform any conversions, and it will fail on any conversion (believing it to be impossible) unless you provide the `--nc/--no-check` command-line flag.
205
+ Your new converter should appear, or else you will probably see an error message which will detail an exception raised when trying to register it. Note that until the converter's information is added to the database (the file `psdi_data_conversion/static/data/data.json`), the CLI will show that it is unable to perform any conversions, and it will fail on any conversion (believing it to be impossible) unless you provide the `--nc/--no-check` command-line flag.
203
206
 
204
207
  For file converters which can be run with a call to a script, this can be streamlined even further by taking advantage of the `ScriptFileConverter` subclass. With this, the converter's subclass can be defined even more succinctly:
205
208
 
@@ -273,7 +276,7 @@ pip install --editable .[test]
273
276
  pytest
274
277
  ```
275
278
 
276
- This installs the project in a virtual environment in "editable" mode (which means the source files will be used from where they are rather than being copied, so any changes to them will be directly reflected in tests and uses of the CLA) and then calls `pytest` to run the unit tests in the project. `pytest` will automatically pick up any extra tests you add and run them as well.
279
+ This installs the project in a virtual environment in "editable" mode (which means the source files will be used from where they are rather than being copied, so any changes to them will be directly reflected in tests and uses of the CLI) and then calls `pytest` to run the unit tests in the project. `pytest` will automatically pick up any extra tests you add and run them as well.
277
280
 
278
281
  #### Web App Integration
279
282
 
@@ -320,13 +323,13 @@ This project uses various GitHub workflows to perform Continuous Integration tas
320
323
  - Testing and scanning (triggered by pushes to `main`, `release`, `feature*`, and `rc*` branches)
321
324
  - Periodic checks for updates to common assets
322
325
  - Automatic creation of pull requests for `feature*` and `rc*` branches
323
- - Automatic tagging, publishing, and deployment of the `release` branch
326
+ - Automatic tagging, publishing, and deployment of the `main` and `release` branches to the development, staging and production environments
324
327
 
325
- See the commons within the files for further details.
328
+ See the comments within the files for further details. See also the [section on deployment](#deployment) for details specific to deployment tasks.
326
329
 
327
330
  ## Publishing
328
331
 
329
- The Python library, CLA, and local GUI are published as a Python package via PyPI. This section describes how the package is set up and how it's published.
332
+ The Python library, CLI, and local GUI are published as a Python package via PyPI. This section describes how the package is set up and how it's published.
330
333
 
331
334
  ### Package Setup
332
335
 
@@ -338,7 +341,7 @@ The version of the package is set to be determined from the version control syst
338
341
 
339
342
  ### Initial Publication
340
343
 
341
- This section details the proceduce for the initial publication of this package - now that this is complete, this section is left in for reference in case of future need.
344
+ This section details the procedure for the initial publication of this package - now that this is complete, this section is left in for reference in case of future need.
342
345
 
343
346
  First, it's necessary to install a couple required packages in order to build a Python package: `build` to build it and `twine` to upload it. These can be installed with pip via:
344
347
 
@@ -376,8 +379,67 @@ The management page can also be used to add or remove collaborators through the
376
379
 
377
380
  ## Deployment
378
381
 
379
- Deployment is handled by the `job-deploy-k8s.yml` reusable workflow, which is triggered from `ci-main.yml` to deploy to dev on each push to `main` and `ci-release.yml` to deploy to staging on each push to `release`. When a push to production is desired, it must be triggered manually by calling the `ci-deploy-production.yml` workflow on the `release` branch (it's set up to fail if run on any other branch).
380
-
381
- TODO - Describe details of setup
382
+ The `ci-main.yml`, `ci-release.yml` and `ci-deploy-production.yml` files in the `.github/workflows` directory house workflows which deploy
383
+ the data conversion service to [Kubernetes](https://kubernetes.io/) clusters hosted in STFC. There are three clusters, each of which correspond
384
+ to a different deployment _environment_ for the data conversion service. The three environments are `development`, `staging` and `production`.
385
+ Deployment to `development`, `staging` and `production` is done from either the `main` or `release` branch. The table below indicates which
386
+ branch deploys to which environment. The table also shows, for each environment:
387
+
388
+ - the URL on which the service is exposed once it is successfully deployed
389
+ - the accessibility of the service. Depending on the environment the service is either accessible to the _public_ at the specified URL,
390
+ or accessible only to IP addresses within the _STFC and University of Southampton subnets_
391
+ - the trigger used to invoke the workflow which deploys the service from the source branch. Deployment is either _automatic_
392
+ upon a commit to the source branch which passes the unit-tests job; or results from a _manual_ invocation of a workflow by a
393
+ developer.
394
+
395
+ | Environment | URL | Accessibility | Source branch | Deployment trigger |
396
+ | ------------- | ------------------------------------------ | ------------------------------------------ | ------------- | ------------------ |
397
+ | `development` | https://data-conversion-dev.psdi.ac.uk | STFC and University of Southampton subnets | `main` | Automatic |
398
+ | `staging` | https://data-conversion-staging.psdi.ac.uk | STFC and University of Southampton subnets | `release` | Automatic |
399
+ | `production` | https://data-conversion.psdi.ac.uk | public | `release` | Manual |
400
+
401
+ Thus the `main` is automatically deployed to the `development` environment, and the `release` branch is automatically deployed to the `staging`
402
+ environment. However deployment from the `release` branch to the `production` environment is a manual process. This is to allow developers to
403
+ manually check that the `release` version works correctly in the `staging` environment before deploying it to the `production` environment.
404
+ The checks to `staging` before deployment to `production` should echo those described in the above
405
+ section [Release Checklist and Procedure](#release-checklist-and-procedure).
406
+
407
+ ### How to deploy to the `production` environment
408
+
409
+ To trigger the workflow which deploys the service to the `production` environment, from the [main page of the repo](https://github.com/PSDI-UK/psdi-data-conversion)
410
+ navigate to [Actions](https://github.com/PSDI-UK/psdi-data-conversion/actions). Here you should see on the right a list of recent workflow
411
+ runs, including whether or not they are successful (as indicated by a green tick); and on the left you should see a list of all workflows.
412
+
413
+ The workflow which deploys the `release` branch to the `staging` environment is named `CI - Release`. As mentioned above, you should verify
414
+ that this workflow successfully deployed the `release` version to `staging` before considering deploying to `production`. If you click on the
415
+ latest workflow run of `CI - Release` then you can see a breakdown of the workflow into its constituent workflows. Note the `deploy-stfc-staging-k8s`
416
+ job. If this job is successful then the `release` version has been successfully deployed to `staging`.
417
+
418
+ Assuming this is the case, navigating back to [Actions](https://github.com/PSDI-UK/psdi-data-conversion/actions), note that there is a workflow
419
+ listed on the left named `CI - Deploy to production cluster`. This is the workflow which must be invoked manually to deploy the `release`
420
+ version to the `production` environment. Clicking on the link to this workflow gives
421
+ [a list of recent invocations](https://github.com/PSDI-UK/psdi-data-conversion/actions/workflows/ci-deploy-production.yml) of the workflow.
422
+ Moreover, a light blue banner appears which says `This workflow has a workflow_dispatch event trigger` on the left and has a `Run workflow` button
423
+ on the right. To invoke the workflow, press this button, _select the `release` branch_ as the option for `Use workflow from` dropdown menu, and
424
+ then finally click the green `Run workflow` button. Once the workflow has been invoked you should be able to see its progress in real time on the
425
+ same page.
426
+
427
+ ### Further technical details
428
+
429
+ The `ci-main.yml`, `ci-release.yml` and `ci-deploy-production.yml` workflows leverage the `job-deploy-k8s.yml` callable workflow to do the heavy
430
+ lifting with regards to deployment to STFC Kubernetes environments. The `job-deploy-k8s.yml` workflow is coded to be invoked on PSDI's GitHub
431
+ runners. These runners are hosted within STFC's network, providing a means to access the three STFC Kubernetes clusters corresponding to the
432
+ `development`, `staging` and `production` environments. The environment to be targeted for deployment is passed to the `job-deploy-k8s.yml`
433
+ workflow as an input parameter. Given a target environment `<env>`, the `job-deploy-k8s.yml` workflow will deploy the service to the
434
+ the `<env>` environment using the Kubernetes manifests stored in the `deploy`/<env>` directory of this repository.
435
+
436
+ The workflow relies on several repository secrets, namely `KUBECONF`, `IMAGEPULLSECRET`, `CERTIFICATE_PRIVATE_KEY` and `CERTIFICATE_PEM` for
437
+ various purposes.
438
+
439
+ - `KUBECONF` provides Kubernetes-specific information pertaining to the the Kubernetes cluster for the target environment. Note that
440
+ `KUBECONF` is an _environment_-dependent secret, taking different values for each of the environments.
441
+ - `IMAGEPULLSECRET` enables the container image housing the data conversion service to be pulled from this GitHub repo to the PSDI runner
442
+ - `CERTIFICATE_PRIVATE_KEY` and `CERTIFICATE_PEM` pertain to the TLS certificates for the service
443
+ For further information see the `job-deploy-k8s.yml` file and aforementioned Kubernetes manifests.
382
444
 
383
445
  The server can be configured by editing the environmental variables set in `Dockerfile`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: psdi_data_conversion
3
- Version: 0.1.6
3
+ Version: 0.2.0
4
4
  Summary: Chemistry file format conversion service, provided by PSDI
5
5
  Project-URL: Homepage, https://data-conversion.psdi.ac.uk/
6
6
  Project-URL: Documentation, https://psdi-uk.github.io/psdi-data-conversion/
@@ -222,8 +222,13 @@ Classifier: Programming Language :: Python :: 3
222
222
  Classifier: Topic :: File Formats
223
223
  Classifier: Topic :: Scientific/Engineering :: Chemistry
224
224
  Requires-Python: >=3.12
225
+ Requires-Dist: igraph
225
226
  Requires-Dist: openbabel-wheel
226
227
  Requires-Dist: py
228
+ Provides-Extra: deploy
229
+ Requires-Dist: flask; extra == 'deploy'
230
+ Requires-Dist: gunicorn; extra == 'deploy'
231
+ Requires-Dist: requests; extra == 'deploy'
227
232
  Provides-Extra: doc
228
233
  Requires-Dist: pdoc3; extra == 'doc'
229
234
  Provides-Extra: gui
@@ -243,6 +248,9 @@ Description-Content-Type: text/markdown
243
248
 
244
249
  # PSDI Data Conversion
245
250
 
251
+ [![License Badge](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
252
+ ![Coverage Badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/brgillis/dbd938192dc4de9b7779978e515c0e79/raw/covbadge.json)
253
+
246
254
  Release date: 2024-04-29
247
255
 
248
256
  This is the repository for the PSDI PF2 Chemistry File Format Conversion project. The goal of this project is to provide utilities to assist in converting files between the many different file formats used in chemistry, providing information on what converters are available for a given conversion and the expected quality of it, and providing multiple interfaces to perform these conversions. These interfaces are:
@@ -327,7 +335,6 @@ This is the repository for the PSDI PF2 Chemistry File Format Conversion project
327
335
  - `LICENSE` (Apache License version 2.0)
328
336
  - `pyproject.toml` (Python project metadata and settings)
329
337
  - `README.md` (This file)
330
- - `requirements.txt` (Requirements for the web app deployment of this project)
331
338
 
332
339
  ## Requirements
333
340
 
@@ -1,5 +1,8 @@
1
1
  # PSDI Data Conversion
2
2
 
3
+ [![License Badge](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
4
+ ![Coverage Badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/brgillis/dbd938192dc4de9b7779978e515c0e79/raw/covbadge.json)
5
+
3
6
  Release date: 2024-04-29
4
7
 
5
8
  This is the repository for the PSDI PF2 Chemistry File Format Conversion project. The goal of this project is to provide utilities to assist in converting files between the many different file formats used in chemistry, providing information on what converters are available for a given conversion and the expected quality of it, and providing multiple interfaces to perform these conversions. These interfaces are:
@@ -84,7 +87,6 @@ This is the repository for the PSDI PF2 Chemistry File Format Conversion project
84
87
  - `LICENSE` (Apache License version 2.0)
85
88
  - `pyproject.toml` (Python project metadata and settings)
86
89
  - `README.md` (This file)
87
- - `requirements.txt` (Requirements for the web app deployment of this project)
88
90
 
89
91
  ## Requirements
90
92
 
@@ -0,0 +1,77 @@
1
+ """app.py
2
+
3
+ Version 1.0, 8th November 2024
4
+
5
+ This script acts as a server for the PSDI Data Conversion Service website.
6
+ """
7
+
8
+ from argparse import ArgumentParser
9
+
10
+ from psdi_data_conversion import constants as const
11
+ from psdi_data_conversion.gui.env import update_env
12
+ from psdi_data_conversion.gui.setup import get_app, limit_upload_size, start_app
13
+ from psdi_data_conversion.main import print_wrap
14
+
15
+ app = get_app()
16
+
17
+
18
+ def main():
19
+ """Standard entry-point function for this script.
20
+ """
21
+
22
+ parser = ArgumentParser()
23
+
24
+ parser.add_argument("--use-env-vars", action="store_true",
25
+ help="If set, all other arguments and defaults for this script are ignored, and environmental "
26
+ "variables and their defaults will instead control execution. These defaults will result in "
27
+ "the app running in production server mode.")
28
+
29
+ parser.add_argument("--max-file-size", type=float, default=const.DEFAULT_MAX_FILE_SIZE/const.MEGABYTE,
30
+ help="The maximum allowed filesize in MB - 0 (default) indicates no maximum")
31
+
32
+ parser.add_argument("--max-file-size-ob", type=float, default=const.DEFAULT_MAX_FILE_SIZE_OB/const.MEGABYTE,
33
+ help="The maximum allowed filesize in MB for the Open Babel converter, taking precendence over "
34
+ "the general maximum file size when Open Babel is used - 0 indicates no maximum. Default 1 MB.")
35
+
36
+ parser.add_argument("--service-mode", action="store_true",
37
+ help="If set, will run as if deploying a service rather than the local GUI")
38
+
39
+ parser.add_argument("--dev-mode", action="store_true",
40
+ help="If set, will expose development elements, such as the SHA of the latest commit")
41
+
42
+ parser.add_argument("--debug", action="store_true",
43
+ help="If set, will run the Flask server in debug mode, which will cause it to automatically "
44
+ "reload if code changes and show an interactive debugger in the case of errors")
45
+
46
+ parser.add_argument("--log-mode", type=str, default=const.LOG_FULL,
47
+ help="How logs should be stored. Allowed values are: \n"
48
+ "- 'full' - Multi-file logging, not recommended for the CLI, but allowed for a compatible "
49
+ "interface with the public web app"
50
+ "- 'simple' - Logs saved to one file"
51
+ "- 'stdout' - Output logs and errors only to stdout"
52
+ "- 'none' - Output only errors to stdout")
53
+
54
+ parser.add_argument("--log-level", type=str, default=None,
55
+ help="The desired level to log at. Allowed values are: 'DEBUG', 'INFO', 'WARNING', 'ERROR, "
56
+ "'CRITICAL'. Default: 'INFO' for logging to file, 'WARNING' for logging to stdout")
57
+
58
+ # Set global variables for settings based on parsed arguments, unless it's set to use env vars
59
+ args = parser.parse_args()
60
+
61
+ if not args.use_env_vars:
62
+ # Overwrite the values from environmental variables with the values from the command-line arguments
63
+ update_env(args)
64
+
65
+ # Set the upload limit based on provided arguments now
66
+ limit_upload_size()
67
+
68
+ print_wrap("Starting the PSDI Data Conversion GUI. This GUI is run as a webpage, which you can open by "
69
+ "right-clicking the link below to open it in your default browser, or by copy-and-pasting it into your "
70
+ "browser of choice.")
71
+
72
+ start_app()
73
+
74
+
75
+ if __name__ == "__main__":
76
+
77
+ main()
@@ -36,6 +36,9 @@ import shutil
36
36
  # The name of the command-line script
37
37
  CL_SCRIPT_NAME = "psdi-data-convert"
38
38
 
39
+ # The name of the Flask app
40
+ APP_NAME = "psdi_data_conversion"
41
+
39
42
  # Environmental variables
40
43
  LOG_MODE_EV = "LOG_MODE"
41
44
  LOG_LEVEL_EV = "LOG_LEVEL"
@@ -52,8 +55,8 @@ MEGABYTE = 1024*1024
52
55
  DEFAULT_MAX_FILE_SIZE = 0 * MEGABYTE
53
56
  DEFAULT_MAX_FILE_SIZE_OB = 1 * MEGABYTE
54
57
 
55
- DEFAULT_UPLOAD_DIR = './psdi_data_conversion/static/uploads'
56
- DEFAULT_DOWNLOAD_DIR = './psdi_data_conversion/static/downloads'
58
+ DEFAULT_INPUT_DIR = './psdi_data_conversion/static/uploads'
59
+ DEFAULT_OUTPUT_DIR = './psdi_data_conversion/static/downloads'
57
60
 
58
61
  # Filename of the database, relative to the base of the python package
59
62
  DATABASE_FILENAME = "static/data/data.json"
@@ -150,11 +153,6 @@ QUAL_2D_LABEL = "2D atomic coordinates are"
150
153
  QUAL_3D_KEY = "three_dim"
151
154
  QUAL_3D_LABEL = "2D atomic coordinates are"
152
155
 
153
- D_QUAL_LABELS = {QUAL_COMP_KEY: QUAL_COMP_LABEL,
154
- QUAL_CONN_KEY: QUAL_CONN_LABEL,
155
- QUAL_2D_KEY: QUAL_2D_LABEL,
156
- QUAL_3D_KEY: QUAL_3D_LABEL}
157
-
158
156
  # Notes for conversion quality
159
157
  QUAL_NOTE_IN_UNKNOWN = ("Potential data extrapolation: {} represented in the output format but its representation in "
160
158
  "the input format is unknown")
@@ -187,3 +185,9 @@ ERR_WRONG_EXTENSIONS = "Input file '{file}' does not have expected extension '{e
187
185
  ERR_EMPTY_ARCHIVE = "No files to convert were contained in archive"
188
186
  ERR_CONVERSION_FAILED = ("File conversion failed for one or more files. Lines from the output log "
189
187
  "{} which indicate possible sources of error: ")
188
+
189
+ # Misc
190
+ # ----
191
+
192
+ # Year in seconds
193
+ YEAR = 365*24*60*60
@@ -5,22 +5,20 @@ Created 2024-12-10 by Bryan Gillis.
5
5
  Class and functions to perform file conversion
6
6
  """
7
7
 
8
- from dataclasses import dataclass, field
9
- import json
10
8
  import glob
11
9
  import importlib
10
+ import json
12
11
  import os
13
12
  import sys
14
13
  import traceback
15
- from typing import Any, Callable, NamedTuple
16
- from multiprocessing import Lock
17
- from psdi_data_conversion import log_utility
18
14
  from collections.abc import Callable
19
15
  from dataclasses import dataclass, field
16
+ from multiprocessing import Lock
20
17
  from tempfile import TemporaryDirectory
21
18
  from typing import Any, NamedTuple
22
19
 
23
20
  from psdi_data_conversion import constants as const
21
+ from psdi_data_conversion import log_utility
24
22
  from psdi_data_conversion.converters import base
25
23
  from psdi_data_conversion.converters.openbabel import CONVERTER_OB
26
24
  from psdi_data_conversion.file_io import (is_archive, is_supported_archive, pack_zip_or_tar, split_archive_ext,
@@ -184,9 +182,9 @@ def get_converter(*args, name=const.CONVERTER_DEFAULT, **converter_kwargs) -> ba
184
182
  use_envvars : bool
185
183
  If set to True, environment variables will be checked for any that set options for this class and used,
186
184
  default False
187
- upload_dir : str
185
+ input_dir : str
188
186
  The location of input files relative to the current directory
189
- download_dir : str
187
+ output_dir : str
190
188
  The location of output files relative to the current directory
191
189
  max_file_size : float
192
190
  The maximum allowed file size for input/output files, in MB, default 1 MB for Open Babel, unlimited for other
@@ -315,11 +313,18 @@ def check_from_format(filename: str,
315
313
  return False
316
314
 
317
315
 
316
+ def _run_single_file_conversion(*args, **kwargs):
317
+ """Run a conversion on a single file, after all arguments have been checked
318
+ """
319
+ return get_converter(*args, **kwargs).run()
320
+
321
+
318
322
  def run_converter(filename: str,
319
323
  to_format: str,
320
324
  *args,
321
325
  from_format: str | None = None,
322
- download_dir=const.DEFAULT_DOWNLOAD_DIR,
326
+ input_dir=const.DEFAULT_INPUT_DIR,
327
+ output_dir=const.DEFAULT_OUTPUT_DIR,
323
328
  max_file_size=None,
324
329
  log_file: str | None = None,
325
330
  log_mode=const.LOG_SIMPLE,
@@ -351,9 +356,9 @@ def run_converter(filename: str,
351
356
  use_envvars : bool
352
357
  If set to True, environment variables will be checked for any that set options for this class and used,
353
358
  default False
354
- upload_dir : str
359
+ input_dir : str
355
360
  The location of input files relative to the current directory
356
- download_dir : str
361
+ output_dir : str
357
362
  The location of output files relative to the current directory
358
363
  strict : bool
359
364
  If True and `from_format` is not None, will fail if any input file has the wrong extension (including files
@@ -410,7 +415,12 @@ def run_converter(filename: str,
410
415
  # converter class, so it needs to be set up here to match what will be set up there
411
416
  if log_file is None:
412
417
  base_filename = os.path.basename(split_archive_ext(filename)[0])
413
- log_file = os.path.join(download_dir, base_filename + const.OUTPUT_LOG_EXT)
418
+ log_file = os.path.join(output_dir, base_filename + const.OUTPUT_LOG_EXT)
419
+
420
+ if os.path.exists(filename):
421
+ qualified_filename = filename
422
+ else:
423
+ qualified_filename = os.path.join(input_dir, filename)
414
424
 
415
425
  # Check if the filename is for an archive file, and handle appropriately
416
426
 
@@ -425,15 +435,16 @@ def run_converter(filename: str,
425
435
  # Not an archive, so just get and run the converter straightforwardly
426
436
  if from_format is not None:
427
437
  check_from_format(filename, from_format, strict=strict)
428
- l_run_output.append(get_converter(filename,
438
+ l_run_output.append(_run_single_file_conversion(filename,
429
439
  to_format,
430
440
  *args,
431
441
  from_format=from_format,
432
- download_dir=download_dir,
442
+ input_dir=input_dir,
443
+ output_dir=output_dir,
433
444
  max_file_size=max_file_size,
434
445
  log_file=log_file,
435
446
  log_mode=log_mode,
436
- **converter_kwargs).run())
447
+ **converter_kwargs))
437
448
 
438
449
  elif not is_supported_archive(filename):
439
450
  raise base.FileConverterInputException(f"{filename} is an unsupported archive type. Supported types are: "
@@ -443,7 +454,7 @@ def run_converter(filename: str,
443
454
  # The filename is of a supported archive type. Make a temporary directory to extract its contents
444
455
  # to, then run the converter on each file extracted
445
456
  with TemporaryDirectory() as extract_dir:
446
- l_filenames = unpack_zip_or_tar(filename, extract_dir=extract_dir)
457
+ l_filenames = unpack_zip_or_tar(qualified_filename, extract_dir=extract_dir)
447
458
 
448
459
  # Check for no files in archive
449
460
  if len(l_filenames) == 0:
@@ -468,15 +479,15 @@ def run_converter(filename: str,
468
479
  individual_log_mode = log_mode if log_mode != const.LOG_FULL else const.LOG_FULL_FORCE
469
480
 
470
481
  try:
471
- individual_run_output = get_converter(extracted_filename,
472
- to_format,
473
- *args,
474
- from_format=from_format,
475
- download_dir=download_dir,
476
- log_file=individual_log_file,
477
- log_mode=individual_log_mode,
478
- max_file_size=remaining_file_size,
479
- **converter_kwargs).run()
482
+ individual_run_output = _run_single_file_conversion(extracted_filename,
483
+ to_format,
484
+ *args,
485
+ from_format=from_format,
486
+ output_dir=output_dir,
487
+ log_file=individual_log_file,
488
+ log_mode=individual_log_mode,
489
+ max_file_size=remaining_file_size,
490
+ **converter_kwargs)
480
491
  except base.FileConverterAbortException as e:
481
492
  # If the run fails, create a run output object to indicate that
482
493
  individual_run_output = base.FileConversionResult(log_filename=individual_log_file,
@@ -573,12 +584,13 @@ def run_converter(filename: str,
573
584
  "input_filename": in_filename,
574
585
  "output_filename": run_output.output_filename[l_index:r_index],
575
586
  "input_size": input_size,
576
- "output_size": output_size }
587
+ "output_size": output_size}
577
588
 
578
- for key in [ "converter", "coordinates", "coordOption", "from_flags",
579
- "to_flags", "from_arg_flags", "to_arg_flags" ]:
589
+ for key in ["converter", "coordinates", "coordOption", "from_flags",
590
+ "to_flags", "from_arg_flags", "to_arg_flags"]:
580
591
  if key in converter_kwargs['data'] and converter_kwargs['data'][key] != "" and not \
581
- ((key == "coordinates" or key == "coordOption") and converter_kwargs['data']['coordinates'] == "neither") :
592
+ ((key == "coordinates" or key == "coordOption") and
593
+ converter_kwargs['data']['coordinates'] == "neither"):
582
594
  entry[key] = converter_kwargs['data'][key]
583
595
 
584
596
  entry["outcome"] = outcome
@@ -595,6 +607,7 @@ def run_converter(filename: str,
595
607
 
596
608
  return run_output
597
609
 
610
+
598
611
  def set_size_units(size):
599
612
  if size >= 1024:
600
613
  return str('%.3f' % (size / 1024)) + ' kB'
@@ -206,8 +206,8 @@ class FileConverter:
206
206
  data: dict[str, Any] | None = None,
207
207
  abort_callback: Callable[[int], None] = abort_raise,
208
208
  use_envvars=False,
209
- upload_dir=const.DEFAULT_UPLOAD_DIR,
210
- download_dir=const.DEFAULT_DOWNLOAD_DIR,
209
+ input_dir=const.DEFAULT_INPUT_DIR,
210
+ output_dir=const.DEFAULT_OUTPUT_DIR,
211
211
  max_file_size=None,
212
212
  no_check=False,
213
213
  log_file: str | None = None,
@@ -235,9 +235,9 @@ class FileConverter:
235
235
  use_envvars : bool
236
236
  If set to True, environment variables will be checked for any that set options for this class and used,
237
237
  default False
238
- upload_dir : str
238
+ input_dir : str
239
239
  The location of input files relative to the current directory
240
- download_dir : str
240
+ output_dir : str
241
241
  The location of output files relative to the current directory
242
242
  max_file_size : float
243
243
  The maximum allowed file size for input/output files, in MB. If 0, will be unlimited. Default 0 (unlimited)
@@ -296,8 +296,8 @@ class FileConverter:
296
296
  # Set member variables directly from input
297
297
  self.in_filename = filename
298
298
  self.to_format = to_format
299
- self.upload_dir = upload_dir
300
- self.download_dir = download_dir
299
+ self.input_dir = input_dir
300
+ self.output_dir = output_dir
301
301
  self.log_file = log_file
302
302
  self.log_mode = log_mode
303
303
  self.log_level = log_level
@@ -328,17 +328,22 @@ class FileConverter:
328
328
  self.err: str | None = None
329
329
  self.quality: str | None = None
330
330
 
331
- # Create directory 'uploads' if not extant.
332
- if not os.path.exists(self.upload_dir):
333
- os.makedirs(self.upload_dir, exist_ok=True)
331
+ # Determine if the filename is fully-qualified, and if not, find it in the upload dir
332
+ if not os.path.exists(self.in_filename):
333
+ qualified_in_filename = os.path.join(self.input_dir, self.in_filename)
334
+ if os.path.exists(qualified_in_filename):
335
+ self.in_filename = qualified_in_filename
336
+ else:
337
+ FileConverterInputException(f"Input file {self.in_filename} not found, either absolute or relative "
338
+ f"to {self.input_dir}")
334
339
 
335
340
  # Create directory 'downloads' if not extant.
336
- if not os.path.exists(self.download_dir):
337
- os.makedirs(self.download_dir, exist_ok=True)
341
+ if not os.path.exists(self.output_dir):
342
+ os.makedirs(self.output_dir, exist_ok=True)
338
343
 
339
344
  self.local_filename = os.path.split(self.in_filename)[1]
340
345
  self.filename_base = os.path.splitext(self.local_filename)[0]
341
- self.out_filename = f"{self.download_dir}/{self.filename_base}.{self.to_format_info.name}"
346
+ self.out_filename = f"{self.output_dir}/{self.filename_base}.{self.to_format_info.name}"
342
347
 
343
348
  # Set up files to log to
344
349
  self._setup_loggers()
@@ -441,7 +446,7 @@ class FileConverter:
441
446
  if self.log_mode == const.LOG_FULL_FORCE:
442
447
  self.output_log = self.log_file
443
448
  else:
444
- self.output_log = os.path.join(self.download_dir, f"{self.filename_base}{const.OUTPUT_LOG_EXT}")
449
+ self.output_log = os.path.join(self.output_dir, f"{self.filename_base}{const.OUTPUT_LOG_EXT}")
445
450
 
446
451
  # If any previous log exists, delete it
447
452
  if os.path.exists(self.output_log):