erdscope 0.5.0__tar.gz → 0.6.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.
- {erdscope-0.5.0/erdscope.egg-info → erdscope-0.6.0}/PKG-INFO +129 -4
- {erdscope-0.5.0 → erdscope-0.6.0}/README.md +128 -3
- {erdscope-0.5.0 → erdscope-0.6.0}/erd.py +945 -71
- {erdscope-0.5.0 → erdscope-0.6.0/erdscope.egg-info}/PKG-INFO +129 -4
- {erdscope-0.5.0 → erdscope-0.6.0}/erdscope.egg-info/SOURCES.txt +3 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/pyproject.toml +1 -1
- {erdscope-0.5.0 → erdscope-0.6.0}/tests/test_config_validation.py +389 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/tests/test_e2e.py +521 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/tests/test_erd.py +116 -0
- erdscope-0.6.0/tests/test_groups.py +325 -0
- erdscope-0.6.0/tests/test_notes.py +556 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/tests/test_pipeline.py +73 -8
- erdscope-0.6.0/tests/test_provider_contract.py +221 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/LICENSE +0 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/erdscope.egg-info/dependency_links.txt +0 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/erdscope.egg-info/entry_points.txt +0 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/erdscope.egg-info/requires.txt +0 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/erdscope.egg-info/top_level.txt +0 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/setup.cfg +0 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/tests/test_build.py +0 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/tests/test_characterization.py +0 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/tests/test_db_integration.py +0 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/tests/test_demo.py +0 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/tests/test_merge_ir.py +0 -0
- {erdscope-0.5.0 → erdscope-0.6.0}/tests/test_rails_schema.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: erdscope
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Interactive, self-contained ER-diagram HTML and Excel table definitions from a MySQL, PostgreSQL, or SQLite database and/or application code (Rails / Prisma / Django) — single file, zero required dependencies
|
|
5
5
|
Author: tas6
|
|
6
6
|
License-Expression: MIT
|
|
@@ -37,9 +37,15 @@ Dynamic: license-file
|
|
|
37
37
|
[](https://github.com/orapli/erdscope/actions/workflows/ci.yml)
|
|
38
38
|
[](https://pypi.org/project/erdscope/)
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
**Interactive ER diagrams and documented schema definitions.** Generate a
|
|
41
|
+
**self-contained, interactive ER diagram** — and an **Excel table-definition
|
|
41
42
|
workbook** — from a live MySQL, PostgreSQL, or SQLite database, with a single-file,
|
|
42
|
-
zero-dependency Python CLI.
|
|
43
|
+
zero-dependency Python CLI. Turn an ER diagram into a documented schema: attach design
|
|
44
|
+
decisions, operational rules, and ADR links to tables and relationships with config
|
|
45
|
+
[`notes:`](#notes-attach-design-decisions-to-the-diagram) — plain text and http(s) links
|
|
46
|
+
only, validated against the real schema so a note can never point at something that
|
|
47
|
+
doesn't exist — and draw a rounded, titled frame around a set of related tables with config
|
|
48
|
+
[`groups:`](#groups-draw-a-frame-around-related-tables).
|
|
43
49
|
|
|
44
50
|
```bash
|
|
45
51
|
pip install erdscope
|
|
@@ -248,6 +254,12 @@ sources:
|
|
|
248
254
|
(The optional top-level `version: 1` is a config-format marker — currently the only
|
|
249
255
|
supported value, with no other effect; it's for future config-format changes to key off.)
|
|
250
256
|
|
|
257
|
+
A typed source that parses **nothing** — say, a Prisma project accidentally declared as
|
|
258
|
+
`rails.models` — is a hard error naming the source id and the layout the type expected,
|
|
259
|
+
never a silently empty diagram. If an empty result is genuinely intended (a scaffolded
|
|
260
|
+
but still-empty `app/models`, for example), opt in per source with `allow_empty: true`;
|
|
261
|
+
a `rails.project` entry passes the flag down to both of its expanded halves.
|
|
262
|
+
|
|
251
263
|
A **`rails.project`** entry is a macro for a whole Rails app root: it expands to both the
|
|
252
264
|
`rails.schema` (`<root>/db/schema.rb`) and `rails.models` (`<root>/app/models`) halves,
|
|
253
265
|
whichever exist —
|
|
@@ -267,6 +279,91 @@ closer to the real database than code is, but a live DB read still wins when bot
|
|
|
267
279
|
present. See the [Input sources chapter of the manual](https://orapli.github.io/erdscope/manual.html#input-sources)
|
|
268
280
|
for the full `sources[]` reference.
|
|
269
281
|
|
|
282
|
+
### Notes: attach design decisions to the diagram
|
|
283
|
+
|
|
284
|
+
Config **`notes:`** attaches short, plain-text write-ups — design decisions, operational
|
|
285
|
+
rules, ADR links — to a table, a specific relation, or the whole diagram. Notes never
|
|
286
|
+
touch the schema itself (no effect on columns, associations, or merge precedence); they're
|
|
287
|
+
a read-only sidecar validated against the final merged schema and rendered next to it:
|
|
288
|
+
|
|
289
|
+
```yaml
|
|
290
|
+
notes:
|
|
291
|
+
- id: user-retention
|
|
292
|
+
target: { type: table, table: users }
|
|
293
|
+
title: Retention policy
|
|
294
|
+
text: Suspended accounts are kept for 1 year, then anonymized.
|
|
295
|
+
links:
|
|
296
|
+
- { label: ADR-004, url: https://example.com/adr/004 }
|
|
297
|
+
|
|
298
|
+
- id: order-ownership
|
|
299
|
+
target: { type: relation, source_table: orders, target_table: users, foreign_key: user_id }
|
|
300
|
+
text: Orders are kept after a user is anonymized (financial record-keeping).
|
|
301
|
+
|
|
302
|
+
- id: diagram-conventions
|
|
303
|
+
target: { type: global }
|
|
304
|
+
title: How to read this diagram
|
|
305
|
+
text: The dotted amber edge is an inferred relation, not a real FK.
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
- Every note needs a config-unique `id` and non-empty `text`; `title` and `links` are
|
|
309
|
+
optional. Link `url`s must be `http://` or `https://` — anything else (`javascript:`,
|
|
310
|
+
`data:`, a bare string) is rejected at load time.
|
|
311
|
+
- `target.type` is `table`, `relation`, or `global`. A `relation` note is identified by
|
|
312
|
+
`source_table` (the side that **holds** the association — the `belongs_to`/FK-holding
|
|
313
|
+
side for a `belongs_to`, the owning side for a `has_many`) and `target_table`;
|
|
314
|
+
`foreign_key`/`name`/`assoc_type`/`through`/`polymorphic` are optional narrowing keys for
|
|
315
|
+
when a table has more than one relation to the same target (`assoc_type` is the
|
|
316
|
+
association kind — `has_many`/`belongs_to`/`has_one`/`has_and_belongs_to_many` — which
|
|
317
|
+
tells apart, say, a `has_many` and a `has_one` that share a name and target). A note that
|
|
318
|
+
matches no relation, or more than one, is a hard error naming the note's `id` — never a
|
|
319
|
+
silent guess.
|
|
320
|
+
- Validation runs **twice**, like `tables:` above: syntax at load time, then semantically
|
|
321
|
+
against the schema that results **after** DB/`--models`/config `tables:` are all merged
|
|
322
|
+
— so a note can reference a table or relation that config itself adds, and a note on
|
|
323
|
+
something config *removes* is correctly an error.
|
|
324
|
+
- Rendered as plain, HTML-escaped text only — no Markdown, no raw HTML, no scripts — in
|
|
325
|
+
the table's detail panel, next to the matching relation, or in the diagram's legend for
|
|
326
|
+
a `global` note. Notes are also searchable (title, text, link labels). A note whose
|
|
327
|
+
table is hidden from the current view simply doesn't appear in the (now absent) detail
|
|
328
|
+
panel; a `global` note's legend entry is unaffected.
|
|
329
|
+
- `write_excel` accepts the same notes but doesn't use them yet in this release — an Excel
|
|
330
|
+
Notes sheet is a possible future addition, not implemented today.
|
|
331
|
+
|
|
332
|
+
See the [Notes chapter of the manual](https://orapli.github.io/erdscope/manual.html#notes)
|
|
333
|
+
for the full reference.
|
|
334
|
+
|
|
335
|
+
### Groups: draw a frame around related tables
|
|
336
|
+
|
|
337
|
+
Config **`groups:`** draws a rounded frame with a title around a set of related tables in
|
|
338
|
+
the diagram — a lightweight way to call out a domain ("Billing", "Orders") without changing
|
|
339
|
+
the schema or the layout. Like `notes:`, it's a read-only sidecar validated against the
|
|
340
|
+
final merged schema:
|
|
341
|
+
|
|
342
|
+
```yaml
|
|
343
|
+
groups:
|
|
344
|
+
- id: billing
|
|
345
|
+
title: Billing
|
|
346
|
+
tables: [invoices, payments, coupons]
|
|
347
|
+
color: "#0d9488"
|
|
348
|
+
|
|
349
|
+
- id: catalog
|
|
350
|
+
tables: [products, categories, product_categories]
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
- Every group needs a config-unique `id` and non-empty `tables`; `title` (defaults to `id`)
|
|
354
|
+
and `color` (a hex string, e.g. `#0d9488`) are optional.
|
|
355
|
+
- A table may belong to **at most one** group — claiming the same table from two groups is
|
|
356
|
+
a hard error naming both group `id`s and the table, not a silently-picked winner. There's
|
|
357
|
+
no support for overlapping/nested groups in this release.
|
|
358
|
+
- Validation runs twice, like `notes:`/`tables:` above: syntax at load time, then
|
|
359
|
+
semantically against the final merged schema, so a group can reference a table config
|
|
360
|
+
itself adds, and a group naming something config *removes* is correctly an error.
|
|
361
|
+
- Purely visual: groups never affect layout, merge precedence, or associations — the frame
|
|
362
|
+
is drawn around wherever its member tables already ended up. In the viewer, drag a
|
|
363
|
+
group's title to move every member together; a "Groups" toolbar toggle shows/hides the
|
|
364
|
+
frames, and both PNG and SVG exports include them. `--only`/`--exclude` narrow a group's
|
|
365
|
+
membership down to the surviving tables, dropping the group entirely if none remain.
|
|
366
|
+
|
|
270
367
|
## Dependencies
|
|
271
368
|
|
|
272
369
|
`erd.py` runs with **zero required dependencies** — everything below is optional, and
|
|
@@ -292,6 +389,24 @@ Test-only, and only if you run that particular suite:
|
|
|
292
389
|
| [openpyxl](https://pypi.org/project/openpyxl/) | Roundtrip-verifying `--excel` output in the unit tests (`tests/test_erd.py`); that one test skips itself if it's missing |
|
|
293
390
|
| [Playwright](https://playwright.dev/python/) | The browser E2E suite (`tests/test_e2e.py`) — see [Tests](#tests) below |
|
|
294
391
|
|
|
392
|
+
## Verified versions
|
|
393
|
+
|
|
394
|
+
The input formats erdscope parses change rarely, so exact versions matter less than
|
|
395
|
+
they might seem — newer releases are expected to keep working, though constructs
|
|
396
|
+
beyond the tested ones may be ignored rather than parsed. For the record, this is what
|
|
397
|
+
each input is actually verified against:
|
|
398
|
+
|
|
399
|
+
| Input | Verified against |
|
|
400
|
+
|---|---|
|
|
401
|
+
| MySQL | **8.4** — real-server integration tests in CI (`information_schema`); developed against 8.x |
|
|
402
|
+
| PostgreSQL | **16** — real-server integration tests in CI (`pg_catalog`/`information_schema`) |
|
|
403
|
+
| SQLite | the `sqlite3` module bundled with CPython (any supported 3.x) |
|
|
404
|
+
| Rails `schema.rb` | the format Rails **7.x / 8.x** writes (`ActiveRecord::Schema[7.x]`, and the classic un-versioned header) |
|
|
405
|
+
| Rails models | the association DSL as of Rails **7.x** — `has_many`/`has_one`/`belongs_to`/`has_and_belongs_to_many`, `through:`, `polymorphic:`, STI, concerns, custom base classes; also exercised against Mastodon's real codebase. Dynamically computed definitions (and `structure.sql`) are out of scope |
|
|
406
|
+
| Prisma | the schema language as of Prisma **5 / 6** — `@map`/`@@map`, enums, named relations, implicit and explicit m2m, self-relations, composite `@@id`/`@@unique`, `@@schema` |
|
|
407
|
+
| Django | models as of Django **4.2 / 5.x** — FK / OneToOne / M2M (incl. `through=`), abstract bases, `db_table`/`db_column`, `GenericForeignKey` (kept as a polymorphic marker); a swappable `AUTH_USER_MODEL` FK keeps its column and skips the edge |
|
|
408
|
+
| Python | 3.9+ (`requires-python`); CI runs the latest CPython 3.x |
|
|
409
|
+
|
|
295
410
|
## What you get
|
|
296
411
|
|
|
297
412
|
Feature highlights — each link goes to the relevant [manual](https://orapli.github.io/erdscope/manual.html) chapter:
|
|
@@ -316,6 +431,12 @@ Feature highlights — each link goes to the relevant [manual](https://orapli.gi
|
|
|
316
431
|
the live view and exports
|
|
317
432
|
- **Extras** — a built-in `?` shortcuts/help popup, dark mode, print stylesheet,
|
|
318
433
|
resizable/collapsible panes
|
|
434
|
+
- **[Notes](#notes-attach-design-decisions-to-the-diagram)** — attach design decisions,
|
|
435
|
+
operational rules, and ADR links to a table, a relation, or the whole diagram, validated
|
|
436
|
+
against the real schema and searchable alongside tables/columns
|
|
437
|
+
- **[Groups](#groups-draw-a-frame-around-related-tables)** — draw a rounded, titled frame
|
|
438
|
+
around a set of related tables to call out a domain, purely visual, draggable by its
|
|
439
|
+
title, with its own toolbar toggle and export support
|
|
319
440
|
|
|
320
441
|
## Tests
|
|
321
442
|
|
|
@@ -324,7 +445,11 @@ python3 -m unittest discover -s tests -v
|
|
|
324
445
|
```
|
|
325
446
|
|
|
326
447
|
The IR builders and the Excel writer are covered by pure unit tests; the overlay
|
|
327
|
-
parsers have
|
|
448
|
+
parsers have fixtures under `tests/fixture_*` — basic plus advanced constructs per
|
|
449
|
+
framework — and every input provider is additionally held to one shared contract
|
|
450
|
+
(`tests/test_provider_contract.py`): typed dispatch, standalone HTML/Excel generation,
|
|
451
|
+
merging over a DB layer, 1:N / 1:1 / M:N / self-reference, association provenance, and
|
|
452
|
+
empty-input diagnostics, all over the same small domain. No database is required to
|
|
328
453
|
run the tests.
|
|
329
454
|
|
|
330
455
|
`tests/test_e2e.py` drives the generated HTML's client-side JS (grid layout,
|
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
[](https://github.com/orapli/erdscope/actions/workflows/ci.yml)
|
|
4
4
|
[](https://pypi.org/project/erdscope/)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
**Interactive ER diagrams and documented schema definitions.** Generate a
|
|
7
|
+
**self-contained, interactive ER diagram** — and an **Excel table-definition
|
|
7
8
|
workbook** — from a live MySQL, PostgreSQL, or SQLite database, with a single-file,
|
|
8
|
-
zero-dependency Python CLI.
|
|
9
|
+
zero-dependency Python CLI. Turn an ER diagram into a documented schema: attach design
|
|
10
|
+
decisions, operational rules, and ADR links to tables and relationships with config
|
|
11
|
+
[`notes:`](#notes-attach-design-decisions-to-the-diagram) — plain text and http(s) links
|
|
12
|
+
only, validated against the real schema so a note can never point at something that
|
|
13
|
+
doesn't exist — and draw a rounded, titled frame around a set of related tables with config
|
|
14
|
+
[`groups:`](#groups-draw-a-frame-around-related-tables).
|
|
9
15
|
|
|
10
16
|
```bash
|
|
11
17
|
pip install erdscope
|
|
@@ -214,6 +220,12 @@ sources:
|
|
|
214
220
|
(The optional top-level `version: 1` is a config-format marker — currently the only
|
|
215
221
|
supported value, with no other effect; it's for future config-format changes to key off.)
|
|
216
222
|
|
|
223
|
+
A typed source that parses **nothing** — say, a Prisma project accidentally declared as
|
|
224
|
+
`rails.models` — is a hard error naming the source id and the layout the type expected,
|
|
225
|
+
never a silently empty diagram. If an empty result is genuinely intended (a scaffolded
|
|
226
|
+
but still-empty `app/models`, for example), opt in per source with `allow_empty: true`;
|
|
227
|
+
a `rails.project` entry passes the flag down to both of its expanded halves.
|
|
228
|
+
|
|
217
229
|
A **`rails.project`** entry is a macro for a whole Rails app root: it expands to both the
|
|
218
230
|
`rails.schema` (`<root>/db/schema.rb`) and `rails.models` (`<root>/app/models`) halves,
|
|
219
231
|
whichever exist —
|
|
@@ -233,6 +245,91 @@ closer to the real database than code is, but a live DB read still wins when bot
|
|
|
233
245
|
present. See the [Input sources chapter of the manual](https://orapli.github.io/erdscope/manual.html#input-sources)
|
|
234
246
|
for the full `sources[]` reference.
|
|
235
247
|
|
|
248
|
+
### Notes: attach design decisions to the diagram
|
|
249
|
+
|
|
250
|
+
Config **`notes:`** attaches short, plain-text write-ups — design decisions, operational
|
|
251
|
+
rules, ADR links — to a table, a specific relation, or the whole diagram. Notes never
|
|
252
|
+
touch the schema itself (no effect on columns, associations, or merge precedence); they're
|
|
253
|
+
a read-only sidecar validated against the final merged schema and rendered next to it:
|
|
254
|
+
|
|
255
|
+
```yaml
|
|
256
|
+
notes:
|
|
257
|
+
- id: user-retention
|
|
258
|
+
target: { type: table, table: users }
|
|
259
|
+
title: Retention policy
|
|
260
|
+
text: Suspended accounts are kept for 1 year, then anonymized.
|
|
261
|
+
links:
|
|
262
|
+
- { label: ADR-004, url: https://example.com/adr/004 }
|
|
263
|
+
|
|
264
|
+
- id: order-ownership
|
|
265
|
+
target: { type: relation, source_table: orders, target_table: users, foreign_key: user_id }
|
|
266
|
+
text: Orders are kept after a user is anonymized (financial record-keeping).
|
|
267
|
+
|
|
268
|
+
- id: diagram-conventions
|
|
269
|
+
target: { type: global }
|
|
270
|
+
title: How to read this diagram
|
|
271
|
+
text: The dotted amber edge is an inferred relation, not a real FK.
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
- Every note needs a config-unique `id` and non-empty `text`; `title` and `links` are
|
|
275
|
+
optional. Link `url`s must be `http://` or `https://` — anything else (`javascript:`,
|
|
276
|
+
`data:`, a bare string) is rejected at load time.
|
|
277
|
+
- `target.type` is `table`, `relation`, or `global`. A `relation` note is identified by
|
|
278
|
+
`source_table` (the side that **holds** the association — the `belongs_to`/FK-holding
|
|
279
|
+
side for a `belongs_to`, the owning side for a `has_many`) and `target_table`;
|
|
280
|
+
`foreign_key`/`name`/`assoc_type`/`through`/`polymorphic` are optional narrowing keys for
|
|
281
|
+
when a table has more than one relation to the same target (`assoc_type` is the
|
|
282
|
+
association kind — `has_many`/`belongs_to`/`has_one`/`has_and_belongs_to_many` — which
|
|
283
|
+
tells apart, say, a `has_many` and a `has_one` that share a name and target). A note that
|
|
284
|
+
matches no relation, or more than one, is a hard error naming the note's `id` — never a
|
|
285
|
+
silent guess.
|
|
286
|
+
- Validation runs **twice**, like `tables:` above: syntax at load time, then semantically
|
|
287
|
+
against the schema that results **after** DB/`--models`/config `tables:` are all merged
|
|
288
|
+
— so a note can reference a table or relation that config itself adds, and a note on
|
|
289
|
+
something config *removes* is correctly an error.
|
|
290
|
+
- Rendered as plain, HTML-escaped text only — no Markdown, no raw HTML, no scripts — in
|
|
291
|
+
the table's detail panel, next to the matching relation, or in the diagram's legend for
|
|
292
|
+
a `global` note. Notes are also searchable (title, text, link labels). A note whose
|
|
293
|
+
table is hidden from the current view simply doesn't appear in the (now absent) detail
|
|
294
|
+
panel; a `global` note's legend entry is unaffected.
|
|
295
|
+
- `write_excel` accepts the same notes but doesn't use them yet in this release — an Excel
|
|
296
|
+
Notes sheet is a possible future addition, not implemented today.
|
|
297
|
+
|
|
298
|
+
See the [Notes chapter of the manual](https://orapli.github.io/erdscope/manual.html#notes)
|
|
299
|
+
for the full reference.
|
|
300
|
+
|
|
301
|
+
### Groups: draw a frame around related tables
|
|
302
|
+
|
|
303
|
+
Config **`groups:`** draws a rounded frame with a title around a set of related tables in
|
|
304
|
+
the diagram — a lightweight way to call out a domain ("Billing", "Orders") without changing
|
|
305
|
+
the schema or the layout. Like `notes:`, it's a read-only sidecar validated against the
|
|
306
|
+
final merged schema:
|
|
307
|
+
|
|
308
|
+
```yaml
|
|
309
|
+
groups:
|
|
310
|
+
- id: billing
|
|
311
|
+
title: Billing
|
|
312
|
+
tables: [invoices, payments, coupons]
|
|
313
|
+
color: "#0d9488"
|
|
314
|
+
|
|
315
|
+
- id: catalog
|
|
316
|
+
tables: [products, categories, product_categories]
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
- Every group needs a config-unique `id` and non-empty `tables`; `title` (defaults to `id`)
|
|
320
|
+
and `color` (a hex string, e.g. `#0d9488`) are optional.
|
|
321
|
+
- A table may belong to **at most one** group — claiming the same table from two groups is
|
|
322
|
+
a hard error naming both group `id`s and the table, not a silently-picked winner. There's
|
|
323
|
+
no support for overlapping/nested groups in this release.
|
|
324
|
+
- Validation runs twice, like `notes:`/`tables:` above: syntax at load time, then
|
|
325
|
+
semantically against the final merged schema, so a group can reference a table config
|
|
326
|
+
itself adds, and a group naming something config *removes* is correctly an error.
|
|
327
|
+
- Purely visual: groups never affect layout, merge precedence, or associations — the frame
|
|
328
|
+
is drawn around wherever its member tables already ended up. In the viewer, drag a
|
|
329
|
+
group's title to move every member together; a "Groups" toolbar toggle shows/hides the
|
|
330
|
+
frames, and both PNG and SVG exports include them. `--only`/`--exclude` narrow a group's
|
|
331
|
+
membership down to the surviving tables, dropping the group entirely if none remain.
|
|
332
|
+
|
|
236
333
|
## Dependencies
|
|
237
334
|
|
|
238
335
|
`erd.py` runs with **zero required dependencies** — everything below is optional, and
|
|
@@ -258,6 +355,24 @@ Test-only, and only if you run that particular suite:
|
|
|
258
355
|
| [openpyxl](https://pypi.org/project/openpyxl/) | Roundtrip-verifying `--excel` output in the unit tests (`tests/test_erd.py`); that one test skips itself if it's missing |
|
|
259
356
|
| [Playwright](https://playwright.dev/python/) | The browser E2E suite (`tests/test_e2e.py`) — see [Tests](#tests) below |
|
|
260
357
|
|
|
358
|
+
## Verified versions
|
|
359
|
+
|
|
360
|
+
The input formats erdscope parses change rarely, so exact versions matter less than
|
|
361
|
+
they might seem — newer releases are expected to keep working, though constructs
|
|
362
|
+
beyond the tested ones may be ignored rather than parsed. For the record, this is what
|
|
363
|
+
each input is actually verified against:
|
|
364
|
+
|
|
365
|
+
| Input | Verified against |
|
|
366
|
+
|---|---|
|
|
367
|
+
| MySQL | **8.4** — real-server integration tests in CI (`information_schema`); developed against 8.x |
|
|
368
|
+
| PostgreSQL | **16** — real-server integration tests in CI (`pg_catalog`/`information_schema`) |
|
|
369
|
+
| SQLite | the `sqlite3` module bundled with CPython (any supported 3.x) |
|
|
370
|
+
| Rails `schema.rb` | the format Rails **7.x / 8.x** writes (`ActiveRecord::Schema[7.x]`, and the classic un-versioned header) |
|
|
371
|
+
| Rails models | the association DSL as of Rails **7.x** — `has_many`/`has_one`/`belongs_to`/`has_and_belongs_to_many`, `through:`, `polymorphic:`, STI, concerns, custom base classes; also exercised against Mastodon's real codebase. Dynamically computed definitions (and `structure.sql`) are out of scope |
|
|
372
|
+
| Prisma | the schema language as of Prisma **5 / 6** — `@map`/`@@map`, enums, named relations, implicit and explicit m2m, self-relations, composite `@@id`/`@@unique`, `@@schema` |
|
|
373
|
+
| Django | models as of Django **4.2 / 5.x** — FK / OneToOne / M2M (incl. `through=`), abstract bases, `db_table`/`db_column`, `GenericForeignKey` (kept as a polymorphic marker); a swappable `AUTH_USER_MODEL` FK keeps its column and skips the edge |
|
|
374
|
+
| Python | 3.9+ (`requires-python`); CI runs the latest CPython 3.x |
|
|
375
|
+
|
|
261
376
|
## What you get
|
|
262
377
|
|
|
263
378
|
Feature highlights — each link goes to the relevant [manual](https://orapli.github.io/erdscope/manual.html) chapter:
|
|
@@ -282,6 +397,12 @@ Feature highlights — each link goes to the relevant [manual](https://orapli.gi
|
|
|
282
397
|
the live view and exports
|
|
283
398
|
- **Extras** — a built-in `?` shortcuts/help popup, dark mode, print stylesheet,
|
|
284
399
|
resizable/collapsible panes
|
|
400
|
+
- **[Notes](#notes-attach-design-decisions-to-the-diagram)** — attach design decisions,
|
|
401
|
+
operational rules, and ADR links to a table, a relation, or the whole diagram, validated
|
|
402
|
+
against the real schema and searchable alongside tables/columns
|
|
403
|
+
- **[Groups](#groups-draw-a-frame-around-related-tables)** — draw a rounded, titled frame
|
|
404
|
+
around a set of related tables to call out a domain, purely visual, draggable by its
|
|
405
|
+
title, with its own toolbar toggle and export support
|
|
285
406
|
|
|
286
407
|
## Tests
|
|
287
408
|
|
|
@@ -290,7 +411,11 @@ python3 -m unittest discover -s tests -v
|
|
|
290
411
|
```
|
|
291
412
|
|
|
292
413
|
The IR builders and the Excel writer are covered by pure unit tests; the overlay
|
|
293
|
-
parsers have
|
|
414
|
+
parsers have fixtures under `tests/fixture_*` — basic plus advanced constructs per
|
|
415
|
+
framework — and every input provider is additionally held to one shared contract
|
|
416
|
+
(`tests/test_provider_contract.py`): typed dispatch, standalone HTML/Excel generation,
|
|
417
|
+
merging over a DB layer, 1:N / 1:1 / M:N / self-reference, association provenance, and
|
|
418
|
+
empty-input diagnostics, all over the same small domain. No database is required to
|
|
294
419
|
run the tests.
|
|
295
420
|
|
|
296
421
|
`tests/test_e2e.py` drives the generated HTML's client-side JS (grid layout,
|