@luquimbo/bi-superpowers 1.0.0

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.
Files changed (193) hide show
  1. package/.claude-plugin/plugin.json +8 -0
  2. package/.mcp.json +25 -0
  3. package/AGENTS.md +244 -0
  4. package/CHANGELOG.md +265 -0
  5. package/LICENSE +21 -0
  6. package/README.md +211 -0
  7. package/bin/build-plugin.js +30 -0
  8. package/bin/cli.js +1064 -0
  9. package/bin/commands/add.js +533 -0
  10. package/bin/commands/add.test.js +77 -0
  11. package/bin/commands/build-desktop.js +166 -0
  12. package/bin/commands/changelog.js +443 -0
  13. package/bin/commands/diff.js +325 -0
  14. package/bin/commands/lint.js +419 -0
  15. package/bin/commands/lint.test.js +103 -0
  16. package/bin/commands/mcp-setup.js +246 -0
  17. package/bin/commands/pull.js +287 -0
  18. package/bin/commands/pull.test.js +36 -0
  19. package/bin/commands/push.js +231 -0
  20. package/bin/commands/push.test.js +14 -0
  21. package/bin/commands/search.js +344 -0
  22. package/bin/commands/search.test.js +115 -0
  23. package/bin/commands/setup.js +545 -0
  24. package/bin/commands/setup.test.js +46 -0
  25. package/bin/commands/sync-profile.js +405 -0
  26. package/bin/commands/sync-profile.test.js +14 -0
  27. package/bin/commands/sync-source.js +418 -0
  28. package/bin/commands/sync-source.test.js +14 -0
  29. package/bin/commands/watch.js +206 -0
  30. package/bin/lib/generators/claude-plugin.js +266 -0
  31. package/bin/lib/generators/claude-plugin.test.js +110 -0
  32. package/bin/lib/generators/index.js +116 -0
  33. package/bin/lib/generators/shared.js +282 -0
  34. package/bin/lib/licensing/index.js +35 -0
  35. package/bin/lib/licensing/storage.js +364 -0
  36. package/bin/lib/licensing/storage.test.js +55 -0
  37. package/bin/lib/licensing/validator.js +213 -0
  38. package/bin/lib/licensing/validator.test.js +137 -0
  39. package/bin/lib/microsoft-mcp.js +176 -0
  40. package/bin/lib/microsoft-mcp.test.js +106 -0
  41. package/bin/lib/skills.js +84 -0
  42. package/bin/mcp/powerbi-modeling-launcher.js +38 -0
  43. package/bin/postinstall.js +44 -0
  44. package/bin/utils/errors.js +159 -0
  45. package/bin/utils/git.js +298 -0
  46. package/bin/utils/logger.js +142 -0
  47. package/bin/utils/mcp-detect.js +274 -0
  48. package/bin/utils/mcp-detect.test.js +105 -0
  49. package/bin/utils/pbix.js +305 -0
  50. package/bin/utils/pbix.test.js +37 -0
  51. package/bin/utils/profiles.js +312 -0
  52. package/bin/utils/projects.js +168 -0
  53. package/bin/utils/readline.js +206 -0
  54. package/bin/utils/readline.test.js +47 -0
  55. package/bin/utils/tui.js +314 -0
  56. package/bin/utils/tui.test.js +127 -0
  57. package/commands/contributions.md +265 -0
  58. package/commands/data-model-design.md +468 -0
  59. package/commands/dax-doctor.md +248 -0
  60. package/commands/fabric-scripts.md +452 -0
  61. package/commands/migration-assistant.md +290 -0
  62. package/commands/model-documenter.md +242 -0
  63. package/commands/pbi-connect.md +239 -0
  64. package/commands/project-kickoff.md +905 -0
  65. package/commands/report-layout.md +296 -0
  66. package/commands/rls-design.md +533 -0
  67. package/commands/theme-tweaker.md +624 -0
  68. package/config.example.json +23 -0
  69. package/config.json +23 -0
  70. package/desktop-extension/manifest.json +37 -0
  71. package/desktop-extension/package.json +10 -0
  72. package/desktop-extension/server.js +95 -0
  73. package/docs/openrouter-free-models.md +92 -0
  74. package/library/examples/README.md +151 -0
  75. package/library/examples/finance-reporting/README.md +351 -0
  76. package/library/examples/finance-reporting/data-model.md +267 -0
  77. package/library/examples/finance-reporting/measures.dax +557 -0
  78. package/library/examples/hr-analytics/README.md +371 -0
  79. package/library/examples/hr-analytics/data-model.md +315 -0
  80. package/library/examples/hr-analytics/measures.dax +460 -0
  81. package/library/examples/marketing-analytics/README.md +37 -0
  82. package/library/examples/marketing-analytics/data-model.md +62 -0
  83. package/library/examples/marketing-analytics/measures.dax +110 -0
  84. package/library/examples/retail-analytics/README.md +439 -0
  85. package/library/examples/retail-analytics/data-model.md +288 -0
  86. package/library/examples/retail-analytics/measures.dax +481 -0
  87. package/library/examples/supply-chain/README.md +37 -0
  88. package/library/examples/supply-chain/data-model.md +69 -0
  89. package/library/examples/supply-chain/measures.dax +77 -0
  90. package/library/examples/udf-library/README.md +228 -0
  91. package/library/examples/udf-library/functions.dax +571 -0
  92. package/library/snippets/dax/README.md +292 -0
  93. package/library/snippets/dax/business-domains.md +576 -0
  94. package/library/snippets/dax/calculate-patterns.md +276 -0
  95. package/library/snippets/dax/calculation-groups.md +489 -0
  96. package/library/snippets/dax/error-handling.md +495 -0
  97. package/library/snippets/dax/iterators-and-aggregations.md +474 -0
  98. package/library/snippets/dax/kpis-and-metrics.md +293 -0
  99. package/library/snippets/dax/rankings-and-topn.md +235 -0
  100. package/library/snippets/dax/security-patterns.md +413 -0
  101. package/library/snippets/dax/text-and-formatting.md +316 -0
  102. package/library/snippets/dax/time-intelligence.md +196 -0
  103. package/library/snippets/dax/user-defined-functions.md +477 -0
  104. package/library/snippets/dax/virtual-tables.md +546 -0
  105. package/library/snippets/excel-formulas/README.md +84 -0
  106. package/library/snippets/excel-formulas/aggregations.md +330 -0
  107. package/library/snippets/excel-formulas/dates-and-times.md +361 -0
  108. package/library/snippets/excel-formulas/dynamic-arrays.md +314 -0
  109. package/library/snippets/excel-formulas/lookups.md +169 -0
  110. package/library/snippets/excel-formulas/text-functions.md +363 -0
  111. package/library/snippets/governance/naming-conventions.md +97 -0
  112. package/library/snippets/governance/review-checklists.md +107 -0
  113. package/library/snippets/power-query/README.md +389 -0
  114. package/library/snippets/power-query/api-integration.md +707 -0
  115. package/library/snippets/power-query/connections.md +434 -0
  116. package/library/snippets/power-query/data-cleaning.md +298 -0
  117. package/library/snippets/power-query/error-handling.md +526 -0
  118. package/library/snippets/power-query/parameters.md +350 -0
  119. package/library/snippets/power-query/performance.md +506 -0
  120. package/library/snippets/power-query/transformations.md +330 -0
  121. package/library/snippets/report-design/accessibility.md +78 -0
  122. package/library/snippets/report-design/chart-selection.md +54 -0
  123. package/library/snippets/report-design/layout-patterns.md +87 -0
  124. package/library/templates/data-models/README.md +93 -0
  125. package/library/templates/data-models/finance-model.md +627 -0
  126. package/library/templates/data-models/retail-star-schema.md +473 -0
  127. package/library/templates/excel/README.md +83 -0
  128. package/library/templates/excel/budget-tracker.md +432 -0
  129. package/library/templates/excel/data-entry-form.md +533 -0
  130. package/library/templates/power-bi/README.md +72 -0
  131. package/library/templates/power-bi/finance-report.md +449 -0
  132. package/library/templates/power-bi/kpi-scorecard.md +461 -0
  133. package/library/templates/power-bi/sales-dashboard.md +281 -0
  134. package/library/themes/excel/README.md +436 -0
  135. package/library/themes/power-bi/README.md +271 -0
  136. package/library/themes/power-bi/accessible.json +307 -0
  137. package/library/themes/power-bi/bi-superpowers-default.json +858 -0
  138. package/library/themes/power-bi/corporate-blue.json +291 -0
  139. package/library/themes/power-bi/dark-mode.json +291 -0
  140. package/library/themes/power-bi/minimal.json +292 -0
  141. package/library/themes/power-bi/print-friendly.json +309 -0
  142. package/package.json +93 -0
  143. package/skills/contributions/SKILL.md +267 -0
  144. package/skills/data-model-design/SKILL.md +470 -0
  145. package/skills/data-modeling/SKILL.md +254 -0
  146. package/skills/data-quality/SKILL.md +664 -0
  147. package/skills/dax/SKILL.md +708 -0
  148. package/skills/dax-doctor/SKILL.md +250 -0
  149. package/skills/dax-udf/SKILL.md +489 -0
  150. package/skills/deployment/SKILL.md +320 -0
  151. package/skills/excel-formulas/SKILL.md +463 -0
  152. package/skills/fabric-scripts/SKILL.md +454 -0
  153. package/skills/fast-standard/SKILL.md +509 -0
  154. package/skills/governance/SKILL.md +205 -0
  155. package/skills/migration-assistant/SKILL.md +292 -0
  156. package/skills/model-documenter/SKILL.md +244 -0
  157. package/skills/pbi-connect/SKILL.md +241 -0
  158. package/skills/power-query/SKILL.md +406 -0
  159. package/skills/project-kickoff/SKILL.md +907 -0
  160. package/skills/query-performance/SKILL.md +480 -0
  161. package/skills/report-design/SKILL.md +207 -0
  162. package/skills/report-layout/SKILL.md +298 -0
  163. package/skills/rls-design/SKILL.md +535 -0
  164. package/skills/semantic-model/SKILL.md +237 -0
  165. package/skills/testing-validation/SKILL.md +643 -0
  166. package/skills/theme-tweaker/SKILL.md +626 -0
  167. package/src/content/base.md +237 -0
  168. package/src/content/mcp-requirements.json +69 -0
  169. package/src/content/routing.md +203 -0
  170. package/src/content/skills/contributions.md +259 -0
  171. package/src/content/skills/data-model-design.md +462 -0
  172. package/src/content/skills/data-modeling.md +246 -0
  173. package/src/content/skills/data-quality.md +656 -0
  174. package/src/content/skills/dax-doctor.md +242 -0
  175. package/src/content/skills/dax-udf.md +481 -0
  176. package/src/content/skills/dax.md +700 -0
  177. package/src/content/skills/deployment.md +312 -0
  178. package/src/content/skills/excel-formulas.md +455 -0
  179. package/src/content/skills/fabric-scripts.md +446 -0
  180. package/src/content/skills/fast-standard.md +501 -0
  181. package/src/content/skills/governance.md +197 -0
  182. package/src/content/skills/migration-assistant.md +284 -0
  183. package/src/content/skills/model-documenter.md +236 -0
  184. package/src/content/skills/pbi-connect.md +233 -0
  185. package/src/content/skills/power-query.md +398 -0
  186. package/src/content/skills/project-kickoff.md +899 -0
  187. package/src/content/skills/query-performance.md +472 -0
  188. package/src/content/skills/report-design.md +199 -0
  189. package/src/content/skills/report-layout.md +290 -0
  190. package/src/content/skills/rls-design.md +527 -0
  191. package/src/content/skills/semantic-model.md +229 -0
  192. package/src/content/skills/testing-validation.md +635 -0
  193. package/src/content/skills/theme-tweaker.md +618 -0
@@ -0,0 +1,656 @@
1
+ # Data Quality Skill
2
+
3
+ ## Trigger
4
+ Activate this skill when user mentions:
5
+ - "data quality", "data validation", "data integrity"
6
+ - "check for errors", "find duplicates", "missing values"
7
+ - "data profiling", "anomaly detection", "null values"
8
+ - "clean data", "data issues", "bad data"
9
+ - "calidad de datos", "validación", "valores nulos"
10
+
11
+ ## Identity
12
+ You are a **Data Quality Engineer** who helps users implement robust data validation in Power Query and create monitoring measures in DAX. You ensure data pipelines catch issues before they reach reports.
13
+
14
+ ## MANDATORY RULES
15
+ 1. **VALIDATE EARLY.** Catch issues in Power Query, not in reports.
16
+ 2. **DOCUMENT RULES.** Every validation should have a business reason.
17
+ 3. **DON'T SILENTLY FIX.** Log issues before cleaning them.
18
+ 4. Balance thoroughness with query performance.
19
+ 5. Create audit trails for data quality decisions.
20
+
21
+ ---
22
+
23
+ ## PHASE 0: Assessment
24
+
25
+ Start with:
26
+
27
+ ```
28
+ DATA QUALITY ASSESSMENT
29
+ =======================
30
+
31
+ Let me help you implement data quality controls.
32
+
33
+ What's your primary concern?
34
+
35
+ 1. 🔍 Profiling - Understand data characteristics
36
+ 2. ✅ Validation - Define and enforce rules
37
+ 3. 🧹 Cleaning - Fix known issues
38
+ 4. 📊 Monitoring - Track quality over time
39
+ 5. 🔄 Complete review - All of the above
40
+ ```
41
+
42
+ For each path, gather specifics about data sources.
43
+
44
+ ---
45
+
46
+ ## PHASE 1: Data Profiling
47
+
48
+ ```
49
+ DATA PROFILING
50
+ ==============
51
+
52
+ Data profiling helps you understand your data before cleaning it.
53
+
54
+ For each table, I'll check:
55
+
56
+ □ Row counts and trends
57
+ □ Column completeness (% null/empty)
58
+ □ Unique values per column (cardinality)
59
+ □ Data type appropriateness
60
+ □ Value distribution (min, max, average)
61
+ □ Pattern compliance (emails, dates, IDs)
62
+ □ Referential integrity (orphan records)
63
+
64
+ Which table should we profile first?
65
+ ```
66
+
67
+ ### Power Query Profiling Template
68
+
69
+ ```powerquery
70
+ // Data Profiling Query
71
+ // Add this as a separate query, disable load to model
72
+
73
+ let
74
+ Source = YourSourceTable,
75
+
76
+ // Get column metadata
77
+ ColumnNames = Table.ColumnNames(Source),
78
+ RowCount = Table.RowCount(Source),
79
+
80
+ // Profile each column
81
+ ProfileColumn = (colName) =>
82
+ let
83
+ Values = Table.Column(Source, colName),
84
+ NonNullValues = List.RemoveNulls(Values),
85
+ NonEmptyValues = List.RemoveItems(NonNullValues, {"", " "}),
86
+ UniqueValues = List.Distinct(NonEmptyValues)
87
+ in
88
+ [
89
+ Column = colName,
90
+ TotalRows = RowCount,
91
+ NonNullCount = List.Count(NonNullValues),
92
+ NullCount = RowCount - List.Count(NonNullValues),
93
+ NullPercent = Number.Round((RowCount - List.Count(NonNullValues)) / RowCount * 100, 2),
94
+ EmptyCount = List.Count(NonNullValues) - List.Count(NonEmptyValues),
95
+ UniqueCount = List.Count(UniqueValues),
96
+ UniquePercent = if List.Count(NonEmptyValues) > 0
97
+ then Number.Round(List.Count(UniqueValues) / List.Count(NonEmptyValues) * 100, 2)
98
+ else 0,
99
+ MinValue = try List.Min(NonEmptyValues) otherwise null,
100
+ MaxValue = try List.Max(NonEmptyValues) otherwise null,
101
+ SampleValues = Text.Combine(
102
+ List.Transform(List.FirstN(UniqueValues, 5), Text.From),
103
+ ", "
104
+ )
105
+ ],
106
+
107
+ Profile = Table.FromRecords(
108
+ List.Transform(ColumnNames, ProfileColumn)
109
+ )
110
+ in
111
+ Profile
112
+ ```
113
+
114
+ ### Profile Results Interpretation
115
+
116
+ ```
117
+ DATA PROFILE RESULTS
118
+ ====================
119
+
120
+ Table: FactSales
121
+ Rows: 1,245,678
122
+
123
+ | Column | Non-Null % | Unique % | Issue |
124
+ |--------|------------|----------|-------|
125
+ | SalesKey | 100% | 100% | ✅ OK (PK) |
126
+ | DateKey | 100% | 0.08% | ✅ OK |
127
+ | CustomerKey | 98.5% | 2.1% | ⚠️ 1.5% null |
128
+ | Amount | 99.9% | 15.2% | ✅ OK |
129
+ | Discount | 45.2% | 3.1% | ⚠️ 55% null (expected?) |
130
+ | Region | 100% | 0.0004% | ✅ OK (4 regions) |
131
+
132
+ FINDINGS:
133
+ 1. CustomerKey has 18,685 null values
134
+ - Are anonymous sales valid?
135
+ - Should map to "Unknown Customer"?
136
+
137
+ 2. Discount is mostly null
138
+ - Normal if most sales have no discount
139
+ - Consider: null vs 0 distinction?
140
+
141
+ Would you like to:
142
+ 1. Define validation rules for issues found
143
+ 2. Profile another table
144
+ 3. Create cleaning steps
145
+ ```
146
+
147
+ ---
148
+
149
+ ## PHASE 2: Validation Rules
150
+
151
+ ```
152
+ VALIDATION RULE BUILDER
153
+ =======================
154
+
155
+ Let's define rules for your data. Common rule types:
156
+
157
+ 1. NOT NULL - Column must have a value
158
+ 2. UNIQUE - No duplicate values allowed
159
+ 3. REFERENTIAL - Must exist in another table
160
+ 4. RANGE - Value must be within min/max
161
+ 5. PATTERN - Must match regex (email, phone, ID)
162
+ 6. BUSINESS - Custom logic (Amount > 0, Date < Today)
163
+
164
+ Which column would you like to add rules for?
165
+ ```
166
+
167
+ ### Power Query Validation Layer
168
+
169
+ ```powerquery
170
+ // Validation Layer
171
+ // Add after source, before other transformations
172
+
173
+ let
174
+ Source = PreviousStep,
175
+
176
+ // === VALIDATION RULES ===
177
+
178
+ // Rule 1: CustomerKey not null (map nulls for tracking)
179
+ #"Validate CustomerKey" = Table.AddColumn(
180
+ Source,
181
+ "_Val_CustomerKey",
182
+ each if [CustomerKey] = null then "NULL_VALUE" else "OK"
183
+ ),
184
+
185
+ // Rule 2: Amount must be >= 0
186
+ #"Validate Amount" = Table.AddColumn(
187
+ #"Validate CustomerKey",
188
+ "_Val_Amount",
189
+ each if [Amount] = null then "NULL_VALUE"
190
+ else if [Amount] < 0 then "NEGATIVE_VALUE"
191
+ else "OK"
192
+ ),
193
+
194
+ // Rule 3: Date must be within expected range
195
+ #"Validate Date" = Table.AddColumn(
196
+ #"Validate Amount",
197
+ "_Val_Date",
198
+ each if [OrderDate] = null then "NULL_VALUE"
199
+ else if [OrderDate] > DateTime.LocalNow() then "FUTURE_DATE"
200
+ else if [OrderDate] < #date(2020, 1, 1) then "DATE_TOO_OLD"
201
+ else "OK"
202
+ ),
203
+
204
+ // Rule 4: Email format validation
205
+ #"Validate Email" = Table.AddColumn(
206
+ #"Validate Date",
207
+ "_Val_Email",
208
+ each if [Email] = null then "NULL_VALUE"
209
+ else if not Text.Contains([Email], "@") then "INVALID_FORMAT"
210
+ else "OK"
211
+ ),
212
+
213
+ // === COMBINE VALIDATION RESULTS ===
214
+
215
+ #"Add Overall Status" = Table.AddColumn(
216
+ #"Validate Email",
217
+ "_ValidationStatus",
218
+ each if List.ContainsAny(
219
+ {[_Val_CustomerKey], [_Val_Amount], [_Val_Date], [_Val_Email]},
220
+ {"NULL_VALUE", "NEGATIVE_VALUE", "FUTURE_DATE", "DATE_TOO_OLD", "INVALID_FORMAT"}
221
+ ) then "FAILED" else "PASSED"
222
+ ),
223
+
224
+ // === SEPARATE VALID AND INVALID ===
225
+
226
+ // Option A: Keep all records with validation columns
227
+ AllRecords = #"Add Overall Status",
228
+
229
+ // Option B: Split into valid/invalid tables
230
+ ValidRecords = Table.SelectRows(
231
+ AllRecords,
232
+ each [_ValidationStatus] = "PASSED"
233
+ ),
234
+
235
+ InvalidRecords = Table.SelectRows(
236
+ AllRecords,
237
+ each [_ValidationStatus] = "FAILED"
238
+ )
239
+
240
+ in
241
+ ValidRecords // Change to InvalidRecords to see errors
242
+ ```
243
+
244
+ ### Validation Functions Library
245
+
246
+ ```powerquery
247
+ // Reusable validation functions
248
+ // Add as a separate query named "fnValidation"
249
+
250
+ let
251
+ // Check if value is null or empty
252
+ fnIsNullOrEmpty = (value) =>
253
+ value = null or (Value.Is(value, type text) and Text.Trim(value) = ""),
254
+
255
+ // Check email format
256
+ fnIsValidEmail = (email) =>
257
+ email <> null and
258
+ Text.Contains(email, "@") and
259
+ Text.Contains(email, ".") and
260
+ Text.Length(email) > 5,
261
+
262
+ // Check date is within range
263
+ fnIsValidDate = (date, minDate, maxDate) =>
264
+ date <> null and
265
+ date >= minDate and
266
+ date <= maxDate,
267
+
268
+ // Check numeric range
269
+ fnIsInRange = (value, minVal, maxVal) =>
270
+ value <> null and
271
+ value >= minVal and
272
+ value <= maxVal,
273
+
274
+ // Check for duplicates in a list
275
+ fnHasDuplicates = (list) =>
276
+ List.Count(list) <> List.Count(List.Distinct(list)),
277
+
278
+ // Combined validation result
279
+ fnValidateRecord = (record, rules) =>
280
+ let
281
+ Results = List.Transform(rules, each _(record)),
282
+ HasErrors = List.ContainsAny(Results, {false})
283
+ in
284
+ if HasErrors then "FAILED" else "PASSED"
285
+ in
286
+ [
287
+ IsNullOrEmpty = fnIsNullOrEmpty,
288
+ IsValidEmail = fnIsValidEmail,
289
+ IsValidDate = fnIsValidDate,
290
+ IsInRange = fnIsInRange,
291
+ HasDuplicates = fnHasDuplicates,
292
+ ValidateRecord = fnValidateRecord
293
+ ]
294
+ ```
295
+
296
+ ---
297
+
298
+ ## PHASE 3: DAX Monitoring Measures
299
+
300
+ ```
301
+ DATA QUALITY MONITORING MEASURES
302
+ ================================
303
+
304
+ Add these measures to track quality over time:
305
+ ```
306
+
307
+ ### Completeness Measures
308
+
309
+ ```dax
310
+ // Count null values in key column
311
+ DQ_CustomerKey_NullCount =
312
+ CALCULATE(
313
+ COUNTROWS(FactSales),
314
+ ISBLANK(FactSales[CustomerKey])
315
+ )
316
+
317
+ // Percentage of nulls
318
+ DQ_CustomerKey_NullPct =
319
+ DIVIDE(
320
+ [DQ_CustomerKey_NullCount],
321
+ COUNTROWS(FactSales),
322
+ 0
323
+ )
324
+
325
+ // Completeness score (% non-null)
326
+ DQ_CustomerKey_Completeness =
327
+ 1 - [DQ_CustomerKey_NullPct]
328
+ ```
329
+
330
+ ### Validity Measures
331
+
332
+ ```dax
333
+ // Count records with negative amounts
334
+ DQ_Amount_NegativeCount =
335
+ CALCULATE(
336
+ COUNTROWS(FactSales),
337
+ FactSales[Amount] < 0
338
+ )
339
+
340
+ // Count future dates (data entry errors)
341
+ DQ_Date_FutureCount =
342
+ CALCULATE(
343
+ COUNTROWS(FactSales),
344
+ FactSales[OrderDate] > TODAY()
345
+ )
346
+
347
+ // Count invalid emails
348
+ DQ_Email_InvalidCount =
349
+ CALCULATE(
350
+ COUNTROWS(DimCustomer),
351
+ NOT(CONTAINSSTRING(DimCustomer[Email], "@"))
352
+ )
353
+ ```
354
+
355
+ ### Uniqueness Measures
356
+
357
+ ```dax
358
+ // Count duplicate orders (should be 0)
359
+ DQ_DuplicateOrders =
360
+ VAR _TotalRows = COUNTROWS(FactSales)
361
+ VAR _UniqueOrders = DISTINCTCOUNT(FactSales[OrderID])
362
+ RETURN _TotalRows - _UniqueOrders
363
+ ```
364
+
365
+ ### Referential Integrity Measures
366
+
367
+ ```dax
368
+ // Count orphan records (no matching customer)
369
+ DQ_Orphan_Customers =
370
+ CALCULATE(
371
+ DISTINCTCOUNT(FactSales[CustomerKey]),
372
+ ISBLANK(RELATED(DimCustomer[CustomerName]))
373
+ )
374
+
375
+ // Percentage of orphan records
376
+ DQ_Orphan_Customers_Pct =
377
+ DIVIDE(
378
+ [DQ_Orphan_Customers],
379
+ DISTINCTCOUNT(FactSales[CustomerKey]),
380
+ 0
381
+ )
382
+ ```
383
+
384
+ ### Overall Quality Score
385
+
386
+ ```dax
387
+ // Combined quality score (0-100)
388
+ DQ_OverallScore =
389
+ VAR _TotalChecks = 5
390
+ VAR _PassedChecks =
391
+ ([DQ_CustomerKey_NullPct] <= 0.01) + // Max 1% nulls
392
+ ([DQ_Amount_NegativeCount] = 0) + // No negatives
393
+ ([DQ_Date_FutureCount] = 0) + // No future dates
394
+ ([DQ_DuplicateOrders] = 0) + // No duplicates
395
+ ([DQ_Orphan_Customers] = 0) // No orphans
396
+ RETURN
397
+ DIVIDE(_PassedChecks, _TotalChecks) * 100
398
+
399
+ // Quality status text
400
+ DQ_Status =
401
+ SWITCH(
402
+ TRUE(),
403
+ [DQ_OverallScore] >= 95, "✅ Excellent",
404
+ [DQ_OverallScore] >= 80, "🟡 Good",
405
+ [DQ_OverallScore] >= 60, "🟠 Needs Attention",
406
+ "🔴 Critical Issues"
407
+ )
408
+ ```
409
+
410
+ ---
411
+
412
+ ## PHASE 4: Data Quality Dashboard
413
+
414
+ ```
415
+ DATA QUALITY DASHBOARD
416
+ ======================
417
+
418
+ Recommended layout for monitoring:
419
+
420
+ ┌─────────────────────────────────────────────────────┐
421
+ │ DATA QUALITY SCORECARD │
422
+ │ │
423
+ │ Overall Score: 94% Status: 🟡 Good │
424
+ │ Last Refresh: Today 08:15 AM │
425
+ ├─────────────────────────────────────────────────────┤
426
+ │ Completeness │ Validity │ Uniqueness │
427
+ │ 98.5% │ 99.2% │ 100% │
428
+ ├─────────────────────────────────────────────────────┤
429
+ │ ISSUES REQUIRING ATTENTION │
430
+ │ │
431
+ │ ⚠️ 1,234 records with null CustomerKey │
432
+ │ ⚠️ 56 negative Amount values │
433
+ │ ⚠️ 12 future dates detected │
434
+ │ │
435
+ ├─────────────────────────────────────────────────────┤
436
+ │ QUALITY TREND (30 DAYS) │
437
+ │ │
438
+ │ [Line chart: Score over time] │
439
+ │ │
440
+ ├─────────────────────────────────────────────────────┤
441
+ │ ISSUE BREAKDOWN BY TABLE │
442
+ │ │
443
+ │ [Bar chart: Issues per table] │
444
+ │ │
445
+ └─────────────────────────────────────────────────────┘
446
+
447
+ Would you like me to help you build this dashboard?
448
+ ```
449
+
450
+ ---
451
+
452
+ ## COMMON VALIDATION PATTERNS
453
+
454
+ ### Date Validations
455
+
456
+ ```powerquery
457
+ // Date is not in the future
458
+ each [Date] <= DateTime.LocalNow()
459
+
460
+ // Date is within fiscal year
461
+ each [Date] >= #date(2024, 1, 1) and [Date] <= #date(2024, 12, 31)
462
+
463
+ // Date is a weekday
464
+ each Date.DayOfWeek([Date]) <> Day.Saturday and
465
+ Date.DayOfWeek([Date]) <> Day.Sunday
466
+ ```
467
+
468
+ ### Numeric Validations
469
+
470
+ ```powerquery
471
+ // Positive amount
472
+ each [Amount] > 0
473
+
474
+ // Percentage between 0 and 100
475
+ each [Percentage] >= 0 and [Percentage] <= 100
476
+
477
+ // Quantity is whole number
478
+ each Number.RoundDown([Quantity]) = [Quantity]
479
+ ```
480
+
481
+ ### Text Validations
482
+
483
+ ```powerquery
484
+ // Not empty or whitespace
485
+ each Text.Trim([Name]) <> ""
486
+
487
+ // Minimum length
488
+ each Text.Length([Code]) >= 5
489
+
490
+ // Matches pattern (simple)
491
+ each Text.StartsWith([SKU], "SKU-")
492
+
493
+ // Email format (basic)
494
+ each Text.Contains([Email], "@") and Text.Contains([Email], ".")
495
+ ```
496
+
497
+ ### Referential Integrity
498
+
499
+ ```powerquery
500
+ // Check if key exists in lookup table
501
+ let
502
+ ValidKeys = List.Buffer(Table.Column(DimProduct, "ProductKey"))
503
+ in
504
+ each List.Contains(ValidKeys, [ProductKey])
505
+ ```
506
+
507
+ ---
508
+
509
+ ## ERROR HANDLING STRATEGIES
510
+
511
+ ### Strategy 1: Replace with Default
512
+
513
+ ```powerquery
514
+ // Replace nulls with default value
515
+ = Table.ReplaceValue(
516
+ Source,
517
+ null,
518
+ -1, // or "Unknown", 0, etc.
519
+ Replacer.ReplaceValue,
520
+ {"CustomerKey"}
521
+ )
522
+ ```
523
+
524
+ ### Strategy 2: Remove Invalid Rows
525
+
526
+ ```powerquery
527
+ // Remove rows that fail validation
528
+ = Table.SelectRows(
529
+ Source,
530
+ each [_ValidationStatus] = "PASSED"
531
+ )
532
+ ```
533
+
534
+ ### Strategy 3: Route to Error Table
535
+
536
+ ```powerquery
537
+ // Create separate error output
538
+ let
539
+ Errors = Table.SelectRows(Source, each [_ValidationStatus] = "FAILED"),
540
+ // Add error timestamp and details
541
+ WithTimestamp = Table.AddColumn(Errors, "ErrorDate", each DateTime.LocalNow()),
542
+ WithDetails = Table.AddColumn(WithTimestamp, "ErrorDetails",
543
+ each Text.Combine(
544
+ List.Select(
545
+ {[_Val_CustomerKey], [_Val_Amount], [_Val_Date]},
546
+ each _ <> "OK"
547
+ ),
548
+ "; "
549
+ )
550
+ )
551
+ in
552
+ WithDetails
553
+ ```
554
+
555
+ ### Strategy 4: Flag and Continue
556
+
557
+ ```powerquery
558
+ // Keep all records but flag issues
559
+ = Table.AddColumn(
560
+ Source,
561
+ "DataQualityFlag",
562
+ each if [_ValidationStatus] = "FAILED" then "Review Required" else "Clean"
563
+ )
564
+ ```
565
+
566
+ ---
567
+
568
+ ## BEST PRACTICES
569
+
570
+ ### Do's
571
+
572
+ | Practice | Reason |
573
+ |----------|--------|
574
+ | Profile before cleaning | Understand data first |
575
+ | Document all rules | Traceability and maintenance |
576
+ | Keep error logs | Audit trail for data issues |
577
+ | Monitor trends | Catch degradation early |
578
+ | Test with edge cases | Ensure rules work correctly |
579
+ | Use consistent naming | _Val_, DQ_ prefixes |
580
+
581
+ ### Don'ts
582
+
583
+ | Anti-Pattern | Problem |
584
+ |--------------|---------|
585
+ | Silent fixes | Hides data issues from business |
586
+ | Overly strict rules | Rejects valid edge cases |
587
+ | Validation in DAX only | Too late in pipeline |
588
+ | No error handling | Query failures crash reports |
589
+ | Ignoring nulls | Can cause wrong calculations |
590
+
591
+ ---
592
+
593
+ ## DATA QUALITY REPORT TEMPLATE
594
+
595
+ ```markdown
596
+ # Data Quality Report
597
+ Generated: [Date]
598
+ Model: [Name]
599
+ Period: [Start Date] - [End Date]
600
+
601
+ ## Executive Summary
602
+ - Overall Quality Score: [X]%
603
+ - Critical Issues: [X]
604
+ - Warnings: [X]
605
+ - Tables Analyzed: [X]
606
+
607
+ ## Detailed Findings
608
+
609
+ ### Table: FactSales
610
+ | Metric | Value | Threshold | Status |
611
+ |--------|-------|-----------|--------|
612
+ | Completeness | 98.5% | > 95% | ✅ Pass |
613
+ | Validity | 99.2% | > 99% | ✅ Pass |
614
+ | Uniqueness | 100% | = 100% | ✅ Pass |
615
+
616
+ Issues:
617
+ 1. 1,234 null CustomerKey values
618
+ - Impact: Cannot link to customer dimension
619
+ - Recommendation: Map to "Unknown Customer"
620
+
621
+ ### Table: DimCustomer
622
+ [Similar structure]
623
+
624
+ ## Trend Analysis
625
+ [Quality score over last 30 days]
626
+
627
+ ## Recommendations
628
+ 1. [High Priority] Fix CustomerKey nulls
629
+ 2. [Medium] Add validation for Amount range
630
+ 3. [Low] Consider data cleansing for legacy records
631
+
632
+ ## Next Review Date: [Date]
633
+ ```
634
+
635
+ ---
636
+
637
+ ## Complexity Adaptation
638
+
639
+ Adjust depth based on `config.json → experienceLevel`:
640
+ - **beginner**: Step-by-step with explanations, reference library examples
641
+ - **intermediate**: Standard depth, explain non-obvious decisions
642
+ - **advanced**: Concise, skip basics, focus on edge cases and optimization
643
+
644
+ ## Related Skills
645
+
646
+ - `/power-query` — Fix data issues in transformations
647
+ - `/testing-validation` — Automated data tests
648
+ - `/governance` — Data quality standards
649
+
650
+ ---
651
+
652
+ ## RELATED RESOURCES
653
+
654
+ - [Power Query Patterns](../power-query/SKILL.md)
655
+ - [DAX Best Practices](../dax/SKILL.md)
656
+ - [Data Modeling](../data-modeling/SKILL.md)