hinge 0.1.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 (112) hide show
  1. hinge-0.1.0/.gitignore +59 -0
  2. hinge-0.1.0/LICENSE +21 -0
  3. hinge-0.1.0/PKG-INFO +309 -0
  4. hinge-0.1.0/README.md +265 -0
  5. hinge-0.1.0/docs/README.md +11 -0
  6. hinge-0.1.0/hinge/__init__.py +3 -0
  7. hinge-0.1.0/hinge/config/__init__.py +0 -0
  8. hinge-0.1.0/hinge/config/logging_setup.py +63 -0
  9. hinge-0.1.0/hinge/config/settings.py +23 -0
  10. hinge-0.1.0/hinge/config/types.yaml +42 -0
  11. hinge-0.1.0/hinge/dbt/.user.yml +1 -0
  12. hinge-0.1.0/hinge/dbt/dbt_project.yml +18 -0
  13. hinge-0.1.0/hinge/dbt/macros/apply_time_window.sql +11 -0
  14. hinge-0.1.0/hinge/dbt/macros/assert_recipe_supported.sql +59 -0
  15. hinge-0.1.0/hinge/dbt/macros/build_incidence.sql +46 -0
  16. hinge-0.1.0/hinge/dbt/macros/collapse_path.sql +26 -0
  17. hinge-0.1.0/hinge/dbt/macros/filter_bots.sql +23 -0
  18. hinge-0.1.0/hinge/dbt/macros/network_edges.sql +39 -0
  19. hinge-0.1.0/hinge/dbt/macros/project_bipartite.sql +33 -0
  20. hinge-0.1.0/hinge/dbt/macros/slice_edges.sql +41 -0
  21. hinge-0.1.0/hinge/dbt/models/hin/hin_accounts.sql +14 -0
  22. hinge-0.1.0/hinge/dbt/models/hin/hin_artifacts.sql +23 -0
  23. hinge-0.1.0/hinge/dbt/models/hin/hin_capabilities.sql +42 -0
  24. hinge-0.1.0/hinge/dbt/models/hin/hin_edges.sql +57 -0
  25. hinge-0.1.0/hinge/dbt/models/hin/hin_nodes.sql +44 -0
  26. hinge-0.1.0/hinge/dbt/models/hin/hin_repositories.sql +15 -0
  27. hinge-0.1.0/hinge/dbt/models/intermediate/int_developer_repo_affiliation.sql +27 -0
  28. hinge-0.1.0/hinge/dbt/models/intermediate/int_user_artifact_incidence.sql +21 -0
  29. hinge-0.1.0/hinge/dbt/models/networks/artifact_reference.sql +27 -0
  30. hinge-0.1.0/hinge/dbt/models/networks/co_commit_user_user.sql +48 -0
  31. hinge-0.1.0/hinge/dbt/models/networks/co_edit_file_user_user.sql +48 -0
  32. hinge-0.1.0/hinge/dbt/models/networks/co_edit_line_user_user.sql +48 -0
  33. hinge-0.1.0/hinge/dbt/models/networks/dev_interaction.sql +98 -0
  34. hinge-0.1.0/hinge/dbt/models/networks/developer_repo_affiliation.sql +20 -0
  35. hinge-0.1.0/hinge/dbt/models/networks/follow_user_user.sql +27 -0
  36. hinge-0.1.0/hinge/dbt/models/networks/fork_repo_repo.sql +27 -0
  37. hinge-0.1.0/hinge/dbt/models/networks/issue_co_participation.sql +50 -0
  38. hinge-0.1.0/hinge/dbt/models/networks/issue_participation.sql +40 -0
  39. hinge-0.1.0/hinge/dbt/models/networks/pr_author_reviewer.sql +66 -0
  40. hinge-0.1.0/hinge/dbt/models/networks/pr_participation.sql +40 -0
  41. hinge-0.1.0/hinge/dbt/models/networks/pr_reviewer_coreview.sql +48 -0
  42. hinge-0.1.0/hinge/dbt/models/networks/repo_shared_contributors.sql +41 -0
  43. hinge-0.1.0/hinge/dbt/models/networks/star_user_repo.sql +27 -0
  44. hinge-0.1.0/hinge/dbt/models/networks/user_mention_user.sql +60 -0
  45. hinge-0.1.0/hinge/dbt/models/networks/watch_user_repo.sql +27 -0
  46. hinge-0.1.0/hinge/dbt/models/sources/sources.yml +63 -0
  47. hinge-0.1.0/hinge/dbt/models/staging/stg_accounts.sql +4 -0
  48. hinge-0.1.0/hinge/dbt/models/staging/stg_adapter_manifest.sql +4 -0
  49. hinge-0.1.0/hinge/dbt/models/staging/stg_artifacts.sql +4 -0
  50. hinge-0.1.0/hinge/dbt/models/staging/stg_relations.sql +4 -0
  51. hinge-0.1.0/hinge/dbt/models/staging/stg_repositories.sql +4 -0
  52. hinge-0.1.0/hinge/dbt/profiles.yml +9 -0
  53. hinge-0.1.0/hinge/dbt/seeds/ref_recipe_requirements.csv +35 -0
  54. hinge-0.1.0/hinge/frontends/__init__.py +0 -0
  55. hinge-0.1.0/hinge/frontends/cli/__init__.py +3 -0
  56. hinge-0.1.0/hinge/frontends/cli/app.py +189 -0
  57. hinge-0.1.0/hinge/frontends/lib.py +148 -0
  58. hinge-0.1.0/hinge/frontends/tui/__init__.py +7 -0
  59. hinge-0.1.0/hinge/frontends/web/__init__.py +6 -0
  60. hinge-0.1.0/hinge/kernel/__init__.py +0 -0
  61. hinge-0.1.0/hinge/kernel/projection/__init__.py +9 -0
  62. hinge-0.1.0/hinge/kernel/projection/projected_graph.py +32 -0
  63. hinge-0.1.0/hinge/kernel/projection/projection_spec.py +35 -0
  64. hinge-0.1.0/hinge/kernel/protocols/__init__.py +24 -0
  65. hinge-0.1.0/hinge/kernel/protocols/dataset_view.py +30 -0
  66. hinge-0.1.0/hinge/kernel/protocols/exporter_stage.py +24 -0
  67. hinge-0.1.0/hinge/kernel/protocols/projection_stage.py +36 -0
  68. hinge-0.1.0/hinge/kernel/protocols/reader_stage.py +52 -0
  69. hinge-0.1.0/hinge/kernel/protocols/store_stage.py +71 -0
  70. hinge-0.1.0/hinge/kernel/registry.py +71 -0
  71. hinge-0.1.0/hinge/kernel/runner.py +236 -0
  72. hinge-0.1.0/hinge/kernel/schema/__init__.py +12 -0
  73. hinge-0.1.0/hinge/kernel/schema/hin_schema.py +38 -0
  74. hinge-0.1.0/hinge/kernel/schema/schema_violation.py +21 -0
  75. hinge-0.1.0/hinge/kernel/schema/typed_edge.py +20 -0
  76. hinge-0.1.0/hinge/kernel/schema/typed_node.py +17 -0
  77. hinge-0.1.0/hinge/stages/__init__.py +0 -0
  78. hinge-0.1.0/hinge/stages/exporters/__init__.py +0 -0
  79. hinge-0.1.0/hinge/stages/exporters/csv_exporter.py +77 -0
  80. hinge-0.1.0/hinge/stages/exporters/dot_exporter.py +13 -0
  81. hinge-0.1.0/hinge/stages/exporters/gexf_exporter.py +13 -0
  82. hinge-0.1.0/hinge/stages/exporters/gml_exporter.py +79 -0
  83. hinge-0.1.0/hinge/stages/exporters/graphml_exporter.py +13 -0
  84. hinge-0.1.0/hinge/stages/exporters/jsonld_exporter.py +13 -0
  85. hinge-0.1.0/hinge/stages/exporters/networkx_exporter.py +57 -0
  86. hinge-0.1.0/hinge/stages/exporters/parquet_exporter.py +13 -0
  87. hinge-0.1.0/hinge/stages/projection/__init__.py +0 -0
  88. hinge-0.1.0/hinge/stages/projection/dbt_projection.py +342 -0
  89. hinge-0.1.0/hinge/stages/projection/specs/__init__.py +0 -0
  90. hinge-0.1.0/hinge/stages/projection/specs/artifact_reference.py +14 -0
  91. hinge-0.1.0/hinge/stages/projection/specs/co_commit_user_user.py +14 -0
  92. hinge-0.1.0/hinge/stages/projection/specs/co_edit_file_user_user.py +14 -0
  93. hinge-0.1.0/hinge/stages/projection/specs/co_edit_line_user_user.py +14 -0
  94. hinge-0.1.0/hinge/stages/projection/specs/dev_interaction.py +26 -0
  95. hinge-0.1.0/hinge/stages/projection/specs/developer_repo_affiliation.py +15 -0
  96. hinge-0.1.0/hinge/stages/projection/specs/follow_user_user.py +14 -0
  97. hinge-0.1.0/hinge/stages/projection/specs/fork_repo_repo.py +14 -0
  98. hinge-0.1.0/hinge/stages/projection/specs/issue_co_participation.py +14 -0
  99. hinge-0.1.0/hinge/stages/projection/specs/issue_participation.py +14 -0
  100. hinge-0.1.0/hinge/stages/projection/specs/pr_author_reviewer.py +14 -0
  101. hinge-0.1.0/hinge/stages/projection/specs/pr_participation.py +14 -0
  102. hinge-0.1.0/hinge/stages/projection/specs/pr_reviewer_coreview.py +14 -0
  103. hinge-0.1.0/hinge/stages/projection/specs/repo_shared_contributors.py +14 -0
  104. hinge-0.1.0/hinge/stages/projection/specs/star_user_repo.py +15 -0
  105. hinge-0.1.0/hinge/stages/projection/specs/user_mention_user.py +14 -0
  106. hinge-0.1.0/hinge/stages/projection/specs/watch_user_repo.py +14 -0
  107. hinge-0.1.0/hinge/stages/readers/__init__.py +0 -0
  108. hinge-0.1.0/hinge/stages/readers/_numfocus_contract_adapter.py +314 -0
  109. hinge-0.1.0/hinge/stages/readers/numfocus_reader.py +69 -0
  110. hinge-0.1.0/hinge/stages/store/__init__.py +0 -0
  111. hinge-0.1.0/hinge/stages/store/duckdb_store.py +466 -0
  112. hinge-0.1.0/pyproject.toml +182 -0
hinge-0.1.0/.gitignore ADDED
@@ -0,0 +1,59 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.so
5
+ *.egg
6
+ *.egg-info/
7
+ dist/
8
+ build/
9
+ .eggs/
10
+
11
+ # uv
12
+ .venv/
13
+ # uv.lock is intentionally committed — do NOT add it here
14
+
15
+ # Type checking / linting caches
16
+ .mypy_cache/
17
+ .ruff_cache/
18
+
19
+ # Testing
20
+ .pytest_cache/
21
+ htmlcov/
22
+ .coverage
23
+ coverage.xml
24
+
25
+ # DuckDB store files (data, not code)
26
+ *.duckdb
27
+ *.duckdb.wal
28
+
29
+ # Exported graphs (outputs, not source)
30
+ *.gml
31
+ *.graphml
32
+ *.gexf
33
+ *.dot
34
+ out/
35
+ output/
36
+ outputs/
37
+
38
+ # dbt build artefacts
39
+ target/
40
+ dbt_packages/
41
+ logs/
42
+
43
+ # Development dataset (large files — never commit)
44
+ num_focus/
45
+
46
+ # Local working notes — internal to the author, not the team
47
+ architecture_decision.md
48
+
49
+ # Docker
50
+ # .dockerignore is intentionally committed for reproducible images.
51
+
52
+ # OS
53
+ .DS_Store
54
+ Thumbs.db
55
+
56
+ # IDE
57
+ .vscode/
58
+ .idea/
59
+ *.iml
hinge-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Filippo Scaramuzza
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.
hinge-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,309 @@
1
+ Metadata-Version: 2.4
2
+ Name: hinge
3
+ Version: 0.1.0
4
+ Summary: Heterogeneous Information Network tool for GitHub research datasets
5
+ Project-URL: Homepage, https://github.com/marcotonnarelli/hinge
6
+ Project-URL: Repository, https://github.com/marcotonnarelli/hinge
7
+ Project-URL: Issues, https://github.com/marcotonnarelli/hinge/issues
8
+ Author-email: Filippo Scaramuzza <f.scaramuzza@tue.nl>, Marco Tonnarelli <m.tonnarelli@tue.nl>, Gergő Bocsárdi <a.g.bocsardi@tue.nl>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: dbt,duckdb,github,graph,heterogeneous-information-network,mining
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.12
21
+ Requires-Dist: dbt-core>=1.8
22
+ Requires-Dist: dbt-duckdb>=1.8
23
+ Requires-Dist: duckdb>=1.0
24
+ Requires-Dist: networkx>=3.3
25
+ Requires-Dist: orjson>=3.10
26
+ Requires-Dist: pyarrow>=16.0
27
+ Requires-Dist: pydantic>=2.7
28
+ Requires-Dist: pyyaml>=6.0
29
+ Requires-Dist: rich>=13.7
30
+ Requires-Dist: typer>=0.12
31
+ Provides-Extra: dev
32
+ Requires-Dist: import-linter>=2.0; extra == 'dev'
33
+ Requires-Dist: mypy>=1.10; extra == 'dev'
34
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
35
+ Requires-Dist: pytest>=8.2; extra == 'dev'
36
+ Requires-Dist: ruff>=0.5; extra == 'dev'
37
+ Requires-Dist: types-pyyaml; extra == 'dev'
38
+ Provides-Extra: tui
39
+ Requires-Dist: textual>=0.70; extra == 'tui'
40
+ Provides-Extra: web
41
+ Requires-Dist: fastapi>=0.111; extra == 'web'
42
+ Requires-Dist: uvicorn>=0.30; extra == 'web'
43
+ Description-Content-Type: text/markdown
44
+
45
+ # HINGE — Heterogeneous Information Network for Generalized Extraction
46
+
47
+ `hinge` ingests GitHub activity datasets (JSONL today; CSV/Parquet planned), persists
48
+ them as a typed multi-relational graph in DuckDB, and exposes **projections** —
49
+ dbt SQL models that derive task-specific sub-graphs and export them to formats
50
+ consumed by Gephi, NetworkX, igraph, etc.
51
+
52
+ This is a research artefact accompanying an ICSME 2026 Tools and Data Showcase submission.
53
+
54
+ ---
55
+
56
+ ## Quick start
57
+
58
+ ```bash
59
+ # 1. Install (uv is the only supported package manager)
60
+ uv sync --all-extras
61
+
62
+ # 2. Ingest a dataset
63
+ uv run hinge ingest path/to/events.jsonl --reader numfocus
64
+ # → ingested 48 elements → 29 nodes, 19 edges (0 violations)
65
+ # → Dataset ID: 78fc87c370944dc2b4a4e2d4bdd97ce1
66
+
67
+ # 3. Use the printed Dataset ID to run a projection and export
68
+ uv run hinge export \
69
+ --dataset 78fc87c370944dc2b4a4e2d4bdd97ce1 \
70
+ --projection user-user-repo-collaboration \
71
+ --format gml \
72
+ -o out/graph.gml
73
+
74
+ # 4. Inspect all ingested datasets
75
+ uv run hinge list datasets
76
+ ```
77
+
78
+ Or via Docker:
79
+
80
+ ```bash
81
+ docker compose build
82
+ cp path/to/events.jsonl ./data/
83
+ docker compose run --rm hinge hinge ingest /data/events.jsonl --reader numfocus
84
+ docker compose run --rm hinge hinge export \
85
+ --dataset <id> --projection user-user-repo-collaboration --format gml -o /output/graph.gml
86
+ ```
87
+
88
+ The DuckDB store lives at `$HINGE_STORE_PATH` (default `./network.duckdb`
89
+ locally, `/store/network.duckdb` inside the container). Multiple datasets
90
+ can coexist in the same file — each ingest run gets a unique Dataset ID.
91
+
92
+ ---
93
+
94
+ ## HIN contract + adapters
95
+
96
+ The source-agnostic core starts at the adapter contract tables:
97
+
98
+ ```text
99
+ source-specific adapter
100
+ -> contract_accounts / contract_repositories / contract_artifacts / contract_relations
101
+ -> hin_nodes / hin_edges
102
+ -> dbt projections
103
+ -> exporters
104
+ ```
105
+
106
+ The `numfocus` reader uses a DuckDB adapter for this repo's NumFocus Actions
107
+ JSONL scrape. It is intentionally source-specific: it knows paths like
108
+ `$.actor.login` and `$.details.pull_request.id`. Other data sources should
109
+ implement their own adapter that writes the same `contract_*` tables; then the
110
+ HIN views and dbt recipes can run unchanged.
111
+
112
+ The generic Python `ReaderStage` path is still available for simple custom
113
+ readers, but large JSONL ingests should use a DuckDB/SQL contract adapter where
114
+ possible.
115
+
116
+ ---
117
+
118
+ ## Adding a new projection
119
+
120
+ A projection is a dbt SQL model that derives a task-specific sub-graph from
121
+ the typed HIN stored in DuckDB. Adding one requires exactly four files/edits
122
+ and no changes to the kernel.
123
+
124
+ ### Step 1 — Write the SQL model
125
+
126
+ Create `hinge/dbt/models/networks/<name>.sql`. The model should read from
127
+ the canonical HIN dbt models and produce a fixed set of columns:
128
+
129
+ ```sql
130
+ -- Inputs (prefer these canonical HIN models, never the raw tables)
131
+ {{ ref('hin_nodes') }}
132
+ {{ ref('hin_edges') }}
133
+
134
+ -- Output (prefer the network_edges macro; it emits this standard schema)
135
+ network_edges(
136
+ recipe_name, recipe_version,
137
+ source_node_id, source_node_type,
138
+ target_node_id, target_node_type,
139
+ directed, edge_type,
140
+ weight, weight_kind,
141
+ n_contexts, n_events,
142
+ first_seen_at, last_seen_at,
143
+ time_bin, bot_policy,
144
+ properties
145
+ )
146
+ ```
147
+
148
+ `DbtProjection` converts this richer schema into `TypedEdge` objects for existing
149
+ exporters, merging standard fields and `properties` into edge attrs.
150
+
151
+ The upstream HIN models are built from `active_*` views created by the store
152
+ immediately before dbt runs — they are already filtered to the requested
153
+ `dataset_id`, so network SQL never needs to reference `dataset_id` at all.
154
+
155
+ **Nodes-only projections:** the pipeline derives output nodes from the union
156
+ of `source_node_id` and `target_node_id` in the result table. A projection that
157
+ emits no edges will therefore produce no nodes either. The workaround is to use
158
+ **self-loop edges** (`source_node_id = target_node_id`): they make the nodes
159
+ visible to the exporter, carry metadata in `properties`, and can be filtered out
160
+ in downstream tools with `G.remove_edges_from(nx.selfloop_edges(G))`.
161
+
162
+ See [dev_interaction.sql](hinge/dbt/models/networks/dev_interaction.sql)
163
+ for a full working example with a documented input/output contract.
164
+
165
+ ### Step 2 — Create the spec module
166
+
167
+ Create `hinge/stages/projection/specs/<name>.py` and expose a `SPEC` constant:
168
+
169
+ ```python
170
+ from hinge.kernel.projection.projection_spec import ProjectionSpec
171
+
172
+ SPEC = ProjectionSpec(
173
+ name="my-projection", # CLI key: --projection my-projection
174
+ description="...",
175
+ model_name="my_projection", # must match the .sql file stem
176
+ output_node_types=["user"],
177
+ output_edge_types=["my_edge_label"],
178
+ )
179
+ ```
180
+
181
+ This is a plain value object — no class, no inheritance. The registry loads
182
+ the module and returns the `SPEC` attribute directly.
183
+
184
+ ### Step 3 — Register the entry-point
185
+
186
+ Add one line to `pyproject.toml`:
187
+
188
+ ```toml
189
+ [project.entry-points."hinge.projection_specs"]
190
+ my-projection = "hinge.stages.projection.specs.my_projection:SPEC"
191
+ ```
192
+
193
+ Third-party packages can register projections the same way — no fork required.
194
+
195
+ ### Step 4 — Re-install so the entry-point is picked up
196
+
197
+ ```bash
198
+ uv sync --all-extras
199
+ uv run hinge list projections # → my-projection should appear
200
+ ```
201
+
202
+ Entry-points are baked into `.dist-info/entry_points.txt` at install time.
203
+ Without this step the registry will not find the new spec.
204
+
205
+ ### Run it
206
+
207
+ ```bash
208
+ uv run hinge export \
209
+ --dataset <id> \
210
+ --projection my-projection \
211
+ --format gml \
212
+ -o output/result.gml
213
+ ```
214
+
215
+ ### Local SQL custom projection
216
+
217
+ For research-specific variants, write a local dbt model and run it without
218
+ packaging or entry-points:
219
+
220
+ ```bash
221
+ uv run hinge export-sql custom_star_user_repo.sql \
222
+ --dataset <id> \
223
+ --format gml \
224
+ -o output/custom.gml
225
+ ```
226
+
227
+ The SQL file is temporarily added to the built-in dbt project, so it can use
228
+ `ref('hin_edges')`, `ref('int_user_artifact_incidence')`, and all macros under
229
+ `hinge/dbt/macros/`. It must still emit the standard `network_edges` schema.
230
+ Use `--name valid_model_name` if the filename is not a valid dbt identifier.
231
+
232
+ See [docs/custom-projections.md](docs/custom-projections.md).
233
+
234
+ ---
235
+
236
+ ## Logging
237
+
238
+ Logs go to **stderr** by default. Use env vars to control verbosity and persistence:
239
+
240
+ | Variable | Purpose | Default |
241
+ |---|---|---|
242
+ | `HINGE_LOG_LEVEL` | `DEBUG` / `INFO` / `WARNING` / `ERROR` | `INFO` |
243
+ | `HINGE_LOG_FILE` | Also write logs to this file at full `DEBUG` detail | _(none)_ |
244
+
245
+ ```bash
246
+ # See all pipeline milestones (default)
247
+ uv run hinge ingest events.jsonl --reader numfocus
248
+
249
+ # See every batch, dbt SQL, store open/close
250
+ HINGE_LOG_LEVEL=DEBUG uv run hinge ingest events.jsonl --reader numfocus
251
+
252
+ # Persist a full debug log to disk (useful for long ingest runs)
253
+ HINGE_LOG_FILE=hinge.log uv run hinge ingest events.jsonl --reader numfocus
254
+ tail -f hinge.log
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Common commands
260
+
261
+ ```bash
262
+ # Ingest NumFocus Actions JSONL via the DuckDB -> HIN contract adapter
263
+ uv run hinge ingest events.jsonl --reader numfocus
264
+
265
+ # Inspect stored datasets
266
+ uv run hinge list datasets
267
+ uv run hinge list projections
268
+ uv run hinge list readers
269
+ uv run hinge list exporters
270
+
271
+ # Export
272
+ uv run hinge export --dataset <id> --projection user-user-repo-collaboration --format gml -o out.gml
273
+
274
+ # Dev / CI
275
+ uv run pytest # tests
276
+ uv run ruff check . # lint
277
+ uv run ruff format . # format
278
+ uv run mypy hinge/kernel hinge/frontends # strict type-check
279
+ uv run lint-imports # enforce kernel/stages/frontends boundary
280
+ ```
281
+
282
+ ---
283
+
284
+ ## Makefile (development)
285
+
286
+ A `Makefile` is provided for quick local iteration. It uses hardcoded defaults
287
+ (fixture file, `numfocus` reader, `user-user-repo-collaboration` projection, GML format) so
288
+ you don't have to remember arguments during development — **not intended for
289
+ production use**.
290
+
291
+ ```bash
292
+ make install # uv sync --all-extras
293
+ make run # ingest fixture + export graph in one shot
294
+ make ingest # ingest tests/fixtures/events_10.jsonl --reader numfocus
295
+ make export # export the latest dataset (auto-detects ID from the store)
296
+ make test # pytest
297
+ make lint # ruff check
298
+ make fmt # ruff format
299
+ make typecheck # mypy on kernel + frontends
300
+ make clean # delete output/, dbt artefacts, caches
301
+ make reset # clean + delete network.duckdb (full fresh start)
302
+ ```
303
+
304
+ Any default can be overridden on the command line:
305
+
306
+ ```bash
307
+ make run READER=numfocus LOG_LEVEL=DEBUG
308
+ make export FORMAT=graphml OUTPUT=output/graph.graphml
309
+ ```
hinge-0.1.0/README.md ADDED
@@ -0,0 +1,265 @@
1
+ # HINGE — Heterogeneous Information Network for Generalized Extraction
2
+
3
+ `hinge` ingests GitHub activity datasets (JSONL today; CSV/Parquet planned), persists
4
+ them as a typed multi-relational graph in DuckDB, and exposes **projections** —
5
+ dbt SQL models that derive task-specific sub-graphs and export them to formats
6
+ consumed by Gephi, NetworkX, igraph, etc.
7
+
8
+ This is a research artefact accompanying an ICSME 2026 Tools and Data Showcase submission.
9
+
10
+ ---
11
+
12
+ ## Quick start
13
+
14
+ ```bash
15
+ # 1. Install (uv is the only supported package manager)
16
+ uv sync --all-extras
17
+
18
+ # 2. Ingest a dataset
19
+ uv run hinge ingest path/to/events.jsonl --reader numfocus
20
+ # → ingested 48 elements → 29 nodes, 19 edges (0 violations)
21
+ # → Dataset ID: 78fc87c370944dc2b4a4e2d4bdd97ce1
22
+
23
+ # 3. Use the printed Dataset ID to run a projection and export
24
+ uv run hinge export \
25
+ --dataset 78fc87c370944dc2b4a4e2d4bdd97ce1 \
26
+ --projection user-user-repo-collaboration \
27
+ --format gml \
28
+ -o out/graph.gml
29
+
30
+ # 4. Inspect all ingested datasets
31
+ uv run hinge list datasets
32
+ ```
33
+
34
+ Or via Docker:
35
+
36
+ ```bash
37
+ docker compose build
38
+ cp path/to/events.jsonl ./data/
39
+ docker compose run --rm hinge hinge ingest /data/events.jsonl --reader numfocus
40
+ docker compose run --rm hinge hinge export \
41
+ --dataset <id> --projection user-user-repo-collaboration --format gml -o /output/graph.gml
42
+ ```
43
+
44
+ The DuckDB store lives at `$HINGE_STORE_PATH` (default `./network.duckdb`
45
+ locally, `/store/network.duckdb` inside the container). Multiple datasets
46
+ can coexist in the same file — each ingest run gets a unique Dataset ID.
47
+
48
+ ---
49
+
50
+ ## HIN contract + adapters
51
+
52
+ The source-agnostic core starts at the adapter contract tables:
53
+
54
+ ```text
55
+ source-specific adapter
56
+ -> contract_accounts / contract_repositories / contract_artifacts / contract_relations
57
+ -> hin_nodes / hin_edges
58
+ -> dbt projections
59
+ -> exporters
60
+ ```
61
+
62
+ The `numfocus` reader uses a DuckDB adapter for this repo's NumFocus Actions
63
+ JSONL scrape. It is intentionally source-specific: it knows paths like
64
+ `$.actor.login` and `$.details.pull_request.id`. Other data sources should
65
+ implement their own adapter that writes the same `contract_*` tables; then the
66
+ HIN views and dbt recipes can run unchanged.
67
+
68
+ The generic Python `ReaderStage` path is still available for simple custom
69
+ readers, but large JSONL ingests should use a DuckDB/SQL contract adapter where
70
+ possible.
71
+
72
+ ---
73
+
74
+ ## Adding a new projection
75
+
76
+ A projection is a dbt SQL model that derives a task-specific sub-graph from
77
+ the typed HIN stored in DuckDB. Adding one requires exactly four files/edits
78
+ and no changes to the kernel.
79
+
80
+ ### Step 1 — Write the SQL model
81
+
82
+ Create `hinge/dbt/models/networks/<name>.sql`. The model should read from
83
+ the canonical HIN dbt models and produce a fixed set of columns:
84
+
85
+ ```sql
86
+ -- Inputs (prefer these canonical HIN models, never the raw tables)
87
+ {{ ref('hin_nodes') }}
88
+ {{ ref('hin_edges') }}
89
+
90
+ -- Output (prefer the network_edges macro; it emits this standard schema)
91
+ network_edges(
92
+ recipe_name, recipe_version,
93
+ source_node_id, source_node_type,
94
+ target_node_id, target_node_type,
95
+ directed, edge_type,
96
+ weight, weight_kind,
97
+ n_contexts, n_events,
98
+ first_seen_at, last_seen_at,
99
+ time_bin, bot_policy,
100
+ properties
101
+ )
102
+ ```
103
+
104
+ `DbtProjection` converts this richer schema into `TypedEdge` objects for existing
105
+ exporters, merging standard fields and `properties` into edge attrs.
106
+
107
+ The upstream HIN models are built from `active_*` views created by the store
108
+ immediately before dbt runs — they are already filtered to the requested
109
+ `dataset_id`, so network SQL never needs to reference `dataset_id` at all.
110
+
111
+ **Nodes-only projections:** the pipeline derives output nodes from the union
112
+ of `source_node_id` and `target_node_id` in the result table. A projection that
113
+ emits no edges will therefore produce no nodes either. The workaround is to use
114
+ **self-loop edges** (`source_node_id = target_node_id`): they make the nodes
115
+ visible to the exporter, carry metadata in `properties`, and can be filtered out
116
+ in downstream tools with `G.remove_edges_from(nx.selfloop_edges(G))`.
117
+
118
+ See [dev_interaction.sql](hinge/dbt/models/networks/dev_interaction.sql)
119
+ for a full working example with a documented input/output contract.
120
+
121
+ ### Step 2 — Create the spec module
122
+
123
+ Create `hinge/stages/projection/specs/<name>.py` and expose a `SPEC` constant:
124
+
125
+ ```python
126
+ from hinge.kernel.projection.projection_spec import ProjectionSpec
127
+
128
+ SPEC = ProjectionSpec(
129
+ name="my-projection", # CLI key: --projection my-projection
130
+ description="...",
131
+ model_name="my_projection", # must match the .sql file stem
132
+ output_node_types=["user"],
133
+ output_edge_types=["my_edge_label"],
134
+ )
135
+ ```
136
+
137
+ This is a plain value object — no class, no inheritance. The registry loads
138
+ the module and returns the `SPEC` attribute directly.
139
+
140
+ ### Step 3 — Register the entry-point
141
+
142
+ Add one line to `pyproject.toml`:
143
+
144
+ ```toml
145
+ [project.entry-points."hinge.projection_specs"]
146
+ my-projection = "hinge.stages.projection.specs.my_projection:SPEC"
147
+ ```
148
+
149
+ Third-party packages can register projections the same way — no fork required.
150
+
151
+ ### Step 4 — Re-install so the entry-point is picked up
152
+
153
+ ```bash
154
+ uv sync --all-extras
155
+ uv run hinge list projections # → my-projection should appear
156
+ ```
157
+
158
+ Entry-points are baked into `.dist-info/entry_points.txt` at install time.
159
+ Without this step the registry will not find the new spec.
160
+
161
+ ### Run it
162
+
163
+ ```bash
164
+ uv run hinge export \
165
+ --dataset <id> \
166
+ --projection my-projection \
167
+ --format gml \
168
+ -o output/result.gml
169
+ ```
170
+
171
+ ### Local SQL custom projection
172
+
173
+ For research-specific variants, write a local dbt model and run it without
174
+ packaging or entry-points:
175
+
176
+ ```bash
177
+ uv run hinge export-sql custom_star_user_repo.sql \
178
+ --dataset <id> \
179
+ --format gml \
180
+ -o output/custom.gml
181
+ ```
182
+
183
+ The SQL file is temporarily added to the built-in dbt project, so it can use
184
+ `ref('hin_edges')`, `ref('int_user_artifact_incidence')`, and all macros under
185
+ `hinge/dbt/macros/`. It must still emit the standard `network_edges` schema.
186
+ Use `--name valid_model_name` if the filename is not a valid dbt identifier.
187
+
188
+ See [docs/custom-projections.md](docs/custom-projections.md).
189
+
190
+ ---
191
+
192
+ ## Logging
193
+
194
+ Logs go to **stderr** by default. Use env vars to control verbosity and persistence:
195
+
196
+ | Variable | Purpose | Default |
197
+ |---|---|---|
198
+ | `HINGE_LOG_LEVEL` | `DEBUG` / `INFO` / `WARNING` / `ERROR` | `INFO` |
199
+ | `HINGE_LOG_FILE` | Also write logs to this file at full `DEBUG` detail | _(none)_ |
200
+
201
+ ```bash
202
+ # See all pipeline milestones (default)
203
+ uv run hinge ingest events.jsonl --reader numfocus
204
+
205
+ # See every batch, dbt SQL, store open/close
206
+ HINGE_LOG_LEVEL=DEBUG uv run hinge ingest events.jsonl --reader numfocus
207
+
208
+ # Persist a full debug log to disk (useful for long ingest runs)
209
+ HINGE_LOG_FILE=hinge.log uv run hinge ingest events.jsonl --reader numfocus
210
+ tail -f hinge.log
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Common commands
216
+
217
+ ```bash
218
+ # Ingest NumFocus Actions JSONL via the DuckDB -> HIN contract adapter
219
+ uv run hinge ingest events.jsonl --reader numfocus
220
+
221
+ # Inspect stored datasets
222
+ uv run hinge list datasets
223
+ uv run hinge list projections
224
+ uv run hinge list readers
225
+ uv run hinge list exporters
226
+
227
+ # Export
228
+ uv run hinge export --dataset <id> --projection user-user-repo-collaboration --format gml -o out.gml
229
+
230
+ # Dev / CI
231
+ uv run pytest # tests
232
+ uv run ruff check . # lint
233
+ uv run ruff format . # format
234
+ uv run mypy hinge/kernel hinge/frontends # strict type-check
235
+ uv run lint-imports # enforce kernel/stages/frontends boundary
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Makefile (development)
241
+
242
+ A `Makefile` is provided for quick local iteration. It uses hardcoded defaults
243
+ (fixture file, `numfocus` reader, `user-user-repo-collaboration` projection, GML format) so
244
+ you don't have to remember arguments during development — **not intended for
245
+ production use**.
246
+
247
+ ```bash
248
+ make install # uv sync --all-extras
249
+ make run # ingest fixture + export graph in one shot
250
+ make ingest # ingest tests/fixtures/events_10.jsonl --reader numfocus
251
+ make export # export the latest dataset (auto-detects ID from the store)
252
+ make test # pytest
253
+ make lint # ruff check
254
+ make fmt # ruff format
255
+ make typecheck # mypy on kernel + frontends
256
+ make clean # delete output/, dbt artefacts, caches
257
+ make reset # clean + delete network.duckdb (full fresh start)
258
+ ```
259
+
260
+ Any default can be overridden on the command line:
261
+
262
+ ```bash
263
+ make run READER=numfocus LOG_LEVEL=DEBUG
264
+ make export FORMAT=graphml OUTPUT=output/graph.graphml
265
+ ```
@@ -0,0 +1,11 @@
1
+ # hinge docs
2
+
3
+ Current maintained docs:
4
+
5
+ | File | Purpose |
6
+ |---|---|
7
+ | [architecture.md](architecture.md) | Microkernel + pipeline architecture, dependency rules, and extension points. |
8
+ | [custom-projections.md](custom-projections.md) | How to write and run local dbt SQL projections. |
9
+ | [architecture.drawio](architecture.drawio) | Current architecture diagram source. |
10
+
11
+ Historical design sketches, backup diagrams, and executable prototype notebook files were removed from `docs/` to keep the public documentation focused on the current `hinge` tool. They remain recoverable from git history.
@@ -0,0 +1,3 @@
1
+ from hinge.frontends.lib import export, export_sql_projection, ingest, project, project_sql
2
+
3
+ __all__ = ["export", "export_sql_projection", "ingest", "project", "project_sql"]
File without changes