
Real, binary .xlsx (and typed JSON) exports for the InterSystems IRIS BI/DeepSee
Analyzer — no more opening “Export to Excel” and getting an HTML table wearing an
.xls extension.
/csp/bi-export/xlsx, /csp/bi-export/json) too, fordocker run -d -p 52773:52773 -p 1972:1972 ghcr.io/asinay/bi-export-plus:latest
Pre-loaded with Samples-BI and this
module - openpyxl included - so there’s real data to export immediately:
| Demo page, no pivot needed | http://localhost:52773/csp/user/BIExport.UI.DemoPage.cls |
| Analyzer, on the sample HoleFoods cube | http://localhost:52773/csp/user/BIExport.UI.Analyzer.cls?CUBE=HOLEFOODS |
Management Portal (login _SYSTEM / SYS) |
http://localhost:52773/csp/sys/UtilHome.csp |
Building it yourself instead of pulling: docker compose up --build (see
https://github.com/asinay/bi-export-plus/blob/master/Dockerfile). Already running IRIS elsewhere? →
Try it in 2 minutes instead, or keep reading for the full
reference below.
The new buttons, right next to the stock Excel/CSV ones.
![]() A genuine Excel Table - autofilter, sortable headers, a native Totals row. |
![]() An Info sheet with title/cube/filters, not squeezed above the data. |
openpyxl importable.xlsx path only; JSON exportInstall openpyxl into IRIS’s embedded Python environment, e.g.:
/bin/irispython -m pip install openpyxl
Verify it from an IRIS terminal in any namespace:
Do ##class(%SYS.Python).Import("openpyxl")
If that errors, fix the Python/openpyxl setup before installing this module -
the xlsx export will fail at runtime otherwise.
If openpyxl turns out not to be importable at runtime anyway, every export surface
(Analyzer buttons, dashboard widget buttons, demo page, REST API) detects it and degrades
gracefully instead of producing a broken download - buttons gray out with an explanation
in their tooltip, and the REST API returns 503 with a plain-text reason. JSON export is
never affected either way, since it needs no Python.
Just want to try it out rather than add it to a real instance? See
Try it now with Docker above instead -
nothing below is needed for that.
Recommended: InterSystems Package Manager (IPM/ZPM). If this module has been
published to the IPM registry:
zpm "install bi-export-plus"
To install straight from a local clone of this repository instead (e.g. before
it’s published, or to run a modified copy):
git clone
zpm "load /path/to/bi-export-plus"
Point this at the directory that contains https://github.com/asinay/bi-export-plus/blob/master/module.xml, not at the
https://github.com/asinay/bi-export-plus/blob/master/module.xml file itself - e.g. on Windows:
zpm:USER>load C:\path\to\bi-export-plus
IPM reads https://github.com/asinay/bi-export-plus/blob/master/module.xml, loads the classes under https://github.com/asinay/bi-export-plus/blob/master/src/, and
registers the /csp/bi-export web application for you. Take note of the
AfterInstallMessage it prints at the end - it points you at the two usage
options below.
Load the classes into your target namespace from an IRIS terminal:
Set path = "/path/to/bi-export-plus/https://github.com/asinay/bi-export-plus/blob/master/src/cls"
Do $system.OBJ.LoadDir(path, "ck", , 1)
("ck" = compile, keep the source-controlled state consistent; the trailing
1 recompiles even if the class already exists.)
Create the REST web application manually (IPM normally does this from the
<WebApplication> block in https://github.com/asinay/bi-export-plus/blob/master/module.xml) - in the Management
Portal: System Administration > Security > Applications > Web
Applications > New Web Application, with:
| Setting | Value |
|---|---|
| Name | /csp/bi-export |
| Namespace | your target namespace |
| Dispatch class | BIExport.REST |
| Enable Password authentication | Yes |
| Enable Unauthenticated access | No |
This step is only needed for the standalone REST API (Option B under
“Usage” below); the in-Analyzer export buttons work without it.
Confirm the openpyxl import (see Requirements above) - manual installs skip
IPM’s install-time checks, so this is easy to miss.
https://github.com/asinay/bi-export-plus/blob/master/dist/BIExport-classes.xml is a single IRIS class
export containing every class in this module - the quickest way to get it
into an existing instance without cloning the repo or using IPM.
Either drag-and-drop it onto Management Portal > System Explorer >
Classes > Import, or from an IRIS terminal in your target namespace:
Do $system.OBJ.Load("/path/to/bi-export-plus/https://github.com/asinay/bi-export-plus/blob/master/dist/BIExport-classes.xml", "ck")
This does not create the /csp/bi-export web application - it’s just the
classes. That’s enough for the in-Analyzer export buttons (Option A under
“Usage” below), but if you also want the standalone REST API, create the web
application manually per step 2 of Option B above.
No pivot needed - BIExport.UI.DemoPage.cls is a self-contained form with a few
canned MDX queries and totals/aggregation toggles. Pick one, click a button, get a
real .xlsx/.json download, using the exact same code path as the buttons below:
/csp/<namespace>/BIExport.UI.DemoPage.cls
Open your pivots via BIExport.UI.Analyzer.cls instead of the stock
_DeepSee.UI.Analyzer.zen - same query params, just a different class name:
/csp/<namespace>/BIExport.UI.Analyzer.cls?CUBE=...
The page’s Export dropdown gains Excel Workbook and JSON entries next
to the stock Excel/CSV ones.
Rather than changing an existing link/shortcut’s URL, add a normal Portal
Favorite that opens a saved pivot through BIExport.UI.Analyzer.cls
directly - it shows up in the User Portal’s Favorites sidebar/cover shelf
like any other bookmark:
Do ##class(BIExport.Favorites).AddAnalyzerFavorite("/.pivot")
The one required argument is the pivot’s full library path (fullName in
the Portal’s pivot list). Optional second/third arguments choose the
bookmark’s folder (default "BIExport") and display title (defaults to the
pivot’s own name). Run once per pivot, per user - Favorites are personal to
each user, there’s no bulk/all-users mode. RemoveAnalyzerFavorite(pFolderName, pTitle) undoes it.
POST an MDX query to /csp/bi-export/xlsx or /csp/bi-export/json:
curl -u _system:PASSWORD -X POST \
http://host:port/csp/bi-export/xlsx \
-H "Content-Type: application/json" \
-d '{
"mdx": "SELECT [Measures].[Amount] ON 0 FROM [HoleFoods Sales]",
"title": "Sales Report",
"cube": "HoleFoods Sales"
}' \
-o report.xlsx
Supported request body fields:
| Field | Required | Description |
|---|---|---|
mdx |
yes | MDX query text |
filename |
no | Defaults to <title>.xlsx/.json, or <cube>_<timestamp> if no title |
title, subtitle |
no | Shown on the Info sheet |
cube |
no | Shown on the Info sheet, used in the default filename |
listing |
no | Listing name, for drillthrough/listing MDX |
rowTotals |
no | true to include a “Total” column (one value per row, summed across columns). Independent of columnTotals — a pivot can have either, both, or neither. |
columnTotals |
no | true to include a “Total” row (one value per column, summed down rows). Independent of rowTotals. |
rowTotalAgg |
no | Aggregation for rowTotals — "sum" (default), "count", "min", "max", "avg", "pct" (% of total) |
columnTotalAgg |
no | Aggregation for columnTotals, same values as rowTotalAgg |
filters |
no | [{"name": "...", "value": "..."}] recap block on the Info sheet |
rowCaptions |
no | Row axis dimension captions (outermost first), for the row-header columns |
rowTotalAgg/columnTotalAgg take the same values as this dropdown - no translation needed.
See sample output in https://github.com/asinay/bi-export-plus/blob/master/samples/.
Two related pieces, for saved Dashboards rather than ad-hoc pivots:
Per-widget buttons. Same Excel Workbook/JSON buttons as the Analyzer, but on an
individual dashboard widget’s own Export dropdown. Existing widgets need to be converted
to the export-capable widget class once:
Do ##class(BIExport.DashboardTools).ConvertWidget("/.dashboard", "")
or, to convert every pivot-family widget on a dashboard in one call:
Do ##class(BIExport.DashboardTools).ConvertAllWidgets("/.dashboard")
RevertWidget(pDashboardFullName, pWidgetName) undoes a single conversion. New
dashboards can also just set a widget’s type to BIExport.UI.Widget.pivot directly in
the Dashboard Editor’s widget definition, instead of converting after the fact.
Whole-dashboard export. Open the dashboard via BIExport.UI.DashboardViewer.cls
instead of the stock _DeepSee.UserPortal.DashboardViewer.zen (same query params):
/csp/<namespace>/BIExport.UI.DashboardViewer.cls?DASHBOARD=...
then use Export Dashboard, in the small text-menu row at the bottom of the page
(next to Home/About/Logout) - it downloads one .xlsx workbook with one sheet per pivot
widget on that dashboard.
Try it against a real dashboard, using the Docker image’s
preloaded Samples-BI data - no conversion needed first, since whole-dashboard export
reads any pivot-family widget, stock type included:
http://localhost:52773/csp/user/BIExport.UI.DashboardViewer.cls?DASHBOARD=Basic%20Dashboard%20Demo.dashboard
Click Export Dashboard at the bottom - downloads a 3-sheet workbook (Info sheet plus
one sheet per pivot widget on that dashboard).
rowTotals/columnTotals aggregate across them regardless of measure, mixing dollarsMIT - see https://github.com/asinay/bi-export-plus/blob/master/LICENSE.