Power BI Fixer — One Line to Fix Them All

How a single Python call in a Fabric Notebook can assess, standardize, and fix your Power BI reports and semantic models at scale.

DISCLAIMER: RUN THE SCRIPT AT YOUR OWN RISK. NOT MICROSOFT AFFILIATED.

Try it out → Go here

1. Why This Matters

Every Power BI developer knows the pain. You inherit a report — or revisit one you built six months ago — and find pie charts everywhere, axis labels on bar charts that serve no purpose, the default 720×1280 page size from 2018, visual filters that confuse end users, no calendar table, no “Last Refresh” indicator, and implicit measures galore.

Fixing all of that manually means clicking through dozens of property panes, writing Tabular Editor scripts, exporting TMDL, or opening the JSON files in Power BI Desktop developer mode. And that is just for one report.

What if you could do it all — for any report in any workspace — in a single line?

pbi_fixer()

That is not a concept. That is a working function, built on top of Semantic Link Labs, running natively inside a Microsoft Fabric Notebook. No external tools. No downloads. No separate C# runtime. Just Python, the Fabric APIs, and one function call.

A big thank you to Michael Kovalsky, the creator and maintainer of Semantic Link Labs. His work laid the groundwork that makes the PBI Fixer possible. He also pointed me in the right direction when I was exploring whether an interactive UI was even feasible inside a Fabric Notebook. Without Semantic Link Labs, none of this would exist.

The Game Changer: A Native Fabric Solution

One Line. (Nothing Else.)

The PBI Fixer is a notebook-native interactive UI built with ipywidgets. When you call pbi_fixer(), it renders a complete control panel directly in your notebook cell output:

  • Workspace / Report / Page input fields to target exactly what you need
  • Mode selector — Fix, Scan, or Scan + Fix
  • 11 fixers split across two categories — Report Visuals and Semantic Model
  • Checkbox control — pick exactly the fixers you want
  • XMLA write confirmation — a safety gate for semantic model modifications
  • Progress log — live output showing every action, every finding, every fix

You get the same result whether you point it at a report in your personal workspace or at a production workspace with a service principal. It is the same API, the same tool, the same one line.

PBI Fixer UI in a Fabric Notebook

If You Are Not on Fabric, You Are Missing Out

Let me be direct: if you are still exclusively on Power BI Pro or Premium Per User without a Fabric capacity, you are missing out on an entire generation of tooling. The PBI Fixer leverages:

  • Fabric Notebooks — serverless Python compute with ipywidgets for interactive UIs
  • PBIR format — the enhanced report format that exposes every visual property as JSON, readable and writable through the Fabric REST API
  • XMLA endpoints — read/write access to the Tabular Object Model (TOM) for semantic model modifications
  • Semantic Link Labs — a community-driven Python library (pip install semantic-link-labs) that wraps all of this into clean, documented functions

None of this requires downloading anything to your machine. You open a notebook, run one cell, and you have an interactive report-fixing tool at your fingertips. This is what a modern BI platform looks like — and Fabric delivers with being able to run this community‑driven tool directly integrated. But also let’s be clear again: the Power BI Fixer is obviously not a Microsoft product.

2. What the PBI Fixer Does — In Detail

The tool is split into two categories, each addressing a different layer of your Power BI solution.

Report Fixers (5 Fixers at the time of writing)

These fixers operate on the report definition — the PBIR JSON files that describe every visual, page, and filter in your report. They use the connect_report() context manager to read (and optionally write) the report definition through the Fabric REST API. Some of the following fixes could more easily be applied by adding on single json design theme to the report, but this would not fix any chart where the default has been touched, therefore the following fixes are directly modifying the property of each visual / page.

1. Fix Pie Charts

Problem: Pie charts are one of the worst chart types for comparing values. Human perception of angles and areas is far less accurate than perception of length (bars). Yet pie charts remain one of the most used visuals in Power BI.

What it does: Replaces every pieChart visual in the report with a clusteredBarChart (configurable in the function itself). The data bindings, filters, and layout are preserved — only the visual type changes.

Scan mode: Reports exactly how many pie charts exist and on which pages, without modifying anything.

🔍 [1/5] Scanning Fix Pie Charts…
   🟡 definition/pages/abc123/visuals/def456/visual.json — pie chart found (would be replaced with clusteredBarChart)

2. Fix Bar Charts

Problem: Bar charts are great — but Power BI’s default formatting includes axis titles nobody reads, axis value labels that duplicate the data labels, and gridlines that add visual clutter.

What it does: Applies five best-practice formatting rules to every barChart and clusteredBarChart:

Setting Target Why
X axis title Off The axis title (“Count of Sales”) adds no value — the data labels speak for themselves
X axis values Off Redundant when data labels are shown directly on the bars
Y axis title Off Category names are self-explanatory
Data labels On The single most important formatting choice — show the actual numbers
Vertical gridlines Off Reduces visual noise

Scan mode: Reports which specific properties deviate from the target for each visual, so you know exactly what would change.

🟡 definition/pages/…/visual.json — needs fixing: X axis title, X axis values, Data labels

3. Fix Column Charts

Problem: Same issues as bar charts, but with axes flipped. Power BI’s defaults for column charts include unnecessary axis titles, missing data labels, and gridlines.

What it does: Applies the equivalent five formatting rules to every columnChart and clusteredColumnChart, with the axis semantics appropriately swapped.

4. Fix Page Size

Problem: The default Power BI page size is 720×1280 pixels — a legacy from when Full HD was not universal. Reports created with this size appear small and waste screen real estate on modern displays.

What it does: Upgrades pages from the default 720×1280 to 1080×1920 (Full HD). Pages with custom sizes are left untouched — only the exact default match triggers a fix.

Scan mode: Reports which pages use the default size and which have custom dimensions.

🟡 “Sales Overview” (definition/pages/abc123/page.json) — default page size (720×1280), would be upgraded to 1080×1920
🟢 “Executive Summary” (definition/pages/def456/page.json) — custom size (800×1200), no change needed

5. Hide Visual Filters

Problem: Power BI automatically creates visual-level filters for every field used in a visual. These filters appear in the Filters pane and confuse end users who do not understand the distinction between visual, page, and report filters. Most report developers would rather hide them entirely.

What it does: Sets isHiddenInViewMode = True on every visual-level filter. For visuals that have query fields but no filterConfig, one is constructed automatically — the tool walks the visual’s queryState, classifies each field as Categorical or Advanced (for measures), and creates the hidden filter entries from scratch.

Scan mode: Reports how many visuals have visible filters and how many filters would be hidden.

Semantic Model Fixers (6 Fixers at the time of writing)

These fixers operate on the semantic model (the dataset) behind the report. They use the XMLA endpoint and the Tabular Object Model (TOM) through connect_semantic_model(). Because XMLA write operations are irreversible (the model can no longer be downloaded as a .pbix with embedded data after modification), the UI requires an explicit confirmation checkbox before running these fixers.

1. Discourage Implicit Measures

Problem: By default, Power BI allows end users to drag columns into the Values bucket and get automatic SUM/COUNT/etc. aggregations. This creates “implicit measures” that cannot be formatted, documented, or governed. It is also a prerequisite for calculation groups — they simply do not work correctly with implicit measures enabled. Actually if a calculation group is added this setting is automatically set to TRUE

What it does: Sets DiscourageImplicitMeasures = True on the model. One property, one toggle, impact the whole model.

2. Add Calendar Table (CalcCalendar)

Problem: Almost every analytical model needs a calendar (date) table. Yet many models either lack one entirely or use a poorly structured one. Without a proper calendar table marked with DataCategory = Time, time intelligence functions do not work and auto date/time creates hidden tables for every date column.

What it does: Checks if any table in the model has DataCategory = “Time”. If none exists, creates a comprehensive CalcCalendar calculated table with:

  • 20 columns — Date, Year, Quarter, Month (number and abbreviation), Day, Fiscal Year (October start), End of Month, Week of Year, Weekday, and 9 boolean/flag columns (Is Current Month, Is Previous Month, Is Current/Previous Calendar Year, Is Current/Previous Fiscal Year, Is Before This Month, Is Current or Past Month, Month Key, Relative Month)
  • 3 hierarchies — Date Hierarchy (Year → Quarter → Month → Day), Fiscal Date Hierarchy (Fiscal Year → Quarter → Month → Day), Calendar Hierarchy (Year → Month MMM → Week of Year → Weekday)
  • Display folders — Organized into Favorites, Calendar Date, Fiscal Date, and Flags
  • Sort-by-column — Month abbreviation sorted by month number
  • Marked as date table with the Date column as key

The DAX expression uses CALENDARAUTO() and derives all columns dynamically. The fiscal year start month is configurable (default: October).

3. Add Last Refresh Table

Problem: Users and administrators need to know when a dataset was last refreshed. This is especially critical for business-critical reports where stale data leads to wrong decisions.

What it does: Creates a hidden “Last Refresh” table with:

  • M partition (Power Query) that captures DateTime.LocalNow() on every refresh
  • Data column storing the refresh timestamp
  • Measure “Last Refresh Measure” displaying the formatted timestamp
  • Smart placement — If a “Measure” table exists, the measure is placed there instead of in the Last Refresh table, keeping things organized

4. Add Measure Table

Problem: Without a dedicated measure table, measures are scattered across fact and dimension tables. This makes them hard to find, hard to maintain, and easy to duplicate.

What it does: Creates an empty calculated table named “Measure” using the minimal expression {0} (a single-row, single-column placeholder). The auto-generated column is hidden, so only measures placed in this table are visible to the end user.

5. Add Units Calculation Group (Thousand / Million)

Problem: Displaying large numbers like “1,234,567” clutters visuals and makes comparison harder. Users often want to see “1,235K” or “1.2M”. Creating individual measures for every combination is not scalable.

What it does: Creates a “Units” calculation group with two items:

  • Thousand — divides by 1,000
  • Million — divides by 1,000,000

Both items include smart skip logic: measures whose name contains % or ratio are passed through unchanged — dividing a percentage by 1,000 would produce nonsense. The DAX uses DIVIDE() for safe division and checks ISNUMBER(SELECTEDMEASURE()) before applying.

Note: Calculation groups affect all visuals that use the slicer. The fixer includes a performance warning in its output.

6. Add Time Intelligence Calculation Group (21 Items)

Problem: Time intelligence is the most common analytical requirement — comparing this year to last year, computing year-to-date, calculating variances. Building these for every measure individually is tedious and error-prone. A calculation group solves this once for all measures.

What it does: Creates a “Time Intelligence” calculation group with 21 calculation items:

Category Items
Base periods AC (Actual), Y-1 (Prior Year), Y-2 (Two Years Ago), Y-3 (Three Years Ago)
Year-to-date YTD, YTD-1 (Prior Year YTD), YTD-2 (Two Years Ago YTD)
Absolute variances abs. AC vs Y-1, abs. AC vs Y-2, abs. AC vs YTD-1, abs. AC vs YTD-2
Relative variances AC vs Y-1 (%), AC vs Y-2 (%), AC vs YTD-1 (%), AC vs YTD-2 (%)
Achievement achiev. AC vs Y-1, achiev. AC vs Y-2, achiev. AC vs YTD-1, achiev. AC vs YTD-2

The DAX is generated dynamically based on the actual calendar table and date column names in the model. It uses SAMEPERIODLASTYEAR(), DATEADD(), DATESYTD(), and TOTALYTD() — all parameterized so they work regardless of how your calendar table is named.

Prerequisite: Requires a calendar table with DataCategory = “Time”. The fixer checks for this and prints a helpful message suggesting to run the Calendar fixer first if none exists.

3. Scan vs. Fix — Assess Before You Act

Every fixer in the PBI Fixer supports two modes:

Scan Mode

Scan mode is read-only. It opens the report or semantic model with readonly=True, checks every condition, and reports what would change — without touching anything. This is your assessment tool.

Use it to:

  • Audit a report before handing it to a client
  • Baseline the current state before applying fixes
  • Review across multiple reports to prioritize which ones need the most work

The output is a detailed log with green dots (✅ already correct), yellow dots (⚠️ would be fixed), and clear descriptions of each finding.

Fix Mode

Fix mode opens the report or semantic model with readonly=False and applies all selected fixes. The report definition changes are saved back to the Fabric service through the REST API. Semantic model changes are saved through the XMLA endpoint via TOM.

Scan + Fix

The third mode runs both phases sequentially: first a complete scan to document the current state, then a full fix pass. This gives you a before-and-after view in a single run.

4. How It Compares to Other Solutions

Tabular Editor C# Scripts

Let me be clear: I am a huge fan of Tabular Editor. For example TE’s C# scripting engine is powerful, mature, and battle-tested. Being able to save reusable C# scripts as one-click actions in your toolbar makes repetitive model tasks fast and consistent. If you work with semantic models professionally, Tabular Editor should absolutely be in your toolkit.

That said, there are scenarios where the PBI Fixer complements what Tabular Editor offers:

  • Local install required — Tabular Editor must be downloaded and installed on a Windows machine (or run as a portable version). The PBI Fixer runs entirely in a Fabric Notebook — no installs, no local machine dependency
  • C# vs. Python — Tabular Editor’s scripting language is C#. If your team lives in notebooks and Python, switching to C# for model automation is a context switch. The PBI Fixer keeps everything in one language and one environment
  • Semantic model only — Tabular Editor (understandably) focuses on the semantic model layer. It does not touch the report definition — it cannot fix pie charts, page sizes, or visual filters. The PBI Fixer covers both layers in a single tool
  • Interactive notebook UI — Tabular Editor scripts are run from a script editor, command line, or via Macros in the TE UI. The PBI Fixer provides an interactive widget directly inside a notebook, which fits naturally into Fabric-native workflows
  • License cost — TE3 (with the full scripting engine and Macros) requires a paid license, mho worth it for professional use, but it is a consideration for teams already invested in Fabric

Use Tabular Editor and its Macros for deep semantic model work at your desk. Use the PBI Fixer when you need report-layer fixes, notebook-native automation, or a single entry point that spans both layers without leaving Fabric.

TMDL Scripts / Git-Based Workflows

TMDL (Tabular Model Definition Language) is Microsoft’s text-based serialization format for semantic models. You can version-control your models in Git and apply changes through text manipulation.

  • Great for version control — TMDL excels at tracking changes over time
  • Manual text editing — adding a 20-column calendar table means writing 200+ lines of TMDL by hand
  • No report layer — TMDL is semantic-model-only, same as Tabular Editor
  • No assessment — you cannot “scan” a TMDL folder to find what is missing; you need to read and interpret the files yourself
  • Deployment pipeline required — changes in TMDL need to be deployed back to the service through Git integration or a deployment tool

The PBI Fixer’s scan mode provides the assessment that TMDL workflows lack, and its fix mode applies changes directly without requiring a full deployment pipeline.

Manual Modifications in Power BI Desktop

The most common approach: open the report, click through property panes, change settings, save, publish.

  • Time-consuming — fixing five formatting properties on ten bar charts means 50+ individual clicks
  • Error-prone — easy to miss a visual or forget a setting
  • Not scalable — doing this for 20 reports across five workspaces is a full day of work
  • No audit trail — no record of what was changed or what the previous state was
  • Not repeatable — the next report you create has the same default problems

The PBI Fixer is repeatable, consistent, and logs everything all by design.

Power BI REST API / Fabric SDK (Custom Python)

You could build this yourself using the raw Fabric REST API and the semantic-link SDK.

  • Maximum flexibility — you can do anything the API supports
  • Significant development effort — understanding the PBIR JSON schema, handling authentication, managing error cases, building a UI
  • No community — your custom scripts are yours to maintain

The PBI Fixer is built on top of Semantic Link Labs, which handles the heavy lifting. It uses connect_report() for the PBIR layer and connect_semantic_model() for TOM — both are well-tested, community-maintained abstractions over the raw APIs.

5. Getting Started

Prerequisites

  • A Microsoft Fabric capacity (F2 or higher, or a trial capacity)
  • A Fabric Notebook in your workspace
  • Semantic Link Labs installed – see script below
  • Reports in PBIR format (required for report fixers — check your Preview Features in Power BI Desktop, enable PBIR, save, and re-publish if your report is still in PBIRLegacy). The Fixer will also tell you in case it is not.
  • XMLA read/write enabled at the tenant level (Admin Portal → Tenant settings → Integration settings → “Allow XMLA endpoints and Analyze in Excel with on-premises datasets”). By default this is enabled. Required for semantic model fixers.
  • Large semantic model storage format enabled (Workspace Settings → Power BI → Large dataset storage format → ON). This applies to all datasets in the workspace and sets DefaultPowerBIDataSourceVersion to V3, which is required for any XMLA write operation. Without it, semantic model fixers will fail.

Usage

Two cells — that is all it takes. Install and import takes approx 1 min, the Fixer itself will run within seconds.

# Cell 1: Install and import
%pip install git+https://github.com/KornAlexander/semantic-link-labs.git
from sempy_labs.report import pbi_fixer

# Cell 2: Run
pbi_fixer()

Splitting this into two cells is intentional. The install only needs to run once per session — after that you can skip Cell 1 entirely and just re-run Cell 2. It also keeps things clean: use “Clear Output” on Cell 1 to remove the verbose pip installation log, so only the interactive PBI Fixer UI remains visible in your notebook.

The interactive UI renders in the cell output. Enter your workspace and report name, select the fixers you want, choose your mode, and click Run.

Note: If your report was uploaded as a .pbix file, it may be in PBIRLegacy format — the visual fixers won’t find any charts until you convert it. Open the report in Power BI Desktop, save it, and re-publish to convert to PBIR. Also make sure Large semantic model storage format is enabled in the workspace settings (see prerequisites above) — without it, all semantic model fixers will fail with an XMLA write error.

> Note: The PBI Fixer currently lives in a fork. Once it is part of the official Semantic Link Labs package, this becomes a true one-liner — just %pip install semantic-link-labs followed by pbi_fixer(). I am working toward getting it merged into the main repository, but given the scope of the fixers and the UI layer, this may take some time and may not happen at all.

For automation or scripting without the UI, call any fixer function directly:

from sempy_labs.report import fix_piecharts, fix_barcharts

# Scan only — see what would change
fix_piecharts(report="Sales Dashboard", workspace="Production", scan_only=True)

# Fix — apply the changes
fix_barcharts(report="Sales Dashboard", workspace="Production")

6. The Road Ahead

The PBI Fixer is designed as a framework, not a fixed set of rules. Each fixer is an independent Python function with a consistent interface: report, workspace, scan only. Adding a new fixer is as simple as writing one function and wiring it into the UI.

Currently available fixers:

Report Fixers

  • fix_piecharts — replaces all pie charts with Clustered Bar Charts
  • fix_barcharts — removes axis titles/values, adds data labels, removes gridlines
  • fix_columncharts — removes axis titles/values, adds data labels, removes gridlines
  • fix_page_size — changes default 720×1280 pages to 1080×1920 (Full HD)
  • fix_hide_visual_filters — sets isHiddenInViewMode on all visual-level filters

Semantic Model Fixers

  • fix_discourage_implicit_measures — sets DiscourageImplicitMeasures to True (recommended & required for calc groups)
  • add_calculated_calendar — adds a calculated calendar table if no table has been marked as a date table
  • add_measure_table — adds an empty “Measure” calculated table to centralise measures
  • add_last_refresh_table — adds a “Last Refresh” table with M partition & measure showing refresh timestamp
  • add_calc_group_units — Thousand & Million items, skips % / ratio measures
  • add_calc_group_time_intelligence — AC, Y-1/Y-2/Y-3, YTD, abs/rel/achiev. variances (requires calendar table)

What’s especially exciting is that this is hopefully just the beginning—because the whole fixer is built for easy extension, anyone can contribute new fixers or improvements. As more people get involved, the checklists and best practices incorporated by the PBI Fixer will continue to grow and evolve. This open and extensible approach means the tool can adapt to new challenges and community needs over time.

Here is what I find most exciting about the extensibility:

IBCS Implementation

The International Business Communication Standards (IBCS) define a comprehensive set of rules for business charts: uniform scaling, standardized notation, consistent color coding, and strict chart type selection. Some of the PBI Fixer’s concepts already align with IBCS principles:

  • Replacing pie charts — IBCS explicitly discourages pie charts in favor of bar charts
  • Removing axis clutter — IBCS emphasizes clean, uncluttered visuals with data labels instead of gridlines
  • Standardizing page sizes — consistent canvas dimensions are a prerequisite for standardized layouts

These are early steps, but the architecture makes it straightforward to explore further IBCS-aligned fixers over time — and to potentially integrate with IBCS-focused custom visuals like TRUECHART for scenarios where native chart types reach their limits.

Simplifying the Defaults

Power BI’s default settings are optimized for getting started quickly — not always for production-quality reports. The idea is that the Fixer will potentially be extended by further best practice checks and fixes:

  • VertipaqAnalyzer integration — Semantic Link Labs already includes vertipaq_analyzer(). A future fixer could scan the model for high-cardinality columns, unused columns, and oversized string columns, then recommend or apply optimizations automatically
  • Report BPA integration — Semantic Link Labs already ships run_report_bpa() with 9 built-in rules that analyze the report definition for issues like oversized visuals, missing alt text, and excessive filters. Integrating this directly into the PBI Fixer as a scan-capable fixer would give you a full report-level health check alongside the visual fixes — and for rules with deterministic resolutions, auto-fix them in the same pass
  • Model BPA integration — The library’s comprehensive Model BPA covers semantic model best practices. Future fixers could take BPA findings and apply fixes for every rule that has a deterministic resolution — turning “assessment” into “assessment + auto-fix” in one step

What You Can Build Today

Even without waiting for future releases, the framework is open. You can write your own fixer function today:

def fix_my_custom_rule(report, page_name=None, workspace=None, scan_only=False):
    with connect_report(report=report, workspace=workspace, readonly=scan_only) as rw:
        # Your logic here — iterate visuals, check properties, apply fixes
        pass

Wire it into the report_fixers list in the UI, and it appears as a checkbox alongside the built-in fixers. Same for semantic model fixers — write a function that uses connect_semantic_model() and add it to sm_fixers.

The barrier to entry is intentionally low. If you can write a Python function that reads and modifies a dictionary, you can write a fixer.

Final Thoughts

The PBI Fixer is not just a tool — it is a shift in how we think about report quality. Instead of manually checking and fixing every visual property, every model setting, every best practice after the fact, we can now codify our standards and apply them at scale with a single function call.

Fabric’s notebook environment, combined with the PBIR format and the XMLA endpoint, has created something that was simply not possible two years ago: a unified, programmatic, interactive interface to both the report layer and the semantic model layer of Power BI — running entirely in the browser, with zero local tooling.

If you are building Power BI solutions professionally, this is the direction things are moving. One line. Everything assessed. Everything fixed.

pbi_fixer()

One thought on “Power BI Fixer — One Line to Fix Them All

Leave a Reply