Skip to content

Commit

Permalink
graphviewer: Allow to configure which paths between central nodes are…
Browse files Browse the repository at this point in the history
… included
  • Loading branch information
thvitt committed Jul 25, 2020
1 parent a565f1b commit 0df71e7
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/graphviewer/graphviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,12 @@ def prepare_agraph():
order = request.values.get('order', False)
collapse = request.values.get('collapse', False)
direction = request.values.get('dir', 'LR').upper()
central_paths = request.values.get('central_paths', 'all').lower()
if direction not in {'LR', 'RL', 'TB', 'BT'}:
direction = 'LR'
if nodes:
g = info.subgraph(*nodes, context=context, abs_dates=abs_dates, paths=extra, keep_timeline=True,
paths_between_nodes=central_paths,
paths_without_timeline=paths_wo_timeline,
direct_assertions=direct_assertions, include_syn_clusters=syn)
if induced_edges:
Expand Down
9 changes: 7 additions & 2 deletions src/graphviewer/templates/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,13 @@
</legend>
<p><input type="text" name="nodes" id="nodes" value="" style="width: 100%;" placeholder="2 V H.13">
</p>
<p><input type="checkbox" name="context" id="context" {% if context %}checked{% endif %}> <label for="context">Nachbarknoten</label></p>
<p><input type="checkbox" name="assertions" id="assertions" {% if assertions %}checked{% endif %} <label for="assertions">unmittelbare Aussagen über Kernknoten</label> </p>
<p>Kanten dazwischen:
<input type="radio" name="central_paths" value="no" id="ce_no"> <label for="ce_no">keine</label>
<input type="radio" name="central_paths" value="dag" id="ce_dag"> <label for="ce_dag">nur positive</label>
<input type="radio" name="central_paths" value="all" id="ce_all" checked> <label for="ce_all">alle</label>
</p>
<p><input type="checkbox" name="context" id="context" > <label for="context">Nachbarknoten</label></p>
<p><input type="checkbox" name="assertions" id="assertions" > <label for="assertions">unmittelbare Aussagen über Kernknoten</label> </p>
{% if models|length > 1 %}
<p>
<label for="model">Modell: </label>
Expand Down
19 changes: 11 additions & 8 deletions src/macrogen/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import pandas as pd
from dataclasses import dataclass

from macrogen.graphutils import is_orphan, find_reachable_by_edge
from macrogen.graphutils import is_orphan, find_reachable_by_edge, path2str
from more_itertools import windowed

from .graphutils import mark_edges_to_delete, remove_edges, in_path, first
Expand Down Expand Up @@ -627,7 +627,8 @@ def add_path(self, graph: nx.MultiDiGraph, source: Node, target: Node, weight='i
if edges_from is None:
edges_from = self.dag
path = nx.shortest_path(edges_from, source, target, weight, method)
logger.debug('Shortest path from %s to %s: %s', source, target, " → ".join(map(str, path)))
if logger.isEnabledFor(logging.DEBUG):
logger.debug('Shortest path from %s to %s: %s', source, target, path2str(path))
edges = list(expand_edges(edges_from, nx.utils.pairwise(path), filter=True))
graph.add_edges_from(edges)
return path
Expand All @@ -639,8 +640,8 @@ def add_path(self, graph: nx.MultiDiGraph, source: Node, target: Node, weight='i

def subgraph(self, *nodes: Node, context: bool = True, path_to: Iterable[Node] = {}, abs_dates: bool = True,
path_from: Iterable[Node] = {}, paths: Iterable[Node] = {}, paths_without_timeline: bool = False,
paths_between_nodes: bool = True, keep_timeline: bool = False, direct_assertions: bool = False,
temp_syn_context: bool = False, include_syn_clusters: bool = False) \
paths_between_nodes: Union[bool, str] = 'all', keep_timeline: bool = False, direct_assertions: bool = False,
temp_syn_context: bool = False, include_syn_clusters: bool = False, include_inscription_clusters = False) \
-> nx.MultiDiGraph:
"""
Extracts a sensible subgraph from the base graph.
Expand All @@ -653,7 +654,8 @@ def subgraph(self, *nodes: Node, context: bool = True, path_to: Iterable[Node] =
path_from: Node from which the shortest path should be included, if any
paths: Node(s) from / to which the spp should be included, if any
paths_without_timeline: Paths should be built without considering the timeline
paths_between_nodes: iff True (the default), include the shortest paths between the given nodes
paths_between_nodes: If `'all'` or `True` (the default), include the shortest paths between the given nodes
on the base graph. If `'dag'`, only allow assertions from the dag.
keep_timeline: if True, keep the timeline as is, otherwise remove useless date nodes
direct_assertions: if True, include all edges induced by the given node(s)
Expand Down Expand Up @@ -722,11 +724,12 @@ def subgraph(self, *nodes: Node, context: bool = True, path_to: Iterable[Node] =
self.add_path(subgraph, source, node, edges_from=path_base)
for target in targets:
self.add_path(subgraph, node, target, edges_from=path_base)
if paths_between_nodes:
if paths_between_nodes in {True, 'dag', 'all'}:
inbetween_base = self.dag if paths_between_nodes == 'dag' else self.base
for other in central_nodes:
if other != node:
self.add_path(subgraph, node, other, edges_from=self.base)
self.add_path(subgraph, other, node, edges_from=self.base)
self.add_path(subgraph, node, other, edges_from=inbetween_base)
self.add_path(subgraph, other, node, edges_from=inbetween_base)

if direct_assertions:
subgraph.add_edges_from(self.base.in_edges(node, keys=True, data=True))
Expand Down
27 changes: 24 additions & 3 deletions src/macrogen/graphutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def collapse_parallel_edges(graph: nx.MultiDiGraph) -> nx.MultiDiGraph:
attrs['source'] = [e['source'] for e in parallel_edges]
attrs['comment'] = '\n'.join(e.get('comment', '') for e in parallel_edges)
attrs['weight'] = sum(e.get('weight', 0) for e in parallel_edges)
attrs['iweight'] = 1/attrs['weight']
attrs['iweight'] = 1 / attrs['weight']
result.add_edge(u, v, **attrs)
return result

Expand Down Expand Up @@ -287,7 +287,7 @@ def base_n(number: int, base: int = 10, neg: Optional[str] = '-') -> str:
Returns:
string representing number_base
"""
if not(isinstance(number, int)):
if not (isinstance(number, int)):
raise TypeError(f"Number must be an integer, not a {type(number)}")
if neg is None and int < 0:
raise ValueError("number must not be negative if no neg character is given")
Expand Down Expand Up @@ -343,4 +343,25 @@ def find_reachable_by_edge(graph: nx.MultiDiGraph, source: T, key, value, symmet
for k, attr in edges.items():
if key in attr and attr[key] == value:
todo.insert(0, neighbor)
return result
return result


def path2str(path: Iterable[Union[date, Reference]], connector=' → ', timeline_connector=' ⤑ ') -> str:
result_and_connectors = []
last_date = None
for node in path:
if isinstance(node, Reference):
if last_date is not None:
result_and_connectors += [timeline_connector, last_date, connector, node]
last_date = None
else:
result_and_connectors += [connector, node]
elif isinstance(node, date):
if last_date:
last_date = node
else:
result_and_connectors += [connector, node]
last_date = True
if last_date:
result_and_connectors += [timeline_connector, last_date]
return ''.join(map(str, result_and_connectors[1:]))

0 comments on commit 0df71e7

Please sign in to comment.