Graphinate 0.9.0__tar.gz → 0.10.0__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.9.0 → graphinate-0.10.0}/.github/workflows/ci.yml +1 -1
- {graphinate-0.9.0 → graphinate-0.10.0}/PKG-INFO +3 -3
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/code/python_ast.py +1 -1
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/system/files.py +8 -4
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/system/processes.py +7 -1
- graphinate-0.10.0/playground/social/requirements.txt +1 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/pyproject.toml +7 -3
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/builders.py +44 -5
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/cli.py +17 -5
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/color.py +2 -1
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/renderers/graphql.py +2 -2
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/starlette/views.py +4 -2
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/viewer/index.html +29 -27
- {graphinate-0.9.0 → graphinate-0.10.0}/tests/graphinate/test_builders.py +3 -2
- {graphinate-0.9.0 → graphinate-0.10.0}/tests/graphinate/test_server.py +1 -1
- {graphinate-0.9.0 → graphinate-0.10.0}/.coveragerc +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/.deepsource.toml +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/.github/dependabot.yml +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/.github/workflows/codeql.yml +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/.github/workflows/publish-docs.yaml +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/.github/workflows/publish.yml +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/.github/workflows/test-beta.yml +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/.github/workflows/test.yml +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/.gitignore +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/.sonarcloud.properties +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/LICENSE +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/README.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/.dates_cache.json +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/acknowledgements.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/assets/badge/v0.json +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/assets/images/logo-128.png +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/assets/images/network_graph.png +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/assets/stylesheets/extra.css +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/examples/code.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/examples/github.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/examples/math.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/examples/social.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/examples/system.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/examples/web.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/gen_ref_pages.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/index.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/introduction.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/start.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/tutorial.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/usage/cli.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/docs/usage/lib.md +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/code/git_commits.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/code/python_dependencies.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/code/requirements.txt +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/code/tokens.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/github/_client.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/github/commits_visibilty_graph.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/github/followers.graphql +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/github/followers.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/github/graphql.config.yml +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/github/repositories.graphql +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/github/repositories.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/github/requirements.txt +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/math/__init__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/math/_test_materializers.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/math/graph_atlas.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/math/graphs.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/math/gui.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/math/materializers.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/math/polygonal_graph.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/math/requirements.txt +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/13/dd/73ce25face7beb30b69b64feeb77.val +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/21/9e/00846f323987ba16cfbe0127d8eb.val +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/70/b6/2aefb0269adce7fedf877fa0d267.val +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/87/f5/ec1739bc369e84c3fcb302bf532a.val +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/ba/fe/3aca7b2c38abff60e7ce5eb486a8.val +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/c7/9e/ce82b0288020b7152779df09bd73.val +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/cache.db +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/d2/53/3b88f2fc162561cfdbbe9abc352a.val +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/e2/d5/5d079f200eabf9b625b0473f6fbe.val +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/gui.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/music_artists.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/social/requirements.txt +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/system/.ignore +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/system/requirements.txt +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/web/html_dom.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/web/page_links.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/examples/web/requirements.txt +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/mkdocs.yml +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/playground/ethernet/traceroute.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/playground/genric_graph.graphql +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/playground/graphql.config.yml +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/playground/house_of_graphs.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/playground/science/caffeine.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/playground/social/albums.json +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/playground/social/musicisians.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/playground/text/nlp_graph.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/playground/text/requirements.txt +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/playground/time_series/requirements.txt +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/playground/time_series/visibility_graph.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/sonar-project.properties +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/__init__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/__main__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/constants.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/converters.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/modeling.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/renderers/__init__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/renderers/matplotlib.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/__init__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/starlette/__init__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/__init__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/elements/__init__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/elements/index.html +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/graphiql/__init__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/graphiql/index.html +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/rapidoc/__init__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/rapidoc/index.html +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/static/images/logo-128.png +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/static/images/logo.svg +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/static/images/network_graph.png +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/viewer/__init__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/voyager/__init__.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/server/web/voyager/index.html +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/tools.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/src/graphinate/typing.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/tests/conftest.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/tests/graphinate/renderers/test_graphql.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/tests/graphinate/renderers/test_matplotlib_draw.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/tests/graphinate/renderers/test_matplotlib_plot.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/tests/graphinate/server/test_starlette.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/tests/graphinate/test_cli.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/tests/graphinate/test_color.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/tests/graphinate/test_converters.py +0 -0
- {graphinate-0.9.0 → graphinate-0.10.0}/tests/graphinate/test_modeling.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Graphinate
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
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
|
|
@@ -34,8 +34,8 @@ Requires-Dist: loguru
|
|
|
34
34
|
Requires-Dist: mappingtools
|
|
35
35
|
Requires-Dist: matplotlib
|
|
36
36
|
Requires-Dist: networkx
|
|
37
|
-
Requires-Dist: networkx-mermaid
|
|
38
|
-
Requires-Dist: networkx-query
|
|
37
|
+
Requires-Dist: networkx-mermaid
|
|
38
|
+
Requires-Dist: networkx-query
|
|
39
39
|
Requires-Dist: strawberry-graphql[asgi,opentelemetry]
|
|
40
40
|
Provides-Extra: plot
|
|
41
41
|
Requires-Dist: scipy; extra == 'plot'
|
|
@@ -55,7 +55,7 @@ def ast_graph_model():
|
|
|
55
55
|
|
|
56
56
|
graph_model = graphinate.model(name=f'AST Graph - {code_object.__qualname__}',)
|
|
57
57
|
|
|
58
|
-
root_ast_node = ast.parse(inspect.getsource(
|
|
58
|
+
root_ast_node = ast.parse(inspect.getsource(code_object))
|
|
59
59
|
|
|
60
60
|
def node_type(ast_node):
|
|
61
61
|
return ast_node.__class__.__name__
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import fnmatch
|
|
2
2
|
import operator
|
|
3
3
|
import pathlib
|
|
4
|
+
from collections.abc import Iterable
|
|
4
5
|
|
|
5
6
|
from magika import Magika
|
|
6
7
|
|
|
7
8
|
import graphinate
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
def load_ignore_patterns(ignore_files):
|
|
11
|
+
def load_ignore_patterns(ignore_files: Iterable[str]) -> set[str]:
|
|
11
12
|
patterns = set()
|
|
12
13
|
for ignore_file in ignore_files:
|
|
13
14
|
if pathlib.Path(ignore_file).exists():
|
|
@@ -19,17 +20,20 @@ def load_ignore_patterns(ignore_files):
|
|
|
19
20
|
return patterns
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def is_ignored(path, patterns):
|
|
23
|
+
def is_ignored(path: pathlib.Path, patterns: Iterable[str]) -> bool:
|
|
23
24
|
return any(fnmatch.fnmatch(path.as_posix(), pattern) for pattern in patterns)
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
def create_filesystem_graph_model(
|
|
27
|
+
def create_filesystem_graph_model(
|
|
28
|
+
input_folder: str = '.',
|
|
29
|
+
ignore_files: Iterable[str] = ('.ignore', '.gitignore', '.dockerignore')
|
|
30
|
+
):
|
|
27
31
|
"""
|
|
28
32
|
Create a graph model of the file system structure.
|
|
29
33
|
|
|
30
34
|
Args:
|
|
31
35
|
input_folder (str): The folder to start the traversal from. Defaults to the current folder.
|
|
32
|
-
ignore_files (
|
|
36
|
+
ignore_files (Iterable): A list of files containing ignore patterns.
|
|
33
37
|
Defaults to ['.ignore', '.gitignore', '.dockerignore'].
|
|
34
38
|
|
|
35
39
|
Returns:
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example: Processes Graph
|
|
3
|
+
This example demonstrates how to create a graph model representing
|
|
4
|
+
processes and their parent-child relationships using the `psutil` library.
|
|
5
|
+
"""
|
|
6
|
+
|
|
1
7
|
import operator
|
|
2
8
|
from collections.abc import Iterable
|
|
3
9
|
|
|
@@ -61,4 +67,4 @@ if __name__ == '__main__':
|
|
|
61
67
|
|
|
62
68
|
# Or
|
|
63
69
|
# 3. Option B - Output as a plot
|
|
64
|
-
graphinate.
|
|
70
|
+
graphinate.renderers.matplotlib.plot(graph)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lxml
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "Graphinate"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.10.0"
|
|
4
4
|
description = "Graphinate. Data to Graphs."
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Eran Rivlis", email = "eran@rivlis.info" },
|
|
@@ -35,8 +35,8 @@ dependencies = [
|
|
|
35
35
|
"mappingtools",
|
|
36
36
|
"matplotlib",
|
|
37
37
|
"networkx",
|
|
38
|
-
"networkx-mermaid
|
|
39
|
-
"networkx-query
|
|
38
|
+
"networkx-mermaid",
|
|
39
|
+
"networkx-query",
|
|
40
40
|
"strawberry-graphql[asgi,opentelemetry]",
|
|
41
41
|
]
|
|
42
42
|
|
|
@@ -56,6 +56,10 @@ server = [
|
|
|
56
56
|
"Source" = "https://github.com/erivlis/graphinate"
|
|
57
57
|
|
|
58
58
|
|
|
59
|
+
[project.scripts]
|
|
60
|
+
graphinate = "graphinate.cli:cli"
|
|
61
|
+
|
|
62
|
+
|
|
59
63
|
[dependency-groups]
|
|
60
64
|
dev = [
|
|
61
65
|
"ruff",
|
|
@@ -438,6 +438,10 @@ class GraphQLBuilder(NetworkxBuilder):
|
|
|
438
438
|
created: datetime | None
|
|
439
439
|
updated: datetime | None
|
|
440
440
|
|
|
441
|
+
@strawberry.enum
|
|
442
|
+
class GraphNodeType(Enum):
|
|
443
|
+
... # pragma: no cover
|
|
444
|
+
|
|
441
445
|
@strawberry.interface(description="Represents a Graph Node")
|
|
442
446
|
class GraphNode(GraphElement):
|
|
443
447
|
node_id: strawberry.ID
|
|
@@ -445,7 +449,9 @@ class GraphQLBuilder(NetworkxBuilder):
|
|
|
445
449
|
lineage: str
|
|
446
450
|
|
|
447
451
|
@strawberry.field()
|
|
448
|
-
def neighbors(self,
|
|
452
|
+
def neighbors(self,
|
|
453
|
+
type: 'GraphQLBuilder.GraphNodeType | None' = None,
|
|
454
|
+
children: bool = False) -> list[Optional['GraphQLBuilder.GraphNode']]:
|
|
449
455
|
... # pragma: no cover
|
|
450
456
|
|
|
451
457
|
@strawberry.field()
|
|
@@ -628,30 +634,63 @@ class GraphQLBuilder(NetworkxBuilder):
|
|
|
628
634
|
description=f"Represents a {capitalized_name} Graph Node"
|
|
629
635
|
)
|
|
630
636
|
|
|
637
|
+
@staticmethod
|
|
638
|
+
def _graphql_enum(name: str, values: list[str]) -> type[Enum]:
|
|
639
|
+
return strawberry.enum(
|
|
640
|
+
Enum(name, {v: v for v in values}),
|
|
641
|
+
name=name,
|
|
642
|
+
description=f"{name} Enumeration"
|
|
643
|
+
)
|
|
644
|
+
|
|
631
645
|
@classmethod
|
|
632
646
|
@functools.lru_cache
|
|
633
647
|
def _children_types(cls, model: GraphModel, node_type: str):
|
|
634
648
|
return model.node_children_types(node_type).get(node_type, [])
|
|
635
649
|
|
|
650
|
+
def _populate_graph_node_type_enum(self, node_types: list[str]):
|
|
651
|
+
from strawberry.types.enum import EnumValue
|
|
652
|
+
|
|
653
|
+
for v in node_types:
|
|
654
|
+
self.GraphNodeType._member_names_.append(v)
|
|
655
|
+
self.GraphNodeType._member_map_[v] = v
|
|
656
|
+
self.GraphNodeType._value2member_map_[v] = v
|
|
657
|
+
|
|
658
|
+
self.GraphNodeType._enum_definition.values.append(
|
|
659
|
+
EnumValue(
|
|
660
|
+
name=v,
|
|
661
|
+
value=v,
|
|
662
|
+
description=f"Graph Node Type: {v}"
|
|
663
|
+
)
|
|
664
|
+
)
|
|
665
|
+
|
|
636
666
|
@property
|
|
637
667
|
@functools.lru_cache
|
|
638
668
|
def _graphql_types(self) -> dict[str, type['GraphQLBuilder.GraphNode']]:
|
|
639
669
|
node_types = list(self._graph.graph['node_types'].keys())
|
|
640
670
|
|
|
671
|
+
self._populate_graph_node_type_enum(node_types)
|
|
672
|
+
|
|
641
673
|
def neighbors_resolver():
|
|
642
674
|
graph = self._graph
|
|
643
675
|
|
|
644
676
|
children_types = set(self._children_types(self.model, node_type))
|
|
645
677
|
|
|
646
|
-
def node_neighbors(self,
|
|
678
|
+
def node_neighbors(self,
|
|
679
|
+
type: 'GraphQLBuilder.GraphNodeType | None' = None,
|
|
680
|
+
children: bool = False) -> list['GraphQLBuilder.GraphNode']:
|
|
647
681
|
node = decode_id(self.id)
|
|
648
|
-
items = (GraphQLBuilder._graph_node(graphql_types[d['type']], n, d)
|
|
682
|
+
items = (GraphQLBuilder._graph_node(graphql_types[d['type']], n, d)
|
|
683
|
+
for n, d in graph.nodes(data=True)
|
|
649
684
|
if n in graph.neighbors(node))
|
|
650
685
|
|
|
686
|
+
if type is not None:
|
|
687
|
+
items = (item for item in items if item.type == type)
|
|
688
|
+
|
|
651
689
|
if children and children_types:
|
|
652
|
-
|
|
690
|
+
items = (item for item in items if item.type in children_types)
|
|
653
691
|
|
|
654
|
-
|
|
692
|
+
items = list(items)
|
|
693
|
+
return items
|
|
655
694
|
|
|
656
695
|
return node_neighbors
|
|
657
696
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import importlib
|
|
2
2
|
import json
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
from typing import Any
|
|
4
5
|
|
|
5
6
|
import click
|
|
@@ -9,7 +10,7 @@ from graphinate.renderers.graphql import DEFAULT_PORT
|
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def _get_kwargs(ctx) -> dict:
|
|
12
|
-
return dict([item.strip('--').split('=') for item in ctx.args if item.startswith("--")])
|
|
13
|
+
return dict([item.strip('--').split('=') for item in ctx.args if item.startswith("--")]) # NOSONAR
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def import_from_string(import_str: Any) -> Any:
|
|
@@ -72,15 +73,26 @@ model_option = click.option('-m', '--model',
|
|
|
72
73
|
@click.group()
|
|
73
74
|
@click.pass_context
|
|
74
75
|
def cli(ctx):
|
|
75
|
-
|
|
76
|
+
ctx.ensure_object(dict)
|
|
76
77
|
|
|
77
78
|
|
|
78
79
|
@cli.command()
|
|
79
80
|
@model_option
|
|
80
81
|
@click.pass_context
|
|
81
|
-
def save(ctx, model):
|
|
82
|
+
def save(ctx, model: GraphModel):
|
|
83
|
+
file_path = Path(f"{model.name}.d3_graph.json")
|
|
84
|
+
|
|
85
|
+
if file_path.is_absolute():
|
|
86
|
+
raise click.ClickException("Please provide a relative file path for saving the graph.")
|
|
87
|
+
|
|
88
|
+
if file_path.parent != Path('.'):
|
|
89
|
+
raise click.ClickException("Saving to subdirectories is not supported. Please provide a file name only.")
|
|
90
|
+
|
|
91
|
+
if file_path.exists():
|
|
92
|
+
click.confirm(f"The file '{file_path}' already exists. Do you want to overwrite it?", abort=True)
|
|
93
|
+
|
|
82
94
|
kwargs = _get_kwargs(ctx)
|
|
83
|
-
with open(
|
|
95
|
+
with open(file_path, mode='w') as fp:
|
|
84
96
|
graph = builders.D3Builder(model, **kwargs).build()
|
|
85
97
|
json.dump(graph, fp=fp, default=str, **kwargs)
|
|
86
98
|
|
|
@@ -100,4 +112,4 @@ def server(ctx, model: GraphModel, port: int, browse: bool):
|
|
|
100
112
|
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚══════╝"""
|
|
101
113
|
click.echo(message)
|
|
102
114
|
schema = builders.GraphQLBuilder(model).build()
|
|
103
|
-
graphql(schema, port=port, browse=browse, **_get_kwargs(ctx))
|
|
115
|
+
graphql.server(schema, port=port, browse=browse, **_get_kwargs(ctx))
|
|
@@ -61,7 +61,8 @@ def color_hex(color: Union[str, Sequence[Union[float, int]]]) -> Union[str, Sequ
|
|
|
61
61
|
msg = "Input values should either be a float between 0 and 1 or an int between 0 and 255"
|
|
62
62
|
raise ValueError(msg)
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
r, g, b = rgb
|
|
65
|
+
return f'#{r:02x}{g:02x}{b:02x}'
|
|
65
66
|
|
|
66
67
|
else:
|
|
67
68
|
return color
|
|
@@ -28,7 +28,7 @@ def _openapi_schema(request: Request) -> ASGIApp:
|
|
|
28
28
|
"""
|
|
29
29
|
schema_data = {
|
|
30
30
|
'openapi': '3.0.0',
|
|
31
|
-
'info': {'title': 'Graphinate API', 'version': '0.
|
|
31
|
+
'info': {'title': 'Graphinate API', 'version': '0.10.0'},
|
|
32
32
|
'paths': {
|
|
33
33
|
'/graphql': {'get': {'responses': {200: {'description': 'GraphQL'}}}},
|
|
34
34
|
'/graphiql': {'get': {'responses': {200: {'description': 'GraphiQL UI.'}}}},
|
|
@@ -80,7 +80,7 @@ def _starlette_app(graphql_app: strawberry.asgi.GraphQL | None = None, port: int
|
|
|
80
80
|
app.add_route("/schema", route=_openapi_schema, include_in_schema=False)
|
|
81
81
|
app.add_route("/openapi.json", route=_openapi_schema, include_in_schema=False)
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
def redirect_to_viewer(request):
|
|
84
84
|
return RedirectResponse(url='/viewer')
|
|
85
85
|
|
|
86
86
|
app.add_route('/', redirect_to_viewer)
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
1
3
|
from starlette.responses import FileResponse
|
|
2
4
|
from starlette.routing import Route
|
|
3
5
|
|
|
4
6
|
from ..web import get_static_path
|
|
5
7
|
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
def favicon(request):
|
|
8
10
|
path = get_static_path('images/logo-128.png').absolute().as_posix()
|
|
9
11
|
return FileResponse(path)
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
@functools.cache
|
|
12
14
|
def favicon_route() -> Route:
|
|
13
15
|
return Route('/favicon.ico', endpoint=favicon, include_in_schema=False)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html lang="">
|
|
2
|
+
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<title>Graphinate Viewer</title>
|
|
5
5
|
<link rel="modulepreload"
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
import * as TweakpaneEssentialsPlugin
|
|
17
17
|
from 'https://cdn.jsdelivr.net/npm/@tweakpane/plugin-essentials@0.2.1/dist/tweakpane-plugin-essentials.min.js';
|
|
18
18
|
// Export it as a global variable
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
globalThis.Tweakpane = Tweakpane;
|
|
20
|
+
globalThis.TweakpaneEssentialsPlugin = TweakpaneEssentialsPlugin;
|
|
21
21
|
</script>
|
|
22
22
|
<script src="https://cdn.jsdelivr.net/npm/3d-force-graph@1.76.2/dist/3d-force-graph.min.js"
|
|
23
23
|
integrity="sha384-zSKZ615fogcuRjUjE+0YstAo3N/t7PzUvtQu5KGA1iqkmHXsTRst7zP+6573D+hK"
|
|
@@ -96,12 +96,12 @@
|
|
|
96
96
|
|
|
97
97
|
function lowerZIndexForHigherPanels(panel) {
|
|
98
98
|
const panels = document.querySelectorAll('.floating-panel');
|
|
99
|
-
|
|
100
|
-
const zIndex = parseInt(
|
|
101
|
-
if (zIndex > parseInt(panel.style.zIndex, 10)) {
|
|
99
|
+
for (const p of panels) {
|
|
100
|
+
const zIndex = Number.parseInt(globalThis.getComputedStyle(p).zIndex, 10);
|
|
101
|
+
if (zIndex > Number.parseInt(panel.style.zIndex, 10)) {
|
|
102
102
|
p.style.zIndex = zIndex - 1;
|
|
103
103
|
}
|
|
104
|
-
}
|
|
104
|
+
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// Function to bring the panel to the front
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
closeButton.addEventListener('click', () => {
|
|
171
171
|
lowerZIndexForHigherPanels(panel);
|
|
172
172
|
highestZIndex--;
|
|
173
|
-
|
|
173
|
+
panel.remove();
|
|
174
174
|
});
|
|
175
175
|
|
|
176
176
|
// Create the maximize toggle button
|
|
@@ -335,7 +335,7 @@
|
|
|
335
335
|
.linkDirectionalArrowLength(link => graphParams.linkDirectionalArrowLength)
|
|
336
336
|
.linkDirectionalArrowRelPos(link => graphParams.linkDirectionalArrowRelPos)
|
|
337
337
|
.linkDirectionalParticles(link => graphParams.linkDirectionalParticles)
|
|
338
|
-
.linkDirectionalParticleSpeed(link => graphParams.linkDirectionalParticleSpeed / 10000
|
|
338
|
+
.linkDirectionalParticleSpeed(link => graphParams.linkDirectionalParticleSpeed / 10000)
|
|
339
339
|
.linkDirectionalParticleWidth(link => graphParams.linkDirectionalParticleWidth)
|
|
340
340
|
.linkDirectionalParticleColor(link => graphParams.linkDirectionalParticleColor)
|
|
341
341
|
.cooldownTicks(100);
|
|
@@ -377,9 +377,9 @@
|
|
|
377
377
|
const linePos = line.geometry.getAttribute('position');
|
|
378
378
|
|
|
379
379
|
// calculate coordinate on the node's surface instead of center
|
|
380
|
-
linePos.set([startR / lineLen, 1 - endR / lineLen].
|
|
380
|
+
linePos.set([startR / lineLen, 1 - endR / lineLen].flatMap(t =>
|
|
381
381
|
['x', 'y', 'z'].map(dim => start[dim] + (end[dim] - start[dim]) * t)
|
|
382
|
-
)
|
|
382
|
+
));
|
|
383
383
|
linePos.needsUpdate = true;
|
|
384
384
|
return true;
|
|
385
385
|
});
|
|
@@ -392,33 +392,34 @@
|
|
|
392
392
|
|
|
393
393
|
function getVisible(gData) {
|
|
394
394
|
const visibleNodes = []
|
|
395
|
-
gData.nodes
|
|
395
|
+
for (const node of gData.nodes) {
|
|
396
396
|
if (node.type in nodeTypeVisibility && nodeTypeVisibility[node.type]) {
|
|
397
397
|
visibleNodes.push(node);
|
|
398
398
|
}
|
|
399
|
-
}
|
|
399
|
+
}
|
|
400
|
+
|
|
400
401
|
const visibleLinks = [];
|
|
401
|
-
gData.links
|
|
402
|
-
|
|
402
|
+
for (const link of gData.links) {
|
|
403
|
+
if (visibleNodes.find((node) => node.id === link.source.id) && visibleNodes.find((node) => node.id === link.target.id)) {
|
|
403
404
|
visibleLinks.push(link);
|
|
404
405
|
}
|
|
405
|
-
}
|
|
406
|
+
}
|
|
406
407
|
|
|
407
408
|
return {nodes: visibleNodes, links: visibleLinks};
|
|
408
409
|
}
|
|
409
410
|
|
|
410
411
|
function setAllNodeTypeVisibility(value) {
|
|
411
|
-
Object.keys(nodeTypeVisibility)
|
|
412
|
+
for (const key of Object.keys(nodeTypeVisibility)) {
|
|
412
413
|
nodeTypeVisibility[key] = value;
|
|
413
|
-
}
|
|
414
|
+
}
|
|
414
415
|
}
|
|
415
416
|
|
|
416
417
|
function updateNodeTypeColorMapping(nodes) {
|
|
417
|
-
|
|
418
|
+
for (const node of nodes) {
|
|
418
419
|
if (!nodeTypeColor[node.type]) {
|
|
419
420
|
nodeTypeColor[node.type] = node.color;
|
|
420
421
|
}
|
|
421
|
-
}
|
|
422
|
+
}
|
|
422
423
|
}
|
|
423
424
|
|
|
424
425
|
function refreshGraph(gData) {
|
|
@@ -451,17 +452,19 @@
|
|
|
451
452
|
label: 'Visibility',
|
|
452
453
|
}).on('click', (ev) => {
|
|
453
454
|
switch (ev.cell.title) {
|
|
454
|
-
case 'All On':
|
|
455
|
+
case 'All On': {
|
|
455
456
|
setAllNodeTypeVisibility(true);
|
|
456
457
|
updateGraph(gData);
|
|
457
458
|
pane.refresh();
|
|
458
459
|
break;
|
|
459
|
-
|
|
460
|
+
}
|
|
461
|
+
case 'All Off': {
|
|
460
462
|
setAllNodeTypeVisibility(false);
|
|
461
463
|
const visibleGData = getVisible(gData);
|
|
462
464
|
updateGraph(visibleGData);
|
|
463
465
|
pane.refresh();
|
|
464
466
|
break;
|
|
467
|
+
}
|
|
465
468
|
default:
|
|
466
469
|
console.log('Unknown action');
|
|
467
470
|
}
|
|
@@ -491,7 +494,7 @@
|
|
|
491
494
|
// Legend tab
|
|
492
495
|
const legendTab = tab.pages[0];
|
|
493
496
|
|
|
494
|
-
graph.nodeTypes
|
|
497
|
+
for (const nodeType of graph.nodeTypes) {
|
|
495
498
|
let info = `V: ${nodeType.count}`
|
|
496
499
|
const edgeType = graph.edgeTypes.find((t) => t.name === nodeType.name);
|
|
497
500
|
if (edgeType) {
|
|
@@ -502,11 +505,11 @@
|
|
|
502
505
|
|
|
503
506
|
nodeTypeFolder.addBinding(nodeTypeColor, nodeType.name, {label: 'Color'})
|
|
504
507
|
.on('change', (ev) => {
|
|
505
|
-
gData.nodes
|
|
508
|
+
for (const node of gData.nodes) {
|
|
506
509
|
if (node.type === nodeType.name) {
|
|
507
510
|
node.color = ev.value;
|
|
508
511
|
}
|
|
509
|
-
}
|
|
512
|
+
}
|
|
510
513
|
refreshGraph(gData);
|
|
511
514
|
});
|
|
512
515
|
|
|
@@ -515,8 +518,7 @@
|
|
|
515
518
|
.on('change', (ev) => {
|
|
516
519
|
refreshGraph(gData);
|
|
517
520
|
});
|
|
518
|
-
}
|
|
519
|
-
|
|
521
|
+
}
|
|
520
522
|
|
|
521
523
|
// Advanced tab
|
|
522
524
|
|
|
@@ -490,8 +490,9 @@ def test_populate_nodes_empty_generator(builder_with_graph):
|
|
|
490
490
|
node_type_absolute_id = ('parent', 'child')
|
|
491
491
|
|
|
492
492
|
def generator(**kwargs):
|
|
493
|
-
|
|
494
|
-
|
|
493
|
+
"""Dummy generator that yields nothing"""
|
|
494
|
+
return # NOSONAR
|
|
495
|
+
yield # NOSONAR
|
|
495
496
|
|
|
496
497
|
node_model = DummyNodeModel(generator=generator)
|
|
497
498
|
builder_with_graph.model._node_models = {node_type_absolute_id: [node_model]}
|
|
@@ -13,7 +13,7 @@ def test_get_static_path():
|
|
|
13
13
|
|
|
14
14
|
@pytest.mark.asyncio
|
|
15
15
|
async def test_favicon():
|
|
16
|
-
actual =
|
|
16
|
+
actual = graphinate.server.starlette.views.favicon(None)
|
|
17
17
|
|
|
18
18
|
assert isinstance(actual, FileResponse)
|
|
19
19
|
assert actual.media_type == 'image/png'
|
|
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.9.0 → graphinate-0.10.0}/examples/social/cache/13/dd/73ce25face7beb30b69b64feeb77.val
RENAMED
|
File without changes
|
{graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/21/9e/00846f323987ba16cfbe0127d8eb.val
RENAMED
|
File without changes
|
{graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/70/b6/2aefb0269adce7fedf877fa0d267.val
RENAMED
|
File without changes
|
{graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/87/f5/ec1739bc369e84c3fcb302bf532a.val
RENAMED
|
File without changes
|
{graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/ba/fe/3aca7b2c38abff60e7ce5eb486a8.val
RENAMED
|
File without changes
|
{graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/c7/9e/ce82b0288020b7152779df09bd73.val
RENAMED
|
File without changes
|
|
File without changes
|
{graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/d2/53/3b88f2fc162561cfdbbe9abc352a.val
RENAMED
|
File without changes
|
{graphinate-0.9.0 → graphinate-0.10.0}/examples/social/cache/e2/d5/5d079f200eabf9b625b0473f6fbe.val
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
|
|
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.9.0 → graphinate-0.10.0}/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
|