diff --git a/docs/main.rst b/docs/main.rst index cc89d1d..1f9c7d6 100644 --- a/docs/main.rst +++ b/docs/main.rst @@ -110,3 +110,6 @@ The :mod:`ihm` Python module .. autoclass:: Collection :members: + +.. autoclass:: BranchDescriptor + :members: diff --git a/ihm/__init__.py b/ihm/__init__.py index b532c15..e08bbfa 100644 --- a/ihm/__init__.py +++ b/ihm/__init__.py @@ -1299,6 +1299,11 @@ def get_chem_comp(s): self.references = [] self.references.extend(references) + #: String descriptors of branched chemical structure. + #: These generally only make sense for oligosaccharide entities, + #: and should be a list of :class:`BranchDescriptor` objects. + self.branch_descriptors = [] + def __str__(self): return "" % self.description @@ -1584,3 +1589,22 @@ class Collection(object): """ def __init__(self, id, name=None, details=None): self.id, self.name, self.details = id, name, details + + +class BranchDescriptor(object): + """String descriptor of branched chemical structure. + These generally only make sense for oligosaccharide entities. + See :attr:`Entity.branch_descriptors`. + + :param str text: The value of this descriptor. + :param str type: The type of the descriptor; one of + "Glycam Condensed Core Sequence", "Glycam Condensed Sequence", + "LINUCS", or "WURCS". + :param str program: The name of the program or library used to compute + the descriptor. + :param str program_version: The version of the program or library + used to compute the descriptor. + """ + def __init__(self, text, type, program=None, program_version=None): + self.text, self.type = text, type + self.program, self.program_version = program, program_version diff --git a/ihm/dumper.py b/ihm/dumper.py index c840f1d..609a4ed 100644 --- a/ihm/dumper.py +++ b/ihm/dumper.py @@ -751,6 +751,19 @@ def dump(self, system, writer): auth_mon_id=comp.id) +class _BranchDescriptorDumper(Dumper): + def dump(self, system, writer): + ordinal = itertools.count(1) + with writer.loop("_pdbx_entity_branch_descriptor", + ["ordinal", "entity_id", "descriptor", "type", + "program", "program_version"]) as lp: + for entity in system.entities: + for d in entity.branch_descriptors: + lp.write(ordinal=next(ordinal), entity_id=entity._id, + descriptor=d.text, type=d.type, program=d.program, + program_version=d.program_version) + + class _AsymIDProvider(object): """Provide unique asym IDs""" def __init__(self, seen_ids): @@ -3293,7 +3306,7 @@ class IHMVariant(Variant): _EntityPolyDumper, _EntityNonPolyDumper, _EntityPolySeqDumper, _EntityPolySegmentDumper, _EntityBranchListDumper, _EntityBranchDumper, _StructAsymDumper, _PolySeqSchemeDumper, - _NonPolySchemeDumper, _BranchSchemeDumper, + _NonPolySchemeDumper, _BranchSchemeDumper, _BranchDescriptorDumper, _AssemblyDumper, _ExternalReferenceDumper, _DatasetDumper, _ModelRepresentationDumper, _StartingModelDumper, _ProtocolDumper, _PostProcessDumper, _PseudoSiteDumper, diff --git a/ihm/reader.py b/ihm/reader.py index 084ad4f..feccc41 100644 --- a/ihm/reader.py +++ b/ihm/reader.py @@ -2544,6 +2544,16 @@ def __call__(self, entity_id, comp_id, num): s.sequence[seq_id - 1] = self.sysr.chem_comps.get_by_id(comp_id) +class _BranchDescriptorHandler(Handler): + category = '_pdbx_entity_branch_descriptor' + + def __call__(self, entity_id, descriptor, type, program, program_version): + e = self.sysr.entities.get_by_id(entity_id) + d = ihm.BranchDescriptor(text=descriptor, type=type, program=program, + program_version=program_version) + e.branch_descriptors.append(d) + + class _CrossLinkListHandler(Handler): category = '_ihm_cross_link_list' ignored_keywords = ['entity_description_1', 'entity_description_2', @@ -3363,7 +3373,7 @@ class IHMVariant(Variant): _SphereHandler, _TorusHandler, _HalfTorusHandler, _AxisHandler, _PlaneHandler, _GeometricRestraintHandler, _PolySeqSchemeHandler, _NonPolySchemeHandler, _BranchSchemeHandler, _EntityBranchListHandler, - _CrossLinkListHandler, + _BranchDescriptorHandler, _CrossLinkListHandler, _CrossLinkRestraintHandler, _CrossLinkPseudoSiteHandler, _CrossLinkResultHandler, _StartingModelSeqDifHandler, _OrderedEnsembleHandler] diff --git a/test/test_dumper.py b/test/test_dumper.py index 5f30f00..4a95370 100644 --- a/test/test_dumper.py +++ b/test/test_dumper.py @@ -4521,6 +4521,31 @@ def test_branch_scheme_dumper(self): # """) + def test_branch_descriptor_dumper(self): + """Test BranchDescriptorDumper""" + system = ihm.System() + e1 = ihm.Entity([ihm.SaccharideChemComp('NAG')]) + bd1 = ihm.BranchDescriptor('foo', type='typ1', program='prog', + program_version='1.0') + bd2 = ihm.BranchDescriptor('bar', type='typ2') + e1.branch_descriptors.extend((bd1, bd2)) + system.entities.append(e1) + ihm.dumper._EntityDumper().finalize(system) + dumper = ihm.dumper._BranchDescriptorDumper() + out = _get_dumper_output(dumper, system) + self.assertEqual(out, """# +loop_ +_pdbx_entity_branch_descriptor.ordinal +_pdbx_entity_branch_descriptor.entity_id +_pdbx_entity_branch_descriptor.descriptor +_pdbx_entity_branch_descriptor.type +_pdbx_entity_branch_descriptor.program +_pdbx_entity_branch_descriptor.program_version +1 1 foo typ1 prog 1.0 +2 1 bar typ2 . . +# +""") + if __name__ == '__main__': unittest.main() diff --git a/test/test_main.py b/test/test_main.py index f420902..644959f 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -1083,6 +1083,15 @@ def test_unknown(self): # Should act like False self.assertFalse(u) + def test_branch_descriptor(self): + """Test the BranchDescriptor class""" + bd = ihm.BranchDescriptor(text='foo', type='bar', program='baz', + program_version="1.0") + self.assertEqual(bd.text, 'foo') + self.assertEqual(bd.type, 'bar') + self.assertEqual(bd.program, 'baz') + self.assertEqual(bd.program_version, '1.0') + if __name__ == '__main__': unittest.main() diff --git a/test/test_reader.py b/test/test_reader.py index 8117b1e..edd3d96 100644 --- a/test/test_reader.py +++ b/test/test_reader.py @@ -4400,6 +4400,31 @@ def test_entity_branch_list_handler(self): c1, c2, c3, c4 = e1.sequence self.assertEqual([c.id for c in e1.sequence], ['BGC'] * 4) + def test_entity_branch_descriptor_handler(self): + """Test EntityBranchDescriptorHandler""" + fh = StringIO(""" +loop_ +_pdbx_entity_branch_descriptor.ordinal +_pdbx_entity_branch_descriptor.entity_id +_pdbx_entity_branch_descriptor.descriptor +_pdbx_entity_branch_descriptor.type +_pdbx_entity_branch_descriptor.program +_pdbx_entity_branch_descriptor.program_version +1 1 foo typ1 prog 1.0 +2 1 bar typ2 . . +""") + s, = ihm.reader.read(fh) + e1, = s.entities + bd1, bd2 = e1.branch_descriptors + self.assertEqual(bd1.text, 'foo') + self.assertEqual(bd1.type, 'typ1') + self.assertEqual(bd1.program, 'prog') + self.assertEqual(bd1.program_version, '1.0') + self.assertEqual(bd2.text, 'bar') + self.assertEqual(bd2.type, 'typ2') + self.assertIsNone(bd2.program) + self.assertIsNone(bd2.program_version) + if __name__ == '__main__': unittest.main()