finalcif 136__tar.gz → 138__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 (153) hide show
  1. {finalcif-136 → finalcif-138}/.gitignore +1 -0
  2. {finalcif-136 → finalcif-138}/PKG-INFO +3 -3
  3. finalcif-138/finalcif/__init__.py +2 -0
  4. {finalcif-136 → finalcif-138}/finalcif/appwindow.py +37 -18
  5. {finalcif-136 → finalcif-138}/finalcif/cif/cif_dict_foo.py +26 -2
  6. {finalcif-136 → finalcif-138}/finalcif/cif/cif_file_io.py +52 -42
  7. {finalcif-136 → finalcif-138}/finalcif/cif/cif_order.py +20 -1
  8. {finalcif-136 → finalcif-138}/finalcif/cif/core_dict.py +1732 -734
  9. {finalcif-136 → finalcif-138}/finalcif/cif/hkl.py +5 -2
  10. {finalcif-136 → finalcif-138}/finalcif/cif/modulation_dict.py +683 -323
  11. {finalcif-136 → finalcif-138}/finalcif/cif/powder_dict.py +500 -194
  12. {finalcif-136 → finalcif-138}/finalcif/cif/restraints_dict.py +323 -128
  13. {finalcif-136 → finalcif-138}/finalcif/cif/twin_dict.py +54 -26
  14. {finalcif-136 → finalcif-138}/finalcif/datafiles/bruker_data.py +21 -20
  15. {finalcif-136 → finalcif-138}/finalcif/datafiles/p4p_reader.py +2 -2
  16. {finalcif-136 → finalcif-138}/finalcif/datafiles/sadabs.py +1 -1
  17. {finalcif-136 → finalcif-138}/finalcif/equip_property/equipment.py +27 -6
  18. {finalcif-136 → finalcif-138}/finalcif/finalcif_start.py +2 -1
  19. {finalcif-136 → finalcif-138}/finalcif/gui/custom_classes.py +3 -1
  20. {finalcif-136 → finalcif-138}/finalcif/gui/import_selector.py +0 -1
  21. {finalcif-136 → finalcif-138}/finalcif/gui/loop_creator.py +1 -1
  22. {finalcif-136 → finalcif-138}/finalcif/gui/loop_creator_ui.py +0 -6
  23. {finalcif-136 → finalcif-138}/finalcif/gui/loop_creator_ui.ui +2 -14
  24. {finalcif-136 → finalcif-138}/finalcif/gui/loops.py +47 -6
  25. {finalcif-136 → finalcif-138}/finalcif/gui/plaintextedit.py +24 -8
  26. {finalcif-136 → finalcif-138}/finalcif/gui/text_templates_ui.py +0 -4
  27. {finalcif-136 → finalcif-138}/finalcif/gui/text_templates_ui.ui +0 -6
  28. {finalcif-136 → finalcif-138}/finalcif/gui/text_value_editor.py +16 -3
  29. finalcif-138/finalcif/gui/validators.py +269 -0
  30. {finalcif-136 → finalcif-138}/finalcif/report/references.py +30 -24
  31. {finalcif-136 → finalcif-138}/finalcif/report/report_text.py +35 -15
  32. {finalcif-136 → finalcif-138}/finalcif/report/tables.py +0 -2
  33. {finalcif-136 → finalcif-138}/finalcif/report/templated_report.py +173 -43
  34. finalcif-138/finalcif/template/machines/D8_VENTURE.cif +16 -0
  35. {finalcif-136 → finalcif-138}/finalcif/template/report.tmpl +20 -20
  36. finalcif-138/finalcif/template/template1.docx +0 -0
  37. finalcif-138/finalcif/template/template_text.docx +0 -0
  38. finalcif-138/finalcif/template/template_without_text.docx +0 -0
  39. {finalcif-136 → finalcif-138}/finalcif/template/templates.py +9 -9
  40. {finalcif-136 → finalcif-138}/finalcif/tools/misc.py +4 -2
  41. {finalcif-136 → finalcif-138}/finalcif/tools/options.py +0 -1
  42. {finalcif-136 → finalcif-138}/finalcif/tools/platon.py +7 -4
  43. {finalcif-136 → finalcif-138}/finalcif.exe +0 -0
  44. {finalcif-136 → finalcif-138}/pyproject.toml +1 -1
  45. finalcif-136/finalcif/__init__.py +0 -2
  46. finalcif-136/finalcif/template/template1.docx +0 -0
  47. finalcif-136/finalcif/template/template_text.docx +0 -0
  48. finalcif-136/finalcif/template/template_without_text.docx +0 -0
  49. finalcif-136/work/bootstrap.min.css +0 -7
  50. {finalcif-136 → finalcif-138}/.gitattributes +0 -0
  51. {finalcif-136 → finalcif-138}/.run/Make Documentation MAC.run.xml +0 -0
  52. {finalcif-136 → finalcif-138}/.run/compile_ui_files.run.xml +0 -0
  53. {finalcif-136 → finalcif-138}/.run/make documentation Windows.run.xml +0 -0
  54. {finalcif-136 → finalcif-138}/CODE_OF_CONDUCT.md +0 -0
  55. {finalcif-136 → finalcif-138}/CONTRIBUTING.md +0 -0
  56. {finalcif-136 → finalcif-138}/Finalcif_installer_win.spec +0 -0
  57. {finalcif-136 → finalcif-138}/Finalcif_linux_onefile.spec +0 -0
  58. {finalcif-136 → finalcif-138}/Finalcif_mac.spec +0 -0
  59. {finalcif-136 → finalcif-138}/Hydrogen_solution_type.cif +0 -0
  60. {finalcif-136 → finalcif-138}/LICENSE +0 -0
  61. {finalcif-136 → finalcif-138}/README.md +0 -0
  62. {finalcif-136 → finalcif-138}/finalcif/app_path.py +0 -0
  63. {finalcif-136 → finalcif-138}/finalcif/cif/__init__.py +0 -0
  64. {finalcif-136 → finalcif-138}/finalcif/cif/all_cif_dicts.py +0 -0
  65. {finalcif-136 → finalcif-138}/finalcif/cif/atoms.py +0 -0
  66. {finalcif-136 → finalcif-138}/finalcif/cif/checkcif/__init__.py +0 -0
  67. {finalcif-136 → finalcif-138}/finalcif/cif/checkcif/checkcif.py +0 -0
  68. {finalcif-136 → finalcif-138}/finalcif/cif/cod/__init__.py +0 -0
  69. {finalcif-136 → finalcif-138}/finalcif/cif/cod/deposit.py +0 -0
  70. {finalcif-136 → finalcif-138}/finalcif/cif/cod/deposit_check.py +0 -0
  71. {finalcif-136 → finalcif-138}/finalcif/cif/cod/deposition_list.py +0 -0
  72. {finalcif-136 → finalcif-138}/finalcif/cif/cod/doi.py +0 -0
  73. {finalcif-136 → finalcif-138}/finalcif/cif/cod/upload.py +0 -0
  74. {finalcif-136 → finalcif-138}/finalcif/cif/cod/website_parser.py +0 -0
  75. {finalcif-136 → finalcif-138}/finalcif/cif/reference.py +0 -0
  76. {finalcif-136 → finalcif-138}/finalcif/cif/text.py +0 -0
  77. {finalcif-136 → finalcif-138}/finalcif/datafiles/__init__.py +0 -0
  78. {finalcif-136 → finalcif-138}/finalcif/datafiles/bruker_frame.py +0 -0
  79. {finalcif-136 → finalcif-138}/finalcif/datafiles/ccdc_mail.py +0 -0
  80. {finalcif-136 → finalcif-138}/finalcif/datafiles/data.py +0 -0
  81. {finalcif-136 → finalcif-138}/finalcif/datafiles/saint.py +0 -0
  82. {finalcif-136 → finalcif-138}/finalcif/datafiles/shelx_lst.py +0 -0
  83. {finalcif-136 → finalcif-138}/finalcif/datafiles/utils.py +0 -0
  84. {finalcif-136 → finalcif-138}/finalcif/displaymol/__init__.py +0 -0
  85. {finalcif-136 → finalcif-138}/finalcif/displaymol/molecule2D.py +0 -0
  86. {finalcif-136 → finalcif-138}/finalcif/displaymol/sdm.py +0 -0
  87. {finalcif-136 → finalcif-138}/finalcif/displaymol/vtk_molecule.py +0 -0
  88. {finalcif-136 → finalcif-138}/finalcif/equip_property/__init__.py +0 -0
  89. {finalcif-136 → finalcif-138}/finalcif/equip_property/author_loop_templates.py +0 -0
  90. {finalcif-136 → finalcif-138}/finalcif/equip_property/properties.py +0 -0
  91. {finalcif-136 → finalcif-138}/finalcif/equip_property/tools.py +0 -0
  92. {finalcif-136 → finalcif-138}/finalcif/gui/__init__.py +0 -0
  93. {finalcif-136 → finalcif-138}/finalcif/gui/combobox.py +0 -0
  94. {finalcif-136 → finalcif-138}/finalcif/gui/dialogs.py +0 -0
  95. {finalcif-136 → finalcif-138}/finalcif/gui/edit_button.py +0 -0
  96. {finalcif-136 → finalcif-138}/finalcif/gui/equipmenttable.py +0 -0
  97. {finalcif-136 → finalcif-138}/finalcif/gui/file_editor.py +0 -0
  98. {finalcif-136 → finalcif-138}/finalcif/gui/finalcif_gui_ui.py +0 -0
  99. {finalcif-136 → finalcif-138}/finalcif/gui/finalcif_gui_ui.ui +0 -0
  100. {finalcif-136 → finalcif-138}/finalcif/gui/import_selector_ui.py +0 -0
  101. {finalcif-136 → finalcif-138}/finalcif/gui/import_selector_ui.ui +0 -0
  102. {finalcif-136 → finalcif-138}/finalcif/gui/mainstackwidget.py +0 -0
  103. {finalcif-136 → finalcif-138}/finalcif/gui/mixins.py +0 -0
  104. {finalcif-136 → finalcif-138}/finalcif/gui/new_key_dialog.py +0 -0
  105. {finalcif-136 → finalcif-138}/finalcif/gui/new_key_dialog_ui.py +0 -0
  106. {finalcif-136 → finalcif-138}/finalcif/gui/new_key_dialog_ui.ui +0 -0
  107. {finalcif-136 → finalcif-138}/finalcif/gui/playground.py +0 -0
  108. {finalcif-136 → finalcif-138}/finalcif/gui/propertytable.py +0 -0
  109. {finalcif-136 → finalcif-138}/finalcif/gui/spell_check_edit.py +0 -0
  110. {finalcif-136 → finalcif-138}/finalcif/gui/vrf_classes.py +0 -0
  111. {finalcif-136 → finalcif-138}/finalcif/icon/__init__.py +0 -0
  112. {finalcif-136 → finalcif-138}/finalcif/icon/finalcif.png +0 -0
  113. {finalcif-136 → finalcif-138}/finalcif/icon/finalcif2.ico +0 -0
  114. {finalcif-136 → finalcif-138}/finalcif/icon/finalcif2.png +0 -0
  115. {finalcif-136 → finalcif-138}/finalcif/icon/multitable.ico +0 -0
  116. {finalcif-136 → finalcif-138}/finalcif/icon/multitable.png +0 -0
  117. {finalcif-136 → finalcif-138}/finalcif/icon/multitable.xcf +0 -0
  118. {finalcif-136 → finalcif-138}/finalcif/report/__init__.py +0 -0
  119. {finalcif-136 → finalcif-138}/finalcif/report/archive_report.py +0 -0
  120. {finalcif-136 → finalcif-138}/finalcif/report/gui/__init__.py +0 -0
  121. {finalcif-136 → finalcif-138}/finalcif/report/gui/mainwindow.py +0 -0
  122. {finalcif-136 → finalcif-138}/finalcif/report/gui/mainwindow.ui +0 -0
  123. {finalcif-136 → finalcif-138}/finalcif/report/mtools.py +0 -0
  124. {finalcif-136 → finalcif-138}/finalcif/report/symm.py +0 -0
  125. {finalcif-136 → finalcif-138}/finalcif/template/__init__.py +0 -0
  126. {finalcif-136 → finalcif-138}/finalcif/template/bootstrap/bootstrap.min.css +0 -0
  127. {finalcif-136 → finalcif-138}/finalcif/template/mathml2omml.xsl +0 -0
  128. {finalcif-136 → finalcif-138}/finalcif/template/template_for_multitable.docx +0 -0
  129. {finalcif-136 → finalcif-138}/finalcif/tools/__init__.py +0 -0
  130. {finalcif-136 → finalcif-138}/finalcif/tools/download.py +0 -0
  131. {finalcif-136 → finalcif-138}/finalcif/tools/dsrmath.py +0 -0
  132. {finalcif-136 → finalcif-138}/finalcif/tools/pupdate.py +0 -0
  133. {finalcif-136 → finalcif-138}/finalcif/tools/settings.py +0 -0
  134. {finalcif-136 → finalcif-138}/finalcif/tools/shred.py +0 -0
  135. {finalcif-136 → finalcif-138}/finalcif/tools/space_groups.py +0 -0
  136. {finalcif-136 → finalcif-138}/finalcif/tools/spgr_format.py +0 -0
  137. {finalcif-136 → finalcif-138}/finalcif/tools/statusbar.py +0 -0
  138. {finalcif-136 → finalcif-138}/finalcif/tools/sumformula.py +0 -0
  139. {finalcif-136 → finalcif-138}/install_requirements +0 -0
  140. {finalcif-136 → finalcif-138}/install_requirements.bat +0 -0
  141. {finalcif-136 → finalcif-138}/requirements.txt +0 -0
  142. {finalcif-136 → finalcif-138}/requirements_devel.txt +0 -0
  143. {finalcif-136 → finalcif-138}/requirements_doku.txt +0 -0
  144. {finalcif-136 → finalcif-138}/run_finalcif +0 -0
  145. {finalcif-136 → finalcif-138}/run_finalcif.bat +0 -0
  146. {finalcif-136 → finalcif-138}/screenshots/finalcif_checkcif.png +0 -0
  147. {finalcif-136 → finalcif-138}/screenshots/finalcif_details.png +0 -0
  148. {finalcif-136 → finalcif-138}/screenshots/finalcif_loops.png +0 -0
  149. {finalcif-136 → finalcif-138}/screenshots/finalcif_loops2.png +0 -0
  150. {finalcif-136 → finalcif-138}/screenshots/finalcif_main.png +0 -0
  151. {finalcif-136 → finalcif-138}/screenshots/finalcif_report.png +0 -0
  152. {finalcif-136 → finalcif-138}/screenshots/finalcif_responses.png +0 -0
  153. {finalcif-136 → finalcif-138}/update.exe +0 -0
@@ -219,3 +219,4 @@ test-data/1000007-multi-multitable.docx
219
219
  test-data/report_1000007-multi-finalcif.docx
220
220
  test.html
221
221
 
222
+ work/~$test.docx
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: finalcif
3
- Version: 136
3
+ Version: 138
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
@@ -11,7 +11,7 @@ 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
14
- Requires-Python: >=3.9
14
+ Requires-Python: >=3.10
15
15
  Requires-Dist: altgraph
16
16
  Requires-Dist: certifi
17
17
  Requires-Dist: chardet
@@ -0,0 +1,2 @@
1
+
2
+ VERSION = 138
@@ -13,18 +13,20 @@ from contextlib import suppress
13
13
  from datetime import datetime
14
14
  from math import sin, radians
15
15
  from pathlib import Path, WindowsPath
16
- from shutil import which
17
16
  from typing import Union, Dict, Tuple, List, Optional
18
17
 
19
18
  import gemmi.cif
20
- import requests # type: ignore
21
- from PyQt5 import QtCore, QtGui, QtWebEngineWidgets
19
+ import requests
20
+ from PyQt5 import QtCore, QtGui, QtWebEngineWidgets, QtWidgets
22
21
  from PyQt5.QtCore import Qt, QEvent
23
- from PyQt5.QtWidgets import QMainWindow, QShortcut, QCheckBox, QListWidgetItem, QApplication, \
24
- QPlainTextEdit, QFileDialog, QMessageBox, QScrollBar
22
+ from PyQt5.QtWidgets import (QMainWindow, QShortcut, QCheckBox, QListWidgetItem, QApplication,
23
+ QPlainTextEdit, QFileDialog, QMessageBox, QScrollBar)
25
24
  from gemmi import cif
26
25
 
27
26
  from finalcif import VERSION
27
+ # QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
28
+ # QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
29
+ from finalcif.cif.checkcif.checkcif import MyHTMLParser, AlertHelp, CheckCif
28
30
  from finalcif.cif.cif_file_io import CifContainer, GemmiError
29
31
  from finalcif.cif.cod.deposit import CODdeposit
30
32
  from finalcif.cif.text import utf8_to_str, quote
@@ -67,7 +69,6 @@ if app is None:
67
69
  app = QApplication([])
68
70
  with suppress(ImportError):
69
71
  import qtawesome as qta
70
- from finalcif.cif.checkcif.checkcif import MyHTMLParser, AlertHelp, CheckCif
71
72
 
72
73
 
73
74
  class AppWindow(QMainWindow):
@@ -633,10 +634,9 @@ class AppWindow(QMainWindow):
633
634
 
634
635
  def setTextEditSizes(self):
635
636
  for ui in [self.ui.Spacegroup_top_LineEdit, self.ui.CCDCNumLineEdit, self.ui.SumFormMainLineEdit]:
636
- ui.setFixedHeight(self.ui.appendCifPushButton.height() + 6)
637
- ui.setFixedHeight(self.ui.appendCifPushButton.height() + 6)
638
- ui.setFixedHeight(self.ui.appendCifPushButton.height() + 6)
637
+ ui.setFixedHeight(self.ui.appendCifPushButton.height())
639
638
  vScrollBar = ui.verticalScrollBar()
639
+ # Scroll down a bit, or text will not be in the vertical center:
640
640
  vScrollBar.triggerAction(QScrollBar.SliderSingleStepAdd)
641
641
 
642
642
  def moveEvent(self, a0: QtGui.QMoveEvent) -> None:
@@ -1035,7 +1035,7 @@ class AppWindow(QMainWindow):
1035
1035
  log_widget=self.ui.CheckCifLogPlainTextEdit,
1036
1036
  output_widget=self.ui.CheckcifPlaintextEdit,
1037
1037
  cif_file=self.cif.fileobj)
1038
- if not Path(which(runner.platon_exe)).exists():
1038
+ if runner.platon_exe is not None and not Path(runner.platon_exe).exists():
1039
1039
  self.ui.CheckCifLogPlainTextEdit.setPlainText('\nPlaton executable not found!')
1040
1040
  self.ui.CheckCifLogPlainTextEdit.appendPlainText(
1041
1041
  'You can download Platon at http://www.platonsoft.nl/platon/\n')
@@ -1135,12 +1135,20 @@ class AppWindow(QMainWindow):
1135
1135
  make_multi_tables(cif=self.cif, output_filename=str(multi_table_document))
1136
1136
  else:
1137
1137
  print('Report with templates')
1138
- t = TemplatedReport(format=ReportFormat.RICHTEXT, options=self.options, cif=self.cif)
1139
- ok = t.make_templated_docx_report(output_filename=str(report_filename),
1140
- picfile=picfile,
1141
- template_path=Path(self.get_checked_templates_list_text()))
1142
- # t = TemplatedReport(format=ReportFormat.HTML, options=self.options, cif=self.cif)
1143
- # t.make_templated_html_report(options=self.options, picfile=picfile)
1138
+ template_path = Path(self.get_checked_templates_list_text())
1139
+ ok = False
1140
+ if template_path.suffix in ('.docx',):
1141
+ t = TemplatedReport(format=ReportFormat.RICHTEXT, options=self.options, cif=self.cif)
1142
+ ok = t.make_templated_docx_report(output_filename=str(report_filename),
1143
+ picfile=picfile,
1144
+ template_path=Path(self.get_checked_templates_list_text()))
1145
+ elif template_path.suffix in ('.html', '.tmpl'):
1146
+ t = TemplatedReport(format=ReportFormat.HTML, options=self.options, cif=self.cif)
1147
+ report_filename = report_filename.with_suffix('.html')
1148
+ ok = t.make_templated_html_report(output_filename=str(report_filename),
1149
+ picfile=picfile,
1150
+ template_path=template_path.parent,
1151
+ template_file=template_path.name)
1144
1152
  if not ok:
1145
1153
  return None
1146
1154
  except FileNotFoundError as e:
@@ -1308,7 +1316,7 @@ class AppWindow(QMainWindow):
1308
1316
  previous_values.append(loop2.values)
1309
1317
  for loop in self.cif.loops:
1310
1318
  if loop.values not in previous_values:
1311
- changes_cif.add_loop_to_cif(loop_tags=loop.tags, loop_values=loop.values)
1319
+ changes_cif.add_loop_to_cif(loop_tags=loop.tags, row_values=loop.values)
1312
1320
  changes_cif.save(filename=self.finalcif_changes_filename)
1313
1321
 
1314
1322
  def get_changes_cif(self, finalcif_changes_filename: Path) -> CifContainer:
@@ -1336,7 +1344,7 @@ class AppWindow(QMainWindow):
1336
1344
  self.add_row(key=key, value=value)
1337
1345
  self.ui.cif_main_table.setText(key=key, column=Column.EDIT, color=None, txt=value)
1338
1346
  for loop in changes.loops:
1339
- self.cif.add_loop_to_cif(loop_tags=loop.tags, loop_values=loop.values)
1347
+ self.cif.add_loop_to_cif(loop_tags=loop.tags, row_values=loop.values)
1340
1348
  return True
1341
1349
 
1342
1350
  def is_row_a_cif_item(self, vhead: str) -> bool:
@@ -1513,6 +1521,7 @@ class AppWindow(QMainWindow):
1513
1521
  raise
1514
1522
  # unable_to_open_message(Path(self.cif.filename), not_ok)
1515
1523
  self.load_recent_cifs_list()
1524
+ self.set_vertical_header_width()
1516
1525
  if self.options.track_changes and load_changes:
1517
1526
  changes_exist = False
1518
1527
  if self.changes_cif_has_zero_size():
@@ -1557,6 +1566,16 @@ class AppWindow(QMainWindow):
1557
1566
  t = QtCore.QTimer(self)
1558
1567
  t.singleShot(1000, self.check_cecksums)
1559
1568
 
1569
+ def set_vertical_header_width(self):
1570
+ """
1571
+ Sets the width of the vertical header to the length of the longest string in the list.
1572
+ """
1573
+ vheader: QtWidgets.QHeaderView = self.ui.cif_main_table.verticalHeader()
1574
+ fm = QtGui.QFontMetrics(vheader.font(), self)
1575
+ # noinspection PyTypeChecker
1576
+ longest_string: str = max(self.ui.cif_main_table.vheaderitems, key=len)
1577
+ vheader.setMaximumWidth(fm.width(longest_string + 'O'))
1578
+
1560
1579
  def changes_cif_has_values(self) -> bool:
1561
1580
  try:
1562
1581
  return not self.get_changes_cif(self.finalcif_changes_filename).is_empty()
@@ -21,6 +21,15 @@ def format_definition(definition: str):
21
21
  return newdef
22
22
 
23
23
 
24
+ formats = {
25
+ 'char' : 'string',
26
+ 'uchar': 'case-insensitive string',
27
+ 'numb' : 'number (int or float)',
28
+ 'null' : '? (unknown)' or '. (not applicable)',
29
+ '' : '--'
30
+ }
31
+
32
+
24
33
  def process_cif_dict(cdic):
25
34
  """
26
35
  Creates a dictionary with cif keywords as key and help text as value.
@@ -36,6 +45,7 @@ def process_cif_dict(cdic):
36
45
  example_detail = item.get('_example_detail', '')
37
46
  enumeration = item.get('_enumeration', '')
38
47
  enumeration_detail = item.get('_enumeration_detail', '')
48
+ default = item.get('_enumeration_default', '')
39
49
  if example:
40
50
  if isinstance(example, list) and not enumeration_detail:
41
51
  example = '\n'.join([str(x) for x in example])
@@ -52,15 +62,29 @@ def process_cif_dict(cdic):
52
62
  ["{}\n\t{}\n".format(str(x), str(y)) for x, y in zip(enumeration, enumeration_detail)])
53
63
  enumeration = format_definition(escape(str(enumeration)))
54
64
  definition = '{}\n\n<h3>Example:</h3>\n{}'.format(definition, enumeration)
65
+ type_format = item.get("_type", "")
66
+ enum_range = item.get('_enumeration_range', '')
55
67
  if isinstance(name, list):
56
68
  for n in name:
57
69
  # For keys like 'atom_site_aniso_b_[]' where the name has several subnames:
58
- alldic[n] = '<pre><h2>{}</h2>{}</pre>'.format(n, format_definition(definition))
70
+ alldic[n] = help_text(definition, n, type_format, enum_range, default)
59
71
  else:
60
- alldic[name] = '<pre><h2>{}</h2>{}</pre>'.format(name, format_definition(definition))
72
+ alldic[name] = help_text(definition, name, type_format, enum_range, default)
61
73
  return alldic
62
74
 
63
75
 
76
+ def help_text(definition: str, name: str, type_format: str, enum_range: str, enum_default) -> str:
77
+ if enum_range.endswith(':'):
78
+ enum_range = enum_range + '∞'
79
+ limits = f'\n<br><p><h4>Limits:</h4> {enum_range} </p>'
80
+ default = f'\n<br><p><h4>Default:</h4> {enum_default} </p>'
81
+ return (f'<pre><h2>{name}</h2>{format_definition(definition)}</pre>'
82
+ f'\n<br><p><h4>Type:</h4> {formats.get(type_format, "")}</p>'
83
+ f'{limits if enum_range else ""}'
84
+ f'{default if enum_default else ""}'
85
+ )
86
+
87
+
64
88
  def load_cif_as_dictionary(link, path_to_cif):
65
89
  if not path_to_cif.exists():
66
90
  get_dictionary_cif(link, path_to_cif)
@@ -13,6 +13,7 @@ from typing import Dict, List, Tuple, Union, Generator
13
13
 
14
14
  import gemmi
15
15
  from gemmi.cif import as_string, Document, Loop
16
+ from packaging.version import Version
16
17
  from shelxfile import Shelxfile
17
18
 
18
19
  from finalcif.cif.cif_order import order, special_keys
@@ -39,6 +40,8 @@ class CifContainer():
39
40
  new_block: Create a new block (new file) if a name is given. Otherwise, just
40
41
  the existing document is opened.
41
42
  """
43
+ self._hkl_file: str = ''
44
+ self._abs_hkl_details: Dict[str, str] = {}
42
45
  if isinstance(file, str):
43
46
  self.fileobj = Path(file)
44
47
  elif isinstance(file, Path):
@@ -94,7 +97,7 @@ class CifContainer():
94
97
  The suffix needs '-finalcif' in order to contain the finalcif ending.
95
98
  "foo/bar/baz-finalcif.cif"
96
99
 
97
- :param forece_strip: Forces to strip the filename also after the '-finalcif' string.
100
+ :param force_strip: Forces to strip the filename also after the '-finalcif' string.
98
101
  """
99
102
  file_witout_finalcif = strip_finalcif_of_name(Path(self.filename).stem, till_name_ends=force_strip)
100
103
  filename = self.path_base.joinpath(Path(prefix + file_witout_finalcif + suffix))
@@ -141,7 +144,9 @@ class CifContainer():
141
144
 
142
145
  @property
143
146
  def hkl_extra_info(self):
144
- return self._abs_hkl_details()
147
+ if not self._abs_hkl_details:
148
+ self._abs_hkl_details = self._sadabs_hkl_details()
149
+ return self._abs_hkl_details
145
150
 
146
151
  def check_hkl_min_max(self) -> None:
147
152
  if not all([self['_diffrn_reflns_limit_h_min'], self['_diffrn_reflns_limit_h_max'],
@@ -198,8 +203,8 @@ class CifContainer():
198
203
  """
199
204
  This method returns an empty string when the item value is '?'
200
205
  """
201
- if self.block.find_value(item):
202
- return as_string(self.block.find_value(item))
206
+ if value := self.block.find_value(item):
207
+ return as_string(value)
203
208
  else:
204
209
  return ''
205
210
 
@@ -255,15 +260,17 @@ class CifContainer():
255
260
  print(f'File {filename} is empty.')
256
261
  return
257
262
  self.order_cif_keys()
258
- print('Saving to', Path(filename).resolve())
259
- """
260
- # For later gemmi versions:
261
- options = gemmi.cif.WriteOptions()
262
- options.prefer_pairs = True
263
- options.compact = False
264
- options.align_pairs = 33
265
- options.align_loops = 30"""
266
- self.doc.write_file(str(filename), gemmi.cif.Style.Indent35)
263
+ print('Saving CIF to', Path(filename).resolve())
264
+ if Version(gemmi.__version__) > Version('0.6.3'):
265
+ options = gemmi.cif.WriteOptions()
266
+ options.prefer_pairs = False
267
+ options.compact = False
268
+ options.align_pairs = 33
269
+ options.align_loops = 15
270
+ options.misuse_hash = False
271
+ self.doc.write_file(str(filename), options=options)
272
+ else:
273
+ self.doc.write_file(str(filename), gemmi.cif.Style.Indent35)
267
274
 
268
275
  def order_cif_keys(self) -> None:
269
276
  """
@@ -304,10 +311,14 @@ class CifContainer():
304
311
  def hkl_file(self) -> str:
305
312
  hkl_loop: Loop = self.get_loop('_diffrn_refln_index_h')
306
313
  if hkl_loop and hkl_loop.width() > 4:
307
- return self._hkl_from_cif_format(hkl_loop)
314
+ if not self._hkl_file:
315
+ self._hkl_file = self._hkl_from_cif_format(hkl_loop)
316
+ return self._hkl_file
308
317
  else:
309
318
  # returns an empty string if no cif hkl was found:
310
- return self._hkl_from_shelx()
319
+ if not self._hkl_file:
320
+ self._hkl_file = self._hkl_from_shelx()
321
+ return self._hkl_file
311
322
 
312
323
  def _hkl_from_cif_format(self, hkl_loop: Loop) -> str:
313
324
  hkl_list = []
@@ -333,7 +344,7 @@ class CifContainer():
333
344
  return ''
334
345
 
335
346
  @property
336
- def hkl_as_cif(self):
347
+ def hkl_as_cif(self) -> str:
337
348
  return HKL(self.hkl_file, self.block.name, hklf_type=self.hklf_number).hkl_as_cif
338
349
 
339
350
  @property
@@ -362,7 +373,7 @@ class CifContainer():
362
373
  return self.hkl_file
363
374
 
364
375
  @staticmethod
365
- def _find_line_of_000(lines: str):
376
+ def _find_line_of_000(lines: str) -> int:
366
377
  pattern = re.compile(r'^\s+0\s+0\s+0\s+0.*')
367
378
  for num, line in enumerate(lines.splitlines(keepends=False)):
368
379
  found = pattern.match(line)
@@ -370,7 +381,7 @@ class CifContainer():
370
381
  return num
371
382
  return 0
372
383
 
373
- def _abs_hkl_details(self) -> Dict[str, str]:
384
+ def _sadabs_hkl_details(self) -> Dict[str, str]:
374
385
  """
375
386
  This method tries to determine the information witten at the end of a cif hkl file by sadabs.
376
387
  """
@@ -411,7 +422,7 @@ class CifContainer():
411
422
  all_sadabs_items[key] = gemmi.cif.as_string(val).strip()
412
423
  return all_sadabs_items
413
424
 
414
- def normal_search(self, hkl):
425
+ def normal_search(self, hkl) -> int:
415
426
  pattern = re.compile(r'\s+0\s+0\s+0\s+0')
416
427
  found = pattern.search(hkl)
417
428
  if found:
@@ -420,7 +431,7 @@ class CifContainer():
420
431
  zero_reflection_position = 0
421
432
  return zero_reflection_position
422
433
 
423
- def reversed_search(self, hkl_splitted: List):
434
+ def reversed_search(self, hkl_splitted: List[str]) -> int:
424
435
  pattern = re.compile(r'\s+0\s+0\s+0\s+0')
425
436
  zero_reflection_position = 0
426
437
  for num, line in enumerate(reversed(hkl_splitted)):
@@ -463,14 +474,20 @@ class CifContainer():
463
474
  loops.append(b.loop)
464
475
  return loops
465
476
 
466
- def add_loop_to_cif(self, loop_tags: List[str], loop_values: Union[list, tuple]) -> gemmi.cif.Loop:
477
+ def add_loop_to_cif(self, loop_tags: List[str], row_values: Union[List[str], Tuple[str]]) -> gemmi.cif.Loop:
467
478
  gemmi_loop = self.init_loop(loop_tags)
468
- for row in list(grouper(loop_values, len(loop_tags))):
479
+ for row in list(grouper(row_values, len(loop_tags))):
469
480
  gemmi_loop.add_row(row)
470
481
  return gemmi_loop
471
482
 
483
+ def add_loop_from_columns(self, loop_tags: List[str],
484
+ column_values: Union[List[List[str]], Tuple[Tuple[str]]]) -> gemmi.cif.Loop:
485
+ gemmi_loop = self.init_loop(loop_tags)
486
+ gemmi_loop.set_all_values(column_values)
487
+ return gemmi_loop
488
+
472
489
  @property
473
- def n_loops(self):
490
+ def n_loops(self) -> int:
474
491
  return len(self.loops)
475
492
 
476
493
  def get_loop(self, key_in_loop: str) -> gemmi.cif.Loop:
@@ -483,7 +500,7 @@ class CifContainer():
483
500
  return self.block.init_loop('', loop_keywords)
484
501
 
485
502
  @property
486
- def z_value(self):
503
+ def z_value(self) -> float:
487
504
  return self.atomic_struct.cell.volume / self.atomic_struct.cell.volume_per_image()
488
505
 
489
506
  @property
@@ -638,7 +655,7 @@ class CifContainer():
638
655
  @property
639
656
  def is_centrosymm(self) -> bool:
640
657
  """
641
- Whether a structuere is centro symmetric or not.
658
+ Whether a structure is centrosymmetric or not.
642
659
  """
643
660
  if not self.symmops or self.symmops == ['']:
644
661
  # Do not crash without symmops
@@ -749,8 +766,7 @@ class CifContainer():
749
766
  publ_loop = self.block.find_loop('_geom_bond_publ_flag')
750
767
  bond = namedtuple('bond', ('label1', 'label2', 'dist', 'symm'))
751
768
  for label1, label2, dist, symm, publ in zip(label1, label2, dist, symm, publ_loop):
752
- if without_h and (self.ishydrogen(label1) or self.ishydrogen(label2)) or \
753
- self.yes_not_set(publ, self._has_publ_flag_set(publ_loop)):
769
+ if (without_h and (self.ishydrogen(label1) or self.ishydrogen(label2)) or self.yes_not_set(publ)):
754
770
  continue
755
771
  else:
756
772
  yield bond(label1=label1, label2=label2, dist=dist, symm=self.checksymm(symm))
@@ -773,9 +789,6 @@ class CifContainer():
773
789
  return p.torsang
774
790
  return None
775
791
 
776
- def _has_publ_flag_set(self, publ_loop: gemmi.cif.Column) -> bool:
777
- return any([x[0].lower() == 'y' for x in list(publ_loop) if x])
778
-
779
792
  def angles(self, without_H: bool = False) -> Generator:
780
793
  label1 = self.block.find_loop('_geom_angle_atom_site_label_1')
781
794
  label2 = self.block.find_loop('_geom_angle_atom_site_label_2')
@@ -787,9 +800,8 @@ class CifContainer():
787
800
  angle = namedtuple('angle', ('label1', 'label2', 'label3', 'angle_val', 'symm1', 'symm2'))
788
801
  for label1, label2, label3, angle_val, symm1, symm2, publ in \
789
802
  zip(label1, label2, label3, angle_val, symm1, symm2, publ_loop):
790
- if without_H and (
791
- self.ishydrogen(label1) or self.ishydrogen(label2) or self.ishydrogen(label3)) or \
792
- self.yes_not_set(publ, self._has_publ_flag_set(publ_loop)):
803
+ if (without_H and (self.ishydrogen(label1) or self.ishydrogen(label2) or
804
+ self.ishydrogen(label3)) or (self.yes_not_set(publ))):
793
805
  continue
794
806
  else:
795
807
  yield angle(label1=label1, label2=label2, label3=label3, angle_val=angle_val,
@@ -837,9 +849,8 @@ class CifContainer():
837
849
  torsang, symm1, symm2,
838
850
  symm3,
839
851
  symm4, publ_loop):
840
- if without_h and (self.ishydrogen(label1) or self.ishydrogen(label2)
841
- or self.ishydrogen(label3) or self.ishydrogen(label3)) or \
842
- self.yes_not_set(publ, self._has_publ_flag_set(publ_loop)):
852
+ if (without_h and (self.ishydrogen(label1) or self.ishydrogen(label2)
853
+ or self.ishydrogen(label3) or self.ishydrogen(label3)) or self.yes_not_set(publ)):
843
854
  continue
844
855
  yield tors(label1=label1, label2=label2, label3=label3, label4=label4, torsang=torsang,
845
856
  symm1=self.checksymm(symm1),
@@ -859,14 +870,14 @@ class CifContainer():
859
870
  publ_loop = self.block.find_loop('_geom_hbond_publ_flag')
860
871
  hydr = namedtuple('HydrogenBond', ('label_d', 'label_h', 'label_a', 'dist_dh', 'dist_ha', 'dist_da',
861
872
  'angle_dha', 'symm'))
862
- for label_d, label_h, label_a, dist_dh, dist_ha, dist_da, angle_dha, symm, publ in \
863
- zip(label_d, label_h, label_a, dist_dh, dist_ha, dist_da, angle_dha, symm, publ_loop):
864
- if self.yes_not_set(publ, self._has_publ_flag_set(publ_loop)):
873
+ for label_d, label_h, label_a, dist_dh, dist_ha, dist_da, angle_dha, symm, publ in (
874
+ zip(label_d, label_h, label_a, dist_dh, dist_ha, dist_da, angle_dha, symm, publ_loop)):
875
+ if self.yes_not_set(publ):
865
876
  continue
866
877
  yield hydr(label_d, label_h, label_a, dist_dh, dist_ha, dist_da, angle_dha, self.checksymm(symm))
867
878
 
868
- def yes_not_set(self, publ: str, publ_flag):
869
- return publ_flag and publ not in {'y', 'yes'} or publ in {'n', 'no'}
879
+ def yes_not_set(self, publ: str):
880
+ return publ not in {'y', 'yes', '?', '.'} or publ in {'n', 'no'}
870
881
 
871
882
  def key_value_pairs(self) -> List[Tuple[str, str]]:
872
883
  """
@@ -957,7 +968,6 @@ class CifContainer():
957
968
  if __name__ == '__main__':
958
969
  # c = CifContainer('../41467_2015.cif')
959
970
  # c = CifContainer('test-data/p21c.cif')
960
- from pprint import pp
961
971
 
962
972
  c = CifContainer('test-data/DK_Zucker2_0m.cif')
963
973
  c.load_this_block(len(c.doc) - 1)
@@ -17,6 +17,13 @@ special_keys = ['_iucr_refine_instructions_details',
17
17
  ]
18
18
 
19
19
  order = [
20
+ '_audit_update_record',
21
+ '_audit_contact_author',
22
+ '_audit_contact_author_name',
23
+ '_audit_contact_author_phone',
24
+ '_audit_contact_author_email',
25
+ '_audit_contact_author_fax',
26
+ '_audit_contact_author_address',
20
27
  '_audit_author_id',
21
28
  '_audit_author_id_audit',
22
29
  '_audit_author_name',
@@ -128,7 +135,6 @@ order = [
128
135
  '_publ_contact_author_id_orcid',
129
136
  '_publ_contact_author_footnote',
130
137
 
131
-
132
138
  '_shelx_SHELXL_version_number',
133
139
  '_chemical_name_systematic',
134
140
  '_chemical_name_common',
@@ -341,4 +347,17 @@ order = [
341
347
  '_geom_details',
342
348
  '_geom_special_details',
343
349
 
350
+ '_bruker_diffrn_runs_wavelength',
351
+ '_bruker_diffrn_runs_temperature',
352
+ '_bruker_diffrn_runs_current',
353
+ '_bruker_diffrn_runs_voltage',
354
+ '_bruker_diffrn_runs_images',
355
+ '_bruker_diffrn_runs_time',
356
+ '_bruker_diffrn_runs_width',
357
+ '_bruker_diffrn_runs_axis',
358
+ '_bruker_diffrn_runs_chi',
359
+ '_bruker_diffrn_runs_phi',
360
+ '_bruker_diffrn_runs_omega',
361
+ '_bruker_diffrn_runs_theta',
362
+ '_bruker_diffrn_runs_distance',
344
363
  ]