oracle2ssrs 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.
- oracle2ssrs-1.0.0/.gitignore +74 -0
- oracle2ssrs-1.0.0/LICENSE +21 -0
- oracle2ssrs-1.0.0/PKG-INFO +425 -0
- oracle2ssrs-1.0.0/README.md +397 -0
- oracle2ssrs-1.0.0/backend/app.py +1841 -0
- oracle2ssrs-1.0.0/backend/cli.py +342 -0
- oracle2ssrs-1.0.0/backend/converter/__init__.py +899 -0
- oracle2ssrs-1.0.0/backend/converter/artifact_enrich.py +481 -0
- oracle2ssrs-1.0.0/backend/converter/audit.py +417 -0
- oracle2ssrs-1.0.0/backend/converter/batch.py +305 -0
- oracle2ssrs-1.0.0/backend/converter/bundle_export.py +252 -0
- oracle2ssrs-1.0.0/backend/converter/burst_driver.ps1.txt +446 -0
- oracle2ssrs-1.0.0/backend/converter/burst_pack.py +840 -0
- oracle2ssrs-1.0.0/backend/converter/bursting.py +855 -0
- oracle2ssrs-1.0.0/backend/converter/compare.py +215 -0
- oracle2ssrs-1.0.0/backend/converter/cross_validate.py +316 -0
- oracle2ssrs-1.0.0/backend/converter/deployment.py +339 -0
- oracle2ssrs-1.0.0/backend/converter/fidelity.py +306 -0
- oracle2ssrs-1.0.0/backend/converter/generators/__init__.py +0 -0
- oracle2ssrs-1.0.0/backend/converter/generators/rdl.py +32657 -0
- oracle2ssrs-1.0.0/backend/converter/ingest.py +607 -0
- oracle2ssrs-1.0.0/backend/converter/licensing.py +55 -0
- oracle2ssrs-1.0.0/backend/converter/models.py +557 -0
- oracle2ssrs-1.0.0/backend/converter/parsers/__init__.py +0 -0
- oracle2ssrs-1.0.0/backend/converter/parsers/oracle_colors.py +249 -0
- oracle2ssrs-1.0.0/backend/converter/parsers/oracle_xml.py +2685 -0
- oracle2ssrs-1.0.0/backend/converter/preview/__init__.py +0 -0
- oracle2ssrs-1.0.0/backend/converter/preview/html_mockup.py +6037 -0
- oracle2ssrs-1.0.0/backend/converter/preview/live_data.py +462 -0
- oracle2ssrs-1.0.0/backend/converter/preview/mockup_variants.py +187 -0
- oracle2ssrs-1.0.0/backend/converter/rdl_postprocess.py +344 -0
- oracle2ssrs-1.0.0/backend/converter/subreports.py +1694 -0
- oracle2ssrs-1.0.0/backend/converter/translators/__init__.py +0 -0
- oracle2ssrs-1.0.0/backend/converter/translators/format_exception.py +401 -0
- oracle2ssrs-1.0.0/backend/converter/translators/plsql_formula.py +1220 -0
- oracle2ssrs-1.0.0/backend/converter/translators/plsql_to_tsql.py +1287 -0
- oracle2ssrs-1.0.0/backend/converter/translators/udf_stubs.py +388 -0
- oracle2ssrs-1.0.0/backend/converter/validators/__init__.py +0 -0
- oracle2ssrs-1.0.0/backend/converter/validators/coverage.py +146 -0
- oracle2ssrs-1.0.0/backend/converter/validators/layout_audit.py +119 -0
- oracle2ssrs-1.0.0/backend/converter/validators/mockup_audit.py +203 -0
- oracle2ssrs-1.0.0/backend/converter/validators/no_prompt_gate.py +564 -0
- oracle2ssrs-1.0.0/backend/converter/validators/preflight.py +1340 -0
- oracle2ssrs-1.0.0/backend/converter/validators/publish_semantics.py +1267 -0
- oracle2ssrs-1.0.0/backend/converter/validators/rdl_check.py +537 -0
- oracle2ssrs-1.0.0/backend/converter/validators/sql_syntax.py +200 -0
- oracle2ssrs-1.0.0/backend/converter/validators/tsql_check.py +515 -0
- oracle2ssrs-1.0.0/backend/converter/validators/vb_expr_check.py +157 -0
- oracle2ssrs-1.0.0/backend/converter/validators/vb_expr_eval.py +780 -0
- oracle2ssrs-1.0.0/backend/db/sample.sqlite +0 -0
- oracle2ssrs-1.0.0/backend/db/seed_sample_db.py +237 -0
- oracle2ssrs-1.0.0/frontend/static/css/style.css +1344 -0
- oracle2ssrs-1.0.0/frontend/static/js/app.js +4929 -0
- oracle2ssrs-1.0.0/frontend/static/js/compare_mode.js +397 -0
- oracle2ssrs-1.0.0/frontend/static/js/demo_mode.js +405 -0
- oracle2ssrs-1.0.0/frontend/static/js/highlight.js +208 -0
- oracle2ssrs-1.0.0/frontend/static/js/scroll_regions.js +180 -0
- oracle2ssrs-1.0.0/frontend/static/js/theme.js +136 -0
- oracle2ssrs-1.0.0/frontend/templates/index.html +725 -0
- oracle2ssrs-1.0.0/oracle2ssrs/__init__.py +22 -0
- oracle2ssrs-1.0.0/oracle2ssrs/__main__.py +44 -0
- oracle2ssrs-1.0.0/pyproject.toml +70 -0
- oracle2ssrs-1.0.0/samples/oracle/SAMPLE_FEE_NOTICE.xml +165 -0
- oracle2ssrs-1.0.0/samples/oracle/SAMPLE_INSPECTION.xml +175 -0
- oracle2ssrs-1.0.0/tools/renderlab/README.md +63 -0
- oracle2ssrs-1.0.0/tools/renderlab/RenderLab.cs +100 -0
- oracle2ssrs-1.0.0/tools/renderlab/RenderLab.exe +0 -0
- oracle2ssrs-1.0.0/tools/renderlab/RenderLab.exe.config +14 -0
- oracle2ssrs-1.0.0/tools/renderlab/blank_measure.py +2560 -0
- oracle2ssrs-1.0.0/tools/renderlab/chart_ink.py +215 -0
- oracle2ssrs-1.0.0/tools/renderlab/fetch_reportviewer.py +123 -0
- oracle2ssrs-1.0.0/tools/renderlab/html_to_png.py +44 -0
- oracle2ssrs-1.0.0/tools/renderlab/ms_layout.py +660 -0
- oracle2ssrs-1.0.0/tools/renderlab/rdl_preview.py +95 -0
- oracle2ssrs-1.0.0/tools/renderlab/render.py +245 -0
- oracle2ssrs-1.0.0/tools/renderlab/render_expr.ps1 +187 -0
- oracle2ssrs-1.0.0/tools/renderlab/render_overlap.py +368 -0
- oracle2ssrs-1.0.0/tools/renderlab/render_rdl.ps1 +138 -0
- oracle2ssrs-1.0.0/tools/renderlab/run_corpus.py +129 -0
- oracle2ssrs-1.0.0/tools/renderlab/shape_matrix.py +174 -0
- oracle2ssrs-1.0.0/tools/renderlab/vb_expr_check.ps1 +407 -0
- oracle2ssrs-1.0.0/tools/renderlab/vb_expr_eval.ps1 +682 -0
- oracle2ssrs-1.0.0/tools/renderlab/vb_invoke.ps1 +99 -0
- oracle2ssrs-1.0.0/tools/renderlab/vb_invoke.py +66 -0
- oracle2ssrs-1.0.0/tools/ssrscheck/README.md +69 -0
- oracle2ssrs-1.0.0/tools/ssrscheck/server_render.ps1 +122 -0
- oracle2ssrs-1.0.0/tools/ssrscheck/smoke_deploy.ps1 +101 -0
- oracle2ssrs-1.0.0/tools/ssrscheck/smoke_deploy.py +221 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Secrets / credentials -- NEVER commit these (the repo is public)
|
|
2
|
+
.env
|
|
3
|
+
*.env
|
|
4
|
+
!.env.example
|
|
5
|
+
backend/.env
|
|
6
|
+
|
|
7
|
+
# Python
|
|
8
|
+
__pycache__/
|
|
9
|
+
*.py[cod]
|
|
10
|
+
*$py.class
|
|
11
|
+
*.so
|
|
12
|
+
.Python
|
|
13
|
+
build/
|
|
14
|
+
develop-eggs/
|
|
15
|
+
dist/
|
|
16
|
+
downloads/
|
|
17
|
+
eggs/
|
|
18
|
+
.eggs/
|
|
19
|
+
lib/
|
|
20
|
+
lib64/
|
|
21
|
+
parts/
|
|
22
|
+
sdist/
|
|
23
|
+
var/
|
|
24
|
+
wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
.venv/
|
|
29
|
+
venv/
|
|
30
|
+
ENV/
|
|
31
|
+
env/
|
|
32
|
+
.pytest_cache/
|
|
33
|
+
pytest-cache-files-*/
|
|
34
|
+
|
|
35
|
+
# Editor / IDE
|
|
36
|
+
.vscode/
|
|
37
|
+
.idea/
|
|
38
|
+
*.swp
|
|
39
|
+
*.swo
|
|
40
|
+
.DS_Store
|
|
41
|
+
Thumbs.db
|
|
42
|
+
|
|
43
|
+
# OS / mount artifacts that bit us all session
|
|
44
|
+
*.sqlite-journal
|
|
45
|
+
*.sqlite-wal
|
|
46
|
+
*.sqlite-shm
|
|
47
|
+
|
|
48
|
+
# Logs
|
|
49
|
+
*.log
|
|
50
|
+
flask.log
|
|
51
|
+
|
|
52
|
+
# Don't commit user-uploaded artifacts (only the bundled samples)
|
|
53
|
+
backend/uploads/
|
|
54
|
+
|
|
55
|
+
# Local Flask runtime
|
|
56
|
+
instance/
|
|
57
|
+
|
|
58
|
+
# Optional: ignore generated RDL outputs (keep the canonical sample)
|
|
59
|
+
samples/expected_rdl/*.rdl
|
|
60
|
+
!samples/expected_rdl/SAMPLE_INSPECTION.rdl
|
|
61
|
+
|
|
62
|
+
# RenderLab: fetched Microsoft DLLs + rendered verification output
|
|
63
|
+
tools/renderlab/lib/
|
|
64
|
+
tools/renderlab/out/
|
|
65
|
+
|
|
66
|
+
# Local scratch runner (never commit)
|
|
67
|
+
_scratch_run.py
|
|
68
|
+
|
|
69
|
+
# Private pre-push watchlist (tools/preflight_push.py) -- contains the very
|
|
70
|
+
# terms that must never appear in this public repo, so it NEVER ships.
|
|
71
|
+
.canary_local
|
|
72
|
+
|
|
73
|
+
# Local editor/assistant tooling (per-machine, never ships)
|
|
74
|
+
.claude/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Max Casteel (github.com/Maximilian741)
|
|
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,425 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: oracle2ssrs
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Convert Oracle Reports XML exports into SSRS RDL reports.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ahmedmgh67/OFR2SSRS
|
|
6
|
+
Project-URL: Source, https://github.com/ahmedmgh67/OFR2SSRS
|
|
7
|
+
Author: Max Casteel
|
|
8
|
+
Maintainer: Ahmed Gamal
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: oracle-reports,rdl,report-migration,ssrs
|
|
12
|
+
Classifier: Framework :: Flask
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Database
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: flask>=3.1.3
|
|
18
|
+
Requires-Dist: lxml>=6.1.3
|
|
19
|
+
Requires-Dist: pymupdf<2,>=1.28.2
|
|
20
|
+
Requires-Dist: pypdf>=6.21.1
|
|
21
|
+
Requires-Dist: python-docx>=1.2.0
|
|
22
|
+
Requires-Dist: python-dotenv>=1.2.4
|
|
23
|
+
Requires-Dist: waitress>=3.0.2
|
|
24
|
+
Requires-Dist: werkzeug>=3.1.9
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: pytest>=9.1.1; extra == 'test'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# Oracle Reports to SSRS Converter
|
|
30
|
+
|
|
31
|
+
**License: [MIT](LICENSE)** — free to use, modify, distribute and build on,
|
|
32
|
+
including commercial and government use, with no fee and no lock-in.
|
|
33
|
+
|
|
34
|
+
Drag an Oracle Reports artifact in, get a deployable SSRS RDL out.
|
|
35
|
+
|
|
36
|
+
Oracle Reports is desupported. Oracle2SSRS converts your reports to SSRS in
|
|
37
|
+
minutes — and **proves** the output instead of promising it:
|
|
38
|
+
|
|
39
|
+
- **791 automated tests**, including renders through **Microsoft's own
|
|
40
|
+
ReportViewer engine** (the same RDL processing code SSRS runs) with the
|
|
41
|
+
produced PDFs measured for page cadence, blank pages, and geometry.
|
|
42
|
+
- **Generated RDL validated** against Microsoft's official RDL 2008 XSD
|
|
43
|
+
schema (`ReportDefinition_2008.xsd`), loaded directly in the test suite.
|
|
44
|
+
- **Never prompts for parameter values** — at upload, Refresh Fields, or
|
|
45
|
+
run. Every parameter and query bind ships wired.
|
|
46
|
+
- **Batch a whole folder** and get a Migration Assessment: per-report
|
|
47
|
+
effort tiers, fidelity scores, and engine-render verdicts.
|
|
48
|
+
|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
## What it does
|
|
52
|
+
|
|
53
|
+
Oracle2SSRS is a local Flask app that ingests Oracle Reports artifacts —
|
|
54
|
+
Oracle Reports XML, raw `.sql`, `.docx` walkthroughs, `.rdf` exports, and
|
|
55
|
+
PNG/JPG reference screenshots, including whole folders of mixed files — and
|
|
56
|
+
emits a structurally valid SSRS 2008+ `.rdl` you can upload to a real Report
|
|
57
|
+
Server. It translates Oracle SQL and PL/SQL formula logic into computable
|
|
58
|
+
SSRS expressions, reproduces the original paper-layout geometry, and wires
|
|
59
|
+
every parameter so the report runs without prompting.
|
|
60
|
+
|
|
61
|
+
A four-pane preview lets you verify the conversion before you deploy: the
|
|
62
|
+
rendered HTML mockup, the raw RDL, bursting (per-recipient distribution)
|
|
63
|
+
detection, and sub-report (drill-through) child generation.
|
|
64
|
+
|
|
65
|
+
## Proves, not promises
|
|
66
|
+
|
|
67
|
+
Most converters stop at "the XML parsed." Oracle2SSRS holds its output to
|
|
68
|
+
two independent standards that catch different classes of defect:
|
|
69
|
+
|
|
70
|
+
- **Schema-valid RDL.** Every generated `.rdl` is validated against
|
|
71
|
+
Microsoft's official `ReportDefinition_2008.xsd` using `lxml`'s
|
|
72
|
+
`XMLSchema`. This is enforced across the test suite, so structurally
|
|
73
|
+
invalid RDL cannot ship.
|
|
74
|
+
- **Engine-rendered RDL.** XSD validation proves the document is
|
|
75
|
+
well-formed; it cannot prove the report lays out correctly. So
|
|
76
|
+
`tools/renderlab` drives Microsoft's ReportViewer `LocalReport` engine —
|
|
77
|
+
the same RDL processing code SSRS runs — to render real PDFs from
|
|
78
|
+
generated RDLs with synthetic data, then measures them for page cadence,
|
|
79
|
+
blank pages, and geometry. This catches publish-time semantic rules that
|
|
80
|
+
XSD validation cannot (for example aggregate-in-`Lookup`, and
|
|
81
|
+
`TablixMember` `RepeatOnNewPage` consistency).
|
|
82
|
+
- **Compile-verified expressions.** A schema-valid RDL that renders can still
|
|
83
|
+
carry a *VB.NET expression* that throws `#Error` the moment the report
|
|
84
|
+
server evaluates it. So every generated `=...` expression — and the report's
|
|
85
|
+
own `<Code>` block — is compiled through the real `System.CodeDom`
|
|
86
|
+
`VBCodeProvider`, the same compilation SSRS performs at publish time. A bad
|
|
87
|
+
`IIf` arity, a trailing comma, an undefined function, or a leaked Oracle `||`
|
|
88
|
+
is caught here, before download, instead of in your live report. (Runs on
|
|
89
|
+
Windows with the .NET Framework; degrades to a clean skip elsewhere.)
|
|
90
|
+
|
|
91
|
+
Run `python tools/renderlab/fetch_reportviewer.py` once to fetch the
|
|
92
|
+
official Microsoft runtime from nuget.org; the rendering checks then run as
|
|
93
|
+
part of `pytest`. They degrade gracefully when the engine host is
|
|
94
|
+
unavailable.
|
|
95
|
+
|
|
96
|
+
## Quick start
|
|
97
|
+
|
|
98
|
+
### Install with pip
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pip install oracle2ssrs
|
|
102
|
+
|
|
103
|
+
oracle2ssrs # web app at http://127.0.0.1:5057
|
|
104
|
+
oracle2ssrs-convert reports/ --out out # batch-convert without the UI
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`oracle2ssrs --host 0.0.0.0 --port 8080` serves other machines on another
|
|
108
|
+
port. Settings (`O2S_*`) are read from a `.env` file in the folder you
|
|
109
|
+
start it from. Requires Python 3.10+.
|
|
110
|
+
|
|
111
|
+
### Run from source
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
git clone https://github.com/Maximilian741/OFR2SSRS.git
|
|
115
|
+
cd OFR2SSRS
|
|
116
|
+
|
|
117
|
+
# 1. ONE-TIME: install the Python dependencies (the launcher never does this)
|
|
118
|
+
python -m pip install -r requirements.txt
|
|
119
|
+
|
|
120
|
+
# 2. Start the app
|
|
121
|
+
run.bat # Windows
|
|
122
|
+
./run.sh # macOS / Linux / WSL / Git Bash
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The launcher only **starts** the app — it deliberately never installs
|
|
126
|
+
anything (on locked-down machines an implicit `pip install` triggers install
|
|
127
|
+
activity the user may be unable to act on). If step 1 was skipped, the
|
|
128
|
+
launcher stops and prints the exact command instead of a Python traceback:
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
ModuleNotFoundError: No module named 'flask' <- what you see without step 1
|
|
132
|
+
python -m pip install -r requirements.txt <- the one-time fix
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Then open <http://127.0.0.1:5057> and drop an Oracle Reports artifact on the
|
|
136
|
+
page. Override the listen port with `PORT=8080 ./run.sh`. Requires
|
|
137
|
+
Python 3.10+.
|
|
138
|
+
|
|
139
|
+
To install into a virtualenv instead:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
python -m venv .venv
|
|
143
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
144
|
+
pip install -r requirements.txt
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## How it works — the four-pane UI
|
|
148
|
+
|
|
149
|
+
Every conversion surfaces **four main tabs**:
|
|
150
|
+
|
|
151
|
+
- **Preview** — what the report actually prints. The *frontend* mode
|
|
152
|
+
renders the generated RDL through **Microsoft's own ReportViewer
|
|
153
|
+
engine** and shows the resulting pages (an instant HTML mockup stands
|
|
154
|
+
in while the engine works, then the true pages swap in); a Sample-rows
|
|
155
|
+
control re-renders at any sample size. The *backend* mode shows the
|
|
156
|
+
Report-Builder skeleton with field-name placeholders. On machines
|
|
157
|
+
without the engine the preview degrades gracefully (rendered PDF, then
|
|
158
|
+
mockup) and always says which renderer produced what you see.
|
|
159
|
+
- **RDL XML** — syntax-highlighted, structurally valid SSRS RDL. Download it
|
|
160
|
+
and upload straight to your Report Server.
|
|
161
|
+
- **Bursting** — automatic detection of Oracle distribution patterns
|
|
162
|
+
(one output file per record, file-name templates, mail destinations) with
|
|
163
|
+
a downloadable **Burst Pack**: the report with a hidden per-key filter
|
|
164
|
+
parameter, a key-list report, and a zero-install PowerShell driver that
|
|
165
|
+
renders one file per key through SSRS URL access on any edition.
|
|
166
|
+
- **Sub-Reports** — when the parser detects drill-through child reports, this
|
|
167
|
+
tab lets you upload the child Oracle XML/SQL/DOCX and generates an RDL for
|
|
168
|
+
each child.
|
|
169
|
+
|
|
170
|
+
Advanced views (side-by-side diff, validation, deploy checklist, and an
|
|
171
|
+
Extras panel with the fidelity report and audit trail) are one click away behind
|
|
172
|
+
an **Advanced views** toggle.
|
|
173
|
+
|
|
174
|
+
## Feature highlights
|
|
175
|
+
|
|
176
|
+
### PL/SQL formula translation
|
|
177
|
+
|
|
178
|
+
The core feature: a real deterministic compiler — a tokenizer plus a
|
|
179
|
+
precedence-climbing parser — not pattern substitution. It compiles an Oracle
|
|
180
|
+
`CF_`/`CP_` formula body into an SSRS VB.NET expression that computes inline.
|
|
181
|
+
|
|
182
|
+
- **Body reduction** folds `IF`/`ELSIF`/`ELSE` and local `var := expr`
|
|
183
|
+
assignments into a single effective return expression (nested `CASE`).
|
|
184
|
+
- **Operator and function mappings** include `||` → `&`, `NVL`/`NVL2`/
|
|
185
|
+
`COALESCE` → `IIf(IsNothing(...))`, `DECODE` → nested `IIf`, searched
|
|
186
|
+
`CASE` → nested `IIf`, plus `SUBSTR`→`Mid`, `INSTR`→`InStr`,
|
|
187
|
+
`LENGTH`→`Len`, `UPPER`/`LOWER`→`UCase`/`LCase`, `REPLACE`/`LPAD`/`RPAD`/
|
|
188
|
+
`TRIM`/`INITCAP`, `TO_CHAR`→`Format` (with Oracle→.NET mask translation),
|
|
189
|
+
`TO_NUMBER`→`CDbl`, `TO_DATE`→`CDate`, `ROUND`/`TRUNC`/`FLOOR`/`CEIL`/
|
|
190
|
+
`ABS`/`POWER`/`MOD`/`SIGN`/`GREATEST`/`LEAST`, `SYSDATE`→`Now()`, and
|
|
191
|
+
`CHR(10/13/9)`→`vbLf`/`vbCr`/`vbTab`.
|
|
192
|
+
- **Conditional `CP_` placeholders** (side-effect `:CP_X := expr` outputs,
|
|
193
|
+
including build-up and branch assignments) are recovered into a `CASE`.
|
|
194
|
+
- **Correlated cross-dataset counts** compile to
|
|
195
|
+
`=LookupSet(key, key, Fields!result.Value, "child").Length`.
|
|
196
|
+
- **Honest fallback.** A formula is reported as translated only when the
|
|
197
|
+
*whole* expression compiles with no unknown calls. An external package
|
|
198
|
+
function is recorded as `unresolved`, and when a formula cannot be
|
|
199
|
+
computed the resolver strips the `CF_`/`CP_`/`CS_`/`CG_`/`CT_` prefix and
|
|
200
|
+
binds to a same-stem real column (via `Lookup` if cross-dataset) — so a
|
|
201
|
+
broken expression never reaches SSRS.
|
|
202
|
+
|
|
203
|
+
The companion SQL translation has a rule-based core (`DECODE`, `NVL`,
|
|
204
|
+
`TO_CHAR`, `TO_DATE`, `TRUNC`, `SYSDATE`, `INSTR`, `SUBSTR`, `CHR`, `||`,
|
|
205
|
+
`(+)`, `LISTAGG`, `ROWNUM`, bind variables, lexical references) and supports
|
|
206
|
+
two SQL targets (see below).
|
|
207
|
+
|
|
208
|
+
### Parameter filters that actually filter
|
|
209
|
+
|
|
210
|
+
Oracle reports often build their `WHERE` clause from a **lexical parameter** —
|
|
211
|
+
a chunk of SQL *text* (`&P_CRITERIA`) that a `BEFORE REPORT` trigger assembles
|
|
212
|
+
at run time from the user's prompts (year, date range, name). SSRS has no
|
|
213
|
+
lexical parameter, so a naive conversion drops that text and the report runs
|
|
214
|
+
**unfiltered** — the prompts render but do nothing.
|
|
215
|
+
|
|
216
|
+
Oracle2SSRS recognizes the standard "criteria builder" idiom (a trigger
|
|
217
|
+
concatenating `F_Criteria_*_Bind(column, 'P_param')` fragments against
|
|
218
|
+
`cv*` column constants) and **reconstructs it into real, `NULL`-safe,
|
|
219
|
+
parameter-bound predicates**:
|
|
220
|
+
|
|
221
|
+
```sql
|
|
222
|
+
AND (:P_Year IS NULL OR TO_CHAR(O.Order_Date,'YYYY') = UPPER(TRIM(:P_Year)))
|
|
223
|
+
AND (:P_Date_From IS NULL OR TRUNC(O.Status_Date) >= :P_Date_From)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Each declared filter parameter is bound as a real query parameter, so picking a
|
|
227
|
+
value filters and leaving it blank widens to all rows — exactly the Oracle
|
|
228
|
+
behavior. Only predicates whose column resolves to a known constant are
|
|
229
|
+
emitted; anything the tool can't reconstruct with confidence is left with an
|
|
230
|
+
honest placeholder rather than a wrong filter.
|
|
231
|
+
|
|
232
|
+
### Layout fidelity
|
|
233
|
+
|
|
234
|
+
- **Positional document rendering** — Oracle paper-layout geometry
|
|
235
|
+
(Top/Left/Width/Height) drives the SSRS body. Single-record forms route to
|
|
236
|
+
a positioned per-record body; tabular sources get a grid. The generator
|
|
237
|
+
picks the right `<Body>` shape for the source's frame structure instead of
|
|
238
|
+
forcing every report into one mold.
|
|
239
|
+
- **Per-segment mixed fonts** — a mixed-font Oracle text field (for example
|
|
240
|
+
an unbold caption next to a bold value) is parsed into runs and emitted as
|
|
241
|
+
one paragraph per segment, each keeping its own weight, size, italic,
|
|
242
|
+
underline, and color; it collapses to the uniform path when the segments
|
|
243
|
+
do not actually differ.
|
|
244
|
+
- **Embedded seals / logos / watermarks** — images stored in the Oracle
|
|
245
|
+
export's `binaryData` (both inline and document-level styles, including a
|
|
246
|
+
nibble-swapped hex encoding some exports use) are decoded, MIME-sniffed,
|
|
247
|
+
and emitted as RDL `<EmbeddedImages>` and into the mockup. Layout
|
|
248
|
+
placeholders without bytes get an upload slot in the sidebar, previewed
|
|
249
|
+
live in the mockup.
|
|
250
|
+
- **White-border-over-watermark fix** — a white border on a white or
|
|
251
|
+
transparent textbox is invisible on the page but paints over a seal; the
|
|
252
|
+
generator suppresses it (`BorderStyle=None`) in that case while keeping a
|
|
253
|
+
real white gridline over a colored background.
|
|
254
|
+
- **Rotated fields** — Oracle `rotationAngle` (for example a sideways
|
|
255
|
+
window-envelope address) maps to SSRS `WritingMode` Rotate270.
|
|
256
|
+
- **Color resolution** — Oracle color names and indices are resolved to hex.
|
|
257
|
+
|
|
258
|
+
### Sub-reports, drill-through, and envelopes
|
|
259
|
+
|
|
260
|
+
When the parser detects drill-through child reports, the Sub-Reports tab
|
|
261
|
+
generates an RDL for each child, declaring the parent's drill-through
|
|
262
|
+
parameters. Parameterized links to an envelope/cover report are emitted with
|
|
263
|
+
a deploy note, and rotated-field handling covers window-envelope address
|
|
264
|
+
blocks.
|
|
265
|
+
|
|
266
|
+
### Bursting
|
|
267
|
+
|
|
268
|
+
Oracle *distribution* (one output file per permit / invoice / recipient,
|
|
269
|
+
written from one run) is detected from the source's own distribution
|
|
270
|
+
declarations and reproduced by a downloadable **Burst Pack**:
|
|
271
|
+
|
|
272
|
+
- the report's RDL gains ONE hidden parameter (`P_O2S_BURST_KEY`) and ONE
|
|
273
|
+
dataset filter -- set it and the report shows that key; leave it empty and
|
|
274
|
+
the report is exactly the one you would deploy anyway (SQL untouched);
|
|
275
|
+
- a companion `<Report>_BurstList.rdl` groups the same dataset by the key,
|
|
276
|
+
so the report server itself produces the key list (rendered as CSV);
|
|
277
|
+
- `Run-Burst.ps1` renders the report once per key through SSRS URL access
|
|
278
|
+
and saves each file under the Oracle file name (or emails it). Windows
|
|
279
|
+
PowerShell 5.1 built-ins only -- nothing is installed on any server, and
|
|
280
|
+
it can be tried from a desk PC with `-DryRun` / `-TestLimit 2` before a
|
|
281
|
+
service account and Task Scheduler take over.
|
|
282
|
+
|
|
283
|
+
SSRS Enterprise's native Data-Driven Subscriptions remain the alternative;
|
|
284
|
+
the pack's README gives the key-list SQL to paste into one.
|
|
285
|
+
|
|
286
|
+
### Batch migration + Migration Assessment
|
|
287
|
+
|
|
288
|
+
Point the CLI at a whole folder of Oracle XML exports:
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
python tools/batch_convert.py path/to/reports -o out --render
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Every report is converted, preflighted, fidelity-scored, optionally
|
|
295
|
+
render-verified through the MS engine, and classified into an effort tier
|
|
296
|
+
(`automatic` / `light-touch` / `assisted` / `manual`). You get
|
|
297
|
+
`out/rdl/*.rdl`, a printable `ASSESSMENT.html` executive summary, and a
|
|
298
|
+
`migration_pack.zip`. The same flow is available in the app sidebar ("Batch
|
|
299
|
+
migration") and over HTTP (`POST /api/batch`). The Community Edition
|
|
300
|
+
processes up to 10 reports per batch (single-report conversion is always
|
|
301
|
+
unlimited); set `O2S_LICENSE=pro` to lift the cap.
|
|
302
|
+
|
|
303
|
+
### Zero-prompt parameters and data-source binding
|
|
304
|
+
|
|
305
|
+
- **Shared data source reference** — the RDL emits a
|
|
306
|
+
`<DataSourceReference>` to a named shared data source (with cached
|
|
307
|
+
credentials) rather than an embedded connection block. This is the
|
|
308
|
+
load-bearing design that stops the "Define Query Parameters" prompt at
|
|
309
|
+
Refresh Fields. Type your report server's shared data source path once in
|
|
310
|
+
the sidebar (for example `/Data Sources/MyOracle`) and every generated
|
|
311
|
+
artifact — main RDL, sub-report RDLs, the burst pack — ships pre-bound to
|
|
312
|
+
it.
|
|
313
|
+
- **Parameters never prompt end users** — undeclared query binds and
|
|
314
|
+
system/path parameters bind to an empty `=Nothing` default rather than
|
|
315
|
+
triggering a runtime prompt, and every query bind is wired, so the
|
|
316
|
+
report uploads and runs without ever asking for values. One honest
|
|
317
|
+
caveat: *manually executing* a parameterized dataset's query in the
|
|
318
|
+
designer (Refresh Fields / Query Designer ▸ Run) makes Report Builder
|
|
319
|
+
itself ask for design-time values — that is Report Builder's dialog for
|
|
320
|
+
any parameterized query, not an RDL defect, and it is entirely
|
|
321
|
+
avoidable because the field list is emitted complete: **never click
|
|
322
|
+
Refresh Fields**.
|
|
323
|
+
- **Hidden internal vs visible user params** — Oracle parameters marked
|
|
324
|
+
`display="no"`, and system/internal name patterns, become hidden
|
|
325
|
+
ReportParameters; genuine user parameters stay visible.
|
|
326
|
+
- **Dual SQL target** — the default (`oracle`) preserves the original
|
|
327
|
+
Oracle SQL with `:P_PARAM` binds and the OracleClient provider; the
|
|
328
|
+
`sqlserver` target emits translated T-SQL with `@P_PARAM` binds and the
|
|
329
|
+
SQL provider.
|
|
330
|
+
|
|
331
|
+
### Heterogeneous ingest
|
|
332
|
+
|
|
333
|
+
Drop mixed artifacts and the ingester classifies each blob
|
|
334
|
+
(`primary_xml` / `rdf_binary` / `sql_files` / `pdfs` / `docs` /
|
|
335
|
+
`screenshots` / `unknown`) with a per-file confidence note, then runs
|
|
336
|
+
whatever subset of the pipeline is feasible:
|
|
337
|
+
|
|
338
|
+
- **`.rdf` onboarding** — `.rdf` is Oracle's proprietary binary and is not
|
|
339
|
+
parseable directly; the app replies with the exact one-line `rwconverter`
|
|
340
|
+
command (Oracle's own tool) to export it to the XML this converter
|
|
341
|
+
consumes, including the wildcard form for whole folders.
|
|
342
|
+
- **Raw `.sql` and `.docx`** — `.sql` files (and SQL extracted from `.docx`
|
|
343
|
+
walkthroughs) become synthetic datasets when no XML is present.
|
|
344
|
+
- **Partial artifacts** — incomplete exports (a customization overlay, a
|
|
345
|
+
data-model-only export, a layout fragment) get a plain-language verdict
|
|
346
|
+
instead of a near-blank RDL.
|
|
347
|
+
- **Encoding robustness** — best-effort decode tries utf-8 / utf-8-sig /
|
|
348
|
+
cp1252 / latin-1 before a replace fallback.
|
|
349
|
+
|
|
350
|
+
### Pre-flight audit and cross-validation
|
|
351
|
+
|
|
352
|
+
The parser cross-checks bind variables, parameter names, and column
|
|
353
|
+
references against any `.sql`, `.docx`, or screenshot you dropped in, and
|
|
354
|
+
surfaces a pre-flight audit.
|
|
355
|
+
|
|
356
|
+
### Bundle download
|
|
357
|
+
|
|
358
|
+
One click produces a `.zip` of the RDL, validation report, deploy checklist,
|
|
359
|
+
audit trail, burst pack, and a README explaining what's inside.
|
|
360
|
+
|
|
361
|
+
## Manual deploy workflow
|
|
362
|
+
|
|
363
|
+
The converter intentionally stops at the file boundary — it does not push to
|
|
364
|
+
a Report Server. After downloading the `.rdl`:
|
|
365
|
+
|
|
366
|
+
1. Upload it to your SSRS folder (Report Manager or Report Builder).
|
|
367
|
+
2. Open the report's Data Source properties and point it at the shared data
|
|
368
|
+
source already configured in that folder.
|
|
369
|
+
3. **Run.** Do not click Refresh Fields — the field list is emitted
|
|
370
|
+
complete and already matches the SQL aliases, so there is nothing to
|
|
371
|
+
refresh. (Refresh Fields executes the query at design time; on a
|
|
372
|
+
parameterized dataset Report Builder asks for design-time values —
|
|
373
|
+
its own dialog for any parameterized query. OK-with-nulls is safe if
|
|
374
|
+
you land there: the SELECT list is static.)
|
|
375
|
+
4. Save, view, export to PDF for end users.
|
|
376
|
+
|
|
377
|
+
## Architecture
|
|
378
|
+
|
|
379
|
+
Pipeline: parse the Oracle artifact into a single `ParsedReport` dataclass,
|
|
380
|
+
translate SQL and PL/SQL formula logic, generate the RDL, render previews,
|
|
381
|
+
validate, and build the deploy checklist and burst pack. Every module reads
|
|
382
|
+
or writes the same dataclass.
|
|
383
|
+
|
|
384
|
+
New here? Start with the **[deployment run guide](docs/RUN_GUIDE.md)** —
|
|
385
|
+
install, convert, read the verdict banner, and deploy to a Report Server.
|
|
386
|
+
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the module-by-module
|
|
387
|
+
breakdown and [docs/API.md](docs/API.md) for the HTTP endpoint reference.
|
|
388
|
+
Running it on an offline Windows Server? See the
|
|
389
|
+
[download list](docs/WINDOWS_SERVER_PREREQUISITES.md) and the
|
|
390
|
+
[Windows Server steps](docs/WINDOWS_SERVER_DEPLOYMENT.md).
|
|
391
|
+
|
|
392
|
+
## Testing
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
pip install -r requirements.txt
|
|
396
|
+
python -m pytest -q
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
The suite is 791 tests. It validates generated RDL against Microsoft's RDL
|
|
400
|
+
2008 XSD and, when the ReportViewer runtime is present, renders RDLs through
|
|
401
|
+
Microsoft's engine and measures the resulting PDFs.
|
|
402
|
+
|
|
403
|
+
## Development
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
python backend/cli.py samples/oracle --out ./out --strict
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
`backend/cli.py` runs a plain conversion (`--out`, `--strict`, `--quiet`);
|
|
410
|
+
`tools/batch_convert.py` runs the batch Migration Assessment (`-o`,
|
|
411
|
+
`--render`, `--target-db`).
|
|
412
|
+
|
|
413
|
+
## Contributing
|
|
414
|
+
|
|
415
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, test, and pull request
|
|
416
|
+
guidelines.
|
|
417
|
+
|
|
418
|
+
## License
|
|
419
|
+
|
|
420
|
+
**MIT** — free to use, modify, distribute, and build on, for any purpose,
|
|
421
|
+
including commercial use. Full text in [LICENSE](LICENSE).
|
|
422
|
+
|
|
423
|
+
Optional paid tiers (Pro / Enterprise: unlimited batch, white-label
|
|
424
|
+
Migration Assessments, support) remain available — open an issue or contact
|
|
425
|
+
the author.
|