コード例 #1
0
ファイル: report_text.py プロジェクト: dkratzert/FinalCif
 def __init__(self, cif: CifContainer, paragraph: Paragraph):
     self.cif = cif
     self.crytsalization_method = gstr(
         self.cif['_exptl_crystal_recrystallization_method'])
     if not self.crytsalization_method:
         self.crytsalization_method = '[No crystallization method was given]'
     sentence = "{}. "
     self.text = sentence.format(
         remove_line_endings(
             retranslate_delimiter(self.crytsalization_method)))
     paragraph.add_run(retranslate_delimiter(self.text))
コード例 #2
0
ファイル: equipment.py プロジェクト: dkratzert/FinalCif
 def load_equipment_to_edit(self) -> None:
     """
     Load/Edit the key/value list of an equipment entry.
     """
     table = self.app.ui.EquipmentEditTableWidget
     listwidget = self.app.ui.EquipmentTemplatesListWidget
     table.blockSignals(True)
     table.clearContents()
     table.setRowCount(0)
     index = listwidget.currentIndex()
     if index.row() == -1:
         # nothing selected
         return
     selected_row_text = listwidget.currentIndex().data()
     table_data = self.settings.load_settings_list(
         property='equipment', item_name=selected_row_text)
     # first load the previous values:
     if table_data:
         for key, value in table_data:
             if not key or not value:
                 continue
             table.add_equipment_row(key, retranslate_delimiter(value))
     else:
         # new empty equipment:
         for _ in range(8):
             table.add_equipment_row('', '')
     table.add_equipment_row('', '')
     table.add_equipment_row('', '')
     self.app.ui.EquipmentTemplatesStackedWidget.setCurrentIndex(1)
     table.resizeRowsToContents()
     table.blockSignals(False)
コード例 #3
0
ファイル: report_text.py プロジェクト: dkratzert/FinalCif
def format_radiation(radiation_type: str) -> list:
    radtype = list(radiation_type.partition("K"))
    if len(radtype) > 2:
        radtype[2] = retranslate_delimiter(radtype[2])
        return radtype
    else:
        return radtype
コード例 #4
0
ファイル: equipment.py プロジェクト: dkratzert/FinalCif
 def import_equipment_from_file(self, filename='') -> None:
     """
     Import an equipment entry from a cif file.
     """
     if not filename:
         filename = cif_file_open_dialog(
             filter="CIF file (*.cif  *.cif_od *.cfx)")
     if not filename:
         print('No file given')
         return
     try:
         doc = cif.read_file(filename)
     except RuntimeError as e:
         show_general_warning(str(e))
         return
     block = doc.sole_block()
     table_data = []
     for item in block:
         if item.pair is not None:
             key, value = item.pair
             if filename.endswith(
                     '.cif_od') and key not in include_equipment_imports:
                 continue
             table_data.append([
                 key,
                 retranslate_delimiter(
                     cif.as_string(value).strip('\n\r ;'))
             ])
     if filename.endswith('.cif_od'):
         name = Path(filename).stem
     else:
         name = block.name.replace('__', ' ')
     self.settings.save_settings_list('equipment', name, table_data)
     self.show_equipment()
コード例 #5
0
ファイル: loops.py プロジェクト: dkratzert/FinalCif
 def data(self, index: QModelIndex, role: int = None):
     row, col = index.row(), index.column()
     value = self._data[row][col]
     if role == Qt.SizeHintRole:
         return QSize(120, 50)
     if role == Qt.TextAlignmentRole:
         pass
         # if isnumeric(value):
         #    return Qt.AlignVCenter + Qt.AlignVertical_Mask
     if role == Qt.BackgroundColorRole:
         if (row, col) in [(x['row'], x['column']) for x in self.modified]:
             return QVariant(QColor("#facaca"))
     if role == Qt.EditRole:
         return retranslate_delimiter(value)
     if role == Qt.DisplayRole:
         return retranslate_delimiter(value)
コード例 #6
0
ファイル: report_text.py プロジェクト: dkratzert/FinalCif
 def __init__(self, cif: CifContainer, paragraph: Paragraph):
     self.cif = cif
     self.difftype = gstr(self.cif['_diffrn_measurement_device_type']) \
                     or '[No measurement device type given]'
     self.device = gstr(self.cif['_diffrn_measurement_device']) \
                   or '[No measurement device given]'
     self.source = gstr(self.cif['_diffrn_source']).strip('\n\r') \
                   or '[No radiation source given]'
     self.monochrom = gstr(self.cif['_diffrn_radiation_monochromator']) \
                      or '[No monochromator type given]'
     if not self.monochrom:
         self.monochrom = '?'
     self.cooling = gstr(self.cif['_olex2_diffrn_ambient_temperature_device']) \
                    or ''
     self.rad_type = gstr(self.cif['_diffrn_radiation_type']) \
                     or '[No radiation type given]'
     radtype = format_radiation(self.rad_type)
     self.wavelen = gstr(self.cif['_diffrn_radiation_wavelength']) \
                    or '[No wavelength given]'
     self.detector_type = ''
     detector_type = gstr(self.cif['_diffrn_detector_type']) \
                     or '[No detector type given]'
     if detector_type:
         self.detector_type = " and a {} detector".format(detector_type)
     sentence1 = "on {0} {1} {2} with {3} {4} using {5} as monochromator{6}. " \
                 "The diffractometer was equipped with {7} {8} low temperature device and used "
     sentence2 = " radiation (λ = {}" + protected_space + "{}). ".format(
         angstrom)
     txt = sentence1.format(get_inf_article(self.difftype),
                            self.difftype, self.device,
                            get_inf_article(self.source), self.source,
                            self.monochrom, self.detector_type,
                            get_inf_article(self.cooling), self.cooling)
     paragraph.add_run(retranslate_delimiter(txt))
     # radiation type e.g. Mo:
     paragraph.add_run(retranslate_delimiter(radtype[0]))
     # K line:
     radrunita = paragraph.add_run(radtype[1])
     radrunita.font.italic = True
     alpha = paragraph.add_run(retranslate_delimiter(radtype[2]))
     alpha.font.italic = True
     alpha.font.subscript = True
     txt2 = sentence2.format(self.wavelen)
     paragraph.add_run(txt2)
コード例 #7
0
ファイル: report_text.py プロジェクト: dkratzert/FinalCif
 def __init__(self, cif: CifContainer, paragraph: Paragraph):
     self.cif = cif
     self.temperature = gstr(self.cif['_diffrn_ambient_temperature'])
     self._name = cif.fileobj.name
     method = 'shock-cooled '
     sentence = "The data for {} were collected from a {}single crystal at {}{}K "
     try:
         if float(self.temperature.split('(')[0]) > 200:
             method = ''
     except ValueError:
         method = ''
     self.txt = sentence.format(self.cif.block.name, method,
                                self.temperature, protected_space)
     paragraph.add_run(retranslate_delimiter(self.txt))
コード例 #8
0
 def general_author_save(self, author: dict):
     if not author.get('name'):
         return
     if author.get('contact'):
         itemtext = '{} (contact author)'.format(
             retranslate_delimiter(as_string(author.get('name'))))
     else:
         itemtext = as_string(author.get('name'))
     authors = self.settings.list_saved_items(property='authors_list')
     if itemtext not in authors:
         self.settings.save_settings_dict(property='authors_list',
                                          name=itemtext,
                                          items=author)
     if not itemtext:
         return
     self.show_author_loops()
コード例 #9
0
 def import_author(self, filename=''):
     """
     Import an author from a cif file.
     """
     cif_auth_to_str = {
         '_publ_contact_author_name': 'name',
         '_publ_contact_author_address': 'address',
         '_publ_contact_author_email': 'email',
         '_publ_contact_author_phone': 'phone',
         '_publ_contact_author_id_orcid': 'orcid',
         #
         '_publ_author_name': 'name',
         '_publ_author_address': 'address',
         '_publ_author_email': 'email',
         '_publ_author_phone': 'phone',
         '_publ_author_id_orcid': 'orcid',
         '_publ_author_footnote': 'footnote',
     }
     if not filename:
         filename = cif_file_open_dialog(filter="CIF file (*.cif)")
     if not filename:
         return
     try:
         doc = read_file(filename)
     except RuntimeError as e:
         show_general_warning(str(e))
         return
     block = doc.sole_block()
     table_data = {}
     for item in block:
         if item.pair is not None:
             key, value = item.pair
             if key not in cif_auth_to_str:
                 continue
             key = cif_auth_to_str.get(key)
             table_data.update({
                 key:
                 retranslate_delimiter(as_string(value).strip('\n\r ;'))
             })
     name = block.name.replace('__', ' ')
     if 'contact author' in name:
         table_data.update({'contact': True})
     if not table_data.get('name'):
         return None
     self.general_author_save(table_data)
     self.show_author_loops()
コード例 #10
0
 def import_property_from_file(self, filename: str = '') -> None:
     """
     Imports a cif file as entry of the property templates list.
     """
     if not filename:
         filename = cif_file_open_dialog(filter="CIF file (*.cif)")
     if not filename:
         return
     try:
         doc = cif.read_file(filename)
     except RuntimeError as e:
         show_general_warning(str(e))
         return
     property_list = self.settings.settings.value('property_list')
     if not property_list:
         property_list = ['']
     block = doc.sole_block()
     template_list = []
     loop_column_name = ''
     for i in block:
         if i.loop is not None:
             if len(i.loop.tags) > 0:
                 loop_column_name = i.loop.tags[0]
             for n in range(i.loop.length()):
                 value = i.loop.val(n, 0)
                 template_list.append(
                     retranslate_delimiter(
                         cif.as_string(value).strip("\n\r ;")))
     block_name = block.name.replace('__', ' ')
     # This is the list shown in the Main menu:
     property_list.append(block_name)
     table = self.app.ui.PropertiesEditTableWidget
     table.setRowCount(0)
     self.app.ui.cifKeywordLineEdit.setText(loop_column_name)
     newlist = [x for x in list(set(property_list)) if x]
     newlist.sort()
     # this list keeps track of the property items:
     self.settings.save_template_list('property_list', newlist)
     template_list.insert(0, '')
     template_list = list(set(template_list))
     # save as dictionary for properties to have "_cif_key : itemlist"
     # for a table item as dropdown menu in the main table.
     table_data = [loop_column_name, template_list]
     self.settings.save_template_list('property/' + block_name, table_data)
     self.show_properties()
コード例 #11
0
ファイル: custom_classes.py プロジェクト: dkratzert/FinalCif
 def add_equipment_row(self, key_text: str = '', value_text: str = ''):
     """
     Add a new row with content to the table (Equipment or Property).
     """
     if not isinstance(value_text, str):
         return
     if not isinstance(key_text, str):
         return
     # Create a empty row at bottom of table
     row_num = self.rowCount()
     self.insertRow(row_num)
     key_item = MyQPlainTextEdit(parent=self)
     key_item.row = row_num
     key_item.setPlainText(key_text)
     # This is critical, because otherwise the add_row_if_needed does not work as expected:
     key_item.textChanged.connect(self.add_row_if_needed)
     self.setCellWidget(row_num, 0, key_item)
     # if len(value) > 38:
     tab_item = MyQPlainTextEdit(self)
     tab_item.setPlainText(retranslate_delimiter(value_text))
     self.setCellWidget(row_num, 1, tab_item)
コード例 #12
0
ファイル: custom_classes.py プロジェクト: dkratzert/FinalCif
 def setText(self,
             key: str,
             column: int,
             txt: str,
             row: int = None,
             color=None):
     """
     Set text in current table cell regardless of the containing item.
     """
     txt = retranslate_delimiter(txt)
     if row is None:
         row = self.vheaderitems.index(key)
     if isinstance(self.cellWidget(row, column), MyComboBox):
         # noinspection PyUnresolvedReferences
         self.cellWidget(row, column).setText(txt)
         return
     item = MyTableWidgetItem(txt)
     self.setItem(row, column, item)
     lentext = max(
         [len(txt),
          len(self.getText(0, row)),
          len(self.getText(1, row))])
     # This is a regular table cell:
     if not (key in text_field_keys) and (lentext < 35):
         item.setText(txt)
         if (column == COL_CIF) or (column == COL_DATA):
             # noinspection PyUnresolvedReferences
             item.setUneditable()
         if color:
             item.setBackground(color)
     else:
         # This is a text field:
         textedit = MyQPlainTextEdit(self)
         self.setCellWidget(row, column, textedit)
         textedit.setText(txt, color=color)
         if (column == COL_CIF) or (column == COL_DATA):
             textedit.setUneditable()
         self.resizeRowToContents(row)
         if color:
             textedit.setBackground(color)
コード例 #13
0
ファイル: report_text.py プロジェクト: dkratzert/FinalCif
 def __init__(self, cif: CifContainer, paragraph: Paragraph,
              ref: ReferenceList):
     self.cif = cif
     integration = gstr(self.cif['_computing_data_reduction']) or '??'
     abstype = gstr(self.cif['_exptl_absorpt_correction_type']) or '??'
     abs_details = gstr(self.cif['_exptl_absorpt_process_details']) or '??'
     data_reduct_ref = DummyReference()
     absorpt_ref = DummyReference()
     integration_prog = '[unknown integration program]'
     scale_prog = '[unknown program]'
     if 'SAINT' in integration:
         data_reduct_ref, integration_prog = self.add_saint_reference(
             integration)
     if 'CrysAlisPro'.lower() in integration.lower():
         data_reduct_ref, absorpt_ref, integration_prog = self.add_crysalispro_reference(
             integration)
     absdetails = cif['_exptl_absorpt_process_details'].replace('-', ' ')
     if 'SADABS' in absdetails.upper() or 'TWINABS' in absdetails.upper():
         if len(absdetails.split()) > 1:
             version = absdetails.split()[1]
         else:
             version = 'unknown version'
         if 'SADABS' in absdetails:
             scale_prog = 'SADABS'
         else:
             scale_prog = 'TWINABS'
         # absorpt_ref = SAINTReference(scale_prog, version)
         absorpt_ref = SadabsTwinabsReference()
     if 'SORTAV' in absdetails.upper():
         scale_prog = 'SORTAV'
         absorpt_ref = SORTAVReference()
     if 'crysalis' in abs_details.lower():
         scale_prog = 'SCALE3 ABSPACK'
     sentence = 'All data were integrated with {} and {} {} absorption correction using {} was applied.'
     txt = sentence.format(integration_prog, get_inf_article(abstype),
                           abstype, scale_prog)
     paragraph.add_run(retranslate_delimiter(txt))
     ref.append([data_reduct_ref, absorpt_ref])
コード例 #14
0
 def load_property_from_settings(self) -> None:
     """
     Load/Edit the value list of a property entry.
     """
     table = self.app.ui.PropertiesEditTableWidget
     listwidget = self.app.ui.PropertiesTemplatesListWidget
     table.blockSignals(True)
     table.clearContents()
     table.setRowCount(0)
     index = listwidget.currentIndex()
     if index.row() == -1:
         # nothing selected
         # self.app.ui.PropertiesEditTableWidget.blockSignals(False)
         return
     selected_row_text = listwidget.currentIndex().data()
     table_data = self.settings.load_settings_list('property',
                                                   selected_row_text)
     if table_data:
         cif_key = table_data[0]
         with suppress(Exception):
             table_data = table_data[1]
         self.app.ui.cifKeywordLineEdit.setText(cif_key)
     if not table_data:
         table_data = ['', '', '']
     for value in table_data:
         try:
             self.add_propeties_row(table,
                                    retranslate_delimiter(str(value)))
         except TypeError:
             print('Bad value in property table')
             continue
     self.add_propeties_row(table, '')
     self.app.ui.PropertiesTemplatesStackedWidget.setCurrentIndex(1)
     table.blockSignals(False)
     # table.setWordWrap(False)
     table.resizeRowsToContents()
コード例 #15
0
ファイル: report_text.py プロジェクト: dkratzert/FinalCif
 def __init__(self, cif: CifContainer, paragraph: Paragraph,
              ref: ReferenceList):
     self.cif = cif
     refineref = DummyReference()
     solveref = DummyReference()
     solution_prog = gstr(self.cif['_computing_structure_solution']) or '??'
     solution_method = gstr(
         self.cif['_atom_sites_solution_primary']) or '??'
     if solution_prog.upper().startswith(('SHELXT', 'XT')):
         solveref = SHELXTReference()
     if 'SHELXS' in solution_prog.upper():
         solveref = SHELXSReference()
     if 'SHELXD' in solution_prog.upper():
         solveref = SHELXDReference()
     refined = gstr(self.cif['_computing_structure_refinement']) or '??'
     if refined.upper().startswith(('SHELXL', 'XL')):
         refineref = SHELXLReference()
     if 'OLEX' in refined.upper():
         refineref = Olex2Reference()
     refine_coef = gstr(self.cif['_refine_ls_structure_factor_coef'])
     sentence = r"The structure were solved by {} methods using {} and refined by full-matrix " \
                "least-squares methods against "
     txt = sentence.format(solution_method.strip('\n\r'),
                           solution_prog.split()[0])
     paragraph.add_run(retranslate_delimiter(txt))
     paragraph.add_run('F').font.italic = True
     if refine_coef.lower() == 'fsqd':
         paragraph.add_run('2').font.superscript = True
     paragraph.add_run(' by {}'.format(refined.split()[0]))
     shelxle = None
     if 'shelxle' in refined.lower(
     ) or 'shelxle' in self.cif['_computing_molecular_graphics'].lower():
         paragraph.add_run(' using ShelXle')
         shelxle = ShelXleReference()
     paragraph.add_run('.')
     ref.append([solveref, refineref, shelxle])
コード例 #16
0
 def set_author_info(self, author: Dict[str, Union[str, None]]):
     if not author:
         return
     if author.get('name'):
         self.ui.FullNameLineEdit.setText(
             retranslate_delimiter(as_string(author.get('name'))))
     if author.get('address'):
         self.ui.AddressTextedit.setText(
             retranslate_delimiter(as_string(author.get('address'))))
     if author.get('email'):
         self.ui.EMailLineEdit.setText(
             retranslate_delimiter(as_string(author.get('email'))))
     if author.get('footnote'):
         self.ui.FootNoteLineEdit.setText(
             retranslate_delimiter(as_string(author.get('footnote'))))
     if author.get('orcid'):
         self.ui.ORCIDLineEdit.setText(
             retranslate_delimiter(as_string(author.get('orcid'))))
     if author.get('phone'):
         self.ui.PhoneLineEdit.setText(
             retranslate_delimiter(as_string(author.get('phone'))))
     self.ui.ContactAuthorCheckBox.setChecked(
         author.get('contact') or False)
コード例 #17
0
ファイル: test_text.py プロジェクト: dkratzert/FinalCif
 def test_cif_str_to_utf8(self):
     r = retranslate_delimiter(r'100 \%C')
     self.assertEqual('100 °C', r)
コード例 #18
0
ファイル: test_text.py プロジェクト: dkratzert/FinalCif
 def test__backwards_delimit_umlaut(self):
     self.assertEqual('äöüç', retranslate_delimiter(r'a\"o\"u\"\,c'))
コード例 #19
0
 def get_loop_column(self, key_in_loop: str) -> List:
     return [
         retranslate_delimiter(as_string(x))
         for x in self.block.find_loop(key_in_loop)
     ]
コード例 #20
0
    def make_templated_report(self, options: Options, file_obj: Path, output_filename: str, picfile: Path,
                              template_path: Path):
        cif = CifContainer(file_obj)
        tpl_doc = DocxTemplate(Path(__file__).parent.parent.joinpath(template_path))
        ba = BondsAndAngles(cif, without_h=options.without_h)
        t = TorsionAngles(cif, without_h=options.without_h)
        h = HydrogenBonds(cif)
        context = {'options'               : options,
                   # {'without_h': True, 'atoms_table': True, 'text': True, 'bonds_table': True},
                   'cif'                   : cif,
                   'space_group'           : self.space_group_subdoc(tpl_doc, cif),
                   'structure_figure'      : self.make_picture(options, picfile, tpl_doc),
                   'crystallization_method': remove_line_endings(retranslate_delimiter(
                       cif['_exptl_crystal_recrystallization_method'])) or '[No crystallization method given!]',
                   'sum_formula'           : self.format_sum_formula(cif['_chemical_formula_sum'].replace(" ", "")),
                   'itnum'                 : cif['_space_group_IT_number'],
                   'crystal_size'          : this_or_quest(cif['_exptl_crystal_size_min']) + timessym +
                                             this_or_quest(cif['_exptl_crystal_size_mid']) + timessym +
                                             this_or_quest(cif['_exptl_crystal_size_max']),
                   'crystal_colour'        : this_or_quest(cif['_exptl_crystal_colour']),
                   'crystal_shape'         : this_or_quest(cif['_exptl_crystal_description']),
                   'radiation'             : self.get_radiation(cif),
                   'wavelength'            : cif['_diffrn_radiation_wavelength'],
                   'theta_range'           : self.get_from_to_theta_range(cif),
                   'diffr_type'            : gstr(cif['_diffrn_measurement_device_type'])
                                             or '[No measurement device type given]',
                   'diffr_device'          : gstr(cif['_diffrn_measurement_device'])
                                             or '[No measurement device given]',
                   'diffr_source'          : gstr(cif['_diffrn_source']).strip('\n\r')
                                             or '[No radiation source given]',
                   'monochromator'         : gstr(cif['_diffrn_radiation_monochromator']) \
                                             or '[No monochromator type given]',
                   'detector'              : gstr(cif['_diffrn_detector_type']) \
                                             or '[No detector type given]',
                   'lowtemp_dev'           : gstr(cif['_olex2_diffrn_ambient_temperature_device']) \
                                             or '',
                   'index_ranges'          : self.hkl_index_limits(cif),
                   'indepentent_refl'      : this_or_quest(cif['_reflns_number_total']),
                   'r_int'                 : this_or_quest(cif['_diffrn_reflns_av_R_equivalents']),
                   'r_sigma'               : this_or_quest(cif['_diffrn_reflns_av_unetI/netI']),
                   'completeness'          : self.get_completeness(cif),
                   'theta_full'            : cif['_diffrn_reflns_theta_full'],
                   'data'                  : this_or_quest(cif['_refine_ls_number_reflns']),
                   'restraints'            : this_or_quest(cif['_refine_ls_number_restraints']),
                   'parameters'            : this_or_quest(cif['_refine_ls_number_parameters']),
                   'goof'                  : this_or_quest(cif['_refine_ls_goodness_of_fit_ref']),
                   'ls_R_factor_gt'        : this_or_quest(cif['_refine_ls_R_factor_gt']),
                   'ls_wR_factor_gt'       : this_or_quest(cif['_refine_ls_wR_factor_gt']),
                   'ls_R_factor_all'       : this_or_quest(cif['_refine_ls_R_factor_all']),
                   'ls_wR_factor_ref'      : this_or_quest(cif['_refine_ls_wR_factor_ref']),
                   'diff_dens_min'         : self.get_diff_density_min(cif).replace('-', minus_sign),
                   'diff_dens_max'         : self.get_diff_density_max(cif).replace('-', minus_sign),
                   'exti'                  : self.get_exti(cif),
                   'flack_x'               : self.get_flackx(cif),
                   'integration_progr'     : self.get_integration_program(cif),
                   'abstype'               : gstr(cif['_exptl_absorpt_correction_type']) or '??',
                   'abs_details'           : self.get_absortion_correction_program(cif),
                   'solution_method'       : self.solution_method(cif),
                   'solution_program'      : self.solution_program(cif),
                   'refinement_prog'       : self.refinement_prog(cif),
                   'atomic_coordinates'    : self.get_atomic_coordinates(cif),
                   'bonds'                 : ba.bonds,
                   'angles'                : ba.angles,
                   'ba_symminfo'           : ba.symminfo,
                   'torsions'              : t.torsion_angles,
                   'torsion_symminfo'      : t.symminfo,
                   'hydrogen_bonds'        : h.hydrogen_bonds,
                   'hydrogen_symminfo'     : h.symminfo,
                   'literature'            : self.literature
                   }

        # Filter definition for {{foobar|filter}} things:
        jinja_env = jinja2.Environment()
        jinja_env.filters['inv_article'] = get_inf_article
        tpl_doc.render(context, jinja_env=jinja_env, autoescape=True)
        tpl_doc.save(output_filename)
コード例 #21
0
ファイル: test_text.py プロジェクト: dkratzert/FinalCif
 def test_retranslate_all(self):
     for char in charcters:
         if char in ('Å', 'Å'):
             continue
         self.assertEqual(char, retranslate_delimiter(delimit_string(char)))
コード例 #22
0
ファイル: test_text.py プロジェクト: dkratzert/FinalCif
 def test_encode_and_decode_utf8(self):
     # Test for quote and immediate decode to utf-8 again:
     self.assertEqual(self.txt,
                      retranslate_delimiter(utf8_to_str(self.txt)))
コード例 #23
0
ファイル: test_text.py プロジェクト: dkratzert/FinalCif
 def test_retranslate_sentence(self):
     r = retranslate_delimiter("Crystals were grown from thf at -20 \%C.")
     self.assertEqual('Crystals were grown from thf at -20 °C.', r)