arguebuf 2.6.2__tar.gz → 2.7.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.
Files changed (86) hide show
  1. {arguebuf-2.6.2 → arguebuf-2.7.0}/PKG-INFO +20 -24
  2. arguebuf-2.7.0/pyproject.toml +55 -0
  3. arguebuf-2.7.0/setup.cfg +4 -0
  4. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/__init__.py +1 -1
  5. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/_dump_dict.py +1 -1
  6. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/scheme.py +11 -4
  7. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/render/_render_d2.py +1 -2
  8. arguebuf-2.7.0/src/arguebuf.egg-info/PKG-INFO +177 -0
  9. arguebuf-2.7.0/src/arguebuf.egg-info/SOURCES.txt +83 -0
  10. arguebuf-2.7.0/src/arguebuf.egg-info/dependency_links.txt +1 -0
  11. arguebuf-2.7.0/src/arguebuf.egg-info/entry_points.txt +2 -0
  12. arguebuf-2.7.0/src/arguebuf.egg-info/requires.txt +15 -0
  13. arguebuf-2.7.0/src/arguebuf.egg-info/top_level.txt +1 -0
  14. arguebuf-2.7.0/tests/test_create_graph.py +208 -0
  15. arguebuf-2.7.0/tests/test_load_casebase.py +116 -0
  16. arguebuf-2.7.0/tests/test_load_edge.py +259 -0
  17. arguebuf-2.7.0/tests/test_load_node.py +385 -0
  18. arguebuf-2.7.0/tests/test_render_graph.py +75 -0
  19. arguebuf-2.6.2/pyproject.toml +0 -56
  20. {arguebuf-2.6.2 → arguebuf-2.7.0}/LICENSE +0 -0
  21. {arguebuf-2.6.2 → arguebuf-2.7.0}/README.md +0 -0
  22. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/cli/__init__.py +0 -0
  23. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/cli/__main__.py +0 -0
  24. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/cli/app.py +0 -0
  25. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/cli/graph.py +0 -0
  26. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/cli/model.py +0 -0
  27. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/cli/server.py +0 -0
  28. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/cli/text.py +0 -0
  29. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/cli/translator.py +0 -0
  30. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dt.py +0 -0
  31. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/__init__.py +0 -0
  32. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/_config.py +0 -0
  33. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/_dump_aif.py +0 -0
  34. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/_dump_d2.py +0 -0
  35. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/_dump_graphviz.py +0 -0
  36. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/_dump_io.py +0 -0
  37. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/_dump_json.py +0 -0
  38. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/_dump_networkx.py +0 -0
  39. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/_dump_path.py +0 -0
  40. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/_dump_protobuf.py +0 -0
  41. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/dump/_dump_xaif.py +0 -0
  42. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/__init__.py +0 -0
  43. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_config.py +0 -0
  44. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_aif.py +0 -0
  45. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_aml.py +0 -0
  46. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_argdown.py +0 -0
  47. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_brat.py +0 -0
  48. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_casebase.py +0 -0
  49. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_dict.py +0 -0
  50. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_io.py +0 -0
  51. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_json.py +0 -0
  52. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_kialo.py +0 -0
  53. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_microtexts.py +0 -0
  54. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_ova.py +0 -0
  55. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_path.py +0 -0
  56. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_protobuf.py +0 -0
  57. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_sadface.py +0 -0
  58. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_text.py +0 -0
  59. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_load_xaif.py +0 -0
  60. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/load/_preprocess_aif.py +0 -0
  61. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/__init__.py +0 -0
  62. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/analyst.py +0 -0
  63. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/edge.py +0 -0
  64. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/graph.py +0 -0
  65. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/metadata.py +0 -0
  66. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/node.py +0 -0
  67. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/participant.py +0 -0
  68. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/reference.py +0 -0
  69. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/resource.py +0 -0
  70. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/typing.py +0 -0
  71. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/userdata.py +0 -0
  72. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/model/utils.py +0 -0
  73. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/py.typed +0 -0
  74. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/render/__init__.py +0 -0
  75. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/render/_render_graphviz.py +0 -0
  76. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/schemas/__init__.py +0 -0
  77. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/schemas/aif.py +0 -0
  78. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/schemas/argdown.py +0 -0
  79. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/schemas/d2.py +0 -0
  80. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/schemas/graphviz.py +0 -0
  81. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/schemas/microtexts.py +0 -0
  82. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/schemas/ova.py +0 -0
  83. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/schemas/sadface.py +0 -0
  84. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/schemas/xaif.py +0 -0
  85. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/traverse.py +0 -0
  86. {arguebuf-2.6.2 → arguebuf-2.7.0/src}/arguebuf/utils.py +0 -0
@@ -1,30 +1,27 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: arguebuf
3
- Version: 2.6.2
3
+ Version: 2.7.0
4
4
  Summary: Create and analyze argument graphs and serialize them via Protobuf
5
- Home-page: https://github.com/recap-utr/arguebuf-python
6
- License: MIT
7
- Author: Mirko Lenz
8
- Author-email: info@mirko-lenz.de
9
- Requires-Python: >=3.11,<4.0
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.11
13
- Classifier: Programming Language :: Python :: 3.12
14
- Classifier: Programming Language :: Python :: 3.13
15
- Provides-Extra: cli
16
- Provides-Extra: graphviz
17
- Requires-Dist: arg-services (>=1.6,<2.0)
18
- Requires-Dist: deepl (>=1.16,<2.0) ; extra == "cli"
19
- Requires-Dist: graphviz (>=0.20,<0.21)
20
- Requires-Dist: lxml (>=5.1,<6.0)
21
- Requires-Dist: networkx (>=3.2,<4.0)
22
- Requires-Dist: pendulum (>=3.0,<4.0)
23
- Requires-Dist: pygraphviz (>=1.12,<2.0) ; extra == "graphviz"
24
- Requires-Dist: typer[all] (>=0.9,<0.10) ; extra == "cli"
25
- Project-URL: Documentation, https://arguebuf.readthedocs.io/en/latest
5
+ Author-email: Mirko Lenz <mirko@mirkolenz.com>
26
6
  Project-URL: Repository, https://github.com/recap-utr/arguebuf-python
7
+ Project-URL: Documentation, https://arguebuf.readthedocs.io
8
+ Project-URL: Issues, https://github.com/recap-utr/arguebuf-python/issues
9
+ Project-URL: Changelog, https://github.com/recap-utr/arguebuf-python/releases
10
+ Requires-Python: >=3.11
27
11
  Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: arg-services<2,>=1
14
+ Requires-Dist: networkx<4,>=3
15
+ Requires-Dist: pendulum<4,>=3
16
+ Requires-Dist: graphviz<1,>=0.20
17
+ Requires-Dist: lxml<6,>=5
18
+ Provides-Extra: all
19
+ Requires-Dist: arguebuf[cli,graphviz]; extra == "all"
20
+ Provides-Extra: cli
21
+ Requires-Dist: typer<1,>=0.9; extra == "cli"
22
+ Requires-Dist: deepl<2,>=1; extra == "cli"
23
+ Provides-Extra: graphviz
24
+ Requires-Dist: pygraphviz<2,>=1; extra == "graphviz"
28
25
 
29
26
  # Arguebuf Python
30
27
 
@@ -178,4 +175,3 @@ ag.dump.file(graph, "./graph.json", ag.GraphFormat.AIF)
178
175
  ## Development
179
176
 
180
177
  To pull the testing data, make sure to install [DVC](https://dvc.org/doc/install) and run `dvc pull` in the root directory of the repository.
181
-
@@ -0,0 +1,55 @@
1
+ [project]
2
+ name = "arguebuf"
3
+ version = "2.7.0"
4
+ description = "Create and analyze argument graphs and serialize them via Protobuf"
5
+ authors = [{ name = "Mirko Lenz", email = "mirko@mirkolenz.com" }]
6
+ readme = "README.md"
7
+ requires-python = ">=3.11"
8
+ dependencies = [
9
+ "arg-services>=1,<2",
10
+ "networkx>=3,<4",
11
+ "pendulum>=3,<4",
12
+ "graphviz>=0.20,<1",
13
+ "lxml>=5,<6",
14
+ ]
15
+
16
+ [project.optional-dependencies]
17
+ all = ["arguebuf[cli,graphviz]"]
18
+ cli = ["typer>=0.9,<1", "deepl>=1,<2"]
19
+ graphviz = ["pygraphviz>=1,<2"]
20
+
21
+ [project.scripts]
22
+ arguebuf = "arguebuf.cli.app:cli"
23
+
24
+ [project.urls]
25
+ Repository = "https://github.com/recap-utr/arguebuf-python"
26
+ Documentation = "https://arguebuf.readthedocs.io"
27
+ Issues = "https://github.com/recap-utr/arguebuf-python/issues"
28
+ Changelog = "https://github.com/recap-utr/arguebuf-python/releases"
29
+
30
+ [dependency-groups]
31
+ test = [
32
+ "pytest>=8,<9",
33
+ "pytest-cov>=6,<7",
34
+ "pytest-xdist>=3,<4",
35
+ "deepdiff>=8,<9",
36
+ "lxml-stubs>=0.5,<1",
37
+ ]
38
+ docs = ["pdoc>=15,<16"]
39
+
40
+ [build-system]
41
+ requires = ["setuptools>=61"]
42
+ build-backend = "setuptools.build_meta"
43
+
44
+ [tool.pytest.ini_options]
45
+ addopts = "--numprocesses 4 --dist load --cov arguebuf --cov-report term-missing --doctest-modules --ignore arguebuf/cli --ignore data --ignore result"
46
+ doctest_optionflags = "NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL ELLIPSIS"
47
+
48
+ [tool.ruff]
49
+ target-version = "py311"
50
+
51
+ [tool.ruff.lint.pydocstyle]
52
+ convention = "google"
53
+
54
+ [tool.uv]
55
+ default-groups = ["test", "docs"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -1,5 +1,5 @@
1
1
  """
2
- .. include:: ../README.md
2
+ .. include:: ../../README.md
3
3
  """
4
4
 
5
5
  import logging
@@ -20,4 +20,4 @@ def dump_dict(graph: Graph, config: Config = DefaultConfig) -> dict[str, t.Any]:
20
20
  elif config.format == Format.XAIF:
21
21
  return t.cast(dict[str, t.Any], dump_xaif(graph))
22
22
 
23
- return MessageToDict(dump_protobuf(graph), including_default_value_fields=False) # type: ignore
23
+ return MessageToDict(dump_protobuf(graph))
@@ -1,4 +1,5 @@
1
1
  from enum import Enum
2
+ from typing import cast
2
3
 
3
4
  from arg_services.graph.v1 import graph_pb2
4
5
 
@@ -118,22 +119,28 @@ class Rephrase(str, Enum):
118
119
  Scheme = Support | Attack | Rephrase | Preference
119
120
 
120
121
  support2protobuf = {
121
- item: graph_pb2.Support.Value(f"SUPPORT_{item.name}") for item in Support
122
+ item: cast(graph_pb2.Support, graph_pb2.Support.Value(f"SUPPORT_{item.name}"))
123
+ for item in Support
122
124
  }
123
125
  protobuf2support = {value: key for key, value in support2protobuf.items()}
124
126
 
125
127
  attack2protobuf = {
126
- item: graph_pb2.Attack.Value(f"ATTACK_{item.name}") for item in Attack
128
+ item: cast(graph_pb2.Attack, graph_pb2.Attack.Value(f"ATTACK_{item.name}"))
129
+ for item in Attack
127
130
  }
128
131
  protobuf2attack = {value: key for key, value in attack2protobuf.items()}
129
132
 
130
133
  rephrase2protobuf = {
131
- item: graph_pb2.Rephrase.Value(f"REPHRASE_{item.name}") for item in Rephrase
134
+ item: cast(graph_pb2.Rephrase, graph_pb2.Rephrase.Value(f"REPHRASE_{item.name}"))
135
+ for item in Rephrase
132
136
  }
133
137
  protobuf2rephrase = {value: key for key, value in rephrase2protobuf.items()}
134
138
 
135
139
  preference2protobuf = {
136
- item: graph_pb2.Preference.Value(f"PREFERENCE_{item.name}") for item in Preference
140
+ item: cast(
141
+ graph_pb2.Preference, graph_pb2.Preference.Value(f"PREFERENCE_{item.name}")
142
+ )
143
+ for item in Preference
137
144
  }
138
145
  protobuf2preference = {value: key for key, value in preference2protobuf.items()}
139
146
 
@@ -20,8 +20,7 @@ def d2(
20
20
 
21
21
  if path.suffix.removeprefix(".") not in FORMATS:
22
22
  raise ValueError(
23
- "You need to provide a path with a file ending supported by d2:"
24
- f" {FORMATS}"
23
+ f"You need to provide a path with a file ending supported by d2: {FORMATS}"
25
24
  )
26
25
 
27
26
  # Create temporary file
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.2
2
+ Name: arguebuf
3
+ Version: 2.7.0
4
+ Summary: Create and analyze argument graphs and serialize them via Protobuf
5
+ Author-email: Mirko Lenz <mirko@mirkolenz.com>
6
+ Project-URL: Repository, https://github.com/recap-utr/arguebuf-python
7
+ Project-URL: Documentation, https://arguebuf.readthedocs.io
8
+ Project-URL: Issues, https://github.com/recap-utr/arguebuf-python/issues
9
+ Project-URL: Changelog, https://github.com/recap-utr/arguebuf-python/releases
10
+ Requires-Python: >=3.11
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: arg-services<2,>=1
14
+ Requires-Dist: networkx<4,>=3
15
+ Requires-Dist: pendulum<4,>=3
16
+ Requires-Dist: graphviz<1,>=0.20
17
+ Requires-Dist: lxml<6,>=5
18
+ Provides-Extra: all
19
+ Requires-Dist: arguebuf[cli,graphviz]; extra == "all"
20
+ Provides-Extra: cli
21
+ Requires-Dist: typer<1,>=0.9; extra == "cli"
22
+ Requires-Dist: deepl<2,>=1; extra == "cli"
23
+ Provides-Extra: graphviz
24
+ Requires-Dist: pygraphviz<2,>=1; extra == "graphviz"
25
+
26
+ # Arguebuf Python
27
+
28
+ Arguebuf is a format for serializing argument graphs and specified using Protobuf.
29
+ The complete specification and documentation is available at the [Buf Schema Registry](https://buf.build/recap/arg-services/docs/main:arg_services.graph.v1).
30
+ While Protobuf automatically generates native code for all major programming languages (including Python), we created a custom implementation that provides some additional benefits, including:
31
+
32
+ - The ability to import existing formats like [AIF](http://www.argumentinterchange.org), [SADFace](https://github.com/Open-Argumentation/SADFace), and a few others.
33
+ - Export of Arguebuf graphs to AIF, [NetworkX](https://networkx.org), and [Graphviz](https://graphviz.org).
34
+ - Integration with the popular NLP library [spaCy](http://spacy.io).
35
+ - Various helper methods to programmatically manipulate/create argument graphs.
36
+ - More pythonic interfaces than the regular code generated by `protoc`.
37
+
38
+ You can easily install the library from [PyPI](https://pypi.org/project/arguebuf/) using pip. The documentation is hosted on [ReadTheDocs](https://arguebuf.readthedocs.io/en/latest/)
39
+
40
+ ## Command Line Interface (CLI)
41
+
42
+ We also offer some tools to simplify dealing with structured argument graphs.
43
+ Among others, it is possible to convert graphs between different formats, translate them, and render images using graphviz.
44
+ To use it, install the `cli` extras when installing the package.
45
+ When using `pip`, this can be accomplished with
46
+
47
+ `pip install arguebuf[cli]`
48
+
49
+ Afterwards, you can execute it by calling `arguebuf`, for example:
50
+
51
+ `arguebuf --help`
52
+
53
+ Alternatively, you can use the Docker image available at `ghcr.io/recap-utr/arguebuf-python`.
54
+ To use it, mount a folder to the container and pass the options as the command.
55
+
56
+ `docker run --rm -it -v $(pwd)/data:/data ghcr.io/recap-utr/arguebuf-python:latest --help`
57
+
58
+ If you use the `nix` package manager, you can run it as follows:
59
+
60
+ `nix run github:recap-utr/arguebuf-python -- --help`
61
+
62
+ ## Theoretical Foundations
63
+
64
+ An argument graph is way to represent _structured_ argumentation.
65
+ What sets it apart from _unstructured_ argumentation (e.g., in newspaper articles) is that only the essential/argumentative parts of a text art part of this representation.
66
+ These _units_ of argumentation are also called ADUs (argumentative discourse units).
67
+ The length of ADUs can differ dramatically (depending on various factors like the context), meaning they might contain only a few words, a sentence, or even a whole paragraph.
68
+ The structure of an argument is then represented through _relations_ between these units.
69
+ For this purpose, they can be further subdivided into _claims_ and _premises_:
70
+ A claim is a statement that is supported or attacked by one or multiple premises.
71
+ At the same time, a claim may also function as a premise for another claim, making it possible to construct even complex _argument graphs_.
72
+ One of the claims is called _major claim_ and represents the overall claim of the whole argument.
73
+ In many cases (but not all), this major claim is located right at the top of the graph
74
+ Here is a rather simple example.
75
+
76
+ ![Exemplary argument graph](./assets/programmatic.png)
77
+
78
+ Claims, premises and the major claim are represented as _atom nodes_ while relations between them are represented by _scheme nodes_.
79
+ The set of nodes $V = A \cup S$ is composed of the set of atom nodes $A$ and the set of scheme nodes $S$.
80
+ The supporting or attacking relations are encoded in a set of edges $E \subseteq V \times V \setminus A \times A$.
81
+ Edges can be drawn between any type of nodes except for two atom nodes.
82
+ For instance, it is possible to connect two scheme nodes to support or attack the _inference_ between two ADUs.
83
+ Based on this, we define an argument graph $G$ as the triple $G = ( V , E , M )$.
84
+
85
+ ## User Guide
86
+
87
+ When importing this library, we recommend using an abbreviation such as `ag` (for _argument graph_).
88
+
89
+ ```python
90
+ import arguebuf as ag
91
+ ```
92
+
93
+ In the following, we will introduce the most important features of `arguebuf`.
94
+ For more details including examples, check out our API documentation.
95
+
96
+ ### Importing an Existing Graph
97
+
98
+ We support multiple established formats to represent argument graphs: `AIF`, `OVA`, `SADFace`, `ArgDown`, `BRAT`, and `Kialo`.
99
+ Given an input file, the library can automatically determine the correct format and convert it to a representation in the `arguebuf` format.
100
+ One can either pass a string pointing to the file or a `pathlib.Path` object.
101
+
102
+ ```python
103
+ graph = ag.load.file("graph.json")
104
+ ```
105
+
106
+ It is also possible to load multiple graphs within a folder.
107
+ Here, you need to pass the folder along with a [glob pattern](https://docs.python.org/3/library/fnmatch.html#module-fnmatch) for selecting the argument graphs.
108
+ This also enables to recursively load all argument graphs from a common parent directory.
109
+
110
+ ```python
111
+ graphs = ag.load.folder("./data", "**/*.json")
112
+ ```
113
+
114
+ Since atom nodes contain textual information that may need to be analyzed using NLP techniques, we support passing a custom `nlp` function to these loader methods.
115
+ This also makes it really easy to integrate the popular [`spacy` library](http://spacy.io) with `arguebuf` as all texts of atom nodes are automatically converted to a spacy `Doc`.
116
+
117
+ ```python
118
+ import spacy
119
+ nlp = spacy.load("en_core_web_lg")
120
+ graph = ag.load.file("graph.json", nlp=nlp)
121
+ ```
122
+
123
+ ### Programmatically Create a New Graph
124
+
125
+ Instead of importing an existing graph, you can also create a new one using an object-oriented API using our library.
126
+ To illustrate this, we generate a graph with two premises that are connected to a major claim.
127
+ **Please note:** In case edges with nodes not yet contained in the graph are added, the respective nodes are added automatically.
128
+
129
+ ```python
130
+ graph = ag.Graph()
131
+
132
+ premise1 = ag.AtomNode("Text of premise 1")
133
+ premise2 = ag.AtomNode("Text of premise 2")
134
+ claim = ag.AtomNode("Text of claim")
135
+
136
+ scheme1 = ag.SchemeNode(ag.Support.DEFAULT)
137
+ scheme2 = ag.SchemeNode(ag.Attack.DEFAULT)
138
+
139
+ graph.add_edge(ag.Edge(premise1, scheme1))
140
+ graph.add_edge(ag.Edge(scheme1, claim))
141
+ graph.add_edge(ag.Edge(premise2, scheme2))
142
+ graph.add_edge(ag.Edge(scheme2, claim))
143
+
144
+ graph.major_claim = claim
145
+ gv_graph = ag.dump.graphviz(graph)
146
+ ag.render.graphviz(gv_graph, "./assets/programmatic.png")
147
+ ```
148
+
149
+ With this code, we get the following output
150
+
151
+ ![Output of programmatic graph creation](./assets/programmatic.png)
152
+
153
+ ### Exporting Argument Graphs
154
+
155
+ We support different output formats and integration with other libraries to ease the use of argument graphs.
156
+ Have a look at the following code snippet to get an overview of the possibilities
157
+
158
+ ```python
159
+ # Export to graphviz DOT format
160
+ dot = ag.dump.graphviz(graph)
161
+
162
+ # Export an image of this dot source to a file
163
+ ag.render.graphviz(dot, "./graph.pdf")
164
+
165
+ # Convert to NetworkX graph
166
+ nx = ag.dump.networkx(graph)
167
+
168
+ # Save the graph as Arguebuf
169
+ ag.dump.file(graph, "./graph.json")
170
+
171
+ # Save the graph as AIF
172
+ ag.dump.file(graph, "./graph.json", ag.GraphFormat.AIF)
173
+ ```
174
+
175
+ ## Development
176
+
177
+ To pull the testing data, make sure to install [DVC](https://dvc.org/doc/install) and run `dvc pull` in the root directory of the repository.
@@ -0,0 +1,83 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/arguebuf/__init__.py
5
+ src/arguebuf/dt.py
6
+ src/arguebuf/py.typed
7
+ src/arguebuf/traverse.py
8
+ src/arguebuf/utils.py
9
+ src/arguebuf.egg-info/PKG-INFO
10
+ src/arguebuf.egg-info/SOURCES.txt
11
+ src/arguebuf.egg-info/dependency_links.txt
12
+ src/arguebuf.egg-info/entry_points.txt
13
+ src/arguebuf.egg-info/requires.txt
14
+ src/arguebuf.egg-info/top_level.txt
15
+ src/arguebuf/cli/__init__.py
16
+ src/arguebuf/cli/__main__.py
17
+ src/arguebuf/cli/app.py
18
+ src/arguebuf/cli/graph.py
19
+ src/arguebuf/cli/model.py
20
+ src/arguebuf/cli/server.py
21
+ src/arguebuf/cli/text.py
22
+ src/arguebuf/cli/translator.py
23
+ src/arguebuf/dump/__init__.py
24
+ src/arguebuf/dump/_config.py
25
+ src/arguebuf/dump/_dump_aif.py
26
+ src/arguebuf/dump/_dump_d2.py
27
+ src/arguebuf/dump/_dump_dict.py
28
+ src/arguebuf/dump/_dump_graphviz.py
29
+ src/arguebuf/dump/_dump_io.py
30
+ src/arguebuf/dump/_dump_json.py
31
+ src/arguebuf/dump/_dump_networkx.py
32
+ src/arguebuf/dump/_dump_path.py
33
+ src/arguebuf/dump/_dump_protobuf.py
34
+ src/arguebuf/dump/_dump_xaif.py
35
+ src/arguebuf/load/__init__.py
36
+ src/arguebuf/load/_config.py
37
+ src/arguebuf/load/_load_aif.py
38
+ src/arguebuf/load/_load_aml.py
39
+ src/arguebuf/load/_load_argdown.py
40
+ src/arguebuf/load/_load_brat.py
41
+ src/arguebuf/load/_load_casebase.py
42
+ src/arguebuf/load/_load_dict.py
43
+ src/arguebuf/load/_load_io.py
44
+ src/arguebuf/load/_load_json.py
45
+ src/arguebuf/load/_load_kialo.py
46
+ src/arguebuf/load/_load_microtexts.py
47
+ src/arguebuf/load/_load_ova.py
48
+ src/arguebuf/load/_load_path.py
49
+ src/arguebuf/load/_load_protobuf.py
50
+ src/arguebuf/load/_load_sadface.py
51
+ src/arguebuf/load/_load_text.py
52
+ src/arguebuf/load/_load_xaif.py
53
+ src/arguebuf/load/_preprocess_aif.py
54
+ src/arguebuf/model/__init__.py
55
+ src/arguebuf/model/analyst.py
56
+ src/arguebuf/model/edge.py
57
+ src/arguebuf/model/graph.py
58
+ src/arguebuf/model/metadata.py
59
+ src/arguebuf/model/node.py
60
+ src/arguebuf/model/participant.py
61
+ src/arguebuf/model/reference.py
62
+ src/arguebuf/model/resource.py
63
+ src/arguebuf/model/scheme.py
64
+ src/arguebuf/model/typing.py
65
+ src/arguebuf/model/userdata.py
66
+ src/arguebuf/model/utils.py
67
+ src/arguebuf/render/__init__.py
68
+ src/arguebuf/render/_render_d2.py
69
+ src/arguebuf/render/_render_graphviz.py
70
+ src/arguebuf/schemas/__init__.py
71
+ src/arguebuf/schemas/aif.py
72
+ src/arguebuf/schemas/argdown.py
73
+ src/arguebuf/schemas/d2.py
74
+ src/arguebuf/schemas/graphviz.py
75
+ src/arguebuf/schemas/microtexts.py
76
+ src/arguebuf/schemas/ova.py
77
+ src/arguebuf/schemas/sadface.py
78
+ src/arguebuf/schemas/xaif.py
79
+ tests/test_create_graph.py
80
+ tests/test_load_casebase.py
81
+ tests/test_load_edge.py
82
+ tests/test_load_node.py
83
+ tests/test_render_graph.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ arguebuf = arguebuf.cli.app:cli
@@ -0,0 +1,15 @@
1
+ arg-services<2,>=1
2
+ networkx<4,>=3
3
+ pendulum<4,>=3
4
+ graphviz<1,>=0.20
5
+ lxml<6,>=5
6
+
7
+ [all]
8
+ arguebuf[cli,graphviz]
9
+
10
+ [cli]
11
+ typer<1,>=0.9
12
+ deepl<2,>=1
13
+
14
+ [graphviz]
15
+ pygraphviz<2,>=1
@@ -0,0 +1 @@
1
+ arguebuf
@@ -0,0 +1,208 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+ from arg_services.graph.v1 import graph_pb2
5
+
6
+ import arguebuf as ag
7
+
8
+
9
+ def generate_graph() -> ag.Graph:
10
+ g = ag.Graph()
11
+ a1 = ag.AtomNode("", id="a1")
12
+ a2 = ag.AtomNode("", id="a2")
13
+ a3 = ag.AtomNode("", id="a3")
14
+ a4 = ag.AtomNode("", id="a4")
15
+ s1 = ag.SchemeNode(id="s1")
16
+ s2 = ag.SchemeNode(id="s2")
17
+
18
+ g.add_edge(ag.Edge(a1, s1))
19
+ g.add_edge(ag.Edge(s1, s2))
20
+ g.add_edge(ag.Edge(s2, a4))
21
+ g.add_edge(ag.Edge(a2, s1))
22
+ g.add_edge(ag.Edge(a3, s2))
23
+
24
+ return g
25
+
26
+
27
+ def test_strip_scheme_nodes():
28
+ g = generate_graph()
29
+ g.strip_scheme_nodes()
30
+
31
+ assert len(g.nodes) == 4
32
+ assert len(g.scheme_nodes) == 0
33
+ assert len(g.edges) == 3
34
+
35
+
36
+ def test_remove_branch():
37
+ g = generate_graph()
38
+ g.remove_branch("s2")
39
+
40
+ assert len(g.nodes) == 1
41
+ assert len(g.edges) == 0
42
+
43
+
44
+ def test_sibling_nodes():
45
+ g = ag.Graph()
46
+
47
+ a1 = ag.AtomNode("", id="a1")
48
+ a2 = ag.AtomNode("", id="a2")
49
+ a3 = ag.AtomNode("", id="a3")
50
+ a4 = ag.AtomNode("", id="a4")
51
+ a5 = ag.AtomNode("", id="a5")
52
+ a6 = ag.AtomNode("", id="a6")
53
+ s1 = ag.SchemeNode(id="s1")
54
+ s2 = ag.SchemeNode(id="s2")
55
+ s3 = ag.SchemeNode(id="s3")
56
+ s4 = ag.SchemeNode(id="s4")
57
+ s5 = ag.SchemeNode(id="s5")
58
+
59
+ g.add_edge(ag.Edge(a1, s1))
60
+ g.add_edge(ag.Edge(a2, s2))
61
+ g.add_edge(ag.Edge(s1, a3))
62
+ g.add_edge(ag.Edge(s2, a3))
63
+ g.add_edge(ag.Edge(a3, s3))
64
+ g.add_edge(ag.Edge(s3, a4))
65
+ g.add_edge(ag.Edge(s4, a4))
66
+ g.add_edge(ag.Edge(a5, s4))
67
+ g.add_edge(ag.Edge(s5, a5))
68
+ g.add_edge(ag.Edge(a6, s5))
69
+
70
+ siblings = g.sibling_node_distances(a2)
71
+
72
+ assert len(siblings) == 3
73
+ assert siblings[a1] == 2
74
+ assert siblings[a6] == 4
75
+
76
+
77
+ def test_create_graph(tmp_path: Path):
78
+ g = ag.Graph("Test")
79
+
80
+ p1 = ag.Participant(
81
+ name="Participant 1",
82
+ username="Parti 1",
83
+ email="Parti1@gmail.com",
84
+ url="thesenuts",
85
+ location="Trier",
86
+ description="Hallo Welt!",
87
+ )
88
+ g.add_participant(p1)
89
+ r1 = ag.Resource("Resource234")
90
+ g.add_resource(r1)
91
+ n1 = ag.AtomNode(
92
+ text="Node 1",
93
+ reference=ag.Reference(r1, 0, "Resource234"),
94
+ participant=p1,
95
+ )
96
+ n2 = ag.SchemeNode(ag.Support.DEFAULT)
97
+ n3 = ag.AtomNode("Node 3")
98
+ n4 = ag.SchemeNode(ag.Support.DEFAULT)
99
+ n5 = ag.AtomNode("Node 5")
100
+ e12 = ag.Edge(n1, n2)
101
+ e23 = ag.Edge(n2, n3)
102
+ e34 = ag.Edge(n3, n4)
103
+ e45 = ag.Edge(n4, n5)
104
+
105
+ g.add_node(n1)
106
+ g.add_edge(e12)
107
+ g.add_edge(e23)
108
+ g.add_edge(e34)
109
+ g.add_edge(e45)
110
+
111
+ assert e12.source == n1
112
+ assert e12.target == n2
113
+ assert e23.source == n2
114
+ assert e23.target == n3
115
+ assert e34.source == n3
116
+ assert e34.target == n4
117
+ assert e45.source == n4
118
+ assert e45.target == n5
119
+
120
+ assert len(g.incoming_nodes(n1)) == 0
121
+ assert len(g.incoming_edges(n1)) == 0
122
+ assert len(g.incoming_nodes(n3)) == 1
123
+ assert len(g.incoming_edges(n3)) == 1
124
+ assert len(g.incoming_edges(n5)) == 1
125
+ assert len(g.incoming_edges(n5)) == 1
126
+
127
+ assert len(g.outgoing_nodes(n1)) == 1
128
+ assert len(g.outgoing_edges(n1)) == 1
129
+ assert len(g.outgoing_nodes(n3)) == 1
130
+ assert len(g.outgoing_edges(n3)) == 1
131
+ assert len(g.outgoing_nodes(n5)) == 0
132
+ assert len(g.outgoing_edges(n5)) == 0
133
+
134
+ assert g.root_node == n5
135
+ assert g.major_claim is None
136
+ g.major_claim = n5
137
+ assert g.major_claim == n5
138
+
139
+ assert set(g.nodes.values()) == {n1, n2, n3, n4, n5}
140
+
141
+ assert ag.traverse.node_distance(n1, n1, g.outgoing_nodes) == 0
142
+ assert ag.traverse.node_distance(n1, n2, g.outgoing_nodes) == 1
143
+ assert ag.traverse.node_distance(n1, n3, g.outgoing_nodes) == 2
144
+ assert ag.traverse.node_distance(n1, n4, g.outgoing_nodes) == 3
145
+ assert ag.traverse.node_distance(n1, n5, g.outgoing_nodes) == 4
146
+ assert ag.traverse.node_distance(n2, n2, g.outgoing_nodes) == 0
147
+ assert ag.traverse.node_distance(n3, n3, g.outgoing_nodes) == 0
148
+ assert ag.traverse.node_distance(n4, n4, g.outgoing_nodes) == 0
149
+ assert ag.traverse.node_distance(n5, n5, g.outgoing_nodes) == 0
150
+ assert g.scheme_between(n1, n3) == n2
151
+ assert g.outgoing_atom_nodes(n1) == {n3}
152
+
153
+ assert len(g.resources) == 1
154
+ r2 = ag.Resource(text="Resource", title="Resourca", source="Wikipedia")
155
+ assert r2.plain_text == "Resource"
156
+ g.add_resource(r2)
157
+ g.add_resource(ag.Resource("Resource2"))
158
+ assert len(g.resources) == 3
159
+ assert len(g.atom_nodes) > 0
160
+ assert len(g.scheme_nodes) > 0
161
+ g.incoming_nodes(n3)
162
+ g.incoming_atom_nodes(n2)
163
+ g.outgoing_nodes(n3)
164
+ g.outgoing_atom_nodes(n2)
165
+ g.incoming_edges(n3)
166
+ g.outgoing_edges(n3)
167
+
168
+ g.remove_node(n4)
169
+ g.add_node(n4)
170
+
171
+ # e4 = "Hallo ich bin keine Kante"
172
+ # g.add_edge(e4)
173
+
174
+ with pytest.raises(ValueError):
175
+ g.add_edge(e12)
176
+
177
+ # r10 = "Hallo ich bin keine Quelle"
178
+ # g.add_resource(r10)
179
+
180
+ with pytest.raises(ValueError):
181
+ g.add_resource(r2)
182
+
183
+ # g.remove_resource(r10)
184
+ g.remove_resource(r2)
185
+
186
+ with pytest.raises(ValueError):
187
+ g.remove_resource(r2)
188
+
189
+ # p3 = "Hallo ich bin kein Teilnehmer"
190
+ # g.add_participant(p3)
191
+
192
+ with pytest.raises(ValueError):
193
+ g.add_participant(p1)
194
+ # g.remove_participant(p3)
195
+ g.remove_participant(p1)
196
+
197
+ with pytest.raises(ValueError):
198
+ g.remove_participant(p1)
199
+
200
+ assert len(g.participants) == 0
201
+
202
+ assert isinstance(ag.dump.protobuf(g), graph_pb2.Graph)
203
+
204
+ gc = ag.copy(g)
205
+
206
+ assert len(g.nodes) == len(gc.nodes)
207
+
208
+ ag.render.graphviz(ag.dump.graphviz(g), tmp_path / "test.pdf")