anxwritter 1.0.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.
- anxwritter-1.0.0/.gitignore +42 -0
- anxwritter-1.0.0/LICENSE +21 -0
- anxwritter-1.0.0/NOTICE.md +37 -0
- anxwritter-1.0.0/PKG-INFO +163 -0
- anxwritter-1.0.0/README.md +129 -0
- anxwritter-1.0.0/anxwritter/__init__.py +93 -0
- anxwritter-1.0.0/anxwritter/_i2_interop.py +102 -0
- anxwritter-1.0.0/anxwritter/builder.py +2573 -0
- anxwritter-1.0.0/anxwritter/chart.py +1810 -0
- anxwritter-1.0.0/anxwritter/cli.py +357 -0
- anxwritter-1.0.0/anxwritter/colors.py +118 -0
- anxwritter-1.0.0/anxwritter/entities.py +318 -0
- anxwritter-1.0.0/anxwritter/enums.py +212 -0
- anxwritter-1.0.0/anxwritter/errors.py +91 -0
- anxwritter-1.0.0/anxwritter/models.py +731 -0
- anxwritter-1.0.0/anxwritter/resolved.py +135 -0
- anxwritter-1.0.0/anxwritter/semantic.py +216 -0
- anxwritter-1.0.0/anxwritter/timezones.json +124 -0
- anxwritter-1.0.0/anxwritter/timing.py +53 -0
- anxwritter-1.0.0/anxwritter/transforms.py +447 -0
- anxwritter-1.0.0/anxwritter/utils.py +125 -0
- anxwritter-1.0.0/anxwritter/validation.py +1248 -0
- anxwritter-1.0.0/docs/getting-started.md +83 -0
- anxwritter-1.0.0/docs/guide/analyst-guide.md +1151 -0
- anxwritter-1.0.0/docs/guide/yaml-json-guide.md +1111 -0
- anxwritter-1.0.0/docs/reference/attributes.md +219 -0
- anxwritter-1.0.0/docs/reference/cards-and-grades.md +203 -0
- anxwritter-1.0.0/docs/reference/cli.md +142 -0
- anxwritter-1.0.0/docs/reference/constructors.md +260 -0
- anxwritter-1.0.0/docs/reference/datetime-and-timezones.md +355 -0
- anxwritter-1.0.0/docs/reference/entities.md +196 -0
- anxwritter-1.0.0/docs/reference/links.md +196 -0
- anxwritter-1.0.0/docs/reference/semantic-types.md +224 -0
- anxwritter-1.0.0/docs/reference/settings.md +302 -0
- anxwritter-1.0.0/docs/reference/validation.md +98 -0
- anxwritter-1.0.0/docs/reference/visual-styling.md +373 -0
- anxwritter-1.0.0/examples/config.json +149 -0
- anxwritter-1.0.0/examples/config.yaml +296 -0
- anxwritter-1.0.0/examples/data.json +144 -0
- anxwritter-1.0.0/examples/data.yaml +131 -0
- anxwritter-1.0.0/examples/investigation_chart.py +236 -0
- anxwritter-1.0.0/pyproject.toml +108 -0
- anxwritter-1.0.0/tests/__init__.py +0 -0
- anxwritter-1.0.0/tests/fixtures/__init__.py +0 -0
- anxwritter-1.0.0/tests/fixtures/equivalence_specs.py +559 -0
- anxwritter-1.0.0/tests/fixtures/invalid_specs.py +233 -0
- anxwritter-1.0.0/tests/fixtures/strategies.py +223 -0
- anxwritter-1.0.0/tests/helpers/__init__.py +0 -0
- anxwritter-1.0.0/tests/helpers/chart_equivalence.py +232 -0
- anxwritter-1.0.0/tests/test_attribute_class_type.py +267 -0
- anxwritter-1.0.0/tests/test_audit_gaps.py +819 -0
- anxwritter-1.0.0/tests/test_chart_build.py +1241 -0
- anxwritter-1.0.0/tests/test_cli.py +255 -0
- anxwritter-1.0.0/tests/test_colors.py +95 -0
- anxwritter-1.0.0/tests/test_config.py +555 -0
- anxwritter-1.0.0/tests/test_config_roundtrip.py +277 -0
- anxwritter-1.0.0/tests/test_constructors.py +1121 -0
- anxwritter-1.0.0/tests/test_dataclass_fields.py +418 -0
- anxwritter-1.0.0/tests/test_datetime_format.py +256 -0
- anxwritter-1.0.0/tests/test_determinism.py +128 -0
- anxwritter-1.0.0/tests/test_entities.py +259 -0
- anxwritter-1.0.0/tests/test_enum_aliases.py +222 -0
- anxwritter-1.0.0/tests/test_enums.py +51 -0
- anxwritter-1.0.0/tests/test_error_completeness.py +151 -0
- anxwritter-1.0.0/tests/test_examples_smoke.py +60 -0
- anxwritter-1.0.0/tests/test_geo_map.py +512 -0
- anxwritter-1.0.0/tests/test_input_equivalence.py +564 -0
- anxwritter-1.0.0/tests/test_perf_scale.py +115 -0
- anxwritter-1.0.0/tests/test_properties.py +112 -0
- anxwritter-1.0.0/tests/test_settings.py +60 -0
- anxwritter-1.0.0/tests/test_utils.py +149 -0
- anxwritter-1.0.0/tests/test_validation.py +388 -0
- anxwritter-1.0.0/tests/test_validation_equivalence.py +138 -0
- anxwritter-1.0.0/tests/test_yaml_user_input.py +561 -0
- anxwritter-1.0.0/uv.lock +286 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.egg-info/
|
|
6
|
+
.pytest_cache/
|
|
7
|
+
.mypy_cache/
|
|
8
|
+
.ruff_cache/
|
|
9
|
+
.coverage
|
|
10
|
+
htmlcov/
|
|
11
|
+
.hypothesis/
|
|
12
|
+
|
|
13
|
+
# Build artifacts
|
|
14
|
+
/dist/
|
|
15
|
+
/build/
|
|
16
|
+
/.build/
|
|
17
|
+
|
|
18
|
+
# Environments
|
|
19
|
+
.venv/
|
|
20
|
+
.venv-*/
|
|
21
|
+
|
|
22
|
+
# IDE / editor
|
|
23
|
+
.idea/
|
|
24
|
+
.vscode/
|
|
25
|
+
.claude/
|
|
26
|
+
.run/
|
|
27
|
+
*.swp
|
|
28
|
+
*.swo
|
|
29
|
+
*~
|
|
30
|
+
|
|
31
|
+
# OS
|
|
32
|
+
.DS_Store
|
|
33
|
+
Thumbs.db
|
|
34
|
+
|
|
35
|
+
# Sandbox
|
|
36
|
+
.sbx/
|
|
37
|
+
|
|
38
|
+
# Scratch / output
|
|
39
|
+
/output/
|
|
40
|
+
*.log
|
|
41
|
+
*.orig
|
|
42
|
+
*.rej
|
anxwritter-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 gustavo-gkmi (https://github.com/gustavo-gkmi)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# NOTICE
|
|
2
|
+
|
|
3
|
+
anxwritter is an independent, MIT-licensed Python library for writing
|
|
4
|
+
i2 Analyst's Notebook Exchange (`.anx`) files. It is not affiliated
|
|
5
|
+
with, endorsed by, or sponsored by the makers of i2 Analyst's Notebook
|
|
6
|
+
(past or present: IBM, i2 Group Limited, i2 Limited, N.Harris Computer
|
|
7
|
+
Corporation, Harris Computer Corporation, Constellation Software Inc.)
|
|
8
|
+
or by Esri, Microsoft, or Google.
|
|
9
|
+
|
|
10
|
+
## Format identifiers reproduced for interoperability
|
|
11
|
+
|
|
12
|
+
To produce structurally valid `.anx` files, this library embeds nine
|
|
13
|
+
functional identifiers required by ANB:
|
|
14
|
+
|
|
15
|
+
- 6 abstract-root GUIDs required by the `lcx:LibraryCatalogue` schema
|
|
16
|
+
- 3 geo-map property GUIDs (Latitude, Longitude, Grid Reference)
|
|
17
|
+
required by ANB's Esri Maps subsystem
|
|
18
|
+
- the LCX XML namespace string `http://www.i2group.com/Schemas/2001-12-07/LCXSchema`
|
|
19
|
+
|
|
20
|
+
These are functional format anchors, not creative content, reproduced
|
|
21
|
+
solely for interoperability.
|
|
22
|
+
|
|
23
|
+
## What is *not* included
|
|
24
|
+
|
|
25
|
+
anxwritter ships **none** of the following from i2/N.Harris/IBM ANB:
|
|
26
|
+
XML schemas (XSDs/XSLs), icon catalogues or image files,
|
|
27
|
+
standard-library semantic-type definitions, palette definitions,
|
|
28
|
+
compiled binaries, DLLs, `.ant` files, help text, or error messages.
|
|
29
|
+
|
|
30
|
+
All such content must be supplied by the user via their own `entity_types`,
|
|
31
|
+
`link_types`, `attribute_classes`, and `semantic_*` declarations.
|
|
32
|
+
|
|
33
|
+
## Trademarks
|
|
34
|
+
|
|
35
|
+
"i2", "i2 Analyst's Notebook", "ANB", "Esri", "Esri Maps",
|
|
36
|
+
"Microsoft", and "Google" are trademarks of their respective owners.
|
|
37
|
+
Referenced for nominative use only.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anxwritter
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Write i2 Analyst's Notebook Exchange (.anx) files from Python, JSON, or YAML
|
|
5
|
+
Project-URL: Repository, https://github.com/gustavo-gkmi/anxwritter
|
|
6
|
+
Project-URL: Issues, https://github.com/gustavo-gkmi/anxwritter/issues
|
|
7
|
+
Author: gustavo-gkmi
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
License-File: NOTICE.md
|
|
11
|
+
Keywords: analyst-notebook,anb,anx,chart,i2,intelligence,law-enforcement,link-analysis,xml
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Other Audience
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: loguru>=0.7
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: hypothesis>=6.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
31
|
+
Provides-Extra: yaml
|
|
32
|
+
Requires-Dist: pyyaml>=6.0; extra == 'yaml'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# anxwritter
|
|
36
|
+
|
|
37
|
+
Write i2 Analyst's Notebook Exchange (`.anx`) files from Python, JSON, or YAML.
|
|
38
|
+
|
|
39
|
+
Output files open directly in i2 ANB 9+ via **File > Open** — no import wizard, no intermediate CSV. The `.anx` file embeds the entire chart as XML.
|
|
40
|
+
|
|
41
|
+
> This project is independent and not affiliated with, endorsed by, or sponsored by the makers of i2 Analyst's Notebook — past or present — including IBM, i2 Group Limited, i2 Limited, N.Harris Computer Corporation, Harris Computer Corporation, or Constellation Software Inc. — nor by Esri, Microsoft, or Google. "i2", "i2 Analyst's Notebook", "ANB", "Esri", "Esri Maps", "Microsoft", and "Google" are trademarks of their respective owners and are referenced here solely to identify the file format and the tools this library interoperates with (nominative use).
|
|
42
|
+
>
|
|
43
|
+
> To produce structurally valid `.anx` files, this library embeds nine format identifiers: six abstract-root GUIDs required by the `lcx:LibraryCatalogue` schema and three geo-map property GUIDs required by ANB's Esri Maps subsystem. These are functional tokens (format anchors), not creative content, reproduced for interoperability purposes. The library ships nothing else from i2's type system — no standard-library types, no icon catalog, no palette definitions. All of that content stays in the user's licensed ANB installation and must be supplied by the user.
|
|
44
|
+
>
|
|
45
|
+
> This design is intentional: organisations are free to define their own default semantic types, palettes, icon mappings, link types, and attribute classes.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# As a library — import in your own code
|
|
53
|
+
pip install anxwritter
|
|
54
|
+
|
|
55
|
+
# With YAML support (optional)
|
|
56
|
+
pip install anxwritter[yaml]
|
|
57
|
+
|
|
58
|
+
# As a command-line tool only (isolated venv, no env conflicts)
|
|
59
|
+
pipx install anxwritter
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
With [uv](https://docs.astral.sh/uv/):
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Add to a uv-managed project
|
|
66
|
+
uv add anxwritter
|
|
67
|
+
uv add 'anxwritter[yaml]' # with YAML support
|
|
68
|
+
|
|
69
|
+
# Install globally as a CLI tool
|
|
70
|
+
uv tool install anxwritter
|
|
71
|
+
|
|
72
|
+
# One-off run without installing
|
|
73
|
+
uvx anxwritter chart.yaml -o my_chart.anx
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Requires Python 3.10+.
|
|
77
|
+
|
|
78
|
+
> Want a standalone executable? `pip install pyinstaller` (or `nuitka`) and
|
|
79
|
+
> point it at `anxwritter/cli.py`. anxwritter does not ship pre-built binaries.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Quick example
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from anxwritter import ANXChart
|
|
87
|
+
|
|
88
|
+
chart = ANXChart()
|
|
89
|
+
|
|
90
|
+
chart.add_icon(id='Alice', type='Person', color='Blue')
|
|
91
|
+
chart.add_icon(id='Bob', type='Person', color='Red')
|
|
92
|
+
|
|
93
|
+
chart.add_link(
|
|
94
|
+
from_id='Alice', to_id='Bob',
|
|
95
|
+
type='Telephone Call', arrow='->',
|
|
96
|
+
date='2024-01-15',
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
chart.to_anx('output/my_chart')
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This writes `output/my_chart.anx`. Open it in ANB.
|
|
103
|
+
|
|
104
|
+
### YAML + CLI
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
# chart.yaml
|
|
108
|
+
entities:
|
|
109
|
+
icons:
|
|
110
|
+
- { id: Alice, type: Person, color: Blue }
|
|
111
|
+
- { id: Bob, type: Person, color: Red }
|
|
112
|
+
links:
|
|
113
|
+
- { from_id: Alice, to_id: Bob, type: Telephone Call, arrow: '->', date: '2024-01-15' }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
anxwritter chart.yaml -o output/my_chart.anx
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## What's in the box
|
|
123
|
+
|
|
124
|
+
- **Typed dataclass API** — `Icon`, `Box`, `Circle`, `ThemeLine`, `EventFrame`, `TextBlock`, `Label`, `Link`, `Card`
|
|
125
|
+
- **Three input forms** — Python objects, JSON, or YAML; all paths produce identical output
|
|
126
|
+
- **Org-level config files** — separate entity types, link types, attribute classes, palettes from per-chart data; layered configs supported
|
|
127
|
+
- **Auto-layout** — circle, grid, or random placement; manual `x`/`y` always wins
|
|
128
|
+
- **Geographic positioning** — map entity attributes to lat/lon for canvas layout and/or ANB Esri Maps
|
|
129
|
+
- **Auto-coloring** — distribute HSV hues across entities; matching link colors
|
|
130
|
+
- **Validation** — collects every error in one pass before writing the file
|
|
131
|
+
- **Semantic types** — full `lcx:LibraryCatalogue` support with custom type extension
|
|
132
|
+
- **CLI** — `anxwritter [--config org.yaml ...] data.{json,yaml} -o out.anx`
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Customize everything ANB exposes
|
|
137
|
+
|
|
138
|
+
No fixed catalog — every configurable surface in ANB is yours to define:
|
|
139
|
+
|
|
140
|
+
- **Entity types & link types** — names, icons, colors, shading, semantic-type bindings
|
|
141
|
+
- **Attribute classes** — type, prefix/suffix, decimals, font, merge behavior, icons, visibility
|
|
142
|
+
- **Palettes** — your own "Insert from Palette" panels with grouped types and pre-filled attribute entries
|
|
143
|
+
- **Legend** — 8 item types (icon, link, line, font, text, attribute, timezone, icon-frame) with full styling
|
|
144
|
+
- **Colors** — 40 named, hex, COLORREF, or auto-distributed HSV with link colors that follow the entity
|
|
145
|
+
- **Strengths, grades, source types, datetime formats, semantic types** — all configurable
|
|
146
|
+
|
|
147
|
+
Define your defaults once in `config.yaml`, layer overrides on top, reuse across every chart.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Documentation
|
|
152
|
+
|
|
153
|
+
- [Getting started](docs/getting-started.md) — install, first chart, where to go next
|
|
154
|
+
- [Analyst guide](docs/guide/analyst-guide.md) — every feature via practical examples
|
|
155
|
+
- [Reference docs](docs/reference/) — field-by-field API reference
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT — see [LICENSE](LICENSE). Copyright (c) 2024-2026 [gustavo-gkmi](https://github.com/gustavo-gkmi).
|
|
162
|
+
|
|
163
|
+
> Developed with the help of AI coding assistants.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# anxwritter
|
|
2
|
+
|
|
3
|
+
Write i2 Analyst's Notebook Exchange (`.anx`) files from Python, JSON, or YAML.
|
|
4
|
+
|
|
5
|
+
Output files open directly in i2 ANB 9+ via **File > Open** — no import wizard, no intermediate CSV. The `.anx` file embeds the entire chart as XML.
|
|
6
|
+
|
|
7
|
+
> This project is independent and not affiliated with, endorsed by, or sponsored by the makers of i2 Analyst's Notebook — past or present — including IBM, i2 Group Limited, i2 Limited, N.Harris Computer Corporation, Harris Computer Corporation, or Constellation Software Inc. — nor by Esri, Microsoft, or Google. "i2", "i2 Analyst's Notebook", "ANB", "Esri", "Esri Maps", "Microsoft", and "Google" are trademarks of their respective owners and are referenced here solely to identify the file format and the tools this library interoperates with (nominative use).
|
|
8
|
+
>
|
|
9
|
+
> To produce structurally valid `.anx` files, this library embeds nine format identifiers: six abstract-root GUIDs required by the `lcx:LibraryCatalogue` schema and three geo-map property GUIDs required by ANB's Esri Maps subsystem. These are functional tokens (format anchors), not creative content, reproduced for interoperability purposes. The library ships nothing else from i2's type system — no standard-library types, no icon catalog, no palette definitions. All of that content stays in the user's licensed ANB installation and must be supplied by the user.
|
|
10
|
+
>
|
|
11
|
+
> This design is intentional: organisations are free to define their own default semantic types, palettes, icon mappings, link types, and attribute classes.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# As a library — import in your own code
|
|
19
|
+
pip install anxwritter
|
|
20
|
+
|
|
21
|
+
# With YAML support (optional)
|
|
22
|
+
pip install anxwritter[yaml]
|
|
23
|
+
|
|
24
|
+
# As a command-line tool only (isolated venv, no env conflicts)
|
|
25
|
+
pipx install anxwritter
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
With [uv](https://docs.astral.sh/uv/):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Add to a uv-managed project
|
|
32
|
+
uv add anxwritter
|
|
33
|
+
uv add 'anxwritter[yaml]' # with YAML support
|
|
34
|
+
|
|
35
|
+
# Install globally as a CLI tool
|
|
36
|
+
uv tool install anxwritter
|
|
37
|
+
|
|
38
|
+
# One-off run without installing
|
|
39
|
+
uvx anxwritter chart.yaml -o my_chart.anx
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Requires Python 3.10+.
|
|
43
|
+
|
|
44
|
+
> Want a standalone executable? `pip install pyinstaller` (or `nuitka`) and
|
|
45
|
+
> point it at `anxwritter/cli.py`. anxwritter does not ship pre-built binaries.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Quick example
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from anxwritter import ANXChart
|
|
53
|
+
|
|
54
|
+
chart = ANXChart()
|
|
55
|
+
|
|
56
|
+
chart.add_icon(id='Alice', type='Person', color='Blue')
|
|
57
|
+
chart.add_icon(id='Bob', type='Person', color='Red')
|
|
58
|
+
|
|
59
|
+
chart.add_link(
|
|
60
|
+
from_id='Alice', to_id='Bob',
|
|
61
|
+
type='Telephone Call', arrow='->',
|
|
62
|
+
date='2024-01-15',
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
chart.to_anx('output/my_chart')
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This writes `output/my_chart.anx`. Open it in ANB.
|
|
69
|
+
|
|
70
|
+
### YAML + CLI
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
# chart.yaml
|
|
74
|
+
entities:
|
|
75
|
+
icons:
|
|
76
|
+
- { id: Alice, type: Person, color: Blue }
|
|
77
|
+
- { id: Bob, type: Person, color: Red }
|
|
78
|
+
links:
|
|
79
|
+
- { from_id: Alice, to_id: Bob, type: Telephone Call, arrow: '->', date: '2024-01-15' }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
anxwritter chart.yaml -o output/my_chart.anx
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## What's in the box
|
|
89
|
+
|
|
90
|
+
- **Typed dataclass API** — `Icon`, `Box`, `Circle`, `ThemeLine`, `EventFrame`, `TextBlock`, `Label`, `Link`, `Card`
|
|
91
|
+
- **Three input forms** — Python objects, JSON, or YAML; all paths produce identical output
|
|
92
|
+
- **Org-level config files** — separate entity types, link types, attribute classes, palettes from per-chart data; layered configs supported
|
|
93
|
+
- **Auto-layout** — circle, grid, or random placement; manual `x`/`y` always wins
|
|
94
|
+
- **Geographic positioning** — map entity attributes to lat/lon for canvas layout and/or ANB Esri Maps
|
|
95
|
+
- **Auto-coloring** — distribute HSV hues across entities; matching link colors
|
|
96
|
+
- **Validation** — collects every error in one pass before writing the file
|
|
97
|
+
- **Semantic types** — full `lcx:LibraryCatalogue` support with custom type extension
|
|
98
|
+
- **CLI** — `anxwritter [--config org.yaml ...] data.{json,yaml} -o out.anx`
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Customize everything ANB exposes
|
|
103
|
+
|
|
104
|
+
No fixed catalog — every configurable surface in ANB is yours to define:
|
|
105
|
+
|
|
106
|
+
- **Entity types & link types** — names, icons, colors, shading, semantic-type bindings
|
|
107
|
+
- **Attribute classes** — type, prefix/suffix, decimals, font, merge behavior, icons, visibility
|
|
108
|
+
- **Palettes** — your own "Insert from Palette" panels with grouped types and pre-filled attribute entries
|
|
109
|
+
- **Legend** — 8 item types (icon, link, line, font, text, attribute, timezone, icon-frame) with full styling
|
|
110
|
+
- **Colors** — 40 named, hex, COLORREF, or auto-distributed HSV with link colors that follow the entity
|
|
111
|
+
- **Strengths, grades, source types, datetime formats, semantic types** — all configurable
|
|
112
|
+
|
|
113
|
+
Define your defaults once in `config.yaml`, layer overrides on top, reuse across every chart.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Documentation
|
|
118
|
+
|
|
119
|
+
- [Getting started](docs/getting-started.md) — install, first chart, where to go next
|
|
120
|
+
- [Analyst guide](docs/guide/analyst-guide.md) — every feature via practical examples
|
|
121
|
+
- [Reference docs](docs/reference/) — field-by-field API reference
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT — see [LICENSE](LICENSE). Copyright (c) 2024-2026 [gustavo-gkmi](https://github.com/gustavo-gkmi).
|
|
128
|
+
|
|
129
|
+
> Developed with the help of AI coding assistants.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
anxwritter — Convert typed Python objects to i2 Analyst's Notebook Exchange (.anx) files.
|
|
3
|
+
|
|
4
|
+
Quick start
|
|
5
|
+
-----------
|
|
6
|
+
::
|
|
7
|
+
|
|
8
|
+
from anxwritter import ANXChart
|
|
9
|
+
|
|
10
|
+
chart = ANXChart()
|
|
11
|
+
|
|
12
|
+
chart.add_icon(id='Alice', type='Person', color='Blue',
|
|
13
|
+
attributes={'phone': '555-0001'})
|
|
14
|
+
chart.add_icon(id='Bob', type='Person')
|
|
15
|
+
|
|
16
|
+
chart.add_link(from_id='Alice', to_id='Bob', type='Call',
|
|
17
|
+
arrow='->', date='2024-01-15',
|
|
18
|
+
attributes={'duration': 120})
|
|
19
|
+
|
|
20
|
+
chart.to_anx('output/my_chart') # writes output/my_chart.anx
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from importlib.metadata import version, metadata, PackageNotFoundError
|
|
24
|
+
|
|
25
|
+
from loguru import logger
|
|
26
|
+
|
|
27
|
+
from .chart import ANXChart
|
|
28
|
+
from .errors import ANXValidationError, ErrorType
|
|
29
|
+
from .colors import NAMED_COLORS, color_to_colorref, rgb_to_colorref
|
|
30
|
+
from .enums import VALID_SHADING_COLORS, MergeBehaviour, DotStyle, Enlargement, AttributeType, Multiplicity, ThemeWiring, ArrowStyle, Representation, LegendItemType, Color
|
|
31
|
+
from .entities import Icon, Box, Circle, ThemeLine, EventFrame, TextBlock, Label
|
|
32
|
+
from .models import (
|
|
33
|
+
Card, Link, AttributeClass, Strength, LegendItem, EntityType, LinkType,
|
|
34
|
+
Palette, PaletteAttributeEntry, DateTimeFormat,
|
|
35
|
+
SemanticEntity, SemanticLink, SemanticProperty,
|
|
36
|
+
Font, Frame, Show, TimeZone, CustomProperty,
|
|
37
|
+
GradeCollection, StrengthCollection,
|
|
38
|
+
Settings, ChartCfg, ViewCfg, GridCfg, WiringCfg, LinksCfg, TimeCfg,
|
|
39
|
+
SummaryCfg, LegendCfg, ExtraCfg, GeoMapCfg,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
__version__ = version("anxwritter")
|
|
44
|
+
_meta = metadata("anxwritter")
|
|
45
|
+
__repo_url__ = ""
|
|
46
|
+
for val in _meta.get_all("Project-URL") or []:
|
|
47
|
+
label, url = val.split(", ", 1)
|
|
48
|
+
if label == "Repository":
|
|
49
|
+
__repo_url__ = url
|
|
50
|
+
break
|
|
51
|
+
except PackageNotFoundError:
|
|
52
|
+
__version__ = "0.0.0"
|
|
53
|
+
__repo_url__ = ""
|
|
54
|
+
|
|
55
|
+
logger.disable("anxwritter")
|
|
56
|
+
|
|
57
|
+
__all__ = [
|
|
58
|
+
# Version
|
|
59
|
+
'__version__',
|
|
60
|
+
# Main class
|
|
61
|
+
'ANXChart',
|
|
62
|
+
'ANXValidationError',
|
|
63
|
+
'ErrorType',
|
|
64
|
+
# Entity classes
|
|
65
|
+
'Icon', 'Box', 'Circle', 'ThemeLine', 'EventFrame', 'TextBlock', 'Label',
|
|
66
|
+
# Chart item classes
|
|
67
|
+
'Card', 'Link', 'AttributeClass', 'Strength', 'LegendItem',
|
|
68
|
+
'GradeCollection', 'StrengthCollection',
|
|
69
|
+
'EntityType', 'LinkType',
|
|
70
|
+
'Palette', 'PaletteAttributeEntry',
|
|
71
|
+
'DateTimeFormat',
|
|
72
|
+
'SemanticEntity', 'SemanticLink', 'SemanticProperty',
|
|
73
|
+
# Settings dataclasses
|
|
74
|
+
'Font', 'Frame', 'Show', 'TimeZone', 'CustomProperty', 'Settings',
|
|
75
|
+
'ChartCfg', 'ViewCfg', 'GridCfg', 'WiringCfg', 'LinksCfg', 'TimeCfg',
|
|
76
|
+
'SummaryCfg', 'LegendCfg', 'ExtraCfg', 'GeoMapCfg',
|
|
77
|
+
# Enums
|
|
78
|
+
'MergeBehaviour',
|
|
79
|
+
'DotStyle',
|
|
80
|
+
'Enlargement',
|
|
81
|
+
'AttributeType',
|
|
82
|
+
'Multiplicity',
|
|
83
|
+
'ThemeWiring',
|
|
84
|
+
'ArrowStyle',
|
|
85
|
+
'Representation',
|
|
86
|
+
'LegendItemType',
|
|
87
|
+
'Color',
|
|
88
|
+
# Color helpers
|
|
89
|
+
'VALID_SHADING_COLORS',
|
|
90
|
+
'NAMED_COLORS',
|
|
91
|
+
'color_to_colorref',
|
|
92
|
+
'rgb_to_colorref',
|
|
93
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
_i2_interop.py — Minimum interoperability constants for the ANX format.
|
|
3
|
+
|
|
4
|
+
This module contains the only i2-derived identifiers this library ships:
|
|
5
|
+
|
|
6
|
+
* Six abstract-root GUIDs required by ANB's ``lcx:LibraryCatalogue`` schema.
|
|
7
|
+
Every semantic type chain must terminate at one of these roots; they are
|
|
8
|
+
structural anchors, not content.
|
|
9
|
+
|
|
10
|
+
* Three geo-map property GUIDs required by ANB's Esri Maps subsystem to
|
|
11
|
+
recognise Latitude/Longitude attributes for geographic rendering.
|
|
12
|
+
|
|
13
|
+
These nine identifiers are the absolute minimum needed to write a structurally
|
|
14
|
+
valid ANX file with semantic types. This library intentionally ships nothing
|
|
15
|
+
else from i2's type system — no standard-library types (Person, Vehicle,
|
|
16
|
+
Phone Call…), no icon catalog, no palette definitions. All of that content
|
|
17
|
+
belongs to the user's licensed ANB installation and must be supplied by the
|
|
18
|
+
user via config files or ``.ant``/``.anx`` reference files.
|
|
19
|
+
|
|
20
|
+
This design is intentional: it leaves organisations free to define their own
|
|
21
|
+
default semantic types, palettes, icon mappings, link types, and attribute
|
|
22
|
+
classes — either independently of i2's standard library or layered on top of
|
|
23
|
+
it using types extracted from their own ANB installation.
|
|
24
|
+
|
|
25
|
+
These nine identifiers are functional tokens (format anchors), not creative
|
|
26
|
+
works, reproduced under the interoperability doctrine
|
|
27
|
+
(EU Directive 2009/24/EC Art. 6).
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from typing import Dict, Set
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# ── Group 1: abstract root GUIDs ─────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
#: i2 abstract root for every entity type. Parent of any user-defined
|
|
37
|
+
#: ``kind_of: Entity`` chain. Required by ANB schema.
|
|
38
|
+
ROOT_ENTITY = 'guid3669EC21-8E41-438A-AA1A-26B477C15BE0'
|
|
39
|
+
|
|
40
|
+
#: i2 abstract root for every link type. Parent of any user-defined
|
|
41
|
+
#: ``kind_of: Link`` chain. Required by ANB schema.
|
|
42
|
+
ROOT_LINK = 'guidC9E54967-BBBF-494B-8348-B9D524F500FD'
|
|
43
|
+
|
|
44
|
+
#: i2 abstract root for text-typed semantic properties.
|
|
45
|
+
ROOT_ABSTRACT_TEXT = 'guid9A224CCF-28F7-4c55-9F14-9E820A0B1631'
|
|
46
|
+
|
|
47
|
+
#: i2 abstract root for numeric-typed semantic properties. Also the indirect
|
|
48
|
+
#: ancestor of the geo-map ``Grid Reference`` / ``Latitude`` / ``Longitude``
|
|
49
|
+
#: property tree.
|
|
50
|
+
ROOT_ABSTRACT_NUM = 'guid6D676796-915D-487f-B384-73503C988ABE'
|
|
51
|
+
|
|
52
|
+
#: i2 abstract root for datetime-typed semantic properties.
|
|
53
|
+
ROOT_ABSTRACT_DT = 'guid6684F871-B607-4ffb-80E8-480535CB44FC'
|
|
54
|
+
|
|
55
|
+
#: i2 abstract root for flag/boolean semantic properties.
|
|
56
|
+
ROOT_ABSTRACT_FLAG = 'guid74F2A516-2F49-4282-989F-F4A468656FF0'
|
|
57
|
+
|
|
58
|
+
#: All six abstract roots.
|
|
59
|
+
ROOTS: Set[str] = {
|
|
60
|
+
ROOT_ENTITY, ROOT_LINK,
|
|
61
|
+
ROOT_ABSTRACT_TEXT, ROOT_ABSTRACT_NUM,
|
|
62
|
+
ROOT_ABSTRACT_DT, ROOT_ABSTRACT_FLAG,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#: Type-tree roots (entity + link). Used to distinguish from property roots
|
|
66
|
+
#: when populating the catalogue's type vs. property lookups.
|
|
67
|
+
TYPE_ROOTS: Set[str] = {ROOT_ENTITY, ROOT_LINK}
|
|
68
|
+
|
|
69
|
+
#: Property-tree roots (text/number/datetime/flag).
|
|
70
|
+
PROPERTY_ROOTS: Set[str] = {
|
|
71
|
+
ROOT_ABSTRACT_TEXT, ROOT_ABSTRACT_NUM,
|
|
72
|
+
ROOT_ABSTRACT_DT, ROOT_ABSTRACT_FLAG,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#: Display name for each root, emitted as the ``<TypeName>`` /
|
|
76
|
+
#: ``<PropertyName>`` of root catalogue entries.
|
|
77
|
+
ROOT_NAMES: Dict[str, str] = {
|
|
78
|
+
ROOT_ENTITY: 'Entity',
|
|
79
|
+
ROOT_LINK: 'Link',
|
|
80
|
+
ROOT_ABSTRACT_TEXT: 'Abstract Text',
|
|
81
|
+
ROOT_ABSTRACT_NUM: 'Abstract Number',
|
|
82
|
+
ROOT_ABSTRACT_DT: 'Abstract Date & Time',
|
|
83
|
+
ROOT_ABSTRACT_FLAG: 'Abstract Flag',
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# ── Group 2: geo-map property GUIDs ──────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
#: i2 standard "Latitude" semantic property GUID. Required for ANB's Esri
|
|
90
|
+
#: Maps subsystem to recognise a Latitude attribute for geographic rendering.
|
|
91
|
+
#: Used only when ``geo_map.mode`` is ``'latlon'`` or ``'both'``.
|
|
92
|
+
LATITUDE_GUID = 'guid5304A03B-FE47-4406-91E7-0D49EC8409A6'
|
|
93
|
+
|
|
94
|
+
#: i2 standard "Longitude" semantic property GUID. Paired with
|
|
95
|
+
#: :data:`LATITUDE_GUID`.
|
|
96
|
+
LONGITUDE_GUID = 'guid14BCA0EC-D67A-4A67-BC36-CFF650FD77A9'
|
|
97
|
+
|
|
98
|
+
#: i2 standard "Grid Reference" semantic property GUID. Parent of Latitude
|
|
99
|
+
#: and Longitude in the property tree (chains up to ``Abstract Number``).
|
|
100
|
+
#: Required because Latitude/Longitude need a non-abstract parent to be
|
|
101
|
+
#: valid in the catalogue chain.
|
|
102
|
+
GRID_REFERENCE_GUID = 'guid7E0F705E-3D39-4E6E-B6C1-5E72B8C573DA'
|