Changeset View
Changeset View
Standalone View
Standalone View
swh/graph/graph.py
# Copyright (C) 2019 The Software Heritage developers | # Copyright (C) 2019-2020 The Software Heritage developers | ||||
# See the AUTHORS file at the top-level directory of this distribution | # See the AUTHORS file at the top-level directory of this distribution | ||||
# License: GNU General Public License version 3, or any later version | # License: GNU General Public License version 3, or any later version | ||||
# See top-level LICENSE file for more information | # See top-level LICENSE file for more information | ||||
import asyncio | import asyncio | ||||
import contextlib | import contextlib | ||||
import functools | import functools | ||||
from swh.graph.backend import Backend | from swh.graph.backend import Backend | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | class GraphNode: | ||||
def _count(self, ttype, direction="forward", edges="*"): | def _count(self, ttype, direction="forward", edges="*"): | ||||
return self.graph.backend.count(ttype, direction, edges, self.id) | return self.graph.backend.count(ttype, direction, edges, self.id) | ||||
count_leaves = functools.partialmethod(_count, ttype="leaves") | count_leaves = functools.partialmethod(_count, ttype="leaves") | ||||
count_neighbors = functools.partialmethod(_count, ttype="neighbors") | count_neighbors = functools.partialmethod(_count, ttype="neighbors") | ||||
count_visit_nodes = functools.partialmethod(_count, ttype="visit_nodes") | count_visit_nodes = functools.partialmethod(_count, ttype="visit_nodes") | ||||
@property | @property | ||||
def pid(self): | def swhid(self): | ||||
return self.graph.node2pid[self.id] | return self.graph.node2swhid[self.id] | ||||
@property | @property | ||||
def kind(self): | def kind(self): | ||||
return self.pid.split(":")[2] | return self.swhid.split(":")[2] | ||||
def __str__(self): | def __str__(self): | ||||
return self.pid | return self.swhid | ||||
def __repr__(self): | def __repr__(self): | ||||
return "<{}>".format(self.pid) | return "<{}>".format(self.swhid) | ||||
def dot_fragment(self): | def dot_fragment(self): | ||||
swh, version, kind, hash = self.pid.split(":") | swh, version, kind, hash = self.swhid.split(":") | ||||
label = "{}:{}..{}".format(kind, hash[0:2], hash[-2:]) | label = "{}:{}..{}".format(kind, hash[0:2], hash[-2:]) | ||||
url = BASE_URL + KIND_TO_URL_FRAGMENT[kind].format(hash) | url = BASE_URL + KIND_TO_URL_FRAGMENT[kind].format(hash) | ||||
shape = KIND_TO_SHAPE[kind] | shape = KIND_TO_SHAPE[kind] | ||||
return '{} [label="{}", href="{}", target="_blank", shape="{}"];'.format( | return '{} [label="{}", href="{}", target="_blank", shape="{}"];'.format( | ||||
self.id, label, url, shape | self.id, label, url, shape | ||||
) | ) | ||||
def _repr_svg_(self): | def _repr_svg_(self): | ||||
nodes = [self, *list(self.children()), *list(self.parents())] | nodes = [self, *list(self.children()), *list(self.parents())] | ||||
dot = graph_dot(nodes) | dot = graph_dot(nodes) | ||||
svg = dot_to_svg(dot) | svg = dot_to_svg(dot) | ||||
return svg | return svg | ||||
class Graph: | class Graph: | ||||
def __init__(self, backend, node2pid, pid2node): | def __init__(self, backend, node2swhid, swhid2node): | ||||
self.backend = backend | self.backend = backend | ||||
self.java_graph = backend.entry.get_graph() | self.java_graph = backend.entry.get_graph() | ||||
self.node2pid = node2pid | self.node2swhid = node2swhid | ||||
self.pid2node = pid2node | self.swhid2node = swhid2node | ||||
def stats(self): | def stats(self): | ||||
return self.backend.stats() | return self.backend.stats() | ||||
@property | @property | ||||
def path(self): | def path(self): | ||||
return self.java_graph.getPath() | return self.java_graph.getPath() | ||||
def __len__(self): | def __len__(self): | ||||
return self.java_graph.getNbNodes() | return self.java_graph.getNbNodes() | ||||
def __getitem__(self, node_id): | def __getitem__(self, node_id): | ||||
if isinstance(node_id, int): | if isinstance(node_id, int): | ||||
self.node2pid[node_id] # check existence | self.node2swhid[node_id] # check existence | ||||
return GraphNode(self, node_id) | return GraphNode(self, node_id) | ||||
elif isinstance(node_id, str): | elif isinstance(node_id, str): | ||||
node_id = self.pid2node[node_id] | node_id = self.swhid2node[node_id] | ||||
return GraphNode(self, node_id) | return GraphNode(self, node_id) | ||||
def __iter__(self): | def __iter__(self): | ||||
for pid, pos in self.backend.pid2node: | for swhid, pos in self.backend.swhid2node: | ||||
yield self[pid] | yield self[swhid] | ||||
def iter_prefix(self, prefix): | def iter_prefix(self, prefix): | ||||
for pid, pos in self.backend.pid2node.iter_prefix(prefix): | for swhid, pos in self.backend.swhid2node.iter_prefix(prefix): | ||||
yield self[pid] | yield self[swhid] | ||||
def iter_type(self, pid_type): | def iter_type(self, swhid_type): | ||||
for pid, pos in self.backend.pid2node.iter_type(pid_type): | for swhid, pos in self.backend.swhid2node.iter_type(swhid_type): | ||||
yield self[pid] | yield self[swhid] | ||||
@contextlib.contextmanager | @contextlib.contextmanager | ||||
def load(graph_path): | def load(graph_path): | ||||
with Backend(graph_path) as backend: | with Backend(graph_path) as backend: | ||||
yield Graph(backend, backend.node2pid, backend.pid2node) | yield Graph(backend, backend.node2swhid, backend.swhid2node) |