Find, remove, copy, and convert measures in Power BI PBIP/TMDL projects
Project description
Power BI Measure Tool
A command-line tool for managing measures in Power BI PBIP projects using the TMDL format.
Features
| Mode | What it does |
|---|---|
| Clean | Scan for unused measures across your semantic model and optionally delete them |
| Copy | Copy or move measures between projects or tables with automatic structure detection |
| Convert | Convert between .pbix and project folders via the external pbi-tools CLI |
Copy mode scenarios (auto-detected)
- Same project → different table — reorganise measures between tables
- Different project, same table structure — auto-maps identical table names
- Different project, different tables — guided interactive table mapping
DAX formatting
All measures are cleaned before writing: dedented, trailing whitespace removed, blank lines collapsed. formatString and description metadata are preserved; ephemeral properties like lineageTag are intentionally dropped so the target file gets fresh ones from Power BI.
Requirements
- Python 3.8+
- Power BI project saved in PBIP format with TMDL enabled (File → Options → Preview features → Store semantic model in TMDL format)
- Convert mode only: the external pbi-tools CLI on your
PATH(or pointed at via--pbi-tools/ thePBI_TOOLS_PATHenv var). Clean and Copy modes have no such dependency.
Installation
pip install powerbi-measure-tool
Or with pipx (recommended for CLI tools — keeps it isolated):
pipx install powerbi-measure-tool
Usage
Clean mode — find and remove unused measures
# Dry run: show which measures are unused (no files changed)
pbi-measure ./MyWorkspace
# Execute: permanently delete unused measures
pbi-measure ./MyWorkspace --execute
# Treat measures that only appear in unapplied filter panes as unused
pbi-measure ./MyWorkspace --execute --ignore-unapplied-filters
Example output:
══════════════════════════════════════════════════════════════
Power BI Measure Tool v0.2.0
══════════════════════════════════════════════════════════════
Source Report : Sales Dashboard.Report
Source SM : Sales Dashboard.SemanticModel
Mode : 🔍 DRY RUN — no files will be changed
══════════════════════════════════════════════════════════════
📊 Measures found:
• 24 in SemanticModel (TMDL)
• 0 report-level
• 24 total
🔄 Analysing usage...
────────────────────────────────────────────────────
Status Table Measure Source
────────────────────────────────────────────────────
✅ USED Sales Total Revenue semantic_model
✅ USED Sales Gross Profit semantic_model
...
❌ UNUSED Sales Old KPI Draft semantic_model
────────────────────────────────────────────────────
Total: 24 | ✅ Used: 23 | ❌ Unused: 1
💡 Run with --execute to permanently delete the 1 unused measure(s).
Copy mode — copy or move measures between projects / tables
# Interactive selection (prompts you to pick measures from a numbered list)
pbi-measure ./SourceWorkspace --copy --target ./TargetWorkspace
# Cherry-pick specific measures by name
pbi-measure ./SourceWorkspace --copy --target ./TargetWorkspace \
--measures "Total Revenue" "Gross Profit" "YTD Sales"
# Copy all measures
pbi-measure ./SourceWorkspace --copy --target ./TargetWorkspace --all-measures
# Copy all and execute (--execute required to write files)
pbi-measure ./SourceWorkspace --copy --target ./TargetWorkspace --all-measures --execute
# Overwrite measures that already exist in the target
pbi-measure ./SourceWorkspace --copy --target ./TargetWorkspace \
--all-measures --overwrite --execute
# Reorganise: move measures to a different table in the same project
pbi-measure ./MyWorkspace --copy --target ./MyWorkspace \
--measures "Total Revenue" --execute
Interactive selection example:
┌──────────────────────────────────────────────────────────┐
│ Step 1 — Select Measures to Copy │
└──────────────────────────────────────────────────────────┘
📋 Sales
[ 1] Gross Profit (#,0.00)
[ 2] Total Revenue (#,0.00)
[ 3] YTD Sales (#,0.00)
📋 Targets
[ 4] Revenue Target
[ 5] Variance to Target
Syntax: single (3), range (3-7), list (1,3,5-8), or 'all'
Enter 'q' to quit.
Your selection: 1,2,4
Convert mode — .pbix ↔ project folder
Convert mode wraps the external pbi-tools CLI, so it must be
installed and on your PATH (or passed via --pbi-tools / PBI_TOOLS_PATH).
Like the other modes, it is a dry run by default — it prints the exact
pbi-tools command it would run, and only touches disk with --execute.
# Extract a .pbix into a source-control-friendly project folder (pbix → pbip)
pbi-measure ./Report.pbix --to-pbip --out ./MyProject --execute
# Compile a project folder back into a .pbix / .pbit (pbip → pbix)
pbi-measure ./MyProject --to-pbix --out ./out --execute
# Force a thin (report-only) project to compile to .pbix
pbi-measure ./ThinReport --to-pbix --format pbix --overwrite --execute
# Point at a specific pbi-tools executable
pbi-measure ./Report.pbix --to-pbip --pbi-tools "C:/tools/pbi-tools.exe" --execute
Honest caveats:
--to-pbipproduces pbi-tools' PbixProj (TMDL) layout — source-control equivalent to Microsoft PBIP, but not byte-identical to Power BI Desktop's "Save as .pbip" output.--to-pbixauto-detects the format: a project containing a data model compiles to a.pbittemplate (opening it in Desktop triggers a refresh), while a report-only project compiles to a real.pbix. Regenerating a full.pbixwith embedded data requires Power BI Desktop and is out of scope for an offline tool. Pass--format pbix|pbitto override auto-detection.
Path formats accepted
The path and --target arguments accept either:
- A workspace folder containing both
.Reportand.SemanticModelsubfolders - A
.Reportfolder directly
MyWorkspace/ ← pass this
├── Sales Dashboard.Report/
└── Sales Dashboard.SemanticModel/
How unused measure detection works
The tool performs a multi-pass analysis:
- Pass 1 — scans all report JSON files (pages, visuals, bookmarks) for structured
Measurereferences and DAX string expressions - Pass 2+ — marks any measure referenced in the DAX of an already-used measure as used (handles measure-calls-measure chains)
- Repeats until no new used measures are found
- Anything still unmarked → UNUSED
Changelog
See CHANGELOG.md for the full version history.
Contributing
Bug reports and pull requests are welcome at GitHub.
- Fork the repo
- Create a feature branch:
git checkout -b my-feature - Commit your changes:
git commit -m 'Add my feature' - Push and open a PR
License
MIT — see LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file powerbi_measure_tool-0.2.0.tar.gz.
File metadata
- Download URL: powerbi_measure_tool-0.2.0.tar.gz
- Upload date:
- Size: 21.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2db918a4b46507bee4bac5bba6ecb28cf4b5caaad40cbf2fb20ee424547e8660
|
|
| MD5 |
07c0fc0406e37fa56fa8a95db05cf7ed
|
|
| BLAKE2b-256 |
951d6691d420e5107a15a2a7b76a7711d6b9ceeb1dda9b790b0f031f82006c22
|
File details
Details for the file powerbi_measure_tool-0.2.0-py3-none-any.whl.
File metadata
- Download URL: powerbi_measure_tool-0.2.0-py3-none-any.whl
- Upload date:
- Size: 20.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
627979442ade108f6b96919c3bc03db7cc4664c4386e278484f9271cd187ccf9
|
|
| MD5 |
c2208b9c8005c2f977f0ee8722030d65
|
|
| BLAKE2b-256 |
931558c64986d1c75aa2fec5ebbd8ed24fde5e01ac0812e9e8f2b841365d4d9d
|