Power BI generator
The Power BI generator turns a built business layer into a Power BI project that opens in Power BI Desktop: a complete .pbip with its semantic model written as TMDL, the format Power BI uses for a project's model. It reads the definition file that every business-layer build writes beside the reporting profile, which lists every table with its columns, declared types and row counts, so it needs no database connection. For Parquet delivery, the Parquet step writes the files first. Nothing here reads or writes Enterprise Architect.
Used by: ea-power-bi.
Runs where: local code execution. It needs Python 3, and pyarrow for the Parquet files only. Power BI Desktop is needed to open the result, not to generate it, with the .pbip save option and "Store semantic model using TMDL format" turned on. Without code execution the generator is not available (see How code engines work).
Logic: Deterministic throughout. The same definition gives the same project, file for file.
The model has the same shape on every delivery way: the same tables, names, columns, types, relationships, measures, descriptions, hidden tables and layout. Only where the rows come from, and the storage mode, differ.
The project generator
What it does. The project generator (powerbi_model) builds the semantic model from the definition file and writes the project.
- Business tables, visible. Every entity table, connector table, tag-row table and the Table Directory, named exactly as the build names them, including the
Con_prefix and the "(not in MDG)" mark, with column types from the definition. - The key columns, hidden but present. The element, connector and tagged-value identifiers are hidden on every business table. The relationships use them, and visuals that already use them keep working.
- EA's nine tables, hidden but present. A report can still reach anything EA holds that no business table carries.
- A description on every business table, column and measure, from the descriptions module (below). EA's hidden tables and the relationships carry none.
- Relationships declared by the generator, never detected by Power BI. The business layer filters EA's tables: each entity table relates to EA's element table by the element's identifier. The other direction makes Power BI refuse the project with "ambiguous paths". Connector tables relate to their source and target entity tables, tag-row tables to the table they belong to, and EA's tables to each other by EA's own keys.
- Active and inactive relationships, decided by rule. A relationship is inactive when it would be a second relationship between the same two tables, or a second filter path between two tables. A combination that links an entity type to itself has one inactive relationship, on the target side.
- Two kinds of measure, and no others. None sits on a hidden table, because a visible measure keeps a hidden table visible.
- A count measure on each entity table, named for the table, for example "Business Application count", counting the table's rows. Connector, tag-row and Table Directory tables get none. A name that collides with another measure or with a column of its table stops the generation and names both; it is never renamed.
- A traversal measure for each inactive relationship whose many side is a connector table, on that connector table, named for what it answers, for example "Uses by target Business Application". It counts the connector rows through the inactive relationship, which is what makes the target side of a self-linking combination reachable.
- One blank member on every entity table. Because the business layer filters EA's element table, which holds every element, Power BI adds a blank row on each entity table's side: "(Blank)" in a slicer and one extra row in a table visual. The count measures are right; a figure one too high is the blank member.
- A default layout: business tables in bands on top (entity tables, then connector tables, then tag-row tables and the Table Directory), and EA's tables in a separate band below.
- The storage mode. Import by default. DirectQuery is accepted for views in the EA database, and refused for a reporting database and for Parquet files.
ea-power-biuses DirectQuery only for views in the EA database, and only when you want live data. The database server and name come from the definition; the business tables are read from the profile's schema and EA's tables from theirs. - Regenerating keeps your report. The semantic model's definition is written afresh in full, so a table that left the definition leaves the project, and an existing report folder is left untouched, so visuals you built are not reset. A first generation adds a report with an Overview page; a project whose report folder already exists does not get one.
- It prints its counts: tables (and how many are hidden), relationships (and how many are inactive), and measures.
- It prints the size line: the model's size in tables plus columns, the unit of the 1,000 limit Microsoft documents for Copilot's index, for the full model with hidden tables and for the visible business layer. Past 1,000 it warns that Copilot may leave some out. It only warns, and chooses nothing.
What it needs. The definition file from the last build or refresh, and the descriptions file when you approved one. To load data, Power BI Desktop needs read access to the database the definition names, or to the Parquet folder.
What an architect checks.
- The layer was checked before the project was generated. An unchecked layer is not a deliverable.
- The printed counts match what Power BI shows for tables, relationships, inactive relationships and measures.
- EA's tables and the key columns are hidden, not absent, each entity table has a count measure, and the traversal measures sit on the connector tables.
- No relationship was added by hand or detected by Power BI.
- One stable project name per model, and a short project folder: Windows limits a path to 260 characters, and the project's nested folders count toward it.
Limitations. The generated files are the source of truth: changes made to the model inside Power BI are lost at the next generation, so business definitions belong in the descriptions file. DirectQuery queries the production database. Regenerating the same model gives the same lineage tags. Regenerating never writes an existing report folder, so it does not repair a damaged report: move the report folder aside and regenerate for a fresh one.
Model, TMDL and project files
Four modules sit under the project generator. They take plain data, return text, and write nothing, except that the descriptions module reads the descriptions file you approved.
| Module | What it does |
|---|---|
semantic_model |
Holds the model: tables, columns, relationships and measures. It does not choose where a table's rows come from; that is passed in |
descriptions |
Gives every business table, column and measure its description. For each object the first source with text wins: the descriptions file you approved; the technology's own notes (the Table Directory's description for a table, a tag's note for a column), never shortened; then a fixed template that names the object, such as "Number of Business Application elements." Templates define no business terms. It refuses the file when an entry names a table, column or measure the model does not have, names one of EA's own tables, is empty, or is longer than 250 characters, and then no project is written |
tmdl |
Renders the model as TMDL text, one file per table plus the model, database, culture and relationship files |
pbip |
Assembles every file of the project: the entry point, a source-control ignore list for the settings and cache files Power BI writes into a project, the semantic model folder, and a report folder with AI Power Tools' own minimal theme and one 1280 by 720 page named "Overview". The page holds a table of the Table Directory (each table's name, kind and description) and a card for each entity count measure, and grows taller only when the cards need more room |
Why the relationships are generated. Measured on one 40-table model loaded with no relationships declared, Power BI's own detection produced 63 relationships where the model needed 38. Twenty-eight were invented from shared values, two tables related because both held the same classification value, and one correct relationship was switched off silently because two paths competed. Nothing in Power BI reports any of this.
Format rules that are not preferences, read from a project Power BI Desktop 2.158 wrote:
- TMDL is tab-indented, with Windows line endings, in UTF-8 without a byte-order mark. A file that deviates is rejected.
- Descriptions are comment lines placed before the object they describe, collapsed to one line.
- A relationship cannot carry a description. One that does makes the whole project fail to open, with no file or line named.
- A one-to-one relationship must filter in both directions, or Power BI rejects the whole file.
- Power BI validates TMDL when it applies it and rejects the whole file on one violation. That suits a generator and makes a hand edit risky.
- Identifiers, including lineage tags, are derived from each object's kind and path, never made up per run, so regenerating gives byte-identical files.
- Automatic date tables are suppressed, and every column summarizes by nothing, so a numeric tag is never offered as a sum.
Values are escaped or refused. Text written into a data source is escaped, so text that looks like an escape sequence stays text. A name containing a line break or tab is refused rather than written. A table name that Windows cannot use as a file name is encoded in the file name only.
Why the report half is built the way it is. The report is validated as strictly as the model and fails far less readably, so the project copies the structure of one Power BI wrote. Two measured failures shaped it: a version setting written as text makes Power BI load nothing; and a report definition carrying only its schema loads the model but fails the report with an opaque activity id, after which every save crashes. A base theme is required: without one, the model loads and the report fails, while a minimal theme of AI Power Tools' own opens cleanly.
What an architect checks. The project opens: the model loads, then the report, and a refresh completes. If the project will not open and names no file, regenerate it rather than editing it by hand.
Limitations. Version-pinned: the schema and version strings were read from a Power BI Desktop 2.158 project. The report has one Overview page and no other visuals, and the project holds no data until it is refreshed.
The Parquet step
What it does. For Parquet delivery, the Parquet step (parquet_tables) reads the definition file and the computed rows that the server's build writes beside the profile, writes one Parquet file per table (the business tables and EA's nine tables) into the profile's folder, and then writes the Power BI project, by default in that folder's parent.
- Types are declared, never inferred. Text, 64-bit integer, double, Boolean and date-time come from the definition, so a refresh cannot change a column's type because of which rows happen to be filled. Every field allows an empty value, and an empty table is still written with its columns.
- A value that does not fit its type is refused, naming the table and column, never repaired.
- Checks come before anything is written: the rows must match the definition's tables exactly, and every table name must be usable as a Windows file name and must not differ from another only by letter case. Only then is
pyarrowneeded, so a wrong input is never reported as a missing package. - Without
pyarrowit stops and gives the command that installs it. CSV and Excel are never substitutes: Parquet carries column names and types, while the same tables loaded through Excel arrived in Power BI asColumn1toColumnN. - Repeatable. The same input and the same
pyarrowversion give byte-identical files. - Descriptions and the size line. It takes the same descriptions file as the project generator, and prints the same size line.
What it needs. Local code execution, Python 3 and pyarrow; the definition file and the rows file from the last build, replay or refresh of a Parquet profile.
What an architect checks.
- The printed table and row counts match the counts the build reported.
- After a refresh, the Parquet step was run again before Power BI was refreshed. A background refresh rewrites the rows and the definition beside the profile, but not the Parquet files or the project.
- The project opens from where it was generated.
Limitations. The project stores the folder as an absolute path: move the folder or the project and you must regenerate. The files are written to a local folder, so a model published to the Power BI service cannot refresh from them. A file for a table that has left the definition stays in the folder; the regenerated project does not read it.