Repository: marrink-lab/vermouth-martinize

Issue:
Printing Molecule does not display anything
`print(Molecule())` does not display anything.

Discussion:

Patch:
diff --git a/bin/martinize2 b/bin/martinize2
index 3b810f8..a27bc9b 100755
--- a/bin/martinize2
+++ b/bin/martinize2
@@ -426,6 +426,10 @@ def entry():
         write_canon=args.write_canon,
     )
 
+    LOGGER.info('Read input.', type='step')
+    for molecule in system.molecules:
+        LOGGER.debug("Read molecule {}.", molecule, type='step')
+
     target_ff = known_force_fields[args.to_ff]
     if args.dssp is not None:
         AnnotateDSSP(executable=args.dssp, savedir='.').run_system(system)
@@ -531,6 +535,9 @@ def entry():
         defines = ()
 
     LOGGER.info('Writing output.', type='step')
+    for molecule in system.molecules:
+        LOGGER.debug("Writing molecule {}.", molecule, type='step')
+
     # Write the topology if requested
     header = [
         'This file was generated using the following command:',
diff --git a/vermouth/molecule.py b/vermouth/molecule.py
index a9c6cfc..79052a1 100644
--- a/vermouth/molecule.py
+++ b/vermouth/molecule.py
@@ -352,6 +352,42 @@ class Molecule(nx.Graph):
             and self.same_interactions(other)
         )
 
+    @staticmethod
+    def _sort_interactions(all_interactions):
+        """
+        Returns keys in interactions sorted by (number_of_atoms, name). Keys
+        with no interactions are skipped.
+        """
+        sort_keys = {}
+        for interaction_type, interactions in all_interactions.items():
+            if not interactions:
+                continue
+            sort_keys[interaction_type] = len(interactions[0].atoms), interaction_type
+        return sorted(sort_keys, key=lambda k: sort_keys[k])
+
+    def __str__(self):
+        moltype = self.meta.get('moltype', 'molecule')
+
+        interaction_count = OrderedDict()
+        # Make sure atoms and edges get sorted first.
+        interaction_count['atoms'] = len(self.nodes)
+        if len(self.interactions.get('bonds', [])) != len(self.edges):
+            interaction_count['edges'] = len(self.edges)
+
+        for itype in self._sort_interactions(self.interactions):
+            interaction_count[itype] = len(self.interactions[itype])
+
+        # interaction_count will always contain at least 'atoms'.
+        last_item = interaction_count.popitem(last=True)
+        out = "{} with ".format(moltype)
+        out += ', '.join('{} {}'.format(number, itype)
+                         for itype, number in interaction_count.items())
+        if interaction_count:
+            out += ', and '
+        out += '{} {}'.format(last_item[1],
+                              last_item[0])
+        return out
+
     @property
     def force_field(self):
         """


Test patch:
diff --git a/vermouth/tests/test_molecule.py b/vermouth/tests/test_molecule.py
index 3b1a573..af2f1cd 100644
--- a/vermouth/tests/test_molecule.py
+++ b/vermouth/tests/test_molecule.py
@@ -1179,3 +1179,66 @@ def test_same_non_edges(left, right, expected):
     link_right.non_edges = right
     assert link_left.same_non_edges(link_right) == expected
     assert link_right.same_non_edges(link_left) == expected
+
+
+
+@pytest.mark.parametrize('interactions, expected', (
+    (
+        {},
+        []
+    ),
+    (
+        {'bonds': []},
+        []
+    ),
+    (
+        {'bonds': [Interaction(atoms=(1, 2), parameters=[], meta=[])]},
+        ['bonds']
+    ),
+    (
+        {'bonds': [Interaction(atoms=(1, 2), parameters=[], meta=[])],
+         'angles': []},
+        ['bonds']
+    ),
+    (
+        {'bonds': [Interaction(atoms=(1, 2), parameters=[], meta=[])],
+         'angles': [Interaction(atoms=(1, 2, 3), parameters=[], meta=[])]},
+        ['bonds', 'angles']
+    ),
+    (
+        {'angles': [Interaction(atoms=(1, 2, 3), parameters=[], meta=[])],
+         'bonds': [Interaction(atoms=(1, 2), parameters=[], meta=[])],},
+        ['bonds', 'angles']
+    ),
+    (
+        {'angles': [Interaction(atoms=(1, 2, 3), parameters=[], meta=[])],
+         'a_bonds': [Interaction(atoms=(10, 2), parameters=[], meta=[])],
+         'bonds': [Interaction(atoms=(1, 2), parameters=[], meta=[])],},
+        ['a_bonds', 'bonds', 'angles']
+    ),
+))
+def test_interaction_sort(interactions, expected):
+    """
+    Test the order produced by Molecule._sort_interactions.
+    """
+    assert vermouth.molecule.Molecule._sort_interactions(interactions) == expected
+
+
+@hypothesis.given(moltype=st.one_of(st.none(), st.text()), mol=random_molecule())
+def test_str_method(mol, moltype):
+    """
+    Test Molecule.__str__
+    """
+    if moltype is not None:
+        mol.meta['moltype'] = moltype
+    else: 
+        moltype = 'molecule'
+    found = str(mol)
+    assert '{} with '.format(moltype) in found
+    assert '{} {}'.format(len(mol), 'atoms') in found
+    hypothesis.assume('' not in mol.interactions)
+    for itype, interactions in mol.interactions.items():
+        if interactions:
+            assert '{} {}'.format(len(interactions), itype) in found
+        else:
+            assert '{} {}'.format(0, itype) not in found