diff options
Diffstat (limited to 'test/test_reader.py')
| -rw-r--r-- | test/test_reader.py | 2003 |
1 files changed, 2003 insertions, 0 deletions
diff --git a/test/test_reader.py b/test/test_reader.py new file mode 100644 index 0000000..f971cd1 --- /dev/null +++ b/test/test_reader.py @@ -0,0 +1,2003 @@ +from datetime import date +import unittest +import utils +import os +import datetime +from io import StringIO + +TOPDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +utils.set_search_paths(TOPDIR) +import modelcif.reader +import modelcif.reference +import ihm +import ihm.reader + + +ASYM_ENTITY = """ +loop_ +_entity_poly_seq.entity_id +_entity_poly_seq.num +_entity_poly_seq.mon_id +_entity_poly_seq.hetero +1 1 MET . +1 2 CYS . +1 3 MET . +1 4 SER . +# +loop_ +_struct_asym.id +_struct_asym.entity_id +_struct_asym.details +A 1 foo +""" + + +class Tests(unittest.TestCase): + + def test_old_file_read_default(self): + """Test default handling of old files""" + cif = """ +loop_ +_audit_conform.dict_name +_audit_conform.dict_version +mmcif_pdbx.dic 5.311 +mmcif_ma.dic 0.14 +""" + s, = modelcif.reader.read(StringIO(cif)) + + def test_old_file_read_fail(self): + """Test failure reading old files""" + cif = """ +loop_ +_audit_conform.dict_name +_audit_conform.dict_version +mmcif_pdbx.dic 5.311 +mmcif_ma.dic 0.1.3 +""" + self.assertRaises(modelcif.reader.OldFileError, + modelcif.reader.read, StringIO(cif), + reject_old_file=True) + + def test_new_file_read_ok(self): + """Test success reading not-old files""" + # File read is OK if version is new enough, or version cannot be parsed + # because it is non-int or has too many elements + for ver in ('1.3.2', '1.3', '0.0.4.3', '0.0a'): + cif = """ +loop_ +_audit_conform.dict_name +_audit_conform.dict_version +mmcif_pdbx.dic 5.311 +mmcif_ma.dic %s +""" % ver + s, = modelcif.reader.read(StringIO(cif), reject_old_file=True) + + def test_software_group_handler(self): + """Test SoftwareGroupHandler and SoftwareParameterHandler""" + cif = """ +loop_ +_ma_software_parameter.parameter_id +_ma_software_parameter.group_id +_ma_software_parameter.data_type +_ma_software_parameter.name +_ma_software_parameter.value +_ma_software_parameter.description +1 1 integer foo 42 foodesc +2 1 boolean bar YES . +3 1 string baz ok . +4 1 integer-csv intlist 1,2,3,4 . +5 1 float-csv floatlist 1.5,3.8 . +# +loop_ +_ma_software_group.ordinal_id +_ma_software_group.group_id +_ma_software_group.software_id +_ma_software_group.parameter_group_id +1 1 1 . +2 1 2 . +3 2 3 . +4 2 4 1 +""" + s, = modelcif.reader.read(StringIO(cif)) + s1, s2, s3, s4 = s.software + g1, g2 = s.software_groups + self.assertEqual(len(g1), 2) + self.assertEqual(len(g2), 2) + self.assertIsInstance(g1[0], modelcif.Software) + self.assertIsInstance(g1[1], modelcif.Software) + self.assertEqual(g1[0], s1) + self.assertEqual(g1[1], s2) + + self.assertIsInstance(g2[0], modelcif.Software) + self.assertIsInstance(g2[1], modelcif.SoftwareWithParameters) + self.assertEqual(g2[0], s3) + self.assertEqual(g2[1].software, s4) + p1, p2, p3, intlist, floatlist = g2[1].parameters + self.assertEqual(p1.name, 'foo') + self.assertEqual(p1.value, 42) + self.assertEqual(p1.description, 'foodesc') + self.assertEqual(p2.name, 'bar') + self.assertTrue(p2.value) + self.assertIsNone(p2.description) + self.assertEqual(p3.name, 'baz') + self.assertEqual(p3.value, 'ok') + self.assertIsNone(p3.description) + self.assertEqual(intlist.value, [1, 2, 3, 4]) + f1, f2 = floatlist.value + self.assertAlmostEqual(f1, 1.5, delta=1e-1) + self.assertAlmostEqual(f2, 3.8, delta=1e-1) + + def test_enumeration_mapper(self): + """Test EnumerationMapper class""" + m = modelcif.reader._EnumerationMapper( + modelcif.reference, modelcif.reference.TargetReference) + # Check get of a handled enumeration value + unp = m.get('UNP', None) + self.assertIs(unp, modelcif.reference.UniProt) + self.assertEqual(unp.name, 'UNP') + self.assertIsNone(unp.other_details) + # We should get the same class each time (case insensitive) + unp2 = m.get('unp', None) + self.assertIs(unp, unp2) + # Check get of an unhandled value + miss = m.get('MIS', None) + self.assertEqual(miss.name, 'MIS') + self.assertIsNone(unp.other_details) + # We should get the same class each time (case insensitive) + miss2 = m.get('mis', None) + self.assertIs(miss, miss2) + # Check get of a custom "other" value + custom = m.get('other', "custom type 1") + self.assertEqual(custom.name, 'Other') + self.assertEqual(custom.other_details, "custom type 1") + # We should get the same class each time (case insensitive) + custom2 = m.get('Other', "CUSTOM TYPE 1") + self.assertIs(custom, custom2) + # Check get of a different custom "other" value + custom = m.get('other', "custom type 2") + self.assertEqual(custom.name, 'Other') + self.assertEqual(custom.other_details, "custom type 2") + + def test_database_handler(self): + """Test DatabaseHandler""" + cif = """ +_database_2.database_id 'PDB' +_database_2.database_code '5HVP' +""" + s, = modelcif.reader.read(StringIO(cif)) + self.assertEqual(s.database.id, 'PDB') + self.assertEqual(s.database.code, '5HVP') + + def test_target_ref_db_handler(self): + """Test TargetRefDBHander""" + cif = """ +loop_ +_ma_target_ref_db_details.target_entity_id +_ma_target_ref_db_details.db_name +_ma_target_ref_db_details.db_name_other_details +_ma_target_ref_db_details.db_code +_ma_target_ref_db_details.db_accession +_ma_target_ref_db_details.seq_db_isoform +_ma_target_ref_db_details.seq_db_align_begin +_ma_target_ref_db_details.seq_db_align_end +_ma_target_ref_db_details.ncbi_taxonomy_id +_ma_target_ref_db_details.organism_scientific +_ma_target_ref_db_details.seq_db_sequence_version_date +_ma_target_ref_db_details.seq_db_sequence_checksum +_ma_target_ref_db_details.is_primary +1 UNP . MED1_YEAST Q12321 test_iso 1 10 test_tax test_org 1996-11-01 +637FEA3E78D915BC YES +1 Other foo . . ? 1 10 . . . . NO +1 other bar . . ? 1 10 . . . . . +1 MIS baz . . ? 1 10 . . . . . +""" + s, = modelcif.reader.read(StringIO(cif)) + e, = s.entities + r1, r2, r3, r4 = e.references + self.assertIsInstance(r1, modelcif.reference.UniProt) + self.assertEqual(r1.code, 'MED1_YEAST') + self.assertEqual(r1.accession, 'Q12321') + self.assertEqual(r1.isoform, 'test_iso') + self.assertEqual(r1.align_begin, 1) + self.assertEqual(r1.align_end, 10) + self.assertEqual(r1.ncbi_taxonomy_id, 'test_tax') + self.assertEqual(r1.organism_scientific, 'test_org') + self.assertEqual(r1.sequence_version_date, date(1996, 11, 1)) + self.assertIsNone(r1.sequence) + self.assertIsNone(r1.details) + self.assertTrue(r1.is_primary) + self.assertEqual(r1.alignments, []) + self.assertEqual(r2.name, 'Other') + self.assertFalse(r2.is_primary) + self.assertEqual(r2.other_details, 'foo') + self.assertEqual(r3.name, 'Other') + self.assertEqual(r3.other_details, 'bar') + self.assertIsNone(r3.is_primary) + self.assertEqual(r4.name, 'MIS') + self.assertIsNone(r4.other_details) # should be ignored + + def test_target_ref_db_handler_with_struct_ref(self): + """Test TargetRefDBHander combined with struct_ref info""" + cif = """ +loop_ +_struct_ref.id +_struct_ref.entity_id +_struct_ref.db_name +_struct_ref.db_code +_struct_ref.pdbx_db_accession +_struct_ref.pdbx_align_begin +_struct_ref.pdbx_seq_one_letter_code +_struct_ref.details +1 1 UNP MED1_YEAST Q12321 1 DSYVETLDCC "test details" +2 1 UNP sr_only_code sr_only_acc 1 DSYVETLDPP . +# +# +loop_ +_struct_ref_seq.align_id +_struct_ref_seq.ref_id +_struct_ref_seq.seq_align_beg +_struct_ref_seq.seq_align_end +_struct_ref_seq.db_align_beg +_struct_ref_seq.db_align_end +1 1 1 10 1 10 +2 2 1 10 1 10 +# +loop_ +_ma_target_ref_db_details.target_entity_id +_ma_target_ref_db_details.db_name +_ma_target_ref_db_details.db_name_other_details +_ma_target_ref_db_details.db_code +_ma_target_ref_db_details.db_accession +_ma_target_ref_db_details.seq_db_isoform +_ma_target_ref_db_details.seq_db_align_begin +_ma_target_ref_db_details.seq_db_align_end +_ma_target_ref_db_details.ncbi_taxonomy_id +_ma_target_ref_db_details.organism_scientific +_ma_target_ref_db_details.seq_db_sequence_version_date +_ma_target_ref_db_details.seq_db_sequence_checksum +_ma_target_ref_db_details.is_primary +1 UNP . MED1_YEAST Q12321 test_iso 1 10 test_tax test_org 1996-11-01 +637FEA3E78D915BC YES +1 UNP . rd_only_code rd_only_acc rd_only_iso . . . . . . NO +""" + s, = modelcif.reader.read(StringIO(cif)) + e, = s.entities + r1, r2, r3 = e.references + # r1 should contain both target_ref_db and struct_ref info + self.assertIsInstance(r1, modelcif.reference.UniProt) + self.assertEqual(r1.code, 'MED1_YEAST') + self.assertEqual(r1.accession, 'Q12321') + self.assertEqual(r1.isoform, 'test_iso') + self.assertEqual(r1.align_begin, 1) + self.assertEqual(r1.align_end, 10) + self.assertEqual(r1.ncbi_taxonomy_id, 'test_tax') + self.assertEqual(r1.organism_scientific, 'test_org') + self.assertEqual(r1.sequence_version_date, date(1996, 11, 1)) + self.assertEqual(r1.sequence, 'DSYVETLDCC') + self.assertEqual(r1.details, "test details") + self.assertTrue(r1.is_primary) + a, = r1.alignments + self.assertEqual(a.db_begin, 1) + self.assertEqual(a.db_end, 10) + self.assertEqual(a.entity_begin, 1) + self.assertEqual(a.entity_end, 10) + # r2 should contain only target_ref_db info + self.assertIsInstance(r2, modelcif.reference.UniProt) + self.assertEqual(r2.code, 'rd_only_code') + self.assertEqual(r2.accession, 'rd_only_acc') + self.assertEqual(r2.isoform, 'rd_only_iso') + self.assertIsNone(r2.sequence) + self.assertFalse(r2.is_primary) + # r3 should contain only struct_ref info + self.assertIsInstance(r3, modelcif.reference.UniProt) + self.assertEqual(r3.code, 'sr_only_code') + self.assertEqual(r3.accession, 'sr_only_acc') + self.assertIsNone(r3.isoform) + self.assertIsNone(r3.ncbi_taxonomy_id) + self.assertEqual(r3.sequence, 'DSYVETLDPP') + self.assertIsNone(r3.is_primary) + a, = r3.alignments + self.assertEqual(a.db_begin, 1) + self.assertEqual(a.db_end, 10) + self.assertEqual(a.entity_begin, 1) + self.assertEqual(a.entity_end, 10) + + def test_transformation_handler(self): + """Test _TransformationHandler""" + cif = """ +loop_ +_ma_template_trans_matrix.id +_ma_template_trans_matrix.rot_matrix[1][1] +_ma_template_trans_matrix.rot_matrix[2][1] +_ma_template_trans_matrix.rot_matrix[3][1] +_ma_template_trans_matrix.rot_matrix[1][2] +_ma_template_trans_matrix.rot_matrix[2][2] +_ma_template_trans_matrix.rot_matrix[3][2] +_ma_template_trans_matrix.rot_matrix[1][3] +_ma_template_trans_matrix.rot_matrix[2][3] +_ma_template_trans_matrix.rot_matrix[3][3] +_ma_template_trans_matrix.tr_vector[1] +_ma_template_trans_matrix.tr_vector[2] +_ma_template_trans_matrix.tr_vector[3] +1 1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 +1.000000 0.000 0.000 0.000 +""" + s, = modelcif.reader.read(StringIO(cif)) + t, = s.template_transformations + self.assertAlmostEqual(t.rot_matrix[0][0], 1.0, delta=1e-6) + self.assertAlmostEqual(t.tr_vector[0], 0.0, delta=1e-6) + + def test_template_details_handler(self): + """Test _TemplateDetailsHandler""" + cif = """ +loop_ +_ma_template_details.ordinal_id +_ma_template_details.template_id +_ma_template_details.template_origin +_ma_template_details.template_entity_type +_ma_template_details.template_trans_matrix_id +_ma_template_details.template_data_id +_ma_template_details.target_asym_id +_ma_template_details.template_label_asym_id +_ma_template_details.template_label_entity_id +_ma_template_details.template_model_num +_ma_template_details.template_auth_asym_id +1 1 'reference database' polymer 1 2 A B 3 4 Z +2 2 'reference database' polymer 2 3 . B 3 4 Z +3 3 customized polymer 2 3 . B 3 4 Z +""" + s, = modelcif.reader.read(StringIO(cif)) + t, t2, t3 = s.templates + self.assertIsInstance(t, modelcif.Template) + self.assertIsInstance(t2, modelcif.Template) + self.assertIsInstance(t3, modelcif.CustomTemplate) + self.assertEqual(t.entity_id, '3') + self.assertEqual(t.model_num, 4) + self.assertEqual(t.asym_id, 'B') + self.assertEqual(t.strand_id, 'Z') + self.assertEqual(len(s.alignments), 0) + + def test_template_customized_handler(self): + """Test _TemplateCustomizedHandler""" + cif = """ +loop_ +_ma_template_details.ordinal_id +_ma_template_details.template_id +1 1 +# +loop_ +_ma_template_customized.template_id +_ma_template_customized.details +1 'details x' +2 'details y' +""" + s, = modelcif.reader.read(StringIO(cif)) + t1, t2 = s.templates + # template_details does not specify template_origin, so template #1 + # will be initially instantiated as a Template, and should be corrected + # to CustomTemplate on reading template_customized: + self.assertIsInstance(t1, modelcif.CustomTemplate) + self.assertEqual(t1.details, 'details x') + self.assertEqual(len(t1.atoms), 0) + self.assertIsInstance(t2, modelcif.CustomTemplate) + self.assertEqual(t2.details, 'details y') + self.assertEqual(len(t2.atoms), 0) + + def test_template_details_handler_nonpoly(self): + """Test _TemplateDetailsHandler with nonpolymeric template""" + cif = """ +loop_ +_pdbx_entity_nonpoly.entity_id +_pdbx_entity_nonpoly.name +_pdbx_entity_nonpoly.comp_id +_pdbx_entity_nonpoly.ma_model_mode +3 Heme HEM explicit +# +loop_ +_ma_template_details.ordinal_id +_ma_template_details.template_id +_ma_template_details.template_origin +_ma_template_details.template_entity_type +_ma_template_details.template_trans_matrix_id +_ma_template_details.template_data_id +_ma_template_details.target_asym_id +_ma_template_details.template_label_asym_id +_ma_template_details.template_label_entity_id +_ma_template_details.template_model_num +_ma_template_details.template_auth_asym_id +1 1 'reference database' non-polymer 1 2 A B 3 4 Z +# +loop_ +_ma_template_non_poly.template_id +_ma_template_non_poly.comp_id +_ma_template_non_poly.details +1 HEM "Template Heme" +""" + s, = modelcif.reader.read(StringIO(cif)) + t, = s.templates + self.assertEqual(t.entity_id, '3') + self.assertEqual(t.model_num, 4) + self.assertEqual(t.asym_id, 'B') + self.assertEqual(t.strand_id, 'Z') + self.assertEqual(len(s.alignments), 0) + self.assertEqual(t.entity.description, 'Template Heme') + a, = s.asym_units + self.assertIsInstance(a, modelcif.NonPolymerFromTemplate) + self.assertIs(a.template, t) + self.assertTrue(a.explicit) + + def test_custom_template_coord_handler(self): + """Test reading of coordinates for CustomTemplate""" + cif = """ +loop_ +_ma_template_details.ordinal_id +_ma_template_details.template_id +_ma_template_details.template_origin +_ma_template_details.template_entity_type +_ma_template_details.template_trans_matrix_id +_ma_template_details.template_data_id +_ma_template_details.target_asym_id +_ma_template_details.template_label_asym_id +_ma_template_details.template_label_entity_id +_ma_template_details.template_model_num +_ma_template_details.template_auth_asym_id +1 1 customized polymer 2 3 . B 3 4 Z +# +loop_ +_ma_template_customized.template_id +_ma_template_customized.details +1 'Provided by user' +# +loop_ +_ma_template_coord.template_id +_ma_template_coord.group_PDB +_ma_template_coord.ordinal_id +_ma_template_coord.type_symbol +_ma_template_coord.label_atom_id +_ma_template_coord.label_comp_id +_ma_template_coord.label_seq_id +_ma_template_coord.label_asym_id +_ma_template_coord.auth_seq_id +_ma_template_coord.auth_asym_id +_ma_template_coord.auth_atom_id +_ma_template_coord.auth_comp_id +_ma_template_coord.Cartn_x +_ma_template_coord.Cartn_y +_ma_template_coord.Cartn_z +_ma_template_coord.occupancy +_ma_template_coord.label_entity_id +_ma_template_coord.B_iso_or_equiv +_ma_template_coord.formal_charge +1 ATOM 1 C CA ALA 1 A 42 A X XXX 0 1.000 2.000 0.500 9 2.000 1.000 +1 ATOM 2 O OXT CYS 2 A . A . . 1.000 2.000 3.000 . 9 . . +# +""" + s, = modelcif.reader.read(StringIO(cif)) + t, = s.templates + self.assertIsInstance(t, modelcif.CustomTemplate) + self.assertEqual(t.details, 'Provided by user') + self.assertEqual(len(t.atoms), 2) + a1 = t.atoms[0] + self.assertEqual(a1.seq_id, 1) + self.assertEqual(a1.atom_id, 'CA') + self.assertEqual(a1.type_symbol, 'C') + self.assertAlmostEqual(a1.x, 0.0, delta=1e-2) + self.assertAlmostEqual(a1.y, 1.0, delta=1e-2) + self.assertAlmostEqual(a1.z, 2.0, delta=1e-2) + self.assertAlmostEqual(a1.occupancy, 0.5, delta=1e-2) + self.assertAlmostEqual(a1.biso, 2.0, delta=1e-2) + self.assertAlmostEqual(a1.charge, 1.0, delta=1e-2) + self.assertEqual(a1.auth_seq_id, 42) + self.assertEqual(a1.auth_comp_id, 'XXX') + self.assertEqual(a1.auth_atom_id, 'X') + + a2 = t.atoms[1] + self.assertEqual(a2.seq_id, 2) + self.assertEqual(a2.atom_id, 'OXT') + self.assertEqual(a2.type_symbol, 'O') + + def test_entity_nonpoly_bad_model_mode(self): + """Test pdbx_entity_nonpoly with missing ma_model_mode""" + cif = """ +loop_ +_struct_asym.id +_struct_asym.entity_id +A 1 +B 2 +C 3 +# +loop_ +_pdbx_entity_nonpoly.entity_id +_pdbx_entity_nonpoly.name +_pdbx_entity_nonpoly.comp_id +_pdbx_entity_nonpoly.ma_model_mode +1 test1 TE1 explicit +2 test2 TE2 . +3 test3 TE3 ? +# +loop_ +_ma_template_details.ordinal_id +_ma_template_details.template_id +_ma_template_details.template_origin +_ma_template_details.template_entity_type +_ma_template_details.template_trans_matrix_id +_ma_template_details.template_data_id +_ma_template_details.target_asym_id +_ma_template_details.template_label_asym_id +_ma_template_details.template_label_entity_id +_ma_template_details.template_model_num +_ma_template_details.template_auth_asym_id +1 1 'reference database' non-polymer 1 2 A A 1 4 Z +2 2 'reference database' non-polymer 1 3 B B 2 4 Z +3 3 'reference database' non-polymer 1 4 C C 3 4 Z +# +loop_ +_ma_template_non_poly.template_id +_ma_template_non_poly.comp_id +_ma_template_non_poly.details +1 TE1 test1 +2 TE2 test2 +3 TE3 test3 +""" + s, = modelcif.reader.read(StringIO(cif)) + a1, a2, a3 = s.asym_units + self.assertTrue(a1.explicit) + self.assertIsNone(a2.explicit) + self.assertIs(a3.explicit, ihm.unknown) + + def test_template_ref_db_handler(self): + """Test _TemplateRefDBHandler""" + cif = """ +loop_ +_ma_template_ref_db_details.template_id +_ma_template_ref_db_details.db_name +_ma_template_ref_db_details.db_name_other_details +_ma_template_ref_db_details.db_accession_code +_ma_template_ref_db_details.db_version_date +1 PDB . 3nc1 2021-10-06 +1 MIS . testacc . +1 Other foo acc2 . +1 PubChem . 1046 . +1 AlphaFoldDB . I6XD65 2022-06-01 +""" + s, = modelcif.reader.read(StringIO(cif)) + t, = s.templates + r1, r2, r3, r4, r5 = t.references + self.assertIsInstance(r1, modelcif.reference.PDB) + self.assertEqual(r1.accession, '3nc1') + self.assertEqual(r1.db_version_date, date(2021, 10, 6)) + self.assertEqual(r2.name, 'MIS') + self.assertIsNone(r2.other_details) + self.assertIsNone(r2.db_version_date) + self.assertEqual(r3.name, 'Other') + self.assertEqual(r3.other_details, 'foo') + self.assertIsInstance(r4, modelcif.reference.PubChem) + self.assertEqual(r4.accession, '1046') + self.assertIsInstance(r5, modelcif.reference.AlphaFoldDB) + self.assertEqual(r5.accession, 'I6XD65') + self.assertEqual(r5.db_version_date, date(2022, 6, 1)) + + def _get_models_cif(self, old=False): + if old: + cif = """ +loop_ +_ma_model_list.ordinal_id +_ma_model_list.model_id +_ma_model_list.model_group_id +_ma_model_list.model_name +_ma_model_list.model_group_name +_ma_model_list.assembly_id +_ma_model_list.data_id +_ma_model_list.model_type +_ma_model_list.model_type_other_details +1 1 1 'Best scoring model' 'All models' 99 4 'Homology model' . +2 2 1 '2nd best scoring model' 'All models' 99 5 'Ab initio model' . +3 3 2 'Best scoring model' 'group2' 99 6 'Other' 'Custom other model' +# +""" + else: + cif = """ +loop_ +_ma_model_list.ordinal_id +_ma_model_list.model_name +_ma_model_list.assembly_id +_ma_model_list.data_id +_ma_model_list.model_type +_ma_model_list.model_type_other_details +1 'Best scoring model' 99 4 'Homology model' . +2 '2nd best scoring model' 99 5 'Ab initio model' . +3 'Best scoring model' 99 6 'Other' 'Custom other model' +# +loop_ +_ma_model_group.id +_ma_model_group.name +_ma_model_group.details +1 'All models' . +2 'group2' 'second group details' +# +# +loop_ +_ma_model_group_link.group_id +_ma_model_group_link.model_id +1 1 +1 2 +2 3 +# +""" + cif += """ +loop_ +_atom_site.group_PDB +_atom_site.id +_atom_site.type_symbol +_atom_site.label_atom_id +_atom_site.label_alt_id +_atom_site.label_comp_id +_atom_site.label_seq_id +_atom_site.auth_seq_id +_atom_site.pdbx_PDB_ins_code +_atom_site.label_asym_id +_atom_site.Cartn_x +_atom_site.Cartn_y +_atom_site.Cartn_z +_atom_site.occupancy +_atom_site.label_entity_id +_atom_site.auth_asym_id +_atom_site.B_iso_or_equiv +_atom_site.pdbx_PDB_model_num +ATOM 1 C CA . ASP 1 1 ? A 1.000 2.000 3.000 . 1 A . 6 +ATOM 2 C CA . ASP 1 1 ? A 1.000 2.000 3.000 . 1 A . 8 +""" + return cif + + def test_model_list_handler_default_old(self): + """Test _ModelListHandler with default model class, old dictionary""" + self._test_model_list_handler_default(old=True) + + def test_model_list_handler_default(self): + """Test _ModelListHandler with default model class""" + self._test_model_list_handler_default(old=False) + + def _test_model_list_handler_default(self, old): + cif = self._get_models_cif(old=old) + s, = modelcif.reader.read(StringIO(cif)) + mg1, mg2, mg3 = s.model_groups + self.assertEqual(mg1.name, 'All models') + m1, m2 = list(mg1) + self.assertIsInstance(m1, modelcif.model.HomologyModel) + self.assertEqual(m1.model_type, 'Homology model') + self.assertIsNone(m1.other_details) + self.assertIsInstance(m2, modelcif.model.AbInitioModel) + self.assertEqual(m2.model_type, 'Ab initio model') + self.assertIsNone(m2.other_details) + self.assertEqual(m1.name, 'Best scoring model') + self.assertEqual(m2.name, '2nd best scoring model') + self.assertEqual(mg2.name, 'group2') + m1, = list(mg2) + self.assertEqual(m1.model_type, 'Other') + self.assertEqual(m1.other_details, 'Custom other model') + self.assertEqual(m1.name, 'Best scoring model') + self.assertEqual(m1.assembly._id, '99') + # Last group is auto-created to contain the non-grouped models + # referenced by atom_site + self.assertIsNone(mg3.name) + m1, m2 = list(mg3) + self.assertEqual(m1.model_type, 'Other') + self.assertEqual(m1._id, '6') + self.assertEqual(m2.model_type, 'Other') + self.assertEqual(m2._id, '8') + + def test_model_list_handler_group_new_old(self): + """Test _ModelListHandler handling mix of new and old style groups""" + cif = """ +loop_ +_ma_model_list.ordinal_id +_ma_model_list.model_id +_ma_model_list.model_group_id +_ma_model_list.model_name +_ma_model_list.model_group_name +_ma_model_list.data_id +_ma_model_list.model_type +_ma_model_list.model_type_other_details +1 1 1 . . 4 'Homology model' . +2 2 . . . 4 'Homology model' . +3 3 1 . . 4 'Homology model' . +# +# +loop_ +_ma_model_group.id +_ma_model_group.name +_ma_model_group.details +1 'group1' . +2 'group2' . +# +# +loop_ +_ma_model_group_link.model_id +_ma_model_group_link.group_id +2 1 +3 2 +""" + s, = modelcif.reader.read(StringIO(cif)) + # model1 is in group1, using old-style tables; + # model2 is in group1, using new-style tables; + # model3 is in group2 according to new-style tables but group1 + # according to old style (new-style should take precedence) + mg1, mg2 = s.model_groups + self.assertEqual(mg1._id, '1') + self.assertEqual(mg2._id, '2') + self.assertEqual([m._id for m in mg1], ['2', '1']) + self.assertEqual([m._id for m in mg2], ['3']) + + def test_model_list_handler_custom(self): + """Test _ModelListHandler with custom model class""" + class MyModel(modelcif.model.Model): + """Custom model type""" + pass + cif = self._get_models_cif() + s, = modelcif.reader.read(StringIO(cif), model_class=MyModel) + mg1, mg2, mg3 = s.model_groups + m1, m2 = list(mg1) + m3, = list(mg2) + m4, m5 = list(mg3) + # Custom model type should always be returned, regardless of what + # the mmCIF file says it is, but model_type should be set + self.assertIsInstance(m1, MyModel) + self.assertIsInstance(m2, MyModel) + self.assertIsInstance(m3, MyModel) + self.assertIsInstance(m4, MyModel) + self.assertIsInstance(m5, MyModel) + self.assertEqual(m1.model_type, 'Homology model') + self.assertEqual(m2.model_type, 'Ab initio model') + self.assertEqual(m3.model_type, 'Other') + self.assertEqual(m4.model_type, 'Other') + self.assertEqual(m5.model_type, 'Other') + + def test_assembly_handler(self): + """Test _AssemblyHandler and _AssemblyDetailsHandler""" + cif = """ +loop_ +_entity_poly_seq.entity_id +_entity_poly_seq.num +_entity_poly_seq.mon_id +1 1 ALA +1 2 ALA +# +loop_ +_struct_asym.id +_struct_asym.entity_id +_struct_asym.details +A 1 Nup84 +# +loop_ +_ma_struct_assembly.ordinal_id +_ma_struct_assembly.assembly_id +_ma_struct_assembly.entity_id +_ma_struct_assembly.asym_id +_ma_struct_assembly.seq_id_begin +_ma_struct_assembly.seq_id_end +1 1 1 A 1 2 +2 1 1 A 1 1 +3 1 1 A . . +# +loop_ +_ma_struct_assembly_details.assembly_id +_ma_struct_assembly_details.assembly_name +_ma_struct_assembly_details.assembly_description +1 foo bar +""" + s, = modelcif.reader.read(StringIO(cif)) + a, = s.assemblies + self.assertEqual(a.name, 'foo') + self.assertEqual(a.description, 'bar') + self.assertEqual(len(a), 3) + # Complete asym + self.assertIsInstance(a[0], modelcif.AsymUnit) + # asym range + self.assertIsInstance(a[1], modelcif.AsymUnitRange) + self.assertEqual(a[1].seq_id_range, (1, 1)) + # No specified range -> complete asym + self.assertIsInstance(a[2], modelcif.AsymUnit) + + def test_template_poly_segment_handler(self): + """Test _TemplatePolySegmentHandler""" + cif = """ +loop_ +_ma_template_poly_segment.id +_ma_template_poly_segment.template_id +_ma_template_poly_segment.residue_number_begin +_ma_template_poly_segment.residue_number_end +1 42 2 9 +""" + s, = modelcif.reader.read(StringIO(cif)) + seg, = s.template_segments + self.assertEqual(seg.template._id, '42') + self.assertEqual(seg.seq_id_range, (2, 9)) + + def test_data__handler(self): + """Test _DataHandler""" + cif = """ +loop_ +_ma_data.id +_ma_data.name +_ma_data.content_type +_ma_data.content_type_other_details +1 'Template Structure' 'template structure' . +2 'Model subunit' target . +3 'Default model name' 'model coordinates' . +# +loop_ +_ma_template_details.ordinal_id +_ma_template_details.template_id +_ma_template_details.template_origin +_ma_template_details.template_entity_type +_ma_template_details.template_trans_matrix_id +_ma_template_details.template_data_id +_ma_template_details.target_asym_id +_ma_template_details.template_label_asym_id +_ma_template_details.template_label_entity_id +_ma_template_details.template_model_num +_ma_template_details.template_auth_asym_id +1 1 'reference database' polymer 1 1 A A 1 1 Z +# +loop_ +_ma_model_list.ordinal_id +_ma_model_list.model_id +_ma_model_list.model_group_id +_ma_model_list.model_name +_ma_model_list.model_group_name +_ma_model_list.assembly_id +_ma_model_list.data_id +_ma_model_list.model_type +_ma_model_list.model_type_other_details +1 1 1 'Model name' 'All models' 1 3 'Homology model' . +""" + s, = modelcif.reader.read(StringIO(cif)) + d1, d2, d3 = s.data + self.assertIsInstance(d1, modelcif.Template) + # d2 is not referenced by any other table, so gets Data base class + self.assertIsInstance(d2, modelcif.data.Data) + self.assertIsInstance(d3, modelcif.model.Model) + # Name not given in template_details so taken from ma_data + self.assertEqual(d1.name, 'Template Structure') + self.assertEqual(d2.name, 'Model subunit') + # Name in model_list used rather than that from ma_data + self.assertEqual(d3.name, 'Model name') + + def test_data_group_handler(self): + """Test _DataGroupHandler""" + cif = """ +loop_ +_ma_data.id +_ma_data.name +_ma_data.content_type +_ma_data.content_type_other_details +1 'Template Structure' 'template structure' . +2 'Model subunit' target . +# +loop_ +_ma_template_details.ordinal_id +_ma_template_details.template_id +_ma_template_details.template_origin +_ma_template_details.template_entity_type +_ma_template_details.template_trans_matrix_id +_ma_template_details.template_data_id +_ma_template_details.target_asym_id +_ma_template_details.template_label_asym_id +_ma_template_details.template_label_entity_id +_ma_template_details.template_model_num +_ma_template_details.template_auth_asym_id +1 1 'reference database' polymer 1 1 A A 1 1 Z +# +loop_ +_ma_data_group.ordinal_id +_ma_data_group.group_id +_ma_data_group.data_id +1 1 1 +2 1 2 +3 2 3 +""" + s, = modelcif.reader.read(StringIO(cif)) + g1, g2, = s.data_groups + self.assertEqual(len(g1), 2) + self.assertIsInstance(g1[0], modelcif.Template) + self.assertEqual(g1[0]._data_id, '1') + self.assertIsInstance(g1[1], modelcif.data.Data) + self.assertEqual(g1[1]._data_id, '2') + self.assertEqual(len(g2), 1) + self.assertIsNone(g2[0]) + + def test_data_ref_db_handler(self): + """Test _DataRefDBHandler""" + cif = """ +loop_ +_ma_data.id +_ma_data.name +_ma_data.content_type +_ma_data.content_type_other_details +1 defaultname1 'reference database' . +2 defaultname2 'reference database' . +# +loop_ +_ma_data_ref_db.data_id +_ma_data_ref_db.name +_ma_data_ref_db.location_url +_ma_data_ref_db.version +_ma_data_ref_db.release_date +1 name1 url1 1.0 1979-11-22 +2 . url2 . . +""" + s, = modelcif.reader.read(StringIO(cif)) + d1, d2 = s.data + self.assertIsInstance(d1, modelcif.ReferenceDatabase) + self.assertIsInstance(d2, modelcif.ReferenceDatabase) + # Name in ma_data_ref_db used rather than that from ma_data + self.assertEqual(d1.name, 'name1') + self.assertEqual(d1.url, 'url1') + self.assertEqual(d1.version, '1.0') + self.assertIsInstance(d1.release_date, date) + self.assertEqual(d1.release_date, date(1979, 11, 22)) + # Name not given in ma_data_ref_db so taken from ma_data + self.assertEqual(d2.name, 'defaultname2') + self.assertIsNone(d2.version) + self.assertIsNone(d2.release_date) + + def test_protocol_handler(self): + """Test _ProtocolHandler""" + cif = """ +loop_ +_ma_protocol_step.ordinal_id +_ma_protocol_step.protocol_id +_ma_protocol_step.step_id +_ma_protocol_step.method_type +_ma_protocol_step.step_name +_ma_protocol_step.details +_ma_protocol_step.software_group_id +_ma_protocol_step.input_data_group_id +_ma_protocol_step.output_data_group_id +1 1 1 'template search' 'ModPipe Seq-Prf (0001)' . 1 1 2 +2 1 2 'template selection' . . . . . +3 1 3 'target-template alignment' . . . . . +4 1 4 modeling . . 2 2 1 +5 1 5 'model selection' . . 1 1 1 +6 1 6 'model refinement' . . . . . +7 1 7 other testname testdetails 42 99 66 +""" + s, = modelcif.reader.read(StringIO(cif)) + p, = s.protocols + self.assertEqual(len(p.steps), 7) + s1, s2, s3, s4, s5, s6, s7 = p.steps + self.assertIsInstance(s1, modelcif.protocol.TemplateSearchStep) + self.assertIsInstance(s2, modelcif.protocol.TemplateSelectionStep) + self.assertIsInstance(s3, + modelcif.protocol.TargetTemplateAlignmentStep) + self.assertIsInstance(s4, modelcif.protocol.ModelingStep) + self.assertIsInstance(s5, modelcif.protocol.ModelSelectionStep) + self.assertIsInstance(s6, modelcif.protocol.ModelRefinementStep) + self.assertIsInstance(s7, modelcif.protocol.Step) + self.assertEqual(s7.method_type, "other") + self.assertEqual(s7.name, "testname") + self.assertEqual(s7.details, "testdetails") + self.assertEqual(s7.input_data._id, '99') + self.assertEqual(s7.output_data._id, '66') + self.assertEqual(s7.software._id, '42') + + def test_target_entity_handler(self): + """Test _TargetEntityHandler""" + cif = """ +loop_ +_ma_target_entity.entity_id +_ma_target_entity.data_id +_ma_target_entity.origin +1 2 'reference database' +""" + s, = modelcif.reader.read(StringIO(cif)) + e, = s.entities + self.assertEqual(e._data_id, '2') + + def test_qa_metric_global_handler(self): + """Test _QAMetricGlobalHandler""" + cif = """ +loop_ +_ma_model_list.ordinal_id +_ma_model_list.model_id +_ma_model_list.model_group_id +_ma_model_list.model_name +_ma_model_list.model_group_name +_ma_model_list.assembly_id +_ma_model_list.data_id +_ma_model_list.model_type +_ma_model_list.model_type_other_details +1 1 1 'Best scoring model' 'All models' 1 4 'Homology model' . +# +loop_ +_ma_qa_metric.id +_ma_qa_metric.name +_ma_qa_metric.description +_ma_qa_metric.type +_ma_qa_metric.mode +_ma_qa_metric.type_other_details +_ma_qa_metric.software_group_id +1 MPQS 'ModPipe Quality Score' other global +'composite score, values >1.1 are considered reliable' 1 +2 zDOPE 'Normalized DOPE' zscore global . 2 +3 'TSVMod RMSD' 'TSVMod predicted RMSD (MSALL)' distance global . . +4 'TSVMod NO35' . 'normalized score' global . . +# +loop_ +_ma_qa_metric_global.ordinal_id +_ma_qa_metric_global.model_id +_ma_qa_metric_global.metric_id +_ma_qa_metric_global.metric_value +1 1 1 1.0 +2 1 2 2.0 +3 1 3 3.0 +4 1 4 4.0 +""" + s, = modelcif.reader.read(StringIO(cif)) + mg, = s.model_groups + m, = mg + q1, q2, q3, q4 = m.qa_metrics + self.assertIsInstance(q1, modelcif.qa_metric.Global) + self.assertEqual(q1.type, "other") + self.assertEqual(q1.name, "MPQS") + self.assertEqual(type(q1).__name__, "MPQS") + self.assertEqual(q1.description, "ModPipe Quality Score") + self.assertEqual(q1.__doc__, "ModPipe Quality Score") + self.assertEqual(q1.software._id, '1') + self.assertAlmostEqual(q1.value, 1.0, delta=1e-6) + + self.assertIsInstance(q2, modelcif.qa_metric.Global) + self.assertIsInstance(q2, modelcif.qa_metric.ZScore) + self.assertAlmostEqual(q2.value, 2.0, delta=1e-6) + + self.assertIsInstance(q3, modelcif.qa_metric.Global) + self.assertIsInstance(q3, modelcif.qa_metric.Distance) + self.assertAlmostEqual(q3.value, 3.0, delta=1e-6) + + self.assertIsInstance(q4, modelcif.qa_metric.Global) + self.assertIsInstance(q4, modelcif.qa_metric.NormalizedScore) + self.assertIsNone(q4.description) + self.assertIsNone(q4.__doc__) + + def test_qa_metric_local_handler(self): + """Test _QAMetricLocalHandler""" + cif = """ +loop_ +_ma_model_list.ordinal_id +_ma_model_list.model_id +_ma_model_list.model_group_id +_ma_model_list.model_name +_ma_model_list.model_group_name +_ma_model_list.assembly_id +_ma_model_list.data_id +_ma_model_list.model_type +_ma_model_list.model_type_other_details +1 1 1 'Best scoring model' 'All models' 1 4 'Homology model' . +# +loop_ +_ma_qa_metric.id +_ma_qa_metric.name +_ma_qa_metric.description +_ma_qa_metric.type +_ma_qa_metric.mode +_ma_qa_metric.type_other_details +_ma_qa_metric.software_group_id +1 'test local' 'some local score' 'normalized score' local . . +# +loop_ +_ma_qa_metric_local.ordinal_id +_ma_qa_metric_local.model_id +_ma_qa_metric_local.label_asym_id +_ma_qa_metric_local.label_seq_id +_ma_qa_metric_local.label_comp_id +_ma_qa_metric_local.metric_id +_ma_qa_metric_local.metric_value +1 1 A 2 CYS 1 1.0 +""" + s, = modelcif.reader.read(StringIO(cif)) + mg, = s.model_groups + m, = mg + q1, = m.qa_metrics + self.assertIsInstance(q1, modelcif.qa_metric.Local) + self.assertIsInstance(q1, modelcif.qa_metric.NormalizedScore) + self.assertEqual(q1.type, "normalized score") + self.assertEqual(q1.name, "test local") + self.assertEqual(q1.description, "some local score") + self.assertIsNone(q1.software) + self.assertEqual(q1.residue.asym._id, 'A') + self.assertEqual(q1.residue.seq_id, 2) + self.assertAlmostEqual(q1.value, 1.0, delta=1e-6) + + def test_qa_metric_pairwise_handler(self): + """Test _QAMetricPairwiseHandler""" + cif = """ +loop_ +_ma_model_list.ordinal_id +_ma_model_list.model_id +_ma_model_list.model_group_id +_ma_model_list.model_name +_ma_model_list.model_group_name +_ma_model_list.assembly_id +_ma_model_list.data_id +_ma_model_list.model_type +_ma_model_list.model_type_other_details +1 1 1 'Best scoring model' 'All models' 1 4 'Homology model' . +# +loop_ +_ma_qa_metric.id +_ma_qa_metric.name +_ma_qa_metric.description +_ma_qa_metric.type +_ma_qa_metric.mode +_ma_qa_metric.type_other_details +_ma_qa_metric.software_group_id +1 'test pair' 'some pair score' 'normalized score' local-pairwise . . +# +loop_ +_ma_qa_metric_local_pairwise.ordinal_id +_ma_qa_metric_local_pairwise.model_id +_ma_qa_metric_local_pairwise.label_asym_id_1 +_ma_qa_metric_local_pairwise.label_seq_id_1 +_ma_qa_metric_local_pairwise.label_comp_id_1 +_ma_qa_metric_local_pairwise.label_asym_id_2 +_ma_qa_metric_local_pairwise.label_seq_id_2 +_ma_qa_metric_local_pairwise.label_comp_id_2 +_ma_qa_metric_local_pairwise.metric_id +_ma_qa_metric_local_pairwise.metric_value +1 1 A 2 CYS B 4 GLY 1 1.0 +""" + s, = modelcif.reader.read(StringIO(cif)) + mg, = s.model_groups + m, = mg + q1, = m.qa_metrics + self.assertIsInstance(q1, modelcif.qa_metric.LocalPairwise) + self.assertIsInstance(q1, modelcif.qa_metric.NormalizedScore) + self.assertEqual(q1.type, "normalized score") + self.assertEqual(q1.name, "test pair") + self.assertEqual(q1.description, "some pair score") + self.assertIsNone(q1.software) + self.assertEqual(q1.residue1.asym._id, 'A') + self.assertEqual(q1.residue1.seq_id, 2) + self.assertEqual(q1.residue2.asym._id, 'B') + self.assertEqual(q1.residue2.seq_id, 4) + self.assertAlmostEqual(q1.value, 1.0, delta=1e-6) + + def test_qa_metric_feature_handler(self): + """Test _QAMetricFeatureHandler""" + feat = """ +loop_ +_ma_atom_feature.ordinal_id +_ma_atom_feature.feature_id +_ma_atom_feature.atom_id +1 1 1 +# +loop_ +_ma_poly_residue_feature.ordinal_id +_ma_poly_residue_feature.feature_id +_ma_poly_residue_feature.label_asym_id +_ma_poly_residue_feature.label_seq_id +_ma_poly_residue_feature.label_comp_id +1 2 Y 1 ALA +# +loop_ +_ma_entity_instance_feature.ordinal_id +_ma_entity_instance_feature.feature_id +_ma_entity_instance_feature.label_asym_id +1 3 Y +""" + qa = """ +loop_ +_ma_feature_list.feature_id +_ma_feature_list.feature_type +_ma_feature_list.entity_type +_ma_feature_list.details +1 atom other 'atom f' +2 residue polymer prf +3 'entity instance' polymer . +# +loop_ +_ma_model_list.ordinal_id +_ma_model_list.model_id +_ma_model_list.model_group_id +_ma_model_list.model_name +_ma_model_list.model_group_name +_ma_model_list.assembly_id +_ma_model_list.data_id +_ma_model_list.model_type +_ma_model_list.model_type_other_details +1 1 1 'Best scoring model' 'All models' 1 4 'Homology model' . +# +loop_ +_ma_qa_metric.id +_ma_qa_metric.name +_ma_qa_metric.description +_ma_qa_metric.type +_ma_qa_metric.mode +_ma_qa_metric.type_other_details +_ma_qa_metric.software_group_id +1 'test local' 'some local score' 'normalized score' per-feature . . +# +loop_ +_ma_qa_metric_feature.ordinal_id +_ma_qa_metric_feature.model_id +_ma_qa_metric_feature.feature_id +_ma_qa_metric_feature.metric_id +_ma_qa_metric_feature.metric_value +1 1 1 1 1.0 +2 1 2 1 2.0 +3 1 3 1 3.0 +""" + # Test both ways to make sure features still work if they are + # referenced by ID before their type is known + for cif in (feat + qa, qa + feat): + s, = modelcif.reader.read(StringIO(cif)) + mg, = s.model_groups + m, = mg + q1, q2, q3 = m.qa_metrics + self.assertIsInstance(q1, modelcif.qa_metric.Feature) + self.assertIsInstance(q1, modelcif.qa_metric.NormalizedScore) + self.assertIsInstance(q1.feature, modelcif.AtomFeature) + self.assertEqual(q1.feature.details, 'atom f') + self.assertAlmostEqual(q1.value, 1.0, delta=1e-6) + self.assertIsInstance(q2.feature, modelcif.PolyResidueFeature) + self.assertEqual(len(q2.feature.residues), 1) + self.assertEqual(q2.feature.residues[0].seq_id, 1) + self.assertAlmostEqual(q2.value, 2.0, delta=1e-6) + self.assertIsInstance(q3.feature, modelcif.EntityInstanceFeature) + self.assertEqual(len(q3.feature.asym_units), 1) + self.assertAlmostEqual(q3.value, 3.0, delta=1e-6) + + def test_qa_metric_feature_pairwise_handler(self): + """Test _QAMetricFeaturePairwiseHandler""" + feat = """ +loop_ +_ma_poly_residue_feature.ordinal_id +_ma_poly_residue_feature.feature_id +_ma_poly_residue_feature.label_asym_id +_ma_poly_residue_feature.label_seq_id +_ma_poly_residue_feature.label_comp_id +1 1 Y 1 ALA +2 2 Y 2 CYS +""" + qa = """ +loop_ +_ma_feature_list.feature_id +_ma_feature_list.feature_type +_ma_feature_list.entity_type +_ma_feature_list.details +1 residue polymer . +2 residue polymer . +# +loop_ +_ma_model_list.ordinal_id +_ma_model_list.model_id +_ma_model_list.model_group_id +_ma_model_list.model_name +_ma_model_list.model_group_name +_ma_model_list.assembly_id +_ma_model_list.data_id +_ma_model_list.model_type +_ma_model_list.model_type_other_details +1 1 1 'Best scoring model' 'All models' 1 4 'Homology model' . +# +loop_ +_ma_qa_metric.id +_ma_qa_metric.name +_ma_qa_metric.description +_ma_qa_metric.type +_ma_qa_metric.mode +_ma_qa_metric.type_other_details +_ma_qa_metric.software_group_id +1 'test local' 'some local score' 'normalized score' per-feature-pair . . +# +loop_ +_ma_qa_metric_feature_pairwise.ordinal_id +_ma_qa_metric_feature_pairwise.model_id +_ma_qa_metric_feature_pairwise.feature_id_1 +_ma_qa_metric_feature_pairwise.feature_id_2 +_ma_qa_metric_feature_pairwise.metric_id +_ma_qa_metric_feature_pairwise.metric_value +1 1 1 2 1 50.000 +""" + # Test both ways to make sure features still work if they are + # referenced by ID before their type is known + for cif in (feat + qa, qa + feat): + s, = modelcif.reader.read(StringIO(cif)) + mg, = s.model_groups + m, = mg + q1, = m.qa_metrics + self.assertIsInstance(q1, modelcif.qa_metric.FeaturePairwise) + self.assertIsInstance(q1, modelcif.qa_metric.NormalizedScore) + self.assertIsInstance(q1.feature1, modelcif.PolyResidueFeature) + self.assertIsInstance(q1.feature2, modelcif.PolyResidueFeature) + self.assertAlmostEqual(q1.value, 50.0, delta=1e-6) + + def test_alignment_info_details_handler(self): + """Test _AlignmentInfoHandler and _AlignmentDetailsHandler""" + cif = """ +loop_ +_ma_alignment_info.alignment_id +_ma_alignment_info.data_id +_ma_alignment_info.software_group_id +_ma_alignment_info.alignment_length +_ma_alignment_info.alignment_type +_ma_alignment_info.alignment_mode +1 3 1 . 'target-template pairwise alignment' global +2 4 1 . 'target-template pairwise alignment' global +3 5 1 . 'target-template MSA' local +# +# +loop_ +_ma_alignment_details.ordinal_id +_ma_alignment_details.alignment_id +_ma_alignment_details.template_segment_id +_ma_alignment_details.target_asym_id +_ma_alignment_details.score_type +_ma_alignment_details.score_type_other_details +_ma_alignment_details.score_value +_ma_alignment_details.percent_sequence_identity +_ma_alignment_details.sequence_identity_denominator +_ma_alignment_details.sequence_identity_denominator_other_details +1 1 1 A 'BLAST e-value' . 1.0 45.000 'Length of the shorter sequence' . +2 2 1 A . . . . . . +3 3 1 A 'HHblits e-value' . 2.0 45.000 'Arithmetic mean sequence length' . +# +loop_ +_ma_alignment.ordinal_id +_ma_alignment.alignment_id +_ma_alignment.target_template_flag +_ma_alignment.sequence +1 1 1 DSYV-ETLD +2 1 2 DMACDTFIK +3 1 1 DSYV-ETLD +4 1 2 DMACDTFIK +# +loop_ +_ma_target_template_poly_mapping.id +_ma_target_template_poly_mapping.template_segment_id +_ma_target_template_poly_mapping.target_asym_id +_ma_target_template_poly_mapping.target_seq_id_begin +_ma_target_template_poly_mapping.target_seq_id_end +1 1 A 1 8 +2 1 A 1 8 +""" + s, = modelcif.reader.read(StringIO(cif)) + a1, a2, a3, = s.alignments + self.assertIs(a1.__class__, a2.__class__) + self.assertIsInstance(a1, modelcif.alignment.Global) + self.assertIsInstance(a1, modelcif.alignment.Pairwise) + p, = a1.pairs + self.assertIsInstance(p.score, modelcif.alignment.BLASTEValue) + self.assertAlmostEqual(p.score.value, 1.0, delta=1e-6) + self.assertIsInstance(p.identity, + modelcif.alignment.ShorterSequenceIdentity) + self.assertAlmostEqual(p.identity.value, 45.0, delta=1e-6) + self.assertIsInstance(p.template, modelcif.TemplateSegment) + self.assertEqual(p.template._id, '1') + self.assertEqual(p.template.gapped_sequence, 'DMACDTFIK') + self.assertIsInstance(p.target, ihm.AsymUnitSegment) + self.assertEqual(p.target.asym._id, 'A') + self.assertEqual(p.target.gapped_sequence, 'DSYV-ETLD') + self.assertEqual(p.target.seq_id_range, (1, 8)) + self.assertIsInstance(a3, modelcif.alignment.Local) + self.assertIsInstance(a3, modelcif.alignment.Multiple) + p, = a2.pairs + self.assertIsNone(p.score) + self.assertIsNone(p.identity) + p, = a3.pairs + self.assertIsInstance(p.score, modelcif.alignment.HHblitsEValue) + self.assertAlmostEqual(p.score.value, 2.0, delta=1e-6) + self.assertIsInstance(p.identity, + modelcif.alignment.MeanSequenceIdentity) + + def test_associated_files(self): + """Test _AssociatedHandler and _AssociatedArchiveHandler""" + cif = """ +loop_ +_ma_data.id +_ma_data.name +_ma_data.content_type +_ma_data.content_type_other_details +42 'Model subunit' target . +loop_ +_ma_target_entity.entity_id +_ma_target_entity.data_id +_ma_target_entity.origin +1 99 'reference database' +loop_ +_ma_entry_associated_files.id +_ma_entry_associated_files.entry_id +_ma_entry_associated_files.file_url +_ma_entry_associated_files.file_type +_ma_entry_associated_files.file_format +_ma_entry_associated_files.file_content +_ma_entry_associated_files.details +_ma_entry_associated_files.data_id +1 model https://example.com/foo.txt file other other 'test file' . +2 model https://example.com/t.zip archive zip 'archive with multiple files' . . +3 model baz.txt file other other 'test file3' . +4 model baz.cif file cif other 'test mmCIF' . +5 model baz.bcif file bcif other 'test BinaryCIF' 42 +# +# +loop_ +_ma_associated_archive_file_details.id +_ma_associated_archive_file_details.archive_file_id +_ma_associated_archive_file_details.file_path +_ma_associated_archive_file_details.file_format +_ma_associated_archive_file_details.file_content +_ma_associated_archive_file_details.description +_ma_associated_archive_file_details.data_id +1 2 bar.txt other other 'test file2' . +2 99 99.txt other other 'test file99' . +3 2 bar.cif cif other 'test mmCIF in zip' . +4 2 bar.bcif bcif 'local pairwise QA scores' 'test BinaryCIF in zip' 99 +5 2 bar2.bcif bcif 'QA metrics' 'test BinaryCIF in zip' 99 +""" + s, = modelcif.reader.read(StringIO(cif)) + r1, r2 = s.repositories + self.assertEqual(r1.url_root, 'https://example.com') + f1, zf = r1.files + self.assertIsInstance(f1, modelcif.associated.File) + self.assertEqual(f1.path, 'foo.txt') + self.assertEqual(f1.details, 'test file') + + self.assertIsInstance(zf, modelcif.associated.ZipFile) + self.assertEqual(zf.path, 't.zip') + self.assertIsNone(zf.details) + + f2, f3, f4, f5 = zf.files + self.assertEqual(f2.path, 'bar.txt') + self.assertEqual(f2.details, 'test file2') + self.assertIsNone(f2.data) + self.assertIsInstance(f3, modelcif.associated.CIFFile) + self.assertFalse(f3.binary) + # QA metrics file using old "local pairwise QA scores" name + self.assertIsInstance( + f4, modelcif.associated.QAMetricsFile) + self.assertEqual(f4.file_content, 'QA metrics') + self.assertTrue(f4.binary) + self.assertIsInstance(f4.data, modelcif.Entity) + self.assertIsInstance( + f5, modelcif.associated.QAMetricsFile) + self.assertEqual(f5.file_content, 'QA metrics') + self.assertTrue(f5.binary) + self.assertIsInstance(f5.data, modelcif.Entity) + + self.assertIsNone(r2.url_root) + f3, f4, f5 = r2.files + self.assertEqual(f3.path, 'baz.txt') + self.assertEqual(f3.details, 'test file3') + self.assertIsInstance(f4, modelcif.associated.CIFFile) + self.assertFalse(f4.binary) + self.assertIsNone(f4.data) + self.assertIsInstance(f5, modelcif.associated.CIFFile) + self.assertTrue(f5.binary) + self.assertEqual(f5.data.__class__, modelcif.data.Data) + + def test_template_poly_handler(self): + """Test _TemplatePolyHandler""" + cif = """ +loop_ +_chem_comp.id +_chem_comp.type +_chem_comp.name +_chem_comp.formula +MYTYPE 'D-PEPTIDE LINKING' 'MY CUSTOM COMPONENT' 'C6 H12' +MYTYP2 'D-PEPTIDE LINKING' 'MY CUSTOM COMPONENT2' 'C6 H12' +# +loop_ +_ma_template_details.ordinal_id +_ma_template_details.template_id +_ma_template_details.template_origin +_ma_template_details.template_entity_type +_ma_template_details.template_trans_matrix_id +_ma_template_details.template_data_id +_ma_template_details.target_asym_id +_ma_template_details.template_label_asym_id +_ma_template_details.template_label_entity_id +_ma_template_details.template_model_num +_ma_template_details.template_auth_asym_id +1 1 'reference database' polymer 1 1 A A . 1 A +2 2 'reference database' polymer 1 1 A A . 1 A +3 3 'reference database' polymer 1 1 A A . 1 A +# +loop_ +_ma_template_poly.template_id +_ma_template_poly.seq_one_letter_code +_ma_template_poly.seq_one_letter_code_can +1 A(MYTYPE)V AVV +2 A(MYTYP2)V . +3 . . +4 CCC CCC +""" + s, = modelcif.reader.read(StringIO(cif)) + # template_id=4 in template_poly should be ignored + t1, t2, t3 = s.templates + s1, s2, s3 = t1.entity.sequence + self.assertEqual(s1.id, 'ALA') + self.assertEqual(s1.code, 'A') + # Both one-letter and one-letter-canonical were provided + self.assertEqual(s2.id, 'MYTYPE') + self.assertEqual(s2.code, 'MYTYPE') + self.assertEqual(s2.code_canonical, 'V') + + # Only one-letter was provided + s1, s2, s3 = t2.entity.sequence + self.assertEqual(s2.id, 'MYTYP2') + self.assertEqual(s2.code, 'MYTYP2') + self.assertIsNone(s2.code_canonical) + + # No sequence provided + self.assertEqual(len(t3.entity.sequence), 0) + + def test_template_non_poly_handler(self): + """Test _TemplateNonPolyHandler""" + cif = """ +loop_ +_chem_comp.id +_chem_comp.type +HEM non-polymer +# +loop_ +_ma_template_details.ordinal_id +_ma_template_details.template_id +_ma_template_details.template_origin +_ma_template_details.template_entity_type +_ma_template_details.template_trans_matrix_id +_ma_template_details.template_data_id +_ma_template_details.target_asym_id +_ma_template_details.template_label_asym_id +_ma_template_details.template_label_entity_id +_ma_template_details.template_model_num +_ma_template_details.template_auth_asym_id +1 1 'reference database' polymer 1 1 A A . 1 A +# +loop_ +_ma_template_non_poly.template_id +_ma_template_non_poly.comp_id +_ma_template_non_poly.details +1 HEM Heme +""" + s, = modelcif.reader.read(StringIO(cif)) + t, = s.templates + s1, = t.entity.sequence + self.assertEqual(s1.id, 'HEM') + self.assertEqual(s1.type, 'non-polymer') + self.assertIsInstance(s1, ihm.NonPolymerChemComp) + + def test_chem_comp_handler(self): + """Test ChemCompHandler and ChemCompDescriptorHandler""" + cif = """ +loop_ +_chem_comp.id +_chem_comp.type +_chem_comp.name +_chem_comp.formula +_chem_comp.ma_provenance +MET 'L-peptide linking' . . . +CYS 'D-peptide linking' CYSTEINE . ? +ALA 'L-peptide linking' ALANINE . 'CCD Core' +MATYPE 'L-PEPTIDE LINKING' 'MODELARCHIVE COMPONENT' . 'CCD MA' +MYTYPE 'L-PEPTIDE LINKING' 'MY CUSTOM COMPONENT' . 'CCD local' +# +loop_ +_ma_chem_comp_descriptor.ordinal_id +_ma_chem_comp_descriptor.chem_comp_id +_ma_chem_comp_descriptor.chem_comp_name +_ma_chem_comp_descriptor.type +_ma_chem_comp_descriptor.value +_ma_chem_comp_descriptor.details +_ma_chem_comp_descriptor.software_id +1 MYTYPE 'ignored' 'InChI Key' XDAOLTSRNUSPPH-XMMPIXPASA-N foo 1 +2 MYTYPE ? 'IUPAC Name' foobar . . +# +loop_ +_entity_poly_seq.entity_id +_entity_poly_seq.num +_entity_poly_seq.mon_id +_entity_poly_seq.hetero +1 1 MET . +1 2 CYS . +1 3 ALA . +1 4 MATYPE . +1 5 MYTYPE . +""" + s, = modelcif.reader.read(StringIO(cif)) + e1, = s.entities + s = e1.sequence + self.assertEqual(len(s), 5) + self.assertEqual(s[2].ccd, 'core') + self.assertEqual(s[3].ccd, 'ma') + self.assertEqual(s[4].ccd, 'local') + d1, d2 = s[4].descriptors + self.assertIsInstance(d1, modelcif.descriptor.InChIKey) + self.assertEqual(d1.value, 'XDAOLTSRNUSPPH-XMMPIXPASA-N') + self.assertEqual(d1.details, 'foo') + self.assertEqual(d1.software._id, '1') + self.assertIsInstance(d2, modelcif.descriptor.IUPACName) + self.assertEqual(d2.value, 'foobar') + self.assertIsNone(d2.details) + self.assertIsNone(d2.software) + + def test_add_to_system(self): + """Test adding new mmCIF input to existing System""" + s = modelcif.System() + e = modelcif.Entity('AHC') + e._id = '42' + s.entities.append(e) + fh = StringIO(""" +loop_ +_struct_asym.id +_struct_asym.entity_id +_struct_asym.details +A 42 foo +B 99 bar +""") + s2, = modelcif.reader.read(fh, add_to_system=s) + self.assertIs(s2, s) + self.assertEqual(len(s.asym_units), 2) + # asym A should point to existing entity + self.assertEqual(s.asym_units[0].id, 'A') + self.assertIs(s.asym_units[0].entity, e) + + def test_audit_revision_handler(self): + """Test AuditRevisionHistoryHandler""" + # We leverage the support in python-ihm, so only a basic test here + cif = """ +loop_ +_pdbx_audit_revision_history.ordinal +_pdbx_audit_revision_history.data_content_type +_pdbx_audit_revision_history.major_revision +_pdbx_audit_revision_history.minor_revision +_pdbx_audit_revision_history.revision_date +40 'Structure model' 1 0 ? +41 'Structure model' 1 0 . +42 'Structure model' 2 0 1979-05-03 +""" + s, = modelcif.reader.read(StringIO(cif)) + r1, r2, r3 = s.revisions + self.assertEqual(r3.major, 2) + self.assertEqual(r3.minor, 0) + self.assertEqual(r3.date, datetime.date(1979, 5, 3)) + + def test_data_usage_handler(self): + """Test DataUsageHandler""" + # We leverage the support in python-ihm, so only a basic test here + cif = """ +loop_ +_pdbx_data_usage.id +_pdbx_data_usage.type +_pdbx_data_usage.details +_pdbx_data_usage.url +_pdbx_data_usage.name +1 license 'some license' someurl somename +""" + s, = modelcif.reader.read(StringIO(cif)) + d1, = s.data_usage + self.assertEqual(d1.details, "some license") + + def test_atom_site_handler_water(self): + """Test AtomSiteHandler reading water molecules""" + fh = StringIO(""" +loop_ +_entity.id +_entity.type +1 water +loop_ +_struct_asym.id +_struct_asym.entity_id +_struct_asym.details +A 1 Water +B 1 Water +# +loop_ +_pdbx_nonpoly_scheme.asym_id +_pdbx_nonpoly_scheme.entity_id +_pdbx_nonpoly_scheme.mon_id +_pdbx_nonpoly_scheme.ndb_seq_num +_pdbx_nonpoly_scheme.pdb_seq_num +_pdbx_nonpoly_scheme.auth_seq_num +_pdbx_nonpoly_scheme.auth_mon_id +_pdbx_nonpoly_scheme.pdb_strand_id +_pdbx_nonpoly_scheme.pdb_ins_code +A 1 HOH 1 50 500 HOH A . +# +loop_ +_atom_site.group_PDB +_atom_site.id +_atom_site.type_symbol +_atom_site.label_atom_id +_atom_site.label_alt_id +_atom_site.label_comp_id +_atom_site.label_seq_id +_atom_site.auth_seq_id +_atom_site.pdbx_PDB_ins_code +_atom_site.label_asym_id +_atom_site.Cartn_x +_atom_site.Cartn_y +_atom_site.Cartn_z +_atom_site.occupancy +_atom_site.label_entity_id +_atom_site.auth_asym_id +_atom_site.B_iso_or_equiv +_atom_site.pdbx_PDB_model_num +_atom_site.ihm_model_id +HETATM 1 O O . HOH . 40 ? A 10.000 10.000 10.000 . 1 A . 1 1 +HETATM 2 O O . HOH . 50 ? A 10.000 10.000 10.000 . 1 A . 1 1 +HETATM 3 O O . HOH . 60 . A 20.000 20.000 20.000 . 1 A . 1 1 +HETATM 4 O O . HOH . 70 . B 20.000 20.000 20.000 . 1 B . 1 1 +""") + s, = modelcif.reader.read(fh) + m = s.model_groups[0][0] + a1, a2, a3, b1 = m._atoms + # Should include info from both atom_site and scheme table + self.assertEqual(a1.asym_unit.auth_seq_id_map, + {1: (40, None), 2: (50, None), 3: (60, None)}) + self.assertEqual(a1.asym_unit.orig_auth_seq_id_map, + {2: 500}) + self.assertEqual(b1.asym_unit.auth_seq_id_map, {1: (70, None)}) + self.assertIsNone(b1.asym_unit.orig_auth_seq_id_map) + # Should get a WaterAsymUnit, not regular AsymUnit + self.assertIsInstance(a1.asym_unit, modelcif.WaterAsymUnit) + self.assertIsInstance(b1.asym_unit, modelcif.WaterAsymUnit) + # seq_id should be assigned based on atom_site + self.assertEqual(a1.seq_id, 1) + self.assertEqual(a2.seq_id, 2) + self.assertEqual(a3.seq_id, 3) + self.assertEqual(b1.seq_id, 1) + + def test_nonpoly_scheme_handler(self): + """Test NonPolySchemeHandler""" + fh = StringIO(""" +loop_ +_chem_comp.id +_chem_comp.type +_chem_comp.name +CA non-polymer 'CALCIUM ION' +# +loop_ +_entity.id +_entity.type +_entity.pdbx_description +1 non-polymer 'CALCIUM ION entity' +2 non-polymer 'no-chem-comp entity' +3 water 'no-chem-comp water' +# +loop_ +_pdbx_entity_nonpoly.entity_id +_pdbx_entity_nonpoly.name +_pdbx_entity_nonpoly.comp_id +1 'CALCIUM ION' CA +# +loop_ +_struct_asym.id +_struct_asym.entity_id +_struct_asym.details +A 1 foo +B 2 bar +C 3 baz +# +loop_ +_pdbx_nonpoly_scheme.asym_id +_pdbx_nonpoly_scheme.entity_id +_pdbx_nonpoly_scheme.mon_id +_pdbx_nonpoly_scheme.ndb_seq_num +_pdbx_nonpoly_scheme.pdb_seq_num +_pdbx_nonpoly_scheme.auth_seq_num +_pdbx_nonpoly_scheme.pdb_strand_id +_pdbx_nonpoly_scheme.pdb_ins_code +A 1 BAR 1 101 202 . . +B 2 BAR 1 1 1 Q X +C 3 HOH . 1 1 . . +C 3 HOH 2 2 2 . . +C 3 HOH 3 5 10 . . +C 3 HOH 4 1 20 . . +C 3 HOH 5 7 7 . . +""") + s, = modelcif.reader.read(fh) + e1, e2, e3 = s.entities + # e1 should have sequence filled in by pdbx_entity_nonpoly + self.assertEqual([cc.name for cc in e1.sequence], ['CALCIUM ION']) + # e2,e3 should have sequence filled in by pdbx_nonpoly_scheme + self.assertEqual([(cc.id, cc.name) for cc in e2.sequence], + [('BAR', 'no-chem-comp entity')]) + self.assertEqual([(cc.id, cc.name) for cc in e3.sequence], + [('HOH', 'WATER')]) + asym, a2, a3 = s.asym_units + # non-polymers have no seq_id_range + self.assertEqual(asym.seq_id_range, (None, None)) + self.assertEqual(asym.auth_seq_id_map, {1: (101, None)}) + self.assertEqual(asym.residue(1).auth_seq_id, 101) + self.assertIsNone(asym.residue(1).ins_code) + self.assertEqual(asym.strand_id, asym._id) + self.assertIsNone(asym._strand_id) + self.assertEqual(asym.orig_auth_seq_id_map, {1: 202}) + + self.assertEqual(a2.auth_seq_id_map, {1: (1, 'X')}) + self.assertEqual(a2.residue(1).auth_seq_id, 1) + self.assertEqual(a2.residue(1).ins_code, 'X') + self.assertEqual(a2.strand_id, 'Q') + self.assertEqual(a2._strand_id, 'Q') + self.assertIsNone(a2.orig_auth_seq_id_map) + + self.assertEqual(a3.auth_seq_id_map, {1: (1, None), 2: (2, None), + 3: (5, None), 4: (1, None), + 5: (7, None)}) + self.assertEqual(a3.orig_auth_seq_id_map, {3: 10, 4: 20}) + + def test_poly_seq_scheme_handler_offset(self): + """Test PolySeqSchemeHandler with constant offset""" + fh = StringIO(ASYM_ENTITY + """ +loop_ +_pdbx_poly_seq_scheme.asym_id +_pdbx_poly_seq_scheme.entity_id +_pdbx_poly_seq_scheme.seq_id +_pdbx_poly_seq_scheme.pdb_seq_num +_pdbx_poly_seq_scheme.pdb_strand_id +A 1 1 6 A +A 1 2 7 A +A 1 3 8 A +A 1 4 9 A +""") + s, = modelcif.reader.read(fh) + asym, = s.asym_units + self.assertEqual(asym.auth_seq_id_map, 5) + self.assertIsNone(asym._strand_id) + self.assertEqual([asym.residue(i).auth_seq_id for i in range(1, 5)], + [6, 7, 8, 9]) + self.assertIsNone(asym.orig_auth_seq_id_map) + + def test_poly_seq_scheme_handler_offset_ins_code(self): + """Test PolySeqSchemeHandler with constant offset but inscodes""" + fh = StringIO(ASYM_ENTITY + """ +loop_ +_pdbx_poly_seq_scheme.asym_id +_pdbx_poly_seq_scheme.entity_id +_pdbx_poly_seq_scheme.seq_id +_pdbx_poly_seq_scheme.pdb_seq_num +_pdbx_poly_seq_scheme.pdb_strand_id +_pdbx_poly_seq_scheme.pdb_ins_code +A 1 1 6 A . +A 1 2 7 A . +A 1 3 8 A . +A 1 4 9 A A +""") + s, = modelcif.reader.read(fh) + asym, = s.asym_units + self.assertEqual(asym.auth_seq_id_map, + {1: (6, None), 2: (7, None), 3: (8, None), + 4: (9, 'A')}) + self.assertIsNone(asym._strand_id) + self.assertEqual([asym.residue(i).auth_seq_id for i in range(1, 5)], + [6, 7, 8, 9]) + self.assertIsNone(asym.residue(1).ins_code) + self.assertEqual(asym.residue(4).ins_code, 'A') + self.assertIsNone(asym.orig_auth_seq_id_map) + + def test_poly_seq_scheme_handler_empty(self): + """Test PolySeqSchemeHandler with no poly_seq_scheme""" + fh = StringIO(ASYM_ENTITY) + s, = modelcif.reader.read(fh) + asym, = s.asym_units + self.assertEqual(asym.auth_seq_id_map, 0) + self.assertEqual([asym.residue(i).auth_seq_id for i in range(1, 5)], + [1, 2, 3, 4]) + self.assertIsNone(asym.orig_auth_seq_id_map) + + def test_poly_seq_scheme_handler_nop(self): + """Test PolySeqSchemeHandler with a do-nothing poly_seq_scheme""" + fh = StringIO(ASYM_ENTITY + """ +loop_ +_pdbx_poly_seq_scheme.asym_id +_pdbx_poly_seq_scheme.entity_id +_pdbx_poly_seq_scheme.seq_id +_pdbx_poly_seq_scheme.pdb_seq_num +A 1 1 1 +A 1 2 2 +A 1 3 3 +""") + s, = modelcif.reader.read(fh) + asym, = s.asym_units + self.assertEqual(asym.auth_seq_id_map, 0) + self.assertEqual([asym.residue(i).auth_seq_id for i in range(1, 5)], + [1, 2, 3, 4]) + self.assertIsNone(asym.orig_auth_seq_id_map) + + def test_poly_seq_scheme_handler_partial(self): + """Test PolySeqSchemeHandler with partial information""" + fh = StringIO(ASYM_ENTITY + """ +loop_ +_pdbx_poly_seq_scheme.asym_id +_pdbx_poly_seq_scheme.entity_id +_pdbx_poly_seq_scheme.seq_id +_pdbx_poly_seq_scheme.pdb_seq_num +_pdbx_poly_seq_scheme.auth_seq_num +A 1 1 6 . +A 1 2 7 9 +A 1 3 8 . +""") + s, = modelcif.reader.read(fh) + asym, = s.asym_units + # No mapping for residue 4 (and no insertion codes at all) + self.assertEqual(asym.auth_seq_id_map, {1: (6, None), 2: (7, None), + 3: (8, None)}) + self.assertEqual([asym.residue(i).auth_seq_id for i in range(1, 5)], + [6, 7, 8, 4]) + self.assertIsNone(asym.residue(1).ins_code) + self.assertEqual(asym.orig_auth_seq_id_map, {2: 9}) + + def test_poly_seq_scheme_handler_incon_off(self): + """Test PolySeqSchemeHandler with inconsistent offset""" + fh = StringIO(ASYM_ENTITY + """ +loop_ +_pdbx_poly_seq_scheme.asym_id +_pdbx_poly_seq_scheme.entity_id +_pdbx_poly_seq_scheme.seq_id +_pdbx_poly_seq_scheme.pdb_seq_num +_pdbx_poly_seq_scheme.pdb_strand_id +A 1 1 6 X +A 1 2 7 X +A 1 3 8 X +A 1 4 10 X +""") + s, = modelcif.reader.read(fh) + asym, = s.asym_units + self.assertEqual(asym._strand_id, 'X') + self.assertEqual(asym.auth_seq_id_map, {1: (6, None), 2: (7, None), + 3: (8, None), 4: (10, None)}) + self.assertEqual([asym.residue(i).auth_seq_id for i in range(1, 5)], + [6, 7, 8, 10]) + self.assertIsNone(asym.residue(1).ins_code) + self.assertIsNone(asym.orig_auth_seq_id_map) + + def test_poly_seq_scheme_handler_unknown_auth_seq(self): + """Test PolySeqSchemeHandler with explicit unknown auth_seq_num""" + fh = StringIO(ASYM_ENTITY + """ +loop_ +_pdbx_poly_seq_scheme.asym_id +_pdbx_poly_seq_scheme.entity_id +_pdbx_poly_seq_scheme.seq_id +_pdbx_poly_seq_scheme.pdb_seq_num +_pdbx_poly_seq_scheme.auth_seq_num +_pdbx_poly_seq_scheme.pdb_strand_id +A 1 1 1 1 A +A 1 2 2 2 A +A 1 3 3 ? A +A 1 4 4 4 A +""") + s, = modelcif.reader.read(fh) + asym, = s.asym_units + self.assertEqual(asym.auth_seq_id_map, 0) + self.assertEqual(asym.orig_auth_seq_id_map, {3: ihm.unknown}) + + def test_poly_seq_scheme_handler_str_seq_id(self): + """Test PolySeqSchemeHandler with a non-integer pdb_seq_num""" + fh = StringIO(ASYM_ENTITY + """ +loop_ +_pdbx_poly_seq_scheme.asym_id +_pdbx_poly_seq_scheme.entity_id +_pdbx_poly_seq_scheme.seq_id +_pdbx_poly_seq_scheme.pdb_seq_num +_pdbx_poly_seq_scheme.auth_seq_num +_pdbx_poly_seq_scheme.pdb_strand_id +_pdbx_poly_seq_scheme.pdb_ins_code +A 1 1 6 6 ? . +A 1 2 7 12 ? . +A 1 3 8 24 ? . +A 1 4 9A 48A ? . +""") + s, = modelcif.reader.read(fh) + asym, = s.asym_units + self.assertIsNone(asym._strand_id) + self.assertEqual(asym.auth_seq_id_map, {1: (6, None), 2: (7, None), + 3: (8, None), 4: ('9A', None)}) + self.assertEqual([asym.residue(i).auth_seq_id for i in range(1, 5)], + [6, 7, 8, '9A']) + self.assertIsNone(asym.residue(1).ins_code) + self.assertIsNone(asym.residue(3).ins_code) + self.assertEqual(asym.orig_auth_seq_id_map, {2: 12, 3: 24, 4: '48A'}) + + +if __name__ == '__main__': + unittest.main() |
