Graphinate 0.2.17__tar.gz → 0.2.19__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {graphinate-0.2.17 → graphinate-0.2.19}/.github/workflows/test.yml +3 -3
- {graphinate-0.2.17 → graphinate-0.2.19}/PKG-INFO +4 -4
- graphinate-0.2.19/docs/examples/system.md +15 -0
- graphinate-0.2.19/examples/code/requirements.txt +1 -0
- graphinate-0.2.19/examples/system/processes.py +64 -0
- graphinate-0.2.19/examples/system/requirements.txt +1 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/pyproject.toml +3 -3
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/builders.py +59 -43
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/elements/index.html +3 -2
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/graphiql/index.html +1 -1
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/rapidoc/index.html +1 -1
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/viewer/index.html +2 -1
- graphinate-0.2.19/src/graphinate/tools/__init__.py +5 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/tests/graphinate/test_materializers.py +10 -6
- graphinate-0.2.17/examples/code/requirements.txt +0 -1
- graphinate-0.2.17/src/graphinate/tools/__init__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/.coveragerc +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/.github/dependabot.yml +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/.github/workflows/codeql.yml +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/.github/workflows/publish-docs.yaml +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/.github/workflows/publish.yml +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/.github/workflows/test-beta.yml +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/.gitignore +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/LICENSE +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/README.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/STATS.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/acknowledge.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/assets/images/logo-128.png +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/assets/images/network_graph.png +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/assets/stylesheets/extra.css +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/dev.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/examples/code.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/examples/github.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/examples/math.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/examples/web.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/gen_ref_pages.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/index.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/intro.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/start.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/usage/cli.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/docs/usage/lib.md +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/code/python_ast.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/code/python_dependencies.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/github/_client.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/github/commits_visibilty_graph.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/github/followers.graphql +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/github/followers.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/github/graphql.config.yml +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/github/repositories.graphql +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/github/repositories.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/github/requirements.txt +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/math/graph_atlas.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/math/polygonal_graph.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/web/page_links.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/examples/web/requirements.txt +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/mkdocs.yml +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/playground/ethernet/traceroute.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/playground/genric_graph.graphql +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/playground/graphql.config.yml +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/playground/house_of_graphs.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/playground/social/albums.json +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/playground/social/musicisians.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/playground/text/nlp_graph.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/playground/text/requirements.txt +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/playground/time_series/requirements.txt +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/playground/time_series/visibility_graph.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/sonar-project.properties +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/__init__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/__main__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/cli.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/color.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/materializers/__init__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/materializers/matplotlib.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/modeling.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/__init__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/starlette/__init__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/starlette/views.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/__init__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/elements/__init__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/graphiql/__init__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/rapidoc/__init__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/static/images/logo-128.png +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/static/images/network_graph.png +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/viewer/__init__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/voyager/__init__.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/voyager/index.html +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/tools/converters.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/tools/gui.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/tools/mutators.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/typing.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/tests/conftest.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/tests/graphinate/test_builders.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/tests/graphinate/test_cli.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/tests/graphinate/test_color.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/tests/graphinate/test_modeling.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/tests/graphinate/test_server.py +0 -0
- {graphinate-0.2.17 → graphinate-0.2.19}/tests/graphinate/test_tools.py +0 -0
|
@@ -40,6 +40,6 @@ jobs:
|
|
|
40
40
|
python -m pip install faker pytest pytest-asyncio pytest-cov pytest-randomly pytest-xdist starlette-prometheus uvicorn[standard]
|
|
41
41
|
pytest tests --cov=src --cov-branch --cov-report=xml --junitxml=test_results.xml -n auto
|
|
42
42
|
- name: Upload coverage reports to Codecov
|
|
43
|
-
uses: codecov/codecov-action@
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
uses: codecov/codecov-action@v4
|
|
44
|
+
with:
|
|
45
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: Graphinate
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.19
|
|
4
4
|
Summary: Graphinate. Data to Graphs.
|
|
5
5
|
Project-URL: Homepage, https://erivlis.github.io/graphinate
|
|
6
6
|
Project-URL: Documentation, https://erivlis.github.io/graphinate
|
|
@@ -32,7 +32,7 @@ Requires-Dist: inflect==7.0.0
|
|
|
32
32
|
Requires-Dist: loguru==0.7.2
|
|
33
33
|
Requires-Dist: matplotlib==3.8.3
|
|
34
34
|
Requires-Dist: networkx==3.2.1
|
|
35
|
-
Requires-Dist: strawberry-graphql[asgi,opentelemetry]==0.
|
|
35
|
+
Requires-Dist: strawberry-graphql[asgi,opentelemetry]==0.221.1
|
|
36
36
|
Provides-Extra: dev
|
|
37
37
|
Requires-Dist: pipdeptree; extra == 'dev'
|
|
38
38
|
Requires-Dist: ruff; extra == 'dev'
|
|
@@ -48,7 +48,7 @@ Provides-Extra: plot
|
|
|
48
48
|
Requires-Dist: scipy>=1.12.0; extra == 'plot'
|
|
49
49
|
Provides-Extra: server
|
|
50
50
|
Requires-Dist: starlette-prometheus; extra == 'server'
|
|
51
|
-
Requires-Dist: uvicorn[standard]>=0.
|
|
51
|
+
Requires-Dist: uvicorn[standard]>=0.29.0; extra == 'server'
|
|
52
52
|
Provides-Extra: test
|
|
53
53
|
Requires-Dist: faker>=23.2.1; extra == 'test'
|
|
54
54
|
Requires-Dist: pytest-asyncio>=0.23.5; extra == 'test'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# System
|
|
2
|
+
|
|
3
|
+
## Processes
|
|
4
|
+
|
|
5
|
+
=== "Processes"
|
|
6
|
+
|
|
7
|
+
``` python title="examples/system/processes.py" linenums="1"
|
|
8
|
+
--8<-- "examples/system/processes.py"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
=== "Dependencies"
|
|
12
|
+
|
|
13
|
+
``` text title="examples/system/requirements.txt" linenums="1"
|
|
14
|
+
--8<-- "examples/system/requirements.txt"
|
|
15
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pipdeptree>=2.13.0
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import operator
|
|
2
|
+
from collections.abc import Iterable
|
|
3
|
+
|
|
4
|
+
import networkx as nx
|
|
5
|
+
import psutil
|
|
6
|
+
|
|
7
|
+
import graphinate
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def processes_graph_model():
|
|
11
|
+
graph_model = graphinate.model("Processes Graph")
|
|
12
|
+
|
|
13
|
+
def processes() -> Iterable[psutil.Process]:
|
|
14
|
+
for pid in psutil.pids():
|
|
15
|
+
if psutil.pid_exists(pid):
|
|
16
|
+
yield psutil.Process(pid)
|
|
17
|
+
|
|
18
|
+
processes_list = [{'pid': p.pid, 'name': p.name(), 'parent_pid': p.parent().pid if p.parent() else None} for p in
|
|
19
|
+
processes()]
|
|
20
|
+
|
|
21
|
+
@graph_model.node(key=operator.itemgetter('pid'), label=operator.itemgetter('name'))
|
|
22
|
+
def process():
|
|
23
|
+
yield from processes_list
|
|
24
|
+
|
|
25
|
+
@graph_model.edge()
|
|
26
|
+
def edge():
|
|
27
|
+
for p in processes_list:
|
|
28
|
+
parent_pid = p.get('parent_pid')
|
|
29
|
+
if parent_pid:
|
|
30
|
+
yield {'source': p.get('pid'), 'target': parent_pid}
|
|
31
|
+
|
|
32
|
+
return graph_model
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
model = processes_graph_model()
|
|
36
|
+
|
|
37
|
+
if __name__ == '__main__':
|
|
38
|
+
use_materialize = True
|
|
39
|
+
|
|
40
|
+
if use_materialize:
|
|
41
|
+
# Materialize the GraphModel
|
|
42
|
+
graphinate.materialize(
|
|
43
|
+
model,
|
|
44
|
+
builder=graphinate.builders.GraphQLBuilder,
|
|
45
|
+
actualizer=graphinate.graphql
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
else:
|
|
49
|
+
# Or
|
|
50
|
+
|
|
51
|
+
# 1. Define Graph Builder
|
|
52
|
+
builder = graphinate.builders.NetworkxBuilder(model=model)
|
|
53
|
+
|
|
54
|
+
# Then
|
|
55
|
+
# 2. Build the Graph object
|
|
56
|
+
graph: nx.Graph = builder.build()
|
|
57
|
+
|
|
58
|
+
# Then
|
|
59
|
+
# 3. Option A - Output to console
|
|
60
|
+
print(graph)
|
|
61
|
+
|
|
62
|
+
# Or
|
|
63
|
+
# 3. Option B - Output as a plot
|
|
64
|
+
graphinate.materializers.plot(graph)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
psutil>=5.9.8
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "Graphinate"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.19"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name = "Eran Rivlis", email = "eran@rivlis.info" },
|
|
6
6
|
]
|
|
@@ -33,7 +33,7 @@ dependencies = [
|
|
|
33
33
|
"loguru==0.7.2",
|
|
34
34
|
"matplotlib==3.8.3",
|
|
35
35
|
"networkx==3.2.1",
|
|
36
|
-
"strawberry-graphql[asgi,opentelemetry]==0.
|
|
36
|
+
"strawberry-graphql[asgi,opentelemetry]==0.221.1"
|
|
37
37
|
]
|
|
38
38
|
|
|
39
39
|
|
|
@@ -71,7 +71,7 @@ plot = [
|
|
|
71
71
|
]
|
|
72
72
|
server = [
|
|
73
73
|
"starlette-prometheus",
|
|
74
|
-
"uvicorn[standard]>=0.
|
|
74
|
+
"uvicorn[standard]>=0.29.0"
|
|
75
75
|
]
|
|
76
76
|
|
|
77
77
|
|
|
@@ -26,7 +26,7 @@ from strawberry.extensions import ParserCache, QueryDepthLimiter, ValidationCach
|
|
|
26
26
|
|
|
27
27
|
from . import color
|
|
28
28
|
from .modeling import UNIVERSE_NODE, GraphModel
|
|
29
|
-
from .tools import converters, mutators
|
|
29
|
+
from .tools import converters, mutators, utcnow
|
|
30
30
|
from .typing import NodeTypeAbsoluteId
|
|
31
31
|
|
|
32
32
|
DEFAULT_NODE_DELIMITER = ' ∋ '
|
|
@@ -145,9 +145,9 @@ class NetworkxBuilder(Builder):
|
|
|
145
145
|
super().__init__(model, graph_type)
|
|
146
146
|
|
|
147
147
|
def _initialize_graph(self):
|
|
148
|
-
self._graph = self.graph_type.value(name=self.model.name,
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
self._graph: nx.Graph = self.graph_type.value(name=self.model.name,
|
|
149
|
+
node_types=Counter(),
|
|
150
|
+
edge_types=Counter())
|
|
151
151
|
|
|
152
152
|
def _graph_edges(self, data, default=None):
|
|
153
153
|
params = {'data': data, 'default': default}
|
|
@@ -197,7 +197,7 @@ class NetworkxBuilder(Builder):
|
|
|
197
197
|
if node_id in self._graph:
|
|
198
198
|
self._graph.nodes[node_id]['value'].append(node.value)
|
|
199
199
|
self._graph.nodes[node_id]['magnitude'] += 1
|
|
200
|
-
self._graph.nodes[node_id]['updated'] =
|
|
200
|
+
self._graph.nodes[node_id]['updated'] = utcnow()
|
|
201
201
|
else:
|
|
202
202
|
self._graph.add_node(node_id,
|
|
203
203
|
label=label,
|
|
@@ -205,7 +205,7 @@ class NetworkxBuilder(Builder):
|
|
|
205
205
|
value=[node.value],
|
|
206
206
|
magnitude=1,
|
|
207
207
|
lineage=list(node_lineage),
|
|
208
|
-
created=
|
|
208
|
+
created=utcnow())
|
|
209
209
|
|
|
210
210
|
self._graph.graph['node_types'].update({node_type: 1})
|
|
211
211
|
|
|
@@ -213,7 +213,7 @@ class NetworkxBuilder(Builder):
|
|
|
213
213
|
logger.debug("Adding edge from: '{}' to '{}'", parent_node_id, node_id)
|
|
214
214
|
self._graph.add_edge(parent_node_id,
|
|
215
215
|
node_id,
|
|
216
|
-
created=
|
|
216
|
+
created=utcnow())
|
|
217
217
|
|
|
218
218
|
new_kwargs = kwargs.copy()
|
|
219
219
|
new_kwargs[f"{node_type}_id"] = node.key
|
|
@@ -234,12 +234,12 @@ class NetworkxBuilder(Builder):
|
|
|
234
234
|
type=edge_type,
|
|
235
235
|
value=[edge.value],
|
|
236
236
|
weight=edge_weight,
|
|
237
|
-
created=
|
|
237
|
+
created=utcnow())
|
|
238
238
|
self._graph.graph['edge_types'].update({edge_type: 1})
|
|
239
239
|
else:
|
|
240
240
|
self._graph.edges[edge_id]['value'].append(edge.value)
|
|
241
241
|
self._graph.edges[edge_id]['weight'] += edge_weight
|
|
242
|
-
self._graph.edges[edge_id]['updated'] =
|
|
242
|
+
self._graph.edges[edge_id]['updated'] = utcnow()
|
|
243
243
|
|
|
244
244
|
def _rectify_node_attributes(self, **defaults):
|
|
245
245
|
|
|
@@ -294,7 +294,7 @@ class NetworkxBuilder(Builder):
|
|
|
294
294
|
counter = self._graph.graph[counter_name]
|
|
295
295
|
self._graph.graph[counter_name] = mutators.dictify(counter)
|
|
296
296
|
|
|
297
|
-
self._graph.graph['created'] =
|
|
297
|
+
self._graph.graph['created'] = utcnow()
|
|
298
298
|
|
|
299
299
|
def build(self, **kwargs) -> GraphRepresentation:
|
|
300
300
|
"""
|
|
@@ -393,27 +393,60 @@ class GraphQLBuilder(NetworkxBuilder):
|
|
|
393
393
|
|
|
394
394
|
@strawberry.type
|
|
395
395
|
class Graph:
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
name: str
|
|
399
|
-
created: datetime
|
|
400
|
-
hash: str
|
|
401
|
-
node_type_counts: list['GraphQLBuilder.Measure']
|
|
402
|
-
edge_type_counts: list['GraphQLBuilder.Measure']
|
|
403
|
-
node_count: int
|
|
404
|
-
edge_count: int
|
|
405
|
-
order: int
|
|
406
|
-
size: int
|
|
407
|
-
# girth: int
|
|
408
|
-
average_degree: float
|
|
396
|
+
nx_graph: strawberry.Private[nx.Graph]
|
|
409
397
|
|
|
410
398
|
@strawberry.field()
|
|
411
399
|
def radius(self) -> 'GraphQLBuilder.InfNumber':
|
|
412
|
-
return nx.radius(self.
|
|
400
|
+
return nx.radius(self.nx_graph) if nx.is_connected(self.nx_graph) else math.inf
|
|
413
401
|
|
|
414
402
|
@strawberry.field()
|
|
415
403
|
def diameter(self) -> 'GraphQLBuilder.InfNumber':
|
|
416
|
-
return nx.diameter(self.
|
|
404
|
+
return nx.diameter(self.nx_graph) if nx.is_connected(self.nx_graph) else math.inf
|
|
405
|
+
|
|
406
|
+
@strawberry.field()
|
|
407
|
+
def name(self) -> str:
|
|
408
|
+
return self.nx_graph.graph['name']
|
|
409
|
+
|
|
410
|
+
@strawberry.field()
|
|
411
|
+
def node_type_counts(self) -> list['GraphQLBuilder.Measure']:
|
|
412
|
+
return [GraphQLBuilder.Measure(name=t, value=c) for t, c in self.nx_graph.graph['node_types'].items()]
|
|
413
|
+
|
|
414
|
+
@strawberry.field()
|
|
415
|
+
def edge_type_counts(self) -> list['GraphQLBuilder.Measure']:
|
|
416
|
+
return [GraphQLBuilder.Measure(name=t, value=c) for t, c in self.nx_graph.graph['edge_types'].items()]
|
|
417
|
+
|
|
418
|
+
@strawberry.field()
|
|
419
|
+
def node_count(self) -> int:
|
|
420
|
+
return self.nx_graph.number_of_nodes()
|
|
421
|
+
|
|
422
|
+
@strawberry.field()
|
|
423
|
+
def edge_count(self) -> int:
|
|
424
|
+
return self.nx_graph.number_of_edges()
|
|
425
|
+
|
|
426
|
+
@strawberry.field()
|
|
427
|
+
def order(self) -> int:
|
|
428
|
+
return self.nx_graph.order()
|
|
429
|
+
|
|
430
|
+
@strawberry.field()
|
|
431
|
+
def size(self) -> int:
|
|
432
|
+
return self.nx_graph.size(weight='weight')
|
|
433
|
+
|
|
434
|
+
# @strawberry.field()
|
|
435
|
+
# def girth(self) -> int:
|
|
436
|
+
# return min(len(cycle) for cycle in nx.simple_cycles(self.graph))
|
|
437
|
+
|
|
438
|
+
@strawberry.field()
|
|
439
|
+
def average_degree(self) -> float:
|
|
440
|
+
return self.nx_graph.number_of_nodes() and (
|
|
441
|
+
1.0 * sum(d for _, d in self.nx_graph.degree()) / self.nx_graph.number_of_nodes())
|
|
442
|
+
|
|
443
|
+
@strawberry.field()
|
|
444
|
+
def hash(self) -> str:
|
|
445
|
+
return nx.weisfeiler_lehman_graph_hash(self.nx_graph)
|
|
446
|
+
|
|
447
|
+
@strawberry.field()
|
|
448
|
+
def created(self) -> datetime:
|
|
449
|
+
return self.nx_graph.graph['created']
|
|
417
450
|
|
|
418
451
|
@strawberry.enum(description="""
|
|
419
452
|
See NetworkX documentation for explanations:
|
|
@@ -599,24 +632,7 @@ class GraphQLBuilder(NetworkxBuilder):
|
|
|
599
632
|
|
|
600
633
|
# region - Defining GraphQL Query Class dict - graph field
|
|
601
634
|
def graphql_graph(self) -> GraphQLBuilder.Graph:
|
|
602
|
-
|
|
603
|
-
return GraphQLBuilder.Graph(
|
|
604
|
-
graph=graph,
|
|
605
|
-
name=graph.graph['name'],
|
|
606
|
-
node_type_counts=[GraphQLBuilder.Measure(name=t, value=c) for t, c in
|
|
607
|
-
graph.graph['node_types'].items()],
|
|
608
|
-
edge_type_counts=[GraphQLBuilder.Measure(name=t, value=c) for t, c in
|
|
609
|
-
graph.graph['edge_types'].items()],
|
|
610
|
-
node_count=graph.number_of_nodes(),
|
|
611
|
-
edge_count=graph.number_of_edges(),
|
|
612
|
-
order=graph.order(),
|
|
613
|
-
size=graph.size(weight='weight'),
|
|
614
|
-
# girth=min(len(cycle) for cycle in nx.simple_cycles(graph)),
|
|
615
|
-
average_degree=graph.number_of_nodes() and (
|
|
616
|
-
1.0 * sum(d for _, d in graph.degree()) / graph.number_of_nodes()),
|
|
617
|
-
hash=nx.weisfeiler_lehman_graph_hash(graph),
|
|
618
|
-
created=graph.graph['created'],
|
|
619
|
-
)
|
|
635
|
+
return GraphQLBuilder.Graph(nx_graph=get_graph())
|
|
620
636
|
|
|
621
637
|
self.add_field_resolver(query_class_dict, 'graph', graphql_graph)
|
|
622
638
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
<!
|
|
1
|
+
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
<title>Graphinate - OpanAPI Elements UI</title>
|
|
7
8
|
<!-- Embed elements Elements via Web Component -->
|
|
8
9
|
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js"
|
|
9
10
|
integrity="sha384-yR4p7dGVb43Z+zDGOg8xyIj71nVRZvN/IpUbptwjAtxcq+IcVQ+mNMiL7ppakjKc"></script>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8"> <!-- Important: rapi-doc uses utf8 characters -->
|
|
5
5
|
<script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"
|
|
6
6
|
integrity="sha384-x8lk+1kqb32UOR4ETiOR+bpfZZtNri15JJdU7sXAKtHk2m9yMRVvITZcxS0ILJpC"></script>
|
|
7
|
-
<title>Graphinate OpenAPI RapiDoc UI</title>
|
|
7
|
+
<title>Graphinate - OpenAPI RapiDoc UI</title>
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<rapi-doc
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
<title>Graphinate</title>
|
|
5
5
|
<style> body { margin: 0; } </style>
|
|
6
6
|
<script src="https://cdn.jsdelivr.net/npm/3d-force-graph@1.72.3/dist/3d-force-graph.min.js"
|
|
7
|
-
integrity="
|
|
7
|
+
integrity="sha256-X4nSjPQnyc5QqQ88WM5oVzMx2KUKXj3hxRAvt2WxDRI="
|
|
8
8
|
crossorigin="anonymous"></script>
|
|
9
|
+
|
|
9
10
|
<script src="/static/scripts/murmurhash3_gc.js"
|
|
10
11
|
integrity="sha384-qmbe8KG4+frUnGS6DOKYZgFKbBvbhNNOL/LZ4hSpKauxmgrTX3LALg5+oQJS4AOn"></script>
|
|
11
12
|
</head>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import functools
|
|
2
2
|
from unittest.mock import patch
|
|
3
3
|
|
|
4
|
-
import graphinate
|
|
5
4
|
import pytest
|
|
6
5
|
from matplotlib import pyplot as plt
|
|
7
6
|
|
|
7
|
+
import graphinate
|
|
8
|
+
|
|
8
9
|
MODAL_RADIOBUTTON_CHOOSER = 'graphinate.materializers.modal_radiobutton_chooser'
|
|
9
10
|
|
|
10
11
|
|
|
@@ -33,6 +34,11 @@ def test_materialize_d3graph(map_graph_model, monkeypatch, capsys):
|
|
|
33
34
|
assert captured.err == ""
|
|
34
35
|
|
|
35
36
|
|
|
37
|
+
def valid_materialization(graph_model) -> bool:
|
|
38
|
+
graphinate.materialize(graph_model)
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
|
|
36
42
|
def test_materialize_graphql(map_graph_model, monkeypatch):
|
|
37
43
|
with patch(MODAL_RADIOBUTTON_CHOOSER) as modal_radiobutton_chooser:
|
|
38
44
|
import uvicorn
|
|
@@ -40,8 +46,8 @@ def test_materialize_graphql(map_graph_model, monkeypatch):
|
|
|
40
46
|
modal_radiobutton_chooser.return_value = ('Test', graphinate.materializers.Materializers.GraphQL.value)
|
|
41
47
|
|
|
42
48
|
*_, graph_model = map_graph_model
|
|
43
|
-
|
|
44
|
-
assert
|
|
49
|
+
|
|
50
|
+
assert valid_materialization(graph_model)
|
|
45
51
|
|
|
46
52
|
|
|
47
53
|
networkx_materializers = [
|
|
@@ -49,7 +55,6 @@ networkx_materializers = [
|
|
|
49
55
|
graphinate.materializers.Materializers.NetworkX_with_edge_labels.value,
|
|
50
56
|
(graphinate.builders.NetworkxBuilder,
|
|
51
57
|
functools.partial(graphinate.materializers.matplotlib.plot, with_node_labels=False))
|
|
52
|
-
|
|
53
58
|
]
|
|
54
59
|
|
|
55
60
|
|
|
@@ -60,8 +65,7 @@ def test_materialize_networkx(map_graph_model, materializer, monkeypatch):
|
|
|
60
65
|
with patch('graphinate.materializers.modal_radiobutton_chooser') as modal_radiobutton_chooser:
|
|
61
66
|
modal_radiobutton_chooser.return_value = ('Test', materializer)
|
|
62
67
|
*_, graph_model = map_graph_model
|
|
63
|
-
|
|
64
|
-
assert True
|
|
68
|
+
assert valid_materialization(graph_model)
|
|
65
69
|
|
|
66
70
|
|
|
67
71
|
def test_materialize_none(map_graph_model, monkeypatch):
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pipdeptree==2.13.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/static/images/logo-128.png
RENAMED
|
File without changes
|
{graphinate-0.2.17 → graphinate-0.2.19}/src/graphinate/server/web/static/images/network_graph.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|