finalcif 153__tar.gz → 155__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.
- {finalcif-153 → finalcif-155}/.gitignore +1 -0
- {finalcif-153 → finalcif-155}/PKG-INFO +1 -1
- finalcif-155/finalcif/__init__.py +2 -0
- {finalcif-153 → finalcif-155}/finalcif/appwindow.py +25 -3
- {finalcif-153 → finalcif-155}/finalcif/cif/atoms.py +3 -3
- {finalcif-153 → finalcif-155}/finalcif/cif/checkcif/checkcif.py +4 -1
- {finalcif-153 → finalcif-155}/finalcif/cif/cif_file_io.py +36 -27
- {finalcif-153 → finalcif-155}/finalcif/equip_property/author_loop_templates.py +14 -23
- {finalcif-153 → finalcif-155}/finalcif/finalcif_start.py +1 -1
- {finalcif-153 → finalcif-155}/finalcif/gui/finalcif_gui_ui.py +671 -667
- {finalcif-153 → finalcif-155}/finalcif/gui/finalcif_gui_ui.ui +13 -3
- {finalcif-153 → finalcif-155}/finalcif/report/templated_report.py +62 -39
- {finalcif-153 → finalcif-155}/finalcif/template/report.tmpl +7 -1
- {finalcif-153 → finalcif-155}/finalcif/tools/platon.py +10 -7
- {finalcif-153 → finalcif-155}/finalcif/tools/sumformula.py +0 -2
- finalcif-155/platon/README.txt +29 -0
- finalcif-155/platon/compile_platon.bat +4 -0
- finalcif-155/platon/platon_special.exe +0 -0
- finalcif-155/platon/platon_special.f +167105 -0
- finalcif-153/finalcif/__init__.py +0 -2
- {finalcif-153 → finalcif-155}/.gitattributes +0 -0
- {finalcif-153 → finalcif-155}/.python-version +0 -0
- {finalcif-153 → finalcif-155}/.ruff.toml +0 -0
- {finalcif-153 → finalcif-155}/.run/Make Documentation MAC.run.xml +0 -0
- {finalcif-153 → finalcif-155}/.run/compile_ui_files.run.xml +0 -0
- {finalcif-153 → finalcif-155}/.run/make documentation Windows.run.xml +0 -0
- {finalcif-153 → finalcif-155}/CODE_OF_CONDUCT.md +0 -0
- {finalcif-153 → finalcif-155}/CONTRIBUTING.md +0 -0
- {finalcif-153 → finalcif-155}/Finalcif_installer_win.spec +0 -0
- {finalcif-153 → finalcif-155}/Finalcif_linux_onefile.spec +0 -0
- {finalcif-153 → finalcif-155}/Finalcif_mac.spec +0 -0
- {finalcif-153 → finalcif-155}/Hydrogen_solution_type.cif +0 -0
- {finalcif-153 → finalcif-155}/LICENSE +0 -0
- {finalcif-153 → finalcif-155}/README.md +0 -0
- {finalcif-153 → finalcif-155}/finalcif/app_path.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/all_cif_dicts.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/checkcif/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/cif_dict_foo.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/cif_order.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/cod/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/cod/deposit.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/cod/deposit_check.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/cod/deposition_list.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/cod/doi.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/cod/upload.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/cod/website_parser.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/core_dict.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/hkl.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/modulation_dict.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/powder_dict.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/reference.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/restraints_dict.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/text.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/cif/twin_dict.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/ciforder/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/ciforder/order.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/ciforder/order_ui.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/ciforder/order_ui.ui +0 -0
- {finalcif-153 → finalcif-155}/finalcif/datafiles/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/datafiles/bruker_data.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/datafiles/bruker_frame.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/datafiles/ccdc_mail.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/datafiles/data.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/datafiles/p4p_reader.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/datafiles/sadabs.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/datafiles/saint.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/datafiles/shelx_lst.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/datafiles/utils.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/displaymol/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/displaymol/molecule2D.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/displaymol/sdm.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/displaymol/vtk_molecule.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/equip_property/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/equip_property/equipment.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/equip_property/properties.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/equip_property/tools.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/block_combobox.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/combobox.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/custom_classes.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/dialogs.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/edit_button.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/equipmenttable.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/file_editor.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/import_selector.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/import_selector_ui.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/import_selector_ui.ui +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/loop_creator.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/loop_creator_ui.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/loop_creator_ui.ui +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/loops.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/mainstackwidget.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/mixins.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/new_key_dialog.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/new_key_dialog_ui.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/new_key_dialog_ui.ui +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/plaintextedit.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/playground.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/propertytable.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/spell_check_edit.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/text_templates_ui.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/text_templates_ui.ui +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/text_value_editor.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/validators.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/gui/vrf_classes.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/icon/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/icon/finalcif.png +0 -0
- {finalcif-153 → finalcif-155}/finalcif/icon/finalcif2.ico +0 -0
- {finalcif-153 → finalcif-155}/finalcif/icon/finalcif2.png +0 -0
- {finalcif-153 → finalcif-155}/finalcif/icon/multitable.ico +0 -0
- {finalcif-153 → finalcif-155}/finalcif/icon/multitable.png +0 -0
- {finalcif-153 → finalcif-155}/finalcif/icon/multitable.xcf +0 -0
- {finalcif-153 → finalcif-155}/finalcif/report/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/report/archive_report.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/report/gui/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/report/gui/mainwindow.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/report/gui/mainwindow.ui +0 -0
- {finalcif-153 → finalcif-155}/finalcif/report/mtools.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/report/references.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/report/report_text.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/report/symm.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/report/tables.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/bootstrap/bootstrap.min.css +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/machines/D8_VENTURE.cif +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/mathml2omml.xsl +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/report_default.docx +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/template1.docx +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/template_for_multitable.docx +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/template_text.docx +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/template_without_text.docx +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/templates.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/xsl/convert.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/template/xsl/xmltohtml.xsl +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/__init__.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/chemparse.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/download.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/dsrmath.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/misc.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/options.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/pupdate.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/settings.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/shred.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/space_groups.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/spgr_format.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif/tools/statusbar.py +0 -0
- {finalcif-153 → finalcif-155}/finalcif.exe +0 -0
- {finalcif-153 → finalcif-155}/install_requirements +0 -0
- {finalcif-153 → finalcif-155}/install_requirements.bat +0 -0
- {finalcif-153 → finalcif-155}/pyproject.toml +0 -0
- {finalcif-153 → finalcif-155}/requirements.txt +0 -0
- {finalcif-153 → finalcif-155}/requirements_devel.txt +0 -0
- {finalcif-153 → finalcif-155}/requirements_doku.txt +0 -0
- {finalcif-153 → finalcif-155}/run_finalcif +0 -0
- {finalcif-153 → finalcif-155}/run_finalcif.bat +0 -0
- {finalcif-153 → finalcif-155}/screenshots/finalcif_checkcif.png +0 -0
- {finalcif-153 → finalcif-155}/screenshots/finalcif_details.png +0 -0
- {finalcif-153 → finalcif-155}/screenshots/finalcif_loops.png +0 -0
- {finalcif-153 → finalcif-155}/screenshots/finalcif_loops2.png +0 -0
- {finalcif-153 → finalcif-155}/screenshots/finalcif_main.png +0 -0
- {finalcif-153 → finalcif-155}/screenshots/finalcif_report.png +0 -0
- {finalcif-153 → finalcif-155}/screenshots/finalcif_responses.png +0 -0
- {finalcif-153 → finalcif-155}/update.exe +0 -0
- {finalcif-153 → finalcif-155}/uv.lock +0 -0
|
@@ -127,6 +127,25 @@ class AppWindow(QMainWindow):
|
|
|
127
127
|
if file:
|
|
128
128
|
self.load_cif_file(file)
|
|
129
129
|
self.load_recent_cifs_list()
|
|
130
|
+
self.ui.CheckcifPlaintextEdit.insertPlainText(
|
|
131
|
+
"\n"
|
|
132
|
+
" Offline CheckCIF uses PLATON from Anthony L. Spek, Utrecht University, The Netherlands."
|
|
133
|
+
"\n"
|
|
134
|
+
" https://platonsoft.nl\n"
|
|
135
|
+
" \n"
|
|
136
|
+
" Please cite PLATON in your publication if you use it.\n"
|
|
137
|
+
" \n"
|
|
138
|
+
" - PLATON References : \n"
|
|
139
|
+
" Spek, A.L. (2003). J. Appl. Cryst. 36, 7-13.\n"
|
|
140
|
+
" Spek, A.L. (2009). Acta Cryst. D65, 148-155.\n"
|
|
141
|
+
" Spek, A.L. (2015). Acta Cryst. C71, 9-18.\n"
|
|
142
|
+
" Spek, A.L. (2018). Inorg. Chim. Acta, 470, 232-237.\n"
|
|
143
|
+
" Spek, A.L. (2020). Acta Cryst. E76, 1-11.\n"
|
|
144
|
+
" \n"
|
|
145
|
+
" \n"
|
|
146
|
+
" - Recent versions of PLATON may be obtained from: https://platonsoft.nl/xraysoft\n"
|
|
147
|
+
" "
|
|
148
|
+
)
|
|
130
149
|
self.set_checkcif_output_font(self.ui.CheckcifPlaintextEdit)
|
|
131
150
|
# To make file drag&drop working:
|
|
132
151
|
self.setAcceptDrops(True)
|
|
@@ -902,7 +921,7 @@ class AppWindow(QMainWindow):
|
|
|
902
921
|
url = QtCore.QUrl.fromLocalFile(str(self.htmlfile.resolve()))
|
|
903
922
|
self.ui.MainStackedWidget.go_to_checkcif_page()
|
|
904
923
|
self.ui.CheckCIFResultsTabWidget.setCurrentIndex(1) # Index 1 is html page
|
|
905
|
-
self.checkcif_browser.
|
|
924
|
+
self.checkcif_browser.setHtml(self.htmlfile.resolve().read_text('utf-8', 'ignore'))
|
|
906
925
|
self.ui.ResponsesTabWidget.setCurrentIndex(0)
|
|
907
926
|
threading.Thread(target=self._display_structure_factor_report, args=(parser,)).start()
|
|
908
927
|
# The picture file linked in the html file:
|
|
@@ -960,7 +979,8 @@ class AppWindow(QMainWindow):
|
|
|
960
979
|
self.ckf = CheckCif(parent=self, cif=self.cif, outfile=self.htmlfile,
|
|
961
980
|
hkl_upload=(not self.ui.structfactCheckBox.isChecked()), pdf=False,
|
|
962
981
|
url=self.options.checkcif_url,
|
|
963
|
-
full_iucr=self.ui.fullIucrCheckBox.isChecked()
|
|
982
|
+
full_iucr=self.ui.fullIucrCheckBox.isChecked(),
|
|
983
|
+
check_duplicates=self.ui.checkDuplicatesCheckBox.isChecked())
|
|
964
984
|
self.ckf.progress.connect(self._ckf_progress)
|
|
965
985
|
self.ckf.failed.connect(self._checkcif_failed)
|
|
966
986
|
self.ckf.finished.connect(self._checkcif_finished)
|
|
@@ -1033,7 +1053,8 @@ class AppWindow(QMainWindow):
|
|
|
1033
1053
|
self.ckf = CheckCif(parent=self, cif=self.cif, outfile=htmlfile,
|
|
1034
1054
|
hkl_upload=(not self.ui.structfactCheckBox.isChecked()),
|
|
1035
1055
|
pdf=True, url=self.options.checkcif_url,
|
|
1036
|
-
full_iucr=self.ui.fullIucrCheckBox.isChecked()
|
|
1056
|
+
full_iucr=self.ui.fullIucrCheckBox.isChecked(),
|
|
1057
|
+
check_duplicates=self.ui.checkDuplicatesCheckBox.isChecked())
|
|
1037
1058
|
self.ckf.failed.connect(self._checkcif_failed)
|
|
1038
1059
|
# noinspection PyUnresolvedReferences
|
|
1039
1060
|
self.ckf.finished.connect(self._pdf_checkcif_finished)
|
|
@@ -1073,6 +1094,7 @@ class AppWindow(QMainWindow):
|
|
|
1073
1094
|
runner.finished.connect(lambda: self.ui.CheckcifButton.setEnabled(True))
|
|
1074
1095
|
runner.run_process()
|
|
1075
1096
|
runner.formula.connect(self.add_moiety_furmula)
|
|
1097
|
+
return None
|
|
1076
1098
|
|
|
1077
1099
|
def add_moiety_furmula(self, formula_moiety):
|
|
1078
1100
|
moiety = self.ui.cif_main_table.getTextFromKey(key='_chemical_formula_moiety', col=Column.CIF)
|
|
@@ -816,7 +816,7 @@ def get_atomlabel(input_atom: str) -> str:
|
|
|
816
816
|
else:
|
|
817
817
|
# print('*** {} is not a valid atom!! ***'.format(atom))
|
|
818
818
|
raise KeyError
|
|
819
|
-
except
|
|
819
|
+
except IndexError:
|
|
820
820
|
# print('*** {} is not a valid atom! ***'.format(atom))
|
|
821
821
|
raise KeyError
|
|
822
822
|
|
|
@@ -826,6 +826,6 @@ if __name__ == '__main__':
|
|
|
826
826
|
|
|
827
827
|
failed, attempted = doctest.testmod() # verbose=True)
|
|
828
828
|
if failed == 0:
|
|
829
|
-
print('passed all {} tests!'
|
|
829
|
+
print(f'passed all {attempted} tests!')
|
|
830
830
|
else:
|
|
831
|
-
print('{} of {} tests failed'
|
|
831
|
+
print(f'{failed} of {attempted} tests failed')
|
|
@@ -25,7 +25,7 @@ class CheckCif(QThread):
|
|
|
25
25
|
failed = Signal(str)
|
|
26
26
|
|
|
27
27
|
def __init__(self, parent, cif: CifContainer, outfile: Path, hkl_upload: bool = True,
|
|
28
|
-
pdf: bool = False, url: str = '', full_iucr: bool = False):
|
|
28
|
+
pdf: bool = False, url: str = '', full_iucr: bool = False, check_duplicates: bool = True):
|
|
29
29
|
# hkl == False means no hkl upload
|
|
30
30
|
super().__init__(parent=parent)
|
|
31
31
|
self.hkl_upload = hkl_upload
|
|
@@ -34,6 +34,7 @@ class CheckCif(QThread):
|
|
|
34
34
|
self.pdf = pdf
|
|
35
35
|
self.checkcif_url = url
|
|
36
36
|
self.full_iucr = full_iucr
|
|
37
|
+
self.check_duplicates = check_duplicates
|
|
37
38
|
|
|
38
39
|
def _html_check(self) -> None:
|
|
39
40
|
if self.hkl_upload:
|
|
@@ -63,11 +64,13 @@ class CheckCif(QThread):
|
|
|
63
64
|
validation_type = 'checkcif_with_hkl'
|
|
64
65
|
vrf = self.get_vrf()
|
|
65
66
|
headers = {
|
|
67
|
+
"from_index": "from_index",
|
|
66
68
|
"runtype" : "symmonly",
|
|
67
69
|
"referer" : "checkcif_server",
|
|
68
70
|
"outputtype": 'PDF' if self.pdf else 'HTML',
|
|
69
71
|
"validtype" : validation_type,
|
|
70
72
|
"valout" : vrf,
|
|
73
|
+
"duplic" : "duplicyes" if self.check_duplicates else "duplicno",
|
|
71
74
|
}
|
|
72
75
|
t1 = time.perf_counter()
|
|
73
76
|
self.progress.emit('Report request sent. Please wait...')
|
|
@@ -5,6 +5,7 @@
|
|
|
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
|
+
import os
|
|
8
9
|
import re
|
|
9
10
|
from collections import namedtuple
|
|
10
11
|
from collections.abc import Generator
|
|
@@ -13,6 +14,8 @@ from pathlib import Path
|
|
|
13
14
|
|
|
14
15
|
import gemmi
|
|
15
16
|
|
|
17
|
+
from finalcif.cif import atoms
|
|
18
|
+
|
|
16
19
|
if hasattr(gemmi, 'set_leak_warnings'):
|
|
17
20
|
gemmi.set_leak_warnings(False)
|
|
18
21
|
|
|
@@ -24,8 +27,7 @@ from finalcif.cif.cif_order import order, special_keys, essential_keys, non_cent
|
|
|
24
27
|
from finalcif.cif.hkl import HKL, Limit
|
|
25
28
|
from finalcif.cif.text import utf8_to_str, quote, retranslate_delimiter
|
|
26
29
|
from finalcif.datafiles.utils import DSRFind
|
|
27
|
-
from finalcif.tools.misc import isnumeric, grouper, strip_finalcif_of_name,
|
|
28
|
-
_angstrom_to_x
|
|
30
|
+
from finalcif.tools.misc import isnumeric, grouper, strip_finalcif_of_name, _angstrom_to_x
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
class GemmiError(Exception):
|
|
@@ -45,6 +47,7 @@ class CifContainer:
|
|
|
45
47
|
new_block: Create a new block (new file) if a name is given. Otherwise, just
|
|
46
48
|
the existing document is opened.
|
|
47
49
|
"""
|
|
50
|
+
self.chars_ok = True
|
|
48
51
|
self.picometer: bool = False
|
|
49
52
|
self._abs_hkl_details: dict[str, str] = {}
|
|
50
53
|
if isinstance(file, str):
|
|
@@ -72,6 +75,7 @@ class CifContainer:
|
|
|
72
75
|
self._on_load()
|
|
73
76
|
self.order = []
|
|
74
77
|
self.essential_keys = []
|
|
78
|
+
# May be overwritten later:
|
|
75
79
|
self.set_order_keys(order)
|
|
76
80
|
self.set_essential_keys(essential_keys)
|
|
77
81
|
|
|
@@ -123,7 +127,7 @@ class CifContainer:
|
|
|
123
127
|
for num, line in enumerate(filepath.read_bytes().splitlines(keepends=True)):
|
|
124
128
|
try:
|
|
125
129
|
line.decode('ascii')
|
|
126
|
-
except
|
|
130
|
+
except UnicodeDecodeError:
|
|
127
131
|
line_numbers.append(num)
|
|
128
132
|
return line_numbers
|
|
129
133
|
|
|
@@ -155,9 +159,8 @@ class CifContainer:
|
|
|
155
159
|
self.dsr_used = DSRFind(self.res_file_data).dsr_used
|
|
156
160
|
self.atomic_struct: gemmi.SmallStructure = gemmi.make_small_structure_from_block(self.block)
|
|
157
161
|
# A dictionary to convert Atom names like 'C1_2' or 'Ga3' into Element names like 'C' or 'Ga'
|
|
158
|
-
self._name2elements = dict(
|
|
159
|
-
|
|
160
|
-
[x.upper() for x in self.block.find_loop('_atom_site_type_symbol')], strict=True))
|
|
162
|
+
self._name2elements = dict(zip([x.upper() for x in self.block.find_loop('_atom_site_label')],
|
|
163
|
+
[y.upper() for y in self.block.find_loop('_atom_site_type_symbol')], strict=False))
|
|
161
164
|
self.check_hkl_min_max()
|
|
162
165
|
|
|
163
166
|
@property
|
|
@@ -211,8 +214,8 @@ class CifContainer:
|
|
|
211
214
|
return doc
|
|
212
215
|
|
|
213
216
|
def cif_as_string(self, without_hkl=False) -> str:
|
|
214
|
-
opt = gemmi.cif.WriteOptions(
|
|
215
|
-
|
|
217
|
+
opt = gemmi.cif.WriteOptions()
|
|
218
|
+
opt.align_pairs = 33
|
|
216
219
|
if without_hkl:
|
|
217
220
|
# return a copy, do not delete hkl from original:
|
|
218
221
|
doc = gemmi.cif.Document()
|
|
@@ -257,7 +260,6 @@ class CifContainer:
|
|
|
257
260
|
f", {self.nangles()} angles")
|
|
258
261
|
|
|
259
262
|
def file_is_there_and_writable(self) -> bool:
|
|
260
|
-
import os
|
|
261
263
|
return self.fileobj.exists() and self.fileobj.is_file() and os.access(self.fileobj, os.W_OK)
|
|
262
264
|
|
|
263
265
|
def set_pair_delimited(self, key: str, txt: str):
|
|
@@ -278,13 +280,13 @@ class CifContainer:
|
|
|
278
280
|
|
|
279
281
|
def save(self, filename: Path | None = None) -> None:
|
|
280
282
|
"""
|
|
281
|
-
Saves the current cif file.
|
|
282
|
-
:param filename: Name to save cif file to.
|
|
283
|
+
Saves the current cif file. It uses the '[basename]-finalcif.cif' suffix by default.
|
|
284
|
+
:param filename: Name to save cif file to if the default is not sufficient.
|
|
283
285
|
"""
|
|
284
286
|
if not filename:
|
|
285
287
|
filename = self.finalcif_file
|
|
286
288
|
if self.is_empty():
|
|
287
|
-
print(f'
|
|
289
|
+
print(f'DBG> CIF file {filename} is empty.')
|
|
288
290
|
return
|
|
289
291
|
if not self.is_writable(filename):
|
|
290
292
|
raise PermissionError(f'Failed to open {filename.resolve()} for writing: Operation not permitted')
|
|
@@ -714,18 +716,18 @@ class CifContainer:
|
|
|
714
716
|
Atoms from the CIF where values are returned as string like in the CIF with esds.
|
|
715
717
|
"""
|
|
716
718
|
labels = self.block.find_loop('_atom_site_label')
|
|
717
|
-
types = self.block.find_loop('_atom_site_type_symbol')
|
|
719
|
+
types = self.block.find_loop('_atom_site_type_symbol') or [atoms.get_atomlabel(x) for x in labels]
|
|
718
720
|
x = self.block.find_loop('_atom_site_fract_x')
|
|
719
721
|
y = self.block.find_loop('_atom_site_fract_y')
|
|
720
722
|
z = self.block.find_loop('_atom_site_fract_z')
|
|
721
723
|
part = self.block.find_loop('_atom_site_disorder_group') or ('0',) * len(labels)
|
|
722
724
|
occ = self.block.find_loop('_atom_site_occupancy') or ('1.000000',) * len(labels)
|
|
723
|
-
u_eq = self.block.find_loop('_atom_site_U_iso_or_equiv')
|
|
725
|
+
u_eq = self.block.find_loop('_atom_site_U_iso_or_equiv') or ('.',) * len(labels)
|
|
724
726
|
atom = namedtuple('atom', ('label', 'type', 'x', 'y', 'z', 'part', 'occ', 'u_eq'))
|
|
725
727
|
for label, type, x, y, z, part, occ, u_eq in zip(labels, types, x, y, z,
|
|
726
728
|
part,
|
|
727
729
|
occ,
|
|
728
|
-
u_eq, strict=
|
|
730
|
+
u_eq, strict=False):
|
|
729
731
|
if without_h and self.ishydrogen(label):
|
|
730
732
|
continue
|
|
731
733
|
if self.picometer:
|
|
@@ -761,7 +763,7 @@ class CifContainer:
|
|
|
761
763
|
u13 = self.block.find_loop('_atom_site_aniso_U_13')
|
|
762
764
|
u12 = self.block.find_loop('_atom_site_aniso_U_12')
|
|
763
765
|
adp = namedtuple('adp', ('label', 'U11', 'U22', 'U33', 'U23', 'U13', 'U12'))
|
|
764
|
-
for label, u11, u22, u33, u23, u13, u12 in zip(labels, u11, u22, u33, u23, u13, u12):
|
|
766
|
+
for label, u11, u22, u33, u23, u13, u12 in zip(labels, u11, u22, u33, u23, u13, u12, strict=True):
|
|
765
767
|
if self.picometer:
|
|
766
768
|
u11 = _angstrom_to_x(u11, factor=100 ** 2)
|
|
767
769
|
u22 = _angstrom_to_x(u22, factor=100 ** 2)
|
|
@@ -817,7 +819,7 @@ class CifContainer:
|
|
|
817
819
|
symm = self.block.find_loop('_geom_bond_site_symmetry_2')
|
|
818
820
|
publ_loop = self.block.find_loop('_geom_bond_publ_flag') or len(label1) * ['?']
|
|
819
821
|
bond = namedtuple('bond', ('label1', 'label2', 'dist', 'symm'))
|
|
820
|
-
for label1, label2, dist, symm, publ in zip(label1, label2, dist, symm, publ_loop):
|
|
822
|
+
for label1, label2, dist, symm, publ in zip(label1, label2, dist, symm, publ_loop, strict=False):
|
|
821
823
|
if (without_h and (self.ishydrogen(label1) or self.ishydrogen(label2))) or self.yes_not_set(publ):
|
|
822
824
|
continue
|
|
823
825
|
else:
|
|
@@ -855,17 +857,20 @@ class CifContainer:
|
|
|
855
857
|
symm2 = self.block.find_loop('_geom_angle_site_symmetry_3')
|
|
856
858
|
publ_loop = self.block.find_loop('_geom_angle_publ_flag') or len(label1) * ['?']
|
|
857
859
|
angle = namedtuple('angle', ('label1', 'label2', 'label3', 'angle_val', 'symm1', 'symm2'))
|
|
858
|
-
for label1, label2, label3, angle_val, symm1, symm2, publ in
|
|
859
|
-
|
|
860
|
-
if ((without_H and (self.ishydrogen(label1) or self.ishydrogen(label2) or
|
|
861
|
-
|
|
860
|
+
for label1, label2, label3, angle_val, symm1, symm2, publ in (
|
|
861
|
+
zip(label1, label2, label3, angle_val, symm1, symm2, publ_loop, strict=False)):
|
|
862
|
+
if ((without_H and (self.ishydrogen(label1) or self.ishydrogen(label2) or self.ishydrogen(label3))) or
|
|
863
|
+
self.yes_not_set(publ)):
|
|
862
864
|
continue
|
|
863
865
|
else:
|
|
864
866
|
yield angle(label1=label1, label2=label2, label3=label3, angle_val=angle_val,
|
|
865
867
|
symm1=self.checksymm(symm1), symm2=self.checksymm(symm2))
|
|
866
868
|
|
|
867
869
|
def _iselement(self, name: str) -> str:
|
|
868
|
-
|
|
870
|
+
try:
|
|
871
|
+
return self._name2elements[name.upper()]
|
|
872
|
+
except KeyError:
|
|
873
|
+
return atoms.get_atomlabel(name)
|
|
869
874
|
|
|
870
875
|
def natoms(self, without_h: bool = False) -> int:
|
|
871
876
|
return len(tuple(self.atoms(without_h)))
|
|
@@ -902,12 +907,11 @@ class CifContainer:
|
|
|
902
907
|
tors = namedtuple('Torsion',
|
|
903
908
|
('label1', 'label2', 'label3', 'label4', 'torsang', 'symm1', 'symm2', 'symm3', 'symm4'))
|
|
904
909
|
for label1, label2, label3, label4, torsang, symm1, symm2, symm3, symm4, publ in zip(label1, label2, label3,
|
|
905
|
-
# noqa: B020
|
|
906
910
|
label4,
|
|
907
911
|
torsang, symm1, symm2,
|
|
908
912
|
symm3,
|
|
909
913
|
symm4, publ_loop,
|
|
910
|
-
strict=
|
|
914
|
+
strict=False):
|
|
911
915
|
if ((without_h and (self.ishydrogen(label1) or self.ishydrogen(label2)
|
|
912
916
|
or self.ishydrogen(label3) or self.ishydrogen(label3))) or self.yes_not_set(publ)):
|
|
913
917
|
continue
|
|
@@ -930,7 +934,7 @@ class CifContainer:
|
|
|
930
934
|
hydr = namedtuple('HydrogenBond', ('label_d', 'label_h', 'label_a', 'dist_dh', 'dist_ha', 'dist_da',
|
|
931
935
|
'angle_dha', 'symm'))
|
|
932
936
|
for label_d, label_h, label_a, dist_dh, dist_ha, dist_da, angle_dha, symm, publ in (
|
|
933
|
-
|
|
937
|
+
zip(label_d, label_h, label_a, dist_dh, dist_ha, dist_da, angle_dha, symm, publ_loop, strict=False)):
|
|
934
938
|
if self.yes_not_set(publ):
|
|
935
939
|
continue
|
|
936
940
|
if self.picometer:
|
|
@@ -947,7 +951,12 @@ class CifContainer:
|
|
|
947
951
|
Returns the key/value pairs of a cif file sorted by priority.
|
|
948
952
|
"""
|
|
949
953
|
keys_without_values, keys_with_values = self.keys_with_essentials()
|
|
950
|
-
return keys_without_values
|
|
954
|
+
return [*keys_without_values, ('These below are already in:', '---------------------'), *keys_with_values]
|
|
955
|
+
|
|
956
|
+
def pairs(self):
|
|
957
|
+
for item in self.block:
|
|
958
|
+
if item.pair is not None:
|
|
959
|
+
yield item.pair
|
|
951
960
|
|
|
952
961
|
def _is_centrokey(self, key) -> bool:
|
|
953
962
|
"""
|
|
@@ -973,7 +982,7 @@ class CifContainer:
|
|
|
973
982
|
# continue
|
|
974
983
|
if self._is_centrokey(key):
|
|
975
984
|
continue
|
|
976
|
-
if not value or value
|
|
985
|
+
if not value or value in {'?', "'?'"}:
|
|
977
986
|
questions.append((key, value))
|
|
978
987
|
else:
|
|
979
988
|
with_values.append((key, value))
|
|
@@ -171,34 +171,24 @@ class AuthorLoops:
|
|
|
171
171
|
def save_author_to_loop(self) -> None:
|
|
172
172
|
author = self.get_author_info()
|
|
173
173
|
row = [author.name, author.address, author.email, author.phone, author.orcid, author.iucr_id, author.footnote]
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
gemmi_loop: Loop = self.cif.block.find_loop(author_type).get_loop()
|
|
181
|
-
if tuple(row) in list(grouper(gemmi_loop.values, len(row))):
|
|
182
|
-
self.app.status_bar.show_message('This author already exists.', 10)
|
|
174
|
+
author_loop_tags = self.get_author_loop(author)
|
|
175
|
+
table: gemmi.cif.Table = self.cif.block.find(author_loop_tags)
|
|
176
|
+
if table.width():
|
|
177
|
+
gemmi_loop: Loop = table.loop
|
|
178
|
+
if tuple(row) in list(grouper(gemmi_loop.values, gemmi_loop.width())):
|
|
179
|
+
self.app.status_bar.show_message('This author already exists.', 15)
|
|
183
180
|
print('dbg> Author already exists.')
|
|
184
181
|
return
|
|
185
182
|
else:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
183
|
+
gemmi_loop = self.cif.init_loop(author_loop_tags)
|
|
184
|
+
table = self.cif.block.find(author_loop_tags)
|
|
185
|
+
if gemmi_loop.width() < len(row):
|
|
186
|
+
row = row[:gemmi_loop.width()]
|
|
187
|
+
if row not in table:
|
|
188
|
+
table.append_row(row)
|
|
189
189
|
self.app.make_loops_tables()
|
|
190
190
|
self.show_authors_list()
|
|
191
191
|
|
|
192
|
-
def check_if_loop_and_row_size_fit_together(self, gemmi_loop: gemmi.cif.Loop, row: list[str]) -> None:
|
|
193
|
-
if gemmi_loop.width() < len(row):
|
|
194
|
-
cut_row = row[:gemmi_loop.width()]
|
|
195
|
-
if cut_row not in gemmi_loop.values:
|
|
196
|
-
gemmi_loop.add_row(cut_row)
|
|
197
|
-
elif gemmi_loop.width() > len(row):
|
|
198
|
-
show_general_warning(self, 'An author loop with larger size is already in the CIF. Can not proceed.')
|
|
199
|
-
elif row not in gemmi_loop.values:
|
|
200
|
-
gemmi_loop.add_row(row)
|
|
201
|
-
|
|
202
192
|
def get_author_info(self) -> Author:
|
|
203
193
|
if self.ui.authorEditTabWidget.currentWidget().objectName() == 'page_publication':
|
|
204
194
|
name = quote(utf8_to_str(self.ui.FullNameLineEdit.text()))
|
|
@@ -445,4 +435,5 @@ if __name__ == '__main__':
|
|
|
445
435
|
import pprint
|
|
446
436
|
|
|
447
437
|
l = AuthorLoops(Ui_FinalCifWindow(), CifContainer('test-data/1000007.cif'), None)
|
|
448
|
-
|
|
438
|
+
data = l.export_raw_data()
|
|
439
|
+
pprint.pprint(data)
|
|
@@ -53,7 +53,7 @@ def my_exception_hook(exctype: type[BaseException], value: BaseException, error_
|
|
|
53
53
|
|
|
54
54
|
logfile = Path.home().joinpath(Path(r'finalcif-crash.txt'))
|
|
55
55
|
try:
|
|
56
|
-
logfile.write_text(errortext)
|
|
56
|
+
logfile.write_text(errortext, encoding='utf-8', errors='ignore')
|
|
57
57
|
except PermissionError:
|
|
58
58
|
pass
|
|
59
59
|
sys.__excepthook__(exctype, value, error_traceback)
|