prisma-flow 0.1.1__py3-none-any.whl

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.
@@ -0,0 +1,236 @@
1
+ Metadata-Version: 2.4
2
+ Name: prisma-flow
3
+ Version: 0.1.1
4
+ Summary: Lightweight Python tools for generating PRISMA-style flow diagrams without system dependencies.
5
+ License-Expression: BSD-3-Clause
6
+ License-File: LICENSE
7
+ Keywords: prisma,systematic-review,scoping-review,evidence-synthesis,literature-review,svg,python,open-science
8
+ Author: Open Science Labs Incubator contributors
9
+ Requires-Python: >=3.10,<4
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Intended Audience :: Healthcare Industry
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Classifier: Topic :: Text Processing :: Markup
20
+ Provides-Extra: dev
21
+ Provides-Extra: docs
22
+ Provides-Extra: png
23
+ Provides-Extra: yaml
24
+ Requires-Dist: douki (>=0.12.1) ; extra == "dev"
25
+ Requires-Dist: makim (==1.29.0) ; extra == "dev"
26
+ Requires-Dist: mypy (>=1.10) ; extra == "dev"
27
+ Requires-Dist: pre-commit (>=3) ; extra == "dev"
28
+ Requires-Dist: pydantic (>=2)
29
+ Requires-Dist: pytest (>=8) ; extra == "dev"
30
+ Requires-Dist: pytest-cov (>=5) ; extra == "dev"
31
+ Requires-Dist: pyyaml (>=6) ; extra == "yaml"
32
+ Requires-Dist: resvg (>=0.1.2) ; extra == "png"
33
+ Requires-Dist: ruff (>=0.6) ; extra == "dev"
34
+ Project-URL: Documentation, https://osl-incubator.github.io/prisma-flow/
35
+ Project-URL: Homepage, https://github.com/osl-incubator/prisma-flow
36
+ Project-URL: Issues, https://github.com/osl-incubator/prisma-flow/issues
37
+ Project-URL: Repository, https://github.com/osl-incubator/prisma-flow
38
+ Description-Content-Type: text/markdown
39
+
40
+ # prisma-flow
41
+
42
+ ![CI](https://img.shields.io/github/actions/workflow/status/osl-incubator/prisma-flow/ci.yml?logo=github&label=CI)
43
+ [![Python Versions](https://img.shields.io/pypi/pyversions/prisma-flow)](https://pypi.org/project/prisma-flow/)
44
+ [![Package Version](https://img.shields.io/pypi/v/prisma-flow?color=blue)](https://pypi.org/project/prisma-flow/)
45
+ ![License](https://img.shields.io/pypi/l/prisma-flow?color=blue)
46
+
47
+ `prisma-flow` is a lightweight Python package for generating PRISMA-style flow
48
+ diagrams for evidence synthesis workflows.
49
+
50
+ Unlike Graphviz-based tools, `prisma-flow` does not require system-level graph
51
+ layout binaries. Unlike Mermaid-based tools, it does not require Node or Mermaid
52
+ CLI. The default renderer is a pure-Python, template-based SVG generator.
53
+
54
+ The project is designed for systematic reviews, scoping reviews, evidence
55
+ syntheses, and literature review workflows.
56
+
57
+ ## Features
58
+
59
+ - Pure-Python SVG rendering by default
60
+ - Standalone HTML export
61
+ - Mermaid text export without Mermaid CLI
62
+ - JSON input/output in the base install
63
+ - Optional YAML input/output via `prisma-flow[yaml]`
64
+ - Optional PNG method that clearly reports the missing optional dependency
65
+ - Python API and `prisma-flow` command-line interface
66
+ - PRISMA count validation with errors and warnings
67
+
68
+ ## Installation
69
+
70
+ ```bash
71
+ pip install prisma-flow
72
+ ```
73
+
74
+ or:
75
+
76
+ ```bash
77
+ uv add prisma-flow
78
+ ```
79
+
80
+ Optional YAML support:
81
+
82
+ ```bash
83
+ uv add "prisma-flow[yaml]"
84
+ ```
85
+
86
+ Optional PNG support, when a supported backend is added:
87
+
88
+ ```bash
89
+ uv add "prisma-flow[png]"
90
+ ```
91
+
92
+ ## Python API
93
+
94
+ ```python
95
+ from prismaflow import PrismaFlow
96
+
97
+ flow = PrismaFlow.new_review(
98
+ records_identified_databases=1240,
99
+ records_identified_registers=50,
100
+ records_removed_duplicates=210,
101
+ records_removed_automation=0,
102
+ records_removed_other=0,
103
+ records_screened=1080,
104
+ records_excluded=950,
105
+ reports_sought=130,
106
+ reports_not_retrieved=10,
107
+ reports_assessed=120,
108
+ reports_excluded={
109
+ "Wrong population": 30,
110
+ "Wrong intervention": 20,
111
+ "Wrong outcome": 15,
112
+ "Not primary research": 15,
113
+ },
114
+ studies_included=40,
115
+ )
116
+
117
+ report = flow.validate()
118
+ print(report.format_text())
119
+
120
+ flow.to_svg("prisma.svg")
121
+ flow.to_html("prisma.html")
122
+ flow.to_mermaid("prisma.mmd")
123
+ flow.to_json("review.json")
124
+ ```
125
+
126
+ ## CLI usage
127
+
128
+ Validate input data:
129
+
130
+ ```bash
131
+ prisma-flow validate examples/basic_new_review.json
132
+ ```
133
+
134
+ Render SVG:
135
+
136
+ ```bash
137
+ prisma-flow render examples/basic_new_review.json -o prisma.svg
138
+ ```
139
+
140
+ Render other base-install formats:
141
+
142
+ ```bash
143
+ prisma-flow render examples/basic_new_review.json --format html -o prisma.html
144
+ prisma-flow render examples/basic_new_review.json --format mermaid -o prisma.mmd
145
+ ```
146
+
147
+ If validation fails, the CLI prints a report and exits with a non-zero status:
148
+
149
+ ```text
150
+ Validation failed:
151
+ - records_screened should equal identified records minus removed records. Expected: 1080 Found: 1090
152
+ ```
153
+
154
+ ## Data model
155
+
156
+ The v0.1 implementation supports the PRISMA 2020 new-review databases/registers
157
+ template:
158
+
159
+ ```python
160
+ from prismaflow import (
161
+ EligibilityStage,
162
+ IdentificationStage,
163
+ IncludedStage,
164
+ PrismaFlow,
165
+ PrismaTemplate,
166
+ ScreeningStage,
167
+ )
168
+
169
+ flow = PrismaFlow(
170
+ template=PrismaTemplate.PRISMA_2020_NEW_DATABASES_REGISTERS,
171
+ identification=IdentificationStage(
172
+ records_identified_databases=1240,
173
+ records_identified_registers=50,
174
+ ),
175
+ screening=ScreeningStage(
176
+ records_removed_duplicates=210,
177
+ records_removed_automation=0,
178
+ records_removed_other=0,
179
+ records_screened=1080,
180
+ records_excluded=950,
181
+ ),
182
+ eligibility=EligibilityStage(
183
+ reports_sought=130,
184
+ reports_not_retrieved=10,
185
+ reports_assessed=120,
186
+ reports_excluded={"Wrong population": 30},
187
+ ),
188
+ included=IncludedStage(studies_included=40),
189
+ )
190
+ ```
191
+
192
+ ## Dependency policy
193
+
194
+ SVG, HTML, Mermaid, and JSON work with the base install. YAML is optional. PNG
195
+ is intentionally optional and not implemented as a required renderer in v0.1.
196
+
197
+ The package does **not** require Graphviz, Cairo, CairoSVG, Node, Mermaid CLI,
198
+ Inkscape, Playwright, browser engines, Matplotlib, or Plotly.
199
+
200
+ ## Development
201
+
202
+ ```bash
203
+ conda env create -f conda/dev.yaml
204
+ conda activate prismaflow
205
+ poetry config virtualenvs.create false
206
+ poetry install --extras "dev yaml"
207
+ ```
208
+
209
+ Run the same workflow through Makim:
210
+
211
+ ```bash
212
+ makim tests.linter
213
+ makim tests.unit
214
+ makim package.build
215
+ makim docs.build
216
+ makim all.ci
217
+ ```
218
+
219
+ ## Documentation
220
+
221
+ The documentation site is built with Quarto:
222
+
223
+ ```bash
224
+ quarto render docs
225
+ ```
226
+
227
+ Preview locally:
228
+
229
+ ```bash
230
+ quarto preview docs
231
+ ```
232
+
233
+ ## License
234
+
235
+ BSD-3-Clause.
236
+
@@ -0,0 +1,28 @@
1
+ prismaflow/__init__.py,sha256=899hbwyttDc5loJRvFGwzphxCKlmQMcOPN-TZdO4C2E,912
2
+ prismaflow/cli.py,sha256=gQqb42DbN7nr-SAKVvAzW36wxgfB_2fVvUILmFgHv84,5778
3
+ prismaflow/enums.py,sha256=OVrVv0viAyCe0yTw6c9HULmR1LeNASrsKD3Qzzyj27E,575
4
+ prismaflow/exceptions.py,sha256=ErfXBgK7UXuzkz1Hfy0ckHhjLRH0RZNDsOMKiAQqYVs,541
5
+ prismaflow/io/__init__.py,sha256=dEy96FtJNwnAp5sUw-NMBVXEKD84PaRRwxSDXMqBwRE,206
6
+ prismaflow/io/json.py,sha256=A5yNY7FEXtCgRjehL7M7oOp7KJD3-dDcujWxwKW4lsc,1632
7
+ prismaflow/io/yaml.py,sha256=QpK-uYr-AkjClW44EmfDBJzNrBlnOk1lShhc0waPn_s,3244
8
+ prismaflow/layout/__init__.py,sha256=bpd8kq8lK3-4412XWb0R0Cr7BatyAY-lXpEpeg_xB1A,448
9
+ prismaflow/layout/engine.py,sha256=T933eQCp4ugiIjFJNZtOpaYwwM_El0XR-ZlJqNmNB7Y,4026
10
+ prismaflow/layout/geometry.py,sha256=igOLToIzG3aIj8Y48mn93tP5s4UcL_wueDwGYnPaJnM,3529
11
+ prismaflow/layout/overlap.py,sha256=DFX6F2uUmEAp-VCjMffQI3s7Ynhda1jDiaFBb_tEGo4,1455
12
+ prismaflow/layout/text.py,sha256=oIJ1WrWyBMKIrv0E2vQ1vA4gyrlVJ3EaUqklQLBOMPo,849
13
+ prismaflow/models.py,sha256=rOggTlHZuvJo58JBQrZd-5wruLl-c7f0Y8-PXVivKf4,14839
14
+ prismaflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ prismaflow/renderers/__init__.py,sha256=ssDxOwN30HbAwGo0iq6TnNMUQDQPFZCzrrmIVYUOqYo,317
16
+ prismaflow/renderers/html.py,sha256=CUjYycp_5KdZsosQJSgFYTR6x9kvidhjZko3PK2nWlc,1391
17
+ prismaflow/renderers/mermaid.py,sha256=s8A3fO_W4DMAsTvIFnvZzZYHACuxGTE7Zbj1qAIiKkQ,1949
18
+ prismaflow/renderers/png.py,sha256=kLLU4P_AuLIIRT-Ehgbx8SS5whytQn1NnML6L6HhYA8,1052
19
+ prismaflow/renderers/svg.py,sha256=msM8Mqlu1s3L_EpyMb6TraJpQxeRfMfCKQJBPI59mJk,7927
20
+ prismaflow/templates/__init__.py,sha256=RLHizwx71HVJWPDrY0m-LOVdBFO9KBJhjnHzarfhhhE,223
21
+ prismaflow/templates/base.py,sha256=L5INco9lgdbEd6STwbifM-tpZrl_2ITeXisOXvHI4ZE,598
22
+ prismaflow/templates/prisma_2020_new.py,sha256=kkdeL2mJg7fsnwTzPUJUijfcDE65oPRM0vJwN6sHfUg,7218
23
+ prismaflow/validation.py,sha256=f8qKNUqUQg3hvHNLWRDH8gBhOlombseE2fLMAw45mRQ,8225
24
+ prisma_flow-0.1.1.dist-info/METADATA,sha256=bUTJn4d0oppiMzkfOkEQ8ADd_AKlxWAYCKfupdKYWGo,6397
25
+ prisma_flow-0.1.1.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
26
+ prisma_flow-0.1.1.dist-info/entry_points.txt,sha256=TeiZqSlnbxszvNXeJ2BWhMAgTNSu0OvpUKdIwzrvcqw,51
27
+ prisma_flow-0.1.1.dist-info/licenses/LICENSE,sha256=vY8jl7ZbJTe7b45QSxc7JAy_FC7eZnnOFI1xz70Ab68,1514
28
+ prisma_flow-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.4.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ prisma-flow=prismaflow.cli:main
3
+
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Open Science Labs Incubator
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
prismaflow/__init__.py ADDED
@@ -0,0 +1,40 @@
1
+ """
2
+ title: Lightweight PRISMA-style flow diagram generation.
3
+ """
4
+
5
+ from prismaflow.enums import PrismaTemplate
6
+ from prismaflow.exceptions import (
7
+ OptionalDependencyError,
8
+ PrismaFlowError,
9
+ PrismaValidationError,
10
+ TemplateNotSupportedError,
11
+ )
12
+ from prismaflow.models import (
13
+ EligibilityStage,
14
+ FlowMetadata,
15
+ IdentificationStage,
16
+ IncludedStage,
17
+ PrismaFlow,
18
+ ScreeningStage,
19
+ )
20
+ from prismaflow.validation import ValidationMessage, ValidationReport, validate_flow
21
+
22
+ __version__ = "0.1.1" # semantic-release
23
+
24
+ __all__ = [
25
+ "EligibilityStage",
26
+ "FlowMetadata",
27
+ "IdentificationStage",
28
+ "IncludedStage",
29
+ "OptionalDependencyError",
30
+ "PrismaFlow",
31
+ "PrismaFlowError",
32
+ "PrismaTemplate",
33
+ "PrismaValidationError",
34
+ "ScreeningStage",
35
+ "TemplateNotSupportedError",
36
+ "ValidationMessage",
37
+ "ValidationReport",
38
+ "__version__",
39
+ "validate_flow",
40
+ ]
prismaflow/cli.py ADDED
@@ -0,0 +1,208 @@
1
+ """
2
+ title: Command-line interface for prisma-flow.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import argparse
8
+ import sys
9
+ from collections.abc import Sequence
10
+ from pathlib import Path
11
+
12
+ from pydantic import ValidationError as PydanticValidationError
13
+
14
+ from prismaflow.exceptions import OptionalDependencyError, PrismaFlowError
15
+ from prismaflow.models import PrismaFlow
16
+
17
+ RenderFormat = str
18
+
19
+
20
+ def main(argv: Sequence[str] | None = None) -> int:
21
+ """
22
+ title: Run the prisma-flow command-line interface.
23
+ parameters:
24
+ argv:
25
+ type: Sequence[str] | None
26
+ description: Value for argv.
27
+ returns:
28
+ type: int
29
+ description: Return value.
30
+ """
31
+ parser = build_parser()
32
+ args = parser.parse_args(argv)
33
+ try:
34
+ if args.command == "validate":
35
+ return _validate_command(args)
36
+ if args.command == "render":
37
+ return _render_command(args)
38
+ except PydanticValidationError as exc:
39
+ print("Input model validation failed:", file=sys.stderr)
40
+ print(str(exc), file=sys.stderr)
41
+ return 2
42
+ except OptionalDependencyError as exc:
43
+ print(str(exc), file=sys.stderr)
44
+ return 2
45
+ except PrismaFlowError as exc:
46
+ print(str(exc), file=sys.stderr)
47
+ return 2
48
+ parser.print_help(sys.stderr)
49
+ return 2
50
+
51
+
52
+ def build_parser() -> argparse.ArgumentParser:
53
+ """
54
+ title: Build the CLI argument parser.
55
+ returns:
56
+ type: argparse.ArgumentParser
57
+ description: Return value.
58
+ """
59
+ parser = argparse.ArgumentParser(
60
+ prog="prisma-flow",
61
+ description="Generate PRISMA-style flow diagrams without system dependencies.",
62
+ )
63
+ subparsers = parser.add_subparsers(dest="command", required=True)
64
+
65
+ validate = subparsers.add_parser("validate", help="validate a PRISMA flow file")
66
+ validate.add_argument("input", help="input JSON/YAML file")
67
+ validate.add_argument(
68
+ "--strict-included",
69
+ action="store_true",
70
+ help="treat included-study reconciliation warnings as errors",
71
+ )
72
+
73
+ render = subparsers.add_parser("render", help="render a PRISMA flow file")
74
+ render.add_argument("input", help="input JSON/YAML file")
75
+ render.add_argument(
76
+ "-f",
77
+ "--format",
78
+ choices=["svg", "html", "mermaid", "png", "json", "yaml"],
79
+ help="output format; inferred from --output when omitted",
80
+ )
81
+ render.add_argument("-o", "--output", help="output path")
82
+ render.add_argument(
83
+ "--allow-invalid",
84
+ action="store_true",
85
+ help="render even when PRISMA count validation has errors",
86
+ )
87
+ return parser
88
+
89
+
90
+ def _validate_command(args: argparse.Namespace) -> int:
91
+ """
92
+ title: _validate_command.
93
+ parameters:
94
+ args:
95
+ type: argparse.Namespace
96
+ description: Value for args.
97
+ returns:
98
+ type: int
99
+ description: Return value.
100
+ """
101
+ flow = _load_flow(args.input)
102
+ report = flow.validate(strict_included=args.strict_included)
103
+ print(report.format_text())
104
+ return 0 if report.ok else 1
105
+
106
+
107
+ def _render_command(args: argparse.Namespace) -> int:
108
+ """
109
+ title: _render_command.
110
+ parameters:
111
+ args:
112
+ type: argparse.Namespace
113
+ description: Value for args.
114
+ returns:
115
+ type: int
116
+ description: Return value.
117
+ """
118
+ flow = _load_flow(args.input)
119
+ report = flow.validate()
120
+ if report.has_errors and not args.allow_invalid:
121
+ print(report.format_text(), file=sys.stderr)
122
+ return 1
123
+ if report.has_warnings:
124
+ print(report.format_text(), file=sys.stderr)
125
+
126
+ output_format = args.format or _infer_format(args.output)
127
+ rendered = _render(flow, output_format, args.output)
128
+ if args.output is None and isinstance(rendered, str):
129
+ print(rendered, end="")
130
+ return 0
131
+
132
+
133
+ def _load_flow(path: str | Path) -> PrismaFlow:
134
+ """
135
+ title: _load_flow.
136
+ parameters:
137
+ path:
138
+ type: str | Path
139
+ description: Value for path.
140
+ returns:
141
+ type: PrismaFlow
142
+ description: Return value.
143
+ """
144
+ suffix = Path(path).suffix.lower()
145
+ if suffix in {".yaml", ".yml"}:
146
+ return PrismaFlow.from_yaml(path)
147
+ return PrismaFlow.from_json(path)
148
+
149
+
150
+ def _infer_format(output: str | None) -> RenderFormat:
151
+ """
152
+ title: _infer_format.
153
+ parameters:
154
+ output:
155
+ type: str | None
156
+ description: Value for output.
157
+ returns:
158
+ type: RenderFormat
159
+ description: Return value.
160
+ """
161
+ if not output:
162
+ return "svg"
163
+ suffix = Path(output).suffix.lower().lstrip(".")
164
+ if suffix == "mmd":
165
+ return "mermaid"
166
+ if suffix in {"svg", "html", "png", "json", "yaml", "yml"}:
167
+ return "yaml" if suffix == "yml" else suffix
168
+ return "svg"
169
+
170
+
171
+ def _render(
172
+ flow: PrismaFlow,
173
+ output_format: RenderFormat,
174
+ output: str | None,
175
+ ) -> str | bytes:
176
+ """
177
+ title: _render.
178
+ parameters:
179
+ flow:
180
+ type: PrismaFlow
181
+ description: Value for flow.
182
+ output_format:
183
+ type: RenderFormat
184
+ description: Value for output_format.
185
+ output:
186
+ type: str | None
187
+ description: Value for output.
188
+ returns:
189
+ type: str | bytes
190
+ description: Return value.
191
+ """
192
+ if output_format == "svg":
193
+ return flow.to_svg(output)
194
+ if output_format == "html":
195
+ return flow.to_html(output)
196
+ if output_format == "mermaid":
197
+ return flow.to_mermaid(output)
198
+ if output_format == "png":
199
+ return flow.to_png(output)
200
+ if output_format == "json":
201
+ return flow.to_json(output)
202
+ if output_format == "yaml":
203
+ return flow.to_yaml(output)
204
+ raise PrismaFlowError(f"Unsupported output format: {output_format}")
205
+
206
+
207
+ if __name__ == "__main__": # pragma: no cover
208
+ raise SystemExit(main())
prismaflow/enums.py ADDED
@@ -0,0 +1,20 @@
1
+ """
2
+ title: Enumerations used by prisma-flow.
3
+ """
4
+
5
+ from enum import Enum
6
+
7
+
8
+ class PrismaTemplate(str, Enum):
9
+ """
10
+ title: Supported and planned PRISMA diagram templates.
11
+ """
12
+
13
+ PRISMA_2020_NEW_DATABASES_REGISTERS = "prisma_2020_new_databases_registers"
14
+ PRISMA_2020_NEW_DATABASES_REGISTERS_OTHER = (
15
+ "prisma_2020_new_databases_registers_other"
16
+ )
17
+ PRISMA_2020_UPDATED_DATABASES_REGISTERS = "prisma_2020_updated_databases_registers"
18
+ PRISMA_2020_UPDATED_DATABASES_REGISTERS_OTHER = (
19
+ "prisma_2020_updated_databases_registers_other"
20
+ )
@@ -0,0 +1,27 @@
1
+ """
2
+ title: Exceptions raised by prisma-flow.
3
+ """
4
+
5
+
6
+ class PrismaFlowError(Exception):
7
+ """
8
+ title: Base exception for prisma-flow errors.
9
+ """
10
+
11
+
12
+ class TemplateNotSupportedError(PrismaFlowError):
13
+ """
14
+ title: Raised when a requested PRISMA template is not implemented.
15
+ """
16
+
17
+
18
+ class OptionalDependencyError(PrismaFlowError):
19
+ """
20
+ title: Raised when an optional export backend is not installed.
21
+ """
22
+
23
+
24
+ class PrismaValidationError(PrismaFlowError):
25
+ """
26
+ title: Raised when a flow has validation errors.
27
+ """
@@ -0,0 +1,8 @@
1
+ """
2
+ title: Input/output helpers.
3
+ """
4
+
5
+ from prismaflow.io.json import dump_json, load_json
6
+ from prismaflow.io.yaml import dump_yaml, load_yaml
7
+
8
+ __all__ = ["dump_json", "dump_yaml", "load_json", "load_yaml"]
prismaflow/io/json.py ADDED
@@ -0,0 +1,64 @@
1
+ """
2
+ title: JSON input/output helpers.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from pathlib import Path
8
+
9
+ from prismaflow.models import PrismaFlow
10
+
11
+
12
+ def load_json(source: str | Path) -> PrismaFlow:
13
+ """
14
+ title: Load a PrismaFlow model from a JSON file path or JSON string.
15
+ parameters:
16
+ source:
17
+ type: str | Path
18
+ description: Value for source.
19
+ returns:
20
+ type: PrismaFlow
21
+ description: Return value.
22
+ """
23
+ if isinstance(source, Path) or _looks_like_path(source):
24
+ path = Path(source)
25
+ if path.exists():
26
+ return PrismaFlow.model_validate_json(path.read_text(encoding="utf-8"))
27
+ return PrismaFlow.model_validate_json(str(source))
28
+
29
+
30
+ def dump_json(flow: PrismaFlow, path: str | Path | None = None) -> str:
31
+ """
32
+ title: Serialize a flow to JSON and optionally write it.
33
+ parameters:
34
+ flow:
35
+ type: PrismaFlow
36
+ description: Value for flow.
37
+ path:
38
+ type: str | Path | None
39
+ description: Value for path.
40
+ returns:
41
+ type: str
42
+ description: Return value.
43
+ """
44
+ output = flow.model_dump_json(indent=2)
45
+ if path is not None:
46
+ Path(path).write_text(output + "\n", encoding="utf-8")
47
+ return output
48
+
49
+
50
+ def _looks_like_path(source: str | Path) -> bool:
51
+ """
52
+ title: _looks_like_path.
53
+ parameters:
54
+ source:
55
+ type: str | Path
56
+ description: Value for source.
57
+ returns:
58
+ type: bool
59
+ description: Return value.
60
+ """
61
+ if isinstance(source, Path):
62
+ return True
63
+ text = str(source)
64
+ return text.endswith(".json") or "/" in text or "\\" in text