finalcif 176__tar.gz → 178__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 (207) hide show
  1. finalcif-178/LICENSE.md +5 -0
  2. {finalcif-176 → finalcif-178}/PKG-INFO +5 -8
  3. {finalcif-176 → finalcif-178}/README.md +2 -4
  4. finalcif-178/finalcif/__init__.py +3 -0
  5. {finalcif-176 → finalcif-178}/finalcif/appwindow.py +20 -17
  6. {finalcif-176 → finalcif-178}/finalcif/cif/hkl.py +1 -4
  7. {finalcif-176 → finalcif-178}/finalcif/gui/finalcif_gui_ui.py +1 -1
  8. {finalcif-176 → finalcif-178}/finalcif/gui/finalcif_gui_ui.ui +1 -1
  9. {finalcif-176 → finalcif-178}/finalcif/gui/squeeze_dialog.py +53 -9
  10. {finalcif-176 → finalcif-178}/finalcif/report/references.py +0 -117
  11. {finalcif-176 → finalcif-178}/finalcif/report/report_text.py +1 -50
  12. {finalcif-176 → finalcif-178}/finalcif/report/templated_report.py +35 -13
  13. {finalcif-176 → finalcif-178}/finalcif/template/report.tmpl +9 -4
  14. finalcif-178/finalcif/template/report_default.docx +0 -0
  15. {finalcif-176 → finalcif-178}/finalcif/template/report_fastmolwidget.tmpl +9 -4
  16. finalcif-178/finalcif/template/template_for_multitable.docx +0 -0
  17. finalcif-178/finalcif/template/template_text.docx +0 -0
  18. finalcif-178/finalcif/template/template_without_text.docx +0 -0
  19. {finalcif-176 → finalcif-178}/finalcif/tools/shred.py +1 -1
  20. finalcif-178/finalcif/tools/solvents.py +183 -0
  21. {finalcif-176 → finalcif-178}/finalcif/tools/squeeze.py +41 -3
  22. finalcif-178/githooks/pre-push +76 -0
  23. {finalcif-176 → finalcif-178}/pyproject.toml +4 -3
  24. finalcif-178/uv.lock +1104 -0
  25. finalcif-176/LICENSE +0 -7
  26. finalcif-176/finalcif/__init__.py +0 -3
  27. finalcif-176/finalcif/report/mtools.py +0 -43
  28. finalcif-176/finalcif/report/tables.py +0 -965
  29. finalcif-176/finalcif/template/report_default.docx +0 -0
  30. finalcif-176/finalcif/template/template_for_multitable.docx +0 -0
  31. finalcif-176/finalcif/template/template_text.docx +0 -0
  32. finalcif-176/finalcif/template/template_without_text.docx +0 -0
  33. finalcif-176/uv.lock +0 -1056
  34. {finalcif-176 → finalcif-178}/.gitattributes +0 -0
  35. {finalcif-176 → finalcif-178}/.githooks/pre-push +0 -0
  36. {finalcif-176 → finalcif-178}/.gitignore +0 -0
  37. {finalcif-176 → finalcif-178}/.python-version +0 -0
  38. {finalcif-176 → finalcif-178}/.ruff.toml +0 -0
  39. {finalcif-176 → finalcif-178}/.run/Make Documentation MAC.run.xml +0 -0
  40. {finalcif-176 → finalcif-178}/.run/compile_ui_files.run.xml +0 -0
  41. {finalcif-176 → finalcif-178}/.run/make documentation Windows.run.xml +0 -0
  42. {finalcif-176 → finalcif-178}/AGENTS.md +0 -0
  43. {finalcif-176 → finalcif-178}/CODE_OF_CONDUCT.md +0 -0
  44. {finalcif-176 → finalcif-178}/CONTRIBUTING.md +0 -0
  45. {finalcif-176 → finalcif-178}/Finalcif_installer_win.spec +0 -0
  46. {finalcif-176 → finalcif-178}/Finalcif_linux_onefile.spec +0 -0
  47. {finalcif-176 → finalcif-178}/Finalcif_mac.spec +0 -0
  48. {finalcif-176 → finalcif-178}/finalcif/app_path.py +0 -0
  49. {finalcif-176 → finalcif-178}/finalcif/cif/__init__.py +0 -0
  50. {finalcif-176 → finalcif-178}/finalcif/cif/all_cif_dicts.py +0 -0
  51. {finalcif-176 → finalcif-178}/finalcif/cif/atoms.py +0 -0
  52. {finalcif-176 → finalcif-178}/finalcif/cif/checkcif/__init__.py +0 -0
  53. {finalcif-176 → finalcif-178}/finalcif/cif/checkcif/checkcif.py +0 -0
  54. {finalcif-176 → finalcif-178}/finalcif/cif/cif_dict_foo.py +0 -0
  55. {finalcif-176 → finalcif-178}/finalcif/cif/cif_file_io.py +0 -0
  56. {finalcif-176 → finalcif-178}/finalcif/cif/cif_keyword_mapping.py +0 -0
  57. {finalcif-176 → finalcif-178}/finalcif/cif/cif_order.py +0 -0
  58. {finalcif-176 → finalcif-178}/finalcif/cif/cod/__init__.py +0 -0
  59. {finalcif-176 → finalcif-178}/finalcif/cif/cod/deposit.py +0 -0
  60. {finalcif-176 → finalcif-178}/finalcif/cif/cod/deposit_check.py +0 -0
  61. {finalcif-176 → finalcif-178}/finalcif/cif/cod/deposition_list.py +0 -0
  62. {finalcif-176 → finalcif-178}/finalcif/cif/cod/doi.py +0 -0
  63. {finalcif-176 → finalcif-178}/finalcif/cif/cod/upload.py +0 -0
  64. {finalcif-176 → finalcif-178}/finalcif/cif/cod/website_parser.py +0 -0
  65. {finalcif-176 → finalcif-178}/finalcif/cif/core_dict.py +0 -0
  66. {finalcif-176 → finalcif-178}/finalcif/cif/modulation_dict.py +0 -0
  67. {finalcif-176 → finalcif-178}/finalcif/cif/powder_dict.py +0 -0
  68. {finalcif-176 → finalcif-178}/finalcif/cif/reference.py +0 -0
  69. {finalcif-176 → finalcif-178}/finalcif/cif/restraints_dict.py +0 -0
  70. {finalcif-176 → finalcif-178}/finalcif/cif/text.py +0 -0
  71. {finalcif-176 → finalcif-178}/finalcif/cif/twin_dict.py +0 -0
  72. {finalcif-176 → finalcif-178}/finalcif/cif/vrf_entry.py +0 -0
  73. {finalcif-176 → finalcif-178}/finalcif/ciforder/__init__.py +0 -0
  74. {finalcif-176 → finalcif-178}/finalcif/ciforder/order.py +0 -0
  75. {finalcif-176 → finalcif-178}/finalcif/ciforder/order_ui.py +0 -0
  76. {finalcif-176 → finalcif-178}/finalcif/ciforder/order_ui.ui +0 -0
  77. {finalcif-176 → finalcif-178}/finalcif/datafiles/__init__.py +0 -0
  78. {finalcif-176 → finalcif-178}/finalcif/datafiles/bruker_data.py +0 -0
  79. {finalcif-176 → finalcif-178}/finalcif/datafiles/bruker_frame.py +0 -0
  80. {finalcif-176 → finalcif-178}/finalcif/datafiles/ccdc_mail.py +0 -0
  81. {finalcif-176 → finalcif-178}/finalcif/datafiles/data.py +0 -0
  82. {finalcif-176 → finalcif-178}/finalcif/datafiles/p4p_reader.py +0 -0
  83. {finalcif-176 → finalcif-178}/finalcif/datafiles/sadabs.py +0 -0
  84. {finalcif-176 → finalcif-178}/finalcif/datafiles/saint.py +0 -0
  85. {finalcif-176 → finalcif-178}/finalcif/datafiles/shelx_lst.py +0 -0
  86. {finalcif-176 → finalcif-178}/finalcif/datafiles/utils.py +0 -0
  87. {finalcif-176 → finalcif-178}/finalcif/equip_property/__init__.py +0 -0
  88. {finalcif-176 → finalcif-178}/finalcif/equip_property/author_loop_templates.py +0 -0
  89. {finalcif-176 → finalcif-178}/finalcif/equip_property/equipment.py +0 -0
  90. {finalcif-176 → finalcif-178}/finalcif/equip_property/properties.py +0 -0
  91. {finalcif-176 → finalcif-178}/finalcif/equip_property/tools.py +0 -0
  92. {finalcif-176 → finalcif-178}/finalcif/finalcif_start.py +0 -0
  93. {finalcif-176 → finalcif-178}/finalcif/gui/__init__.py +0 -0
  94. {finalcif-176 → finalcif-178}/finalcif/gui/block_combobox.py +0 -0
  95. {finalcif-176 → finalcif-178}/finalcif/gui/checkcif_browser.py +0 -0
  96. {finalcif-176 → finalcif-178}/finalcif/gui/cif_table_model.py +0 -0
  97. {finalcif-176 → finalcif-178}/finalcif/gui/cif_table_view.py +0 -0
  98. {finalcif-176 → finalcif-178}/finalcif/gui/combobox.py +0 -0
  99. {finalcif-176 → finalcif-178}/finalcif/gui/custom_classes.py +0 -0
  100. {finalcif-176 → finalcif-178}/finalcif/gui/dialogs.py +0 -0
  101. {finalcif-176 → finalcif-178}/finalcif/gui/edit_button.py +0 -0
  102. {finalcif-176 → finalcif-178}/finalcif/gui/equipmenttable.py +0 -0
  103. {finalcif-176 → finalcif-178}/finalcif/gui/file_editor.py +0 -0
  104. {finalcif-176 → finalcif-178}/finalcif/gui/import_selector.py +0 -0
  105. {finalcif-176 → finalcif-178}/finalcif/gui/import_selector_ui.py +0 -0
  106. {finalcif-176 → finalcif-178}/finalcif/gui/import_selector_ui.ui +0 -0
  107. {finalcif-176 → finalcif-178}/finalcif/gui/loop_creator.py +0 -0
  108. {finalcif-176 → finalcif-178}/finalcif/gui/loop_creator_ui.py +0 -0
  109. {finalcif-176 → finalcif-178}/finalcif/gui/loop_creator_ui.ui +0 -0
  110. {finalcif-176 → finalcif-178}/finalcif/gui/loops.py +0 -0
  111. {finalcif-176 → finalcif-178}/finalcif/gui/mainstackwidget.py +0 -0
  112. {finalcif-176 → finalcif-178}/finalcif/gui/mixins.py +0 -0
  113. {finalcif-176 → finalcif-178}/finalcif/gui/new_key_dialog.py +0 -0
  114. {finalcif-176 → finalcif-178}/finalcif/gui/new_key_dialog_ui.py +0 -0
  115. {finalcif-176 → finalcif-178}/finalcif/gui/new_key_dialog_ui.ui +0 -0
  116. {finalcif-176 → finalcif-178}/finalcif/gui/plaintextedit.py +0 -0
  117. {finalcif-176 → finalcif-178}/finalcif/gui/playground.py +0 -0
  118. {finalcif-176 → finalcif-178}/finalcif/gui/propertytable.py +0 -0
  119. {finalcif-176 → finalcif-178}/finalcif/gui/shelx_navigation.py +0 -0
  120. {finalcif-176 → finalcif-178}/finalcif/gui/single_line_textedit.py +0 -0
  121. {finalcif-176 → finalcif-178}/finalcif/gui/spell_check_edit.py +0 -0
  122. {finalcif-176 → finalcif-178}/finalcif/gui/syntax_highlighter.py +0 -0
  123. {finalcif-176 → finalcif-178}/finalcif/gui/text_templates_ui.py +0 -0
  124. {finalcif-176 → finalcif-178}/finalcif/gui/text_templates_ui.ui +0 -0
  125. {finalcif-176 → finalcif-178}/finalcif/gui/text_value_editor.py +0 -0
  126. {finalcif-176 → finalcif-178}/finalcif/gui/timers.py +0 -0
  127. {finalcif-176 → finalcif-178}/finalcif/gui/validators.py +0 -0
  128. {finalcif-176 → finalcif-178}/finalcif/gui/vrf_classes.py +0 -0
  129. {finalcif-176 → finalcif-178}/finalcif/gui/vzs_viewer.py +0 -0
  130. {finalcif-176 → finalcif-178}/finalcif/icon/__init__.py +0 -0
  131. {finalcif-176 → finalcif-178}/finalcif/icon/finalcif.png +0 -0
  132. {finalcif-176 → finalcif-178}/finalcif/icon/finalcif2.ico +0 -0
  133. {finalcif-176 → finalcif-178}/finalcif/icon/finalcif2.png +0 -0
  134. {finalcif-176 → finalcif-178}/finalcif/icon/multitable.ico +0 -0
  135. {finalcif-176 → finalcif-178}/finalcif/icon/multitable.png +0 -0
  136. {finalcif-176 → finalcif-178}/finalcif/icon/multitable.xcf +0 -0
  137. {finalcif-176 → finalcif-178}/finalcif/report/__init__.py +0 -0
  138. {finalcif-176 → finalcif-178}/finalcif/report/archive_report.py +0 -0
  139. {finalcif-176 → finalcif-178}/finalcif/report/gui/__init__.py +0 -0
  140. {finalcif-176 → finalcif-178}/finalcif/report/gui/mainwindow.py +0 -0
  141. {finalcif-176 → finalcif-178}/finalcif/report/gui/mainwindow.ui +0 -0
  142. {finalcif-176 → finalcif-178}/finalcif/report/symm.py +0 -0
  143. {finalcif-176 → finalcif-178}/finalcif/template/__init__.py +0 -0
  144. {finalcif-176 → finalcif-178}/finalcif/template/bootstrap/bootstrap.min.css +0 -0
  145. {finalcif-176 → finalcif-178}/finalcif/template/machines/D8_VENTURE.cif +0 -0
  146. {finalcif-176 → finalcif-178}/finalcif/template/mathml2omml.xsl +0 -0
  147. {finalcif-176 → finalcif-178}/finalcif/template/miew/LICENSES +0 -0
  148. {finalcif-176 → finalcif-178}/finalcif/template/miew/Miew.css +0 -0
  149. {finalcif-176 → finalcif-178}/finalcif/template/miew/Miew.min.js +0 -0
  150. {finalcif-176 → finalcif-178}/finalcif/template/miew/lodash.min.js +0 -0
  151. {finalcif-176 → finalcif-178}/finalcif/template/miew/three.js +0 -0
  152. {finalcif-176 → finalcif-178}/finalcif/template/report.tex +0 -0
  153. {finalcif-176 → finalcif-178}/finalcif/template/report2.tex +0 -0
  154. {finalcif-176 → finalcif-178}/finalcif/template/template1.docx +0 -0
  155. {finalcif-176 → finalcif-178}/finalcif/template/templates.py +0 -0
  156. {finalcif-176 → finalcif-178}/finalcif/template/unicode2latex/FakePlasTeX/FakeContext.py +0 -0
  157. {finalcif-176 → finalcif-178}/finalcif/template/unicode2latex/FakePlasTeX/FakeTokenizer.py +0 -0
  158. {finalcif-176 → finalcif-178}/finalcif/template/unicode2latex/FakePlasTeX/README.md +0 -0
  159. {finalcif-176 → finalcif-178}/finalcif/template/unicode2latex/FakePlasTeX/TokenizerPassThru.py +0 -0
  160. {finalcif-176 → finalcif-178}/finalcif/template/unicode2latex/LICENSE +0 -0
  161. {finalcif-176 → finalcif-178}/finalcif/template/unicode2latex/__init__.py +0 -0
  162. {finalcif-176 → finalcif-178}/finalcif/template/unicode2latex/u2l.py +0 -0
  163. {finalcif-176 → finalcif-178}/finalcif/template/unicode2latex/unicode-math-table.tex +0 -0
  164. {finalcif-176 → finalcif-178}/finalcif/template/unicode2latex/unicode-math-xetex.sty +0 -0
  165. {finalcif-176 → finalcif-178}/finalcif/template/xsl/convert.py +0 -0
  166. {finalcif-176 → finalcif-178}/finalcif/template/xsl/xmltohtml.xsl +0 -0
  167. {finalcif-176 → finalcif-178}/finalcif/template/xsl/xmltolatex.xsl +0 -0
  168. {finalcif-176 → finalcif-178}/finalcif/tools/__init__.py +0 -0
  169. {finalcif-176 → finalcif-178}/finalcif/tools/chemparse.py +0 -0
  170. {finalcif-176 → finalcif-178}/finalcif/tools/download.py +0 -0
  171. {finalcif-176 → finalcif-178}/finalcif/tools/dsrmath.py +0 -0
  172. {finalcif-176 → finalcif-178}/finalcif/tools/formal_charge.py +0 -0
  173. {finalcif-176 → finalcif-178}/finalcif/tools/misc.py +0 -0
  174. {finalcif-176 → finalcif-178}/finalcif/tools/mol_file_writer.py +0 -0
  175. {finalcif-176 → finalcif-178}/finalcif/tools/options.py +0 -0
  176. {finalcif-176 → finalcif-178}/finalcif/tools/platon.py +0 -0
  177. {finalcif-176 → finalcif-178}/finalcif/tools/pupdate.py +0 -0
  178. {finalcif-176 → finalcif-178}/finalcif/tools/selfupdate.py +0 -0
  179. {finalcif-176 → finalcif-178}/finalcif/tools/settings.py +0 -0
  180. {finalcif-176 → finalcif-178}/finalcif/tools/space_groups.py +0 -0
  181. {finalcif-176 → finalcif-178}/finalcif/tools/spgr_format.py +0 -0
  182. {finalcif-176 → finalcif-178}/finalcif/tools/statusbar.py +0 -0
  183. {finalcif-176 → finalcif-178}/finalcif/tools/sumformula.py +0 -0
  184. {finalcif-176 → finalcif-178}/finalcif/tools/z_from_packing.py +0 -0
  185. {finalcif-176 → finalcif-178}/finalcif.exe +0 -0
  186. {finalcif-176 → finalcif-178}/platon/README.txt +0 -0
  187. {finalcif-176 → finalcif-178}/platon/compile_platon.bat +0 -0
  188. {finalcif-176 → finalcif-178}/platon/platon_special.exe +0 -0
  189. {finalcif-176 → finalcif-178}/platon/platon_special.f +0 -0
  190. {finalcif-176 → finalcif-178}/pylock.toml +0 -0
  191. {finalcif-176 → finalcif-178}/run_and_capture.py +0 -0
  192. {finalcif-176 → finalcif-178}/run_finalcif +0 -0
  193. {finalcif-176 → finalcif-178}/run_finalcif.bat +0 -0
  194. {finalcif-176 → finalcif-178}/screenshots/finalcif_checkcif.png +0 -0
  195. {finalcif-176 → finalcif-178}/screenshots/finalcif_details.png +0 -0
  196. {finalcif-176 → finalcif-178}/screenshots/finalcif_loops.png +0 -0
  197. {finalcif-176 → finalcif-178}/screenshots/finalcif_loops2.png +0 -0
  198. {finalcif-176 → finalcif-178}/screenshots/finalcif_main.png +0 -0
  199. {finalcif-176 → finalcif-178}/screenshots/finalcif_report.png +0 -0
  200. {finalcif-176 → finalcif-178}/screenshots/finalcif_responses.png +0 -0
  201. {finalcif-176 → finalcif-178}/screenshots/loops_column_buttons.png +0 -0
  202. {finalcif-176 → finalcif-178}/work/compare_z_platon.py +0 -0
  203. {finalcif-176 → finalcif-178}/work/diagnose_1519506.py +0 -0
  204. {finalcif-176 → finalcif-178}/work/diagnose_z.py +0 -0
  205. {finalcif-176 → finalcif-178}/work/investigate_failures.py +0 -0
  206. {finalcif-176 → finalcif-178}/work/validate_cod.py +0 -0
  207. {finalcif-176 → finalcif-178}/work/validate_zprime.py +0 -0
@@ -0,0 +1,5 @@
1
+ "THE BEER-WARE LICENSE" (Revision 42):
2
+ Daniel Kratzert, dkratzert@gmx.de wrote this file. As long as you retain
3
+ this notice you can do whatever you want with this stuff. If we meet some day,
4
+ and you think this stuff is worth it, you can buy me a beer in return.
5
+ Dr. Daniel Kratzert
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: finalcif
3
- Version: 176
3
+ Version: 178
4
4
  Summary: CIF file editor
5
5
  Project-URL: Homepage, https://dkratzert.de/finalcif.html
6
6
  Project-URL: Bug Tracker, https://github.com/dkratzert/FinalCif/issues
7
7
  Author-email: Daniel Kratzert <dkratzert@gmx.de>
8
- License-File: LICENSE
8
+ License-Expression: Beerware
9
+ License-File: LICENSE.md
9
10
  Classifier: Development Status :: 5 - Production/Stable
10
- Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: OS Independent
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Topic :: Scientific/Engineering :: Chemistry
@@ -33,16 +33,13 @@ Requires-Dist: qtpy
33
33
  Requires-Dist: requests
34
34
  Requires-Dist: shelxfile>=24
35
35
  Requires-Dist: urllib3==2.7.0
36
- Requires-Dist: wheel==0.47.0
37
36
  Description-Content-Type: text/markdown
38
37
 
39
38
  ![Latest Release](https://img.shields.io/github/v/tag/dkratzert/FinalCif?label=Release)
40
39
  [![Unit Tests](https://github.com/dkratzert/FinalCif/actions/workflows/python_tests.yml/badge.svg?branch=master)](https://github.com/dkratzert/FinalCif/actions/workflows/python_tests.yml)
41
40
  ![Contributions](https://img.shields.io/badge/contributions-welcome-blue)
42
- [![PyPI package](https://repology.org/badge/version-for-repo/pypi/python:finalcif.svg)](https://repology.org/project/python:finalcif/versions)
43
- <a href="https://repology.org/project/finalcif/versions">
44
- <img src="https://repology.org/badge/vertical-allrepos/finalcif.svg" alt="Packaging status" align="right">
45
- </a>
41
+ [![PyPI version](https://img.shields.io/pypi/v/finalcif.svg)](https://pypi.org/project/finalcif/)
42
+
46
43
 
47
44
 
48
45
  # FinalCif
@@ -1,10 +1,8 @@
1
1
  ![Latest Release](https://img.shields.io/github/v/tag/dkratzert/FinalCif?label=Release)
2
2
  [![Unit Tests](https://github.com/dkratzert/FinalCif/actions/workflows/python_tests.yml/badge.svg?branch=master)](https://github.com/dkratzert/FinalCif/actions/workflows/python_tests.yml)
3
3
  ![Contributions](https://img.shields.io/badge/contributions-welcome-blue)
4
- [![PyPI package](https://repology.org/badge/version-for-repo/pypi/python:finalcif.svg)](https://repology.org/project/python:finalcif/versions)
5
- <a href="https://repology.org/project/finalcif/versions">
6
- <img src="https://repology.org/badge/vertical-allrepos/finalcif.svg" alt="Packaging status" align="right">
7
- </a>
4
+ [![PyPI version](https://img.shields.io/pypi/v/finalcif.svg)](https://pypi.org/project/finalcif/)
5
+
8
6
 
9
7
 
10
8
  # FinalCif
@@ -0,0 +1,3 @@
1
+
2
+ VERSION = 178
3
+ __version__ = VERSION
@@ -21,7 +21,6 @@ from math import sin, radians
21
21
  from pathlib import Path, WindowsPath
22
22
  from typing import cast, TYPE_CHECKING
23
23
 
24
-
25
24
  if TYPE_CHECKING:
26
25
  from finalcif.cif.checkcif.checkcif import MyHTMLParser
27
26
 
@@ -1625,7 +1624,6 @@ class AppWindow(QMainWindow):
1625
1624
  """
1626
1625
  Generates a report document.
1627
1626
  """
1628
- from finalcif.report.tables import make_multi_tables
1629
1627
  from finalcif.report.templated_report import ReportFormat, TemplatedReport
1630
1628
  from docx.image.exceptions import UnrecognizedImageError
1631
1629
  current_block = self.ui.datanameComboBox.currentIndex()
@@ -1638,7 +1636,7 @@ class AppWindow(QMainWindow):
1638
1636
  return None
1639
1637
  self.load_cif_file(self.cif.finalcif_file, block=current_block, load_changes=False)
1640
1638
  report_filename = self.cif.finalcif_file_prefixed(prefix='report_', suffix='-finalcif.docx')
1641
- multi_table_document = self.cif.finalcif_file_prefixed(prefix='', suffix='-multitable.docx')
1639
+ multi_table_document: Path | None = None
1642
1640
  self.cif.picometer = self.options.use_picometers
1643
1641
  # The picture after the header:
1644
1642
  if self.report_picture_path:
@@ -1652,9 +1650,12 @@ class AppWindow(QMainWindow):
1652
1650
  if template_path.suffix in ('.docx',):
1653
1651
  t = TemplatedReport(format=ReportFormat.RICHTEXT, options=self.options, cif=self.cif)
1654
1652
  ok = t.make_templated_docx_report(output_filename=str(report_filename),
1655
- template_path=Path(self.get_checked_templates_list_text()))
1656
- if self.cif.is_multi_cif and self.cif.doc[0].name != 'global':
1657
- make_multi_tables(cif=self.cif, output_filename=str(multi_table_document))
1653
+ template_path=template_path)
1654
+ if (ok and self.cif.is_multi_cif and self.cif.doc[0].name != 'global'
1655
+ and not t.is_multi_cif_template):
1656
+ multi_table_document = self.cif.finalcif_file_prefixed(prefix='', suffix='-multitable.docx')
1657
+ ok = t.make_templated_docx_report(output_filename=str(multi_table_document),
1658
+ template_path=application_path / 'template' / 'template_for_multitable.docx')
1658
1659
  elif template_path.suffix in ('.html', '.tmpl'):
1659
1660
  t = TemplatedReport(format=ReportFormat.HTML, options=self.options, cif=self.cif)
1660
1661
  report_filename = report_filename.with_suffix('.html')
@@ -1687,16 +1688,16 @@ class AppWindow(QMainWindow):
1687
1688
  if DEBUG:
1688
1689
  print('dbg> Unrecognized image error')
1689
1690
  raise
1690
- show_general_warning(self,'The report contains an unrecognized image format.')
1691
+ show_general_warning(self, 'The report contains an unrecognized image format.')
1691
1692
  return None
1692
1693
  if not self.running_inside_unit_test:
1693
1694
  self.open_report_document(report_filename, multi_table_document)
1694
1695
  # Save report and other files to a zip file:
1695
- self.zip_report(report_filename)
1696
+ self.zip_report(report_filename, multi_table_document)
1696
1697
  return None
1697
1698
  return None
1698
1699
 
1699
- def zip_report(self, report_filename: Path) -> None:
1700
+ def zip_report(self, report_filename: Path, multi_table_document: Path | None = None) -> None:
1700
1701
  from finalcif.report.archive_report import ArchiveReport
1701
1702
  zipfile = self.cif.finalcif_file.with_suffix('.zip')
1702
1703
  if zipfile.exists():
@@ -1711,16 +1712,16 @@ class AppWindow(QMainWindow):
1711
1712
  with suppress(Exception):
1712
1713
  pdfname = self.cif.finalcif_file_prefixed(prefix='checkcif-', suffix='-finalcif.pdf')
1713
1714
  arc.zip.write(filename=pdfname, arcname=pdfname.name)
1714
- with suppress(Exception):
1715
- multitable = self.cif.finalcif_file_prefixed(prefix='', suffix='-multitable.docx')
1716
- arc.zip.write(filename=multitable, arcname=multitable.name)
1715
+ if multi_table_document is not None:
1716
+ with suppress(Exception):
1717
+ arc.zip.write(filename=multi_table_document, arcname=multi_table_document.name)
1717
1718
  with suppress(Exception):
1718
1719
  prp_list = self.cif.finalcif_file.parent.glob('*.prp')
1719
1720
  sorted_prp = sorted(prp_list, key=lambda x: x.stat().st_mtime)
1720
1721
  arc.zip.write(filename=sorted_prp[-1], arcname=sorted_prp[-1].name)
1721
1722
 
1722
- def open_report_document(self, report_filename: Path, multi_table_document: Path) -> None:
1723
- if self.cif.is_multi_cif:
1723
+ def open_report_document(self, report_filename: Path, multi_table_document: Path | None = None) -> None:
1724
+ if multi_table_document is not None:
1724
1725
  open_file(multi_table_document)
1725
1726
  open_file(report_filename)
1726
1727
 
@@ -2295,16 +2296,18 @@ class AppWindow(QMainWindow):
2295
2296
  d = 0.0
2296
2297
  self.ui.numRestraintsLineEdit.setText(self.cif['_refine_ls_number_restraints'])
2297
2298
  self.ui.thetaMaxLineEdit.setText(thetamax)
2298
- self.ui.thetaFullLineEdit.setText(self.cif['_diffrn_reflns_theta_full'])
2299
+ theta_full = self.cif['_diffrn_reflns_theta_full']
2300
+ self.ui.thetaFullLineEdit.setText(theta_full)
2299
2301
  self.ui.dLineEdit.setText(f"{d:5.3f}")
2300
2302
  try:
2301
- compl = float(self.cif['_diffrn_measured_fraction_theta_max']) * 100
2303
+ fraction = self.cif['_diffrn_measured_fraction_theta_full']
2304
+ compl = float(fraction) * 100
2302
2305
  if not compl:
2303
2306
  compl = 0.0
2304
2307
  except (TypeError, ValueError):
2305
2308
  compl = 0.0
2306
2309
  try:
2307
- self.ui.completeLineEdit.setText(f"{compl:<5.1f}")
2310
+ self.ui.completeLineEdit.setText(f"{compl:<5.1f} (at {theta_full}°)")
2308
2311
  except ValueError:
2309
2312
  pass
2310
2313
  self.ui.wavelengthLineEdit.setText(f"{wavelen}")
@@ -34,10 +34,7 @@ class HKL:
34
34
 
35
35
  @property
36
36
  def hkl_as_cif(self) -> str:
37
- if version.parse(gemmi.__version__) < version.parse('0.5.1'):
38
- return self._doc.as_string(style=Style.Simple)
39
- else:
40
- return self._doc.as_string(options=gemmi.cif.WriteOptions(Style.Simple))
37
+ return self._doc.as_string(options=gemmi.cif.WriteOptions(Style.Simple))
41
38
 
42
39
  def _add_hkl_as_loop(self) -> None:
43
40
  """
@@ -2881,7 +2881,7 @@ class Ui_FinalCifWindow(object):
2881
2881
  self.CancelPropertiesButton.setText(QCoreApplication.translate("FinalCifWindow", u"Cancel", None))
2882
2882
  self.ExportPropertyButton.setText(QCoreApplication.translate("FinalCifWindow", u"Export", None))
2883
2883
  self.BackFromLoopsPushButton.setText(QCoreApplication.translate("FinalCifWindow", u"Back to CIF Table", None))
2884
- self.add_squeeze_pushButton.setText(QCoreApplication.translate("FinalCifWindow", u"Add Squeeze Info", None))
2884
+ self.add_squeeze_pushButton.setText(QCoreApplication.translate("FinalCifWindow", u"Add/Edit Squeeze Info", None))
2885
2885
  self.deleteLoopButton.setText(QCoreApplication.translate("FinalCifWindow", u"Delete Loop", None))
2886
2886
  self.newLoopPushButton.setText(QCoreApplication.translate("FinalCifWindow", u"Add New Loop", None))
2887
2887
  self.revertLoopsPushButton.setText(QCoreApplication.translate("FinalCifWindow", u"Revert Changes", None))
@@ -2861,7 +2861,7 @@ The data source will be ignored until next program restart.</string>
2861
2861
  <item row="0" column="2">
2862
2862
  <widget class="QPushButton" name="add_squeeze_pushButton">
2863
2863
  <property name="text">
2864
- <string>Add Squeeze Info</string>
2864
+ <string>Add/Edit Squeeze Info</string>
2865
2865
  </property>
2866
2866
  </widget>
2867
2867
  </item>
@@ -18,14 +18,15 @@ from pathlib import Path
18
18
  from qtpy import QtCore, compat
19
19
  from qtpy.QtGui import QColor
20
20
  from qtpy.QtWidgets import (
21
- QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QMessageBox,
22
- QPlainTextEdit, QPushButton, QSizePolicy, QTableWidget, QTableWidgetItem,
21
+ QAbstractItemView, QCompleter, QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit, QMessageBox,
22
+ QPlainTextEdit, QPushButton, QSizePolicy, QStyledItemDelegate, QTableWidget, QTableWidgetItem,
23
23
  QVBoxLayout, QWidget, QApplication
24
24
  )
25
25
 
26
26
  from finalcif.cif.cif_file_io import CifContainer
27
- from finalcif.cif.text import quote
28
- from finalcif.tools.squeeze import build_details_text, electrons_from_formula
27
+ from finalcif.cif.text import quote, retranslate_delimiter
28
+ from finalcif.tools.solvents import solvent_names
29
+ from finalcif.tools.squeeze import build_details_text, electrons_from_formula, resolve_formula
29
30
 
30
31
 
31
32
  class SqueezeMode(Enum):
@@ -78,7 +79,9 @@ _MODE_CONFIG: dict[SqueezeMode, dict] = {
78
79
  'details_key' : '_platon_squeeze_details',
79
80
  'title' : 'PLATON SQUEEZE \u2013 Assign Solvent Content',
80
81
  'info' : ('PLATON/SQUEEZE was used. '
81
- 'Assign the solvent formula per void <b>per unit cell</b>, e.g. <tt>2(H2O)</tt>.'),
82
+ 'Assign the solvent formula per void <b>per unit cell</b>, e.g. <tt>2(H2O)</tt>.\n'
83
+ 'Common solvent names like <tt>2 thf</tt> or <tt>toluene</tt> are '
84
+ 'converted to their formula.'),
82
85
  'details_label' : 'SQUEEZE details (<i>_platon_squeeze_details</i>):',
83
86
  'electrons_col_header': 'Electrons\n(PLATON)',
84
87
  },
@@ -91,7 +94,9 @@ _MODE_CONFIG: dict[SqueezeMode, dict] = {
91
94
  'details_key' : '_smtbx_masks_special_details',
92
95
  'title' : 'Olex2/SMTBX Masks \u2013 Assign Solvent Content',
93
96
  'info' : ('Olex2/SMTBX solvent masks were used. '
94
- 'Assign the solvent formula per void <b>per unit cell</b>, e.g. <tt>2(H2O)</tt>.'),
97
+ 'Assign the solvent formula per void <b>per unit cell</b>, e.g. <tt>2(H2O)</tt>.\n'
98
+ 'Common solvent names like <tt>2 thf</tt> or <tt>toluene</tt> are '
99
+ 'converted to their formula.'),
95
100
  'details_label' : 'Masks details (<i>_smtbx_masks_special_details</i>):',
96
101
  'electrons_col_header': 'Electrons\n(Masks)',
97
102
  },
@@ -102,6 +107,19 @@ _COLOR_WARN = QColor(255, 200, 200) # light red
102
107
  _COLOR_NEUTRAL = QColor(255, 255, 255) # white
103
108
 
104
109
 
110
+ class SolventNameDelegate(QStyledItemDelegate):
111
+ """Line edit delegate that autocompletes common solvent names."""
112
+
113
+ def createEditor(self, parent: QWidget, option, index) -> QWidget:
114
+ editor = QLineEdit(parent)
115
+ completer = QCompleter(solvent_names(), editor)
116
+ completer.setCaseSensitivity(QtCore.Qt.CaseSensitivity.CaseInsensitive)
117
+ completer.setFilterMode(QtCore.Qt.MatchFlag.MatchContains)
118
+ completer.setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
119
+ editor.setCompleter(completer)
120
+ return editor
121
+
122
+
105
123
  class SqueezeSolventDialog(QDialog):
106
124
  """
107
125
  Modal dialog for entering PLATON SQUEEZE or Olex2/SMTBX solvent mask content.
@@ -212,6 +230,15 @@ class SqueezeSolventDialog(QDialog):
212
230
  self.table.setColumnWidth(_COL_FORMULA, 160)
213
231
  self.table.setColumnWidth(_COL_ELEC_CALC, 85)
214
232
  self.table.verticalHeader().setVisible(False)
233
+ self.table.setEditTriggers(
234
+ QAbstractItemView.EditTrigger.CurrentChanged
235
+ | QAbstractItemView.EditTrigger.SelectedClicked
236
+ | QAbstractItemView.EditTrigger.DoubleClicked
237
+ | QAbstractItemView.EditTrigger.AnyKeyPressed
238
+ | QAbstractItemView.EditTrigger.EditKeyPressed
239
+ )
240
+ self._solvent_delegate = SolventNameDelegate(self.table)
241
+ self.table.setItemDelegateForColumn(_COL_FORMULA, self._solvent_delegate)
215
242
  self.table.itemChanged.connect(self._on_formula_changed)
216
243
  layout.addWidget(self.table)
217
244
 
@@ -242,7 +269,7 @@ class SqueezeSolventDialog(QDialog):
242
269
  existing_details = self.cif[self._cfg['details_key']]
243
270
  if existing_details:
244
271
  self._details_user_modified = True
245
- self.details_edit.setPlainText(existing_details)
272
+ self.details_edit.setPlainText(retranslate_delimiter(existing_details))
246
273
  self.details_edit.textChanged.connect(self._on_details_text_changed)
247
274
 
248
275
  # button box
@@ -408,9 +435,25 @@ class SqueezeSolventDialog(QDialog):
408
435
  def _on_formula_changed(self, item: QTableWidgetItem) -> None:
409
436
  if item.column() != _COL_FORMULA:
410
437
  return
438
+ self._resolve_item_formula(item)
411
439
  self._update_row_calculations(item.row())
412
440
  self._regenerate_details()
413
441
 
442
+ @staticmethod
443
+ def _resolve_item_formula(item: QTableWidgetItem) -> None:
444
+ """Replace a solvent name in *item* by its sum formula."""
445
+ text = item.text()
446
+ resolved = resolve_formula(text)
447
+ if resolved == text:
448
+ return
449
+ table = item.tableWidget()
450
+ blocked = table.signalsBlocked() if table else True
451
+ if table:
452
+ table.blockSignals(True)
453
+ item.setText(resolved)
454
+ if table:
455
+ table.blockSignals(blocked)
456
+
414
457
  def _update_row_calculations(self, row_idx: int) -> None:
415
458
  """Recalculate Electrons (calc.) and Δ for a given row."""
416
459
  formula_item = self.table.item(row_idx, _COL_FORMULA)
@@ -458,6 +501,7 @@ class SqueezeSolventDialog(QDialog):
458
501
  item = self.table.item(row_idx, _COL_FORMULA)
459
502
  if item:
460
503
  item.setText(formula)
504
+ self._resolve_item_formula(item)
461
505
  self._update_row_calculations(row_idx)
462
506
  self.table.blockSignals(False)
463
507
  self._regenerate_details()
@@ -475,7 +519,7 @@ class SqueezeSolventDialog(QDialog):
475
519
  'nr' : row_idx + 1,
476
520
  'volume' : vol_item.text() if vol_item else '?',
477
521
  'electrons_platon': elec_item.text() if elec_item else '?',
478
- 'formula' : formula_item.text() if formula_item else '',
522
+ 'formula' : resolve_formula(formula_item.text()) if formula_item else '',
479
523
  })
480
524
  text = build_details_text(void_rows, method=self._loop_mode.value)
481
525
  # Block textChanged so our programmatic update doesn't set _details_user_modified
@@ -513,7 +557,7 @@ class SqueezeSolventDialog(QDialog):
513
557
  new_formulae: list[str] = []
514
558
  for row_idx in range(n_rows):
515
559
  item = self.table.item(row_idx, _COL_FORMULA)
516
- formula = item.text().strip() if item else '?'
560
+ formula = resolve_formula(item.text()).strip() if item else '?'
517
561
  new_formulae.append(quote(formula) if formula else '?')
518
562
 
519
563
  # Reconstruct column data with the updated content column
@@ -5,10 +5,6 @@
5
5
  # and you think this stuff is worth it, you can buy me a beer in return.
6
6
  # Dr. Daniel Kratzert
7
7
  # ----------------------------------------------------------------------------
8
- from contextlib import suppress
9
- from typing import Union
10
-
11
- from docx.text.paragraph import Paragraph
12
8
  from docxtpl import RichText
13
9
 
14
10
  from finalcif import VERSION
@@ -43,89 +39,6 @@ Missing: d*trek
43
39
  """
44
40
 
45
41
 
46
- class ReferenceList:
47
- """
48
- This reference list holds a list of all used references. During each self.append(Reference()), a new reference is
49
- appended to the list.
50
- At the end of the document, a numbered list of references can be generated with self.make_make_literature_list().
51
- """
52
-
53
- def __init__(self, paragraph: Paragraph):
54
- self.paragraph = paragraph
55
- self._references = []
56
-
57
- def append(self, ref: Union[list['Reference'], tuple['Reference'], 'Reference']) -> None:
58
- """Adds a superscript list of reference numbers in brackets to the document."""
59
- if isinstance(ref, list | tuple):
60
- if not ref:
61
- return None
62
- self._append_list(ref)
63
- else:
64
- if ref not in self._references:
65
- self._references.append(ref)
66
- self.paragraph.add_run(f'[{self._references.index(ref) + 1}]').font.superscript = True
67
- # better not here:
68
- # self.paragraph.add_run(' ')
69
-
70
- def _append_list(self, reflist: list) -> None:
71
- reflst_long = []
72
- self.paragraph.add_run('[').font.superscript = True
73
- reflist = [x for x in reflist if x]
74
- for n, ref in enumerate(reflist):
75
- if ref not in self._references:
76
- self._references.append(ref)
77
- refnum = self._references.index(ref) + 1
78
- reflst_long.append(refnum)
79
- reftxt = self.get_sequence(reflst_long)
80
- self.paragraph.add_run(reftxt).font.superscript = True
81
- self.paragraph.add_run(']').font.superscript = True
82
-
83
- @staticmethod
84
- def get_sequence(stringlist: list[int]) -> str:
85
- """
86
- Converts a list of numbers into a string of numbers where recurring sequences
87
- are described with a range.
88
-
89
- >>> ReferenceList.get_sequence([1, 3, 4, 5, 6, 8, 11])
90
- '1,3-6,8,11'
91
- """
92
- folg = []
93
- start = 0
94
- for n, val in enumerate(stringlist):
95
- val = int(val)
96
- nextval = 0
97
- with suppress(IndexError):
98
- nextval = int(stringlist[n + 1])
99
- nextnext = 0
100
- with suppress(IndexError):
101
- nextnext = int(stringlist[n + 2])
102
- # a sequence starts:
103
- if nextnext == val + 2 and not start:
104
- start = val
105
- # in a sequence and next value is not +1 -> squence ends with val:
106
- if start and nextval != val + 1:
107
- folg.append(f'{start}-{val}')
108
- start = 0
109
- continue
110
- # everything outside a sequence:
111
- if not start:
112
- folg.append(val)
113
- return ','.join([str(x) for x in folg])
114
-
115
- def make_literature_list(self, document):
116
- if len(self._references) < 10:
117
- template = 'references_ni'
118
- else:
119
- template = 'references'
120
- for num, ref in enumerate(self._references, 1):
121
- paragraph_reflist = document.add_paragraph('', template)
122
- paragraph_reflist.add_run(f'[{num!s}] \t')
123
- ref.add_reference(paragraph_reflist)
124
-
125
- def __repr__(self):
126
- return '\n'.join([f'[{num}] {x}' for num, x in enumerate(self._references)])
127
-
128
-
129
42
  class Reference:
130
43
  def __init__(self):
131
44
  self.authors = ''
@@ -137,31 +50,6 @@ class Reference:
137
50
  self.program = ''
138
51
  self.count = 0
139
52
 
140
- def add_reference(self, p: Paragraph) -> None:
141
- if self.authors:
142
- p.add_run(self.authors)
143
- p.add_run(', ')
144
- if self.journal:
145
- p.add_run(self.journal).italic = True
146
- if not self.journal.endswith('.'):
147
- p.add_run(', ')
148
- else:
149
- p.add_run(' ')
150
- if self.year:
151
- p.add_run(self.year).bold = True
152
- p.add_run(', ')
153
- if self.volume:
154
- p.add_run(self.volume).italic = True
155
- p.add_run(', ')
156
- if self.pages:
157
- p.add_run(self.pages)
158
- if self.doi:
159
- p.add_run(', ')
160
- if self.doi:
161
- p.add_run(self.doi)
162
- if any([self.journal, self.pages, self.year, self.volume, self.doi]):
163
- p.add_run('.')
164
-
165
53
  @property
166
54
  def richtext(self) -> RichText:
167
55
  r = RichText('')
@@ -589,8 +477,3 @@ class CrysalisProReference(Reference):
589
477
  self.journal = version
590
478
  self.year = year
591
479
  self.pages = pages
592
-
593
-
594
- if __name__ == '__main__':
595
- r = ReferenceList.get_sequence([1, 2, 3, 6])
596
- print(r)
@@ -1,18 +1,14 @@
1
1
  import os
2
2
 
3
3
  import gemmi
4
- from docx import Document
5
4
  from docx.oxml.xmlchemy import BaseOxmlElement
6
- from docx.text.paragraph import Paragraph
7
- from docx.text.run import Run
8
5
  from lxml import etree
9
6
  from lxml.etree import XSLTAccessControl
10
7
 
11
8
  from finalcif.app_path import application_path
12
9
  from finalcif.cif.cif_file_io import CifContainer
13
10
  from finalcif.cif.text import retranslate_delimiter, string_to_utf8
14
- from finalcif.report import references
15
- from finalcif.tools.misc import (protected_space, angstrom, remove_line_endings)
11
+ from finalcif.tools.misc import angstrom, remove_line_endings
16
12
 
17
13
 
18
14
  def math_to_word(eq: str) -> BaseOxmlElement:
@@ -57,41 +53,6 @@ def _get_cooling_device(cif: CifContainer) -> str:
57
53
  return ''
58
54
 
59
55
 
60
- class FormatMixin:
61
-
62
- def bold(self, run: Run):
63
- r = run.bold = True
64
- return r
65
-
66
-
67
- class SpaceChar:
68
- def __init__(self, paragraph: Paragraph):
69
- self.p = paragraph
70
-
71
- def regular(self) -> None:
72
- self.p.add_run(' ')
73
-
74
- def protected(self):
75
- self.p.add_run(protected_space)
76
-
77
-
78
- class RefinementDetails:
79
- def __init__(self, cif: CifContainer, document: Document):
80
- ph = document.add_paragraph(style='Heading 2')
81
- ph.add_run(text=fr"Refinement details for {cif.block.name}")
82
- p = document.add_paragraph()
83
- try:
84
- p.style = document.styles['fliesstext']
85
- except KeyError:
86
- print('DBG> Text style not found')
87
- text = ' '.join(cif['_refine_special_details'].splitlines(keepends=False))
88
- if cif['_olex2_refine_details']:
89
- text += ' '.join(cif['_olex2_refine_details'].splitlines(keepends=False))
90
- # Replacing semicolon, because it can damage the CIF:
91
- text = text.replace(';', '.')
92
- p.add_run(string_to_utf8(text).strip())
93
-
94
-
95
56
  def get_inf_article(next_word: str) -> str:
96
57
  if not next_word:
97
58
  return 'a'
@@ -133,13 +94,3 @@ def format_radiation(radiation_type: str) -> list:
133
94
  return radtype
134
95
  else:
135
96
  return radtype
136
-
137
-
138
- def make_report_text(cif, document: Document) -> references.ReferenceList:
139
- paragr = document.add_paragraph()
140
- try:
141
- paragr.style = document.styles['fliesstext']
142
- except KeyError:
143
- print('DBG> Text style not found')
144
- ref = references.ReferenceList(paragr)
145
- return ref