Repository: pybel/pybel
Pull request #494 (2021-07-06 22:25:35)

Update dependencies
Upgrade bioregistry and pyobo versions


Patch:
diff --git a/README.rst b/README.rst
index aedde007..617aa88d 100644
--- a/README.rst
+++ b/README.rst
@@ -232,14 +232,19 @@ The development of PyBEL has been supported by several projects/organizations (i
 
 - `The Cytoscape Consortium <https://cytoscape.org/>`_
 - `Enveda Biosciences <https://envedabio.com/>`_
+- `Fraunhofer Center for Machine Learning <https://www.cit.fraunhofer.de/de/zentren/maschinelles-lernen.html>`_
+- `Fraunhofer Institute for Algorithms and Scientific Computing (SCAI) <https://www.scai.fraunhofer.de>`_
+- `Harvard Program in Therapeutic Science - Laboratory of Systems Pharmacology <https://hits.harvard.edu/the-program/laboratory-of-systems-pharmacology>`_
+- `University of Bonn <https://www.uni-bonn.de>`_
+
+Funding
+~~~~~~~
+- DARPA Young Faculty Award W911NF2010255 (PI: Benjamin M. Gyori).
 - The `European Union <https://europa.eu>`_, `European Federation of Pharmaceutical Industries and Associations
   (EFPIA) <https://www.efpia.eu/>`_, and `Innovative Medicines Initiative <https://www.imi.europa.eu>`_ Joint
   Undertaking under `AETIONOMY <https://www.aetionomy.eu/>`_ [grant number 115568], resources of which
   are composed of financial contribution from the European Union's Seventh Framework Programme (FP7/2007-2013) and
   EFPIA companies in kind contribution.
-- `Fraunhofer Center for Machine Learning <https://www.cit.fraunhofer.de/de/zentren/maschinelles-lernen.html>`_
-- `Fraunhofer Institute for Algorithms and Scientific Computing (SCAI) <https://www.scai.fraunhofer.de>`_
-- `University of Bonn <https://www.uni-bonn.de>`_
 
 Logo
 ~~~~
diff --git a/setup.cfg b/setup.cfg
index 48a0bb99..b6c317d3 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -67,8 +67,8 @@ install_requires =
     tabulate
     pandas
     jsonschema
-    pyobo>=0.4.0
-    bioregistry
+    pyobo>=0.5.0
+    bioregistry>=0.2.3
     ratelimit
     pystow>=0.1.2
     psycopg2-binary
diff --git a/src/pybel/grounding.py b/src/pybel/grounding.py
index 7d324adc..952bdb2f 100644
--- a/src/pybel/grounding.py
+++ b/src/pybel/grounding.py
@@ -54,7 +54,7 @@ from typing import Any, Collection, Mapping, Optional, Tuple, Union
 
 import pyobo
 from protmapper.uniprot_client import get_id_from_mnemonic, get_mnemonic
-from pyobo.getters import MissingOboBuild, NoOboFoundry
+from pyobo.getters import NoBuild
 from pyobo.identifier_utils import normalize_prefix
 from pyobo.xrefdb.sources.famplex import get_remapping
 from tqdm.autonotebook import tqdm
@@ -361,7 +361,7 @@ def _handle_identifier_not_name(
 
     try:
         id_name_mapping = pyobo.api.names.get_id_name_mapping(prefix)
-    except (NoOboFoundry, MissingOboBuild):
+    except NoBuild:
         return False
 
     if id_name_mapping is None:
@@ -445,7 +445,7 @@ def _handle_name_and_not_identifier(
 
     try:
         id_name_mapping = pyobo.api.names.get_name_id_mapping(prefix)
-    except (NoOboFoundry, MissingOboBuild) as e:
+    except NoBuild as e:
         logger.warning('could not get namespace %s - %s', prefix, e)
         return False
 
diff --git a/src/pybel/io/emmaa.py b/src/pybel/io/emmaa.py
index a90ecbd7..adf4be70 100644
--- a/src/pybel/io/emmaa.py
+++ b/src/pybel/io/emmaa.py
@@ -29,7 +29,12 @@ __all__ = [
 logger = logging.getLogger(__name__)
 
 
-def from_emmaa(model: str, *, date: Optional[str] = None, extension: Optional[str] = None) -> BELGraph:
+def from_emmaa(
+    model: str, *,
+    date: Optional[str] = None,
+    extension: Optional[str] = None,
+    suppress_warnings: bool = False,
+) -> BELGraph:
     """Get an EMMAA model as a BEL graph.
 
     Get the most recent COVID-19 model from EMMAA with the following:
@@ -52,17 +57,27 @@ def from_emmaa(model: str, *, date: Optional[str] = None, extension: Optional[st
         covid19_emmaa_graph = pybel.from_emmaa('covid19', '2020-04-23-17-44-57', extension='jsonl')
         covid19_emmaa_graph.summarize()
     """
-    statements = get_statements_from_emmaa(model=model, date=date, extension=extension)
+    statements = get_statements_from_emmaa(
+        model=model, date=date, extension=extension, suppress_warnings=suppress_warnings,
+    )
     return from_indra_statements(statements, name=model, version=date)
 
 
-def get_statements_from_emmaa(model: str, *, date: Optional[str] = None, extension: Optional[str] = None):
+def get_statements_from_emmaa(
+    model: str, *,
+    date: Optional[str] = None,
+    extension: Optional[str] = None,
+    suppress_warnings: bool = False,
+):
     """Get INDRA statements from EMMAA.
 
     :rtype: List[indra.statements.Statement]
     """
     from indra.statements import stmts_from_json
 
+    if suppress_warnings:
+        logging.getLogger('indra.assemblers.pybel.assembler').setLevel(logging.ERROR)
+        logging.getLogger('indra.sources.bel.processor').setLevel(logging.ERROR)
     if extension is None:
         extension = 'json'
 
@@ -85,7 +100,7 @@ def get_statements_from_emmaa(model: str, *, date: Optional[str] = None, extensi
     elif extension == 'gz':
         raise NotImplementedError
     else:
-        raise ValueError
+        raise ValueError(f'unhandled extension: {extension}')
 
 
 def _get_latest_date(model: str) -> str:
diff --git a/src/pybel/struct/graph.py b/src/pybel/struct/graph.py
index b88fd11b..32c2c70e 100644
--- a/src/pybel/struct/graph.py
+++ b/src/pybel/struct/graph.py
@@ -8,6 +8,7 @@ from collections import Counter, defaultdict
 from copy import deepcopy
 from functools import partialmethod
 from itertools import chain
+from textwrap import dedent
 from typing import Any, Dict, Hashable, Iterable, List, Mapping, Optional, Set, TextIO, Tuple, Union
 
 import networkx as nx
@@ -158,6 +159,9 @@ class BELGraph(nx.MultiDiGraph):
         """A dispatch to summarize the graph."""
         return self._summary
 
+    def _repr_html_(self):
+        return self._summary._repr_html_()
+
     @property
     def expand(self) -> 'ExpandDispatch':  # noqa: D401
         """A dispatch to expand the graph w.r.t. its parent."""
@@ -1127,14 +1131,29 @@ class SummarizeDispatch(Dispatch):
         self.edges(file=file, examples=examples)
         print('', file=file)
 
+    def _repr_html_(self) -> str:
+        from .summary import supersummary as ss
+        return dedent(f'''\
+            <h2>Metadata</h2>
+            {tabulate(self._metadata_list(), tablefmt='html')}
+            <h2>Statistics</h2>
+            {tabulate(self._statistics_list(prose_prefix=False), tablefmt='html')}
+            <h2>Nodes</h2>
+            {ss.functions_str(self.graph, examples=True, add_count=False, tablefmt='html')}
+            <h2>Namespaces</h2>
+            {ss.namespaces_str(self.graph, examples=True, add_count=False, tablefmt='html')}
+            <h2>Edges</h2>
+            {ss.edges_str(self.graph, examples=True, add_count=False, tablefmt='html')}
+        ''')
+
     def statistics(self, file: Optional[TextIO] = None):
         """Print summary statistics on the graph."""
         print(self.str(), file=file)
 
     def nodes(self, file: Optional[TextIO] = None, examples: bool = True):
         """Print a summary of the nodes' functions in the graph."""
-        from .summary.supersummary import functions
-        functions(self.graph, file=file, examples=examples)
+        from .summary.supersummary import functions_str
+        print(functions_str(self.graph, examples=examples), file=file)
 
     def namespaces(self, file: Optional[TextIO] = None, examples: bool = True):
         """Print a summary of the nodes' namespaces in the graph."""
@@ -1155,32 +1174,43 @@ class SummarizeDispatch(Dispatch):
         """Return a dictionary that summarizes the graph."""
         return dict(self.list())
 
-    def str(self) -> str:
+    def str(self, **kwargs) -> str:
         """Return a string that summarizes the graph."""
-        return tabulate(self.list())
+        return tabulate(self.list(), **kwargs)
 
-    def list(self) -> List[Tuple[str, Any]]:
-        """Return a list of tuples that summarize the graph."""
-        number_nodes = self.graph.number_of_nodes()
+    def _metadata_list(self) -> List[Tuple[str, Any]]:
         rv = [
             ('Name', self.graph.name),
             ('Version', self.graph.version),
-            ('Number of Nodes', number_nodes),
-            ('Number of Namespaces', len(self.graph.count.namespaces())),
-            ('Number of Edges', self.graph.number_of_edges()),
-            ('Number of Annotations', len(self.graph.count.annotations())),
-            ('Number of Citations', self.graph.number_of_citations()),
-            ('Number of Authors', self.graph.number_of_authors()),
-            ('Network Density', '{:.2E}'.format(nx.density(self.graph))),
-            ('Number of Components', nx.number_weakly_connected_components(self.graph)),
-            ('Number of Warnings', self.graph.number_of_warnings()),
         ]
         if self.graph.authors:
-            authors = self.graph.authors
-            key = 'Author' if authors.count(',') == 0 else 'Authors'
-            rv.insert(2, (key, authors))
+            rv.append(('Authors', self.graph.authors))
+        return rv
+
+    def _statistics_list(self, prose_prefix: bool = True) -> List[Tuple[str, Any]]:
+        number_nodes = self.graph.number_of_nodes()
+        rv = [
+            ('Nodes', number_nodes),
+            ('Namespaces', len(self.graph.count.namespaces())),
+            ('Edges', self.graph.number_of_edges()),
+            ('Annotations', len(self.graph.count.annotations())),
+            ('Citations', self.graph.number_of_citations()),
+            ('Authors', self.graph.number_of_authors()),
+            ('Components', nx.number_weakly_connected_components(self.graph)),
+            ('Warnings', self.graph.number_of_warnings()),
+        ]
+        if prose_prefix:
+            rv = [(f'Number of {x}', y) for x, y in rv]
+        rv.append(('Network Density', '{:.2E}'.format(nx.density(self.graph))))
         return rv
 
+    def list(self) -> List[Tuple[str, Any]]:
+        """Return a list of tuples that summarize the graph."""
+        return [
+            *self._metadata_list(),
+            *self._statistics_list(),
+        ]
+
 
 class PlotDispatch(Dispatch):
     """A dispatch for count functions that can be found at :data:`pybel.BELGraph.plot`."""
diff --git a/src/pybel/struct/summary/supersummary.py b/src/pybel/struct/summary/supersummary.py
index d230e40b..dc03ab99 100644
--- a/src/pybel/struct/summary/supersummary.py
+++ b/src/pybel/struct/summary/supersummary.py
@@ -7,6 +7,7 @@ import random
 from collections import Counter
 from typing import Optional, TextIO
 
+import bioregistry
 import pandas as pd
 from humanize import intword
 from tabulate import tabulate
@@ -35,12 +36,18 @@ def function_table_df(graph: BELGraph, examples: bool = True) -> pd.DataFrame:
     )
 
 
-def functions(graph, file: Optional[TextIO] = None, examples: bool = True):
-    """Print a summary of the functions in the graph."""
+def functions_str(graph, examples: bool = True, add_count: bool = True, **kwargs) -> str:
+    """Make a summary string of the functions in the graph."""
     df = function_table_df(graph, examples=examples)
     headers = list(df.columns)
-    headers[0] += ' ({})'.format(len(df.index))
-    print(tabulate(df.values, headers=headers), file=file)
+    if add_count:
+        headers[0] += ' ({})'.format(len(df.index))
+    return tabulate(df.values, headers=headers, **kwargs)
+
+
+def functions(graph, file: Optional[TextIO] = None, examples: bool = True, **kwargs) -> None:
+    """Print a summary of the functions in the graph."""
+    print(functions_str(graph=graph, examples=examples, **kwargs), file=file)
 
 
 def namespaces_table_df(graph: BELGraph, examples: bool = True) -> pd.DataFrame:
@@ -52,22 +59,29 @@ def namespaces_table_df(graph: BELGraph, examples: bool = True) -> pd.DataFrame:
     return pd.DataFrame(
         [
             (
-                namespace,
+                prefix,
+                bioregistry.get_name(prefix),
                 count,
-                random.choice(namespace_mapping[namespace]) if namespace in namespace_mapping else '',  # noqa:S311
+                random.choice(namespace_mapping[prefix]) if prefix in namespace_mapping else '',  # noqa:S311
             )
-            for namespace, count in namespace_c.most_common()
+            for prefix, count in namespace_c.most_common()
         ],
-        columns=['Namespace', 'Count', 'Example'],
+        columns=['Prefix', 'Name', 'Count', 'Example'],
     )
 
 
-def namespaces(graph: BELGraph, file: Optional[TextIO] = None, examples: bool = True) -> None:
-    """Print a summary of the namespaces in the graph."""
+def namespaces_str(graph: BELGraph, examples: bool = True, add_count: bool = True, **kwargs) -> None:
+    """Make a summary string of the namespaces in the graph."""
     df = namespaces_table_df(graph, examples=examples)
     headers = list(df.columns)
-    headers[0] += ' ({})'.format(len(df.index))
-    print(tabulate(df.values, headers=headers), file=file)
+    if add_count:
+        headers[0] += ' ({})'.format(len(df.index))
+    return tabulate(df.values, headers=headers, **kwargs)
+
+
+def namespaces(graph: BELGraph, file: Optional[TextIO] = None, examples: bool = True, **kwargs) -> None:
+    """Print a summary of the namespaces in the graph."""
+    print(namespaces_str(graph=graph, examples=examples, **kwargs), file=file)
 
 
 def edge_table_df(graph: BELGraph, *, examples: bool = True, minimum: Optional[int] = None) -> pd.DataFrame:
@@ -93,18 +107,32 @@ def edge_table_df(graph: BELGraph, *, examples: bool = True, minimum: Optional[i
     return pd.DataFrame(rows, columns=columns)
 
 
+def edges_str(
+    graph: BELGraph,
+    *,
+    examples: bool = True,
+    add_count: bool = True,
+    minimum: Optional[int] = None,
+    **kwargs,
+) -> str:
+    """Make a summary str of the edges in the graph."""
+    df = edge_table_df(graph, examples=examples, minimum=minimum)
+    headers = list(df.columns)
+    if add_count:
+        headers[0] += ' ({})'.format(intword(len(df.index)))
+    return tabulate(df.values, headers=headers, **kwargs)
+
+
 def edges(
     graph: BELGraph,
     *,
     examples: bool = True,
     minimum: Optional[int] = None,
     file: Optional[TextIO] = None,
+    **kwargs,
 ) -> None:
     """Print a summary of the edges in the graph."""
-    df = edge_table_df(graph, examples=examples, minimum=minimum)
-    headers = list(df.columns)
-    headers[0] += ' ({})'.format(intword(len(df.index)))
-    print(tabulate(df.values, headers=headers), file=file)
+    print(edges_str(graph=graph, examples=examples, minimum=minimum, **kwargs), file=file)
 
 
 def citations(graph: BELGraph, n: Optional[int] = 15, file: Optional[TextIO] = None) -> None:


Test patch:
diff --git a/tests/test_struct/test_struct_graph.py b/tests/test_struct/test_struct_graph.py
index fbe66f46..afd3d699 100644
--- a/tests/test_struct/test_struct_graph.py
+++ b/tests/test_struct/test_struct_graph.py
@@ -101,16 +101,16 @@ class TestStruct(unittest.TestCase):
         test_str = """---------------------  -------------------
 Name                   Sialic Acid Graph
 Version                1.0.0
-Author                 Charles Tapley Hoyt
+Authors                Charles Tapley Hoyt
 Number of Nodes        9
 Number of Namespaces   3
 Number of Edges        11
 Number of Annotations  2
 Number of Citations    1
 Number of Authors      0
-Network Density        1.53E-01
 Number of Components   1
 Number of Warnings     0
+Network Density        1.53E-01
 ---------------------  -------------------
 
 Type (3)      Count