@luquimbo/bi-superpowers 3.1.1 → 3.2.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 (107) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.claude-plugin/skill-manifest.json +1 -1
  4. package/.plugin/plugin.json +1 -1
  5. package/bin/build-plugin.js +6 -6
  6. package/bin/cli.js +169 -310
  7. package/bin/commands/install.js +87 -70
  8. package/bin/lib/agents.js +19 -0
  9. package/bin/lib/mcp-config.js +23 -4
  10. package/desktop-extension/manifest.json +4 -11
  11. package/desktop-extension/server.js +34 -25
  12. package/package.json +3 -9
  13. package/skills/pbi-connect/SKILL.md +1 -1
  14. package/skills/project-kickoff/SKILL.md +1 -1
  15. package/bin/commands/add.js +0 -533
  16. package/bin/commands/add.test.js +0 -77
  17. package/bin/commands/changelog.js +0 -443
  18. package/bin/commands/pull.js +0 -287
  19. package/bin/commands/pull.test.js +0 -36
  20. package/bin/commands/push.js +0 -231
  21. package/bin/commands/push.test.js +0 -14
  22. package/bin/commands/search.js +0 -344
  23. package/bin/commands/search.test.js +0 -115
  24. package/bin/commands/setup.js +0 -545
  25. package/bin/commands/setup.test.js +0 -46
  26. package/bin/commands/sync-profile.js +0 -405
  27. package/bin/commands/sync-profile.test.js +0 -14
  28. package/bin/commands/sync-source.js +0 -418
  29. package/bin/commands/sync-source.test.js +0 -14
  30. package/bin/utils/errors.js +0 -159
  31. package/bin/utils/git.js +0 -298
  32. package/bin/utils/logger.js +0 -142
  33. package/bin/utils/pbix.js +0 -305
  34. package/bin/utils/pbix.test.js +0 -37
  35. package/bin/utils/profiles.js +0 -312
  36. package/bin/utils/projects.js +0 -169
  37. package/bin/utils/readline.js +0 -206
  38. package/bin/utils/readline.test.js +0 -47
  39. package/docs/openrouter-free-models.md +0 -92
  40. package/library/examples/README.md +0 -151
  41. package/library/examples/finance-reporting/README.md +0 -351
  42. package/library/examples/finance-reporting/data-model.md +0 -267
  43. package/library/examples/finance-reporting/measures.dax +0 -557
  44. package/library/examples/hr-analytics/README.md +0 -371
  45. package/library/examples/hr-analytics/data-model.md +0 -315
  46. package/library/examples/hr-analytics/measures.dax +0 -460
  47. package/library/examples/marketing-analytics/README.md +0 -37
  48. package/library/examples/marketing-analytics/data-model.md +0 -62
  49. package/library/examples/marketing-analytics/measures.dax +0 -110
  50. package/library/examples/retail-analytics/README.md +0 -439
  51. package/library/examples/retail-analytics/data-model.md +0 -288
  52. package/library/examples/retail-analytics/measures.dax +0 -481
  53. package/library/examples/supply-chain/README.md +0 -37
  54. package/library/examples/supply-chain/data-model.md +0 -69
  55. package/library/examples/supply-chain/measures.dax +0 -77
  56. package/library/examples/udf-library/README.md +0 -228
  57. package/library/examples/udf-library/functions.dax +0 -571
  58. package/library/snippets/dax/README.md +0 -292
  59. package/library/snippets/dax/business-domains.md +0 -576
  60. package/library/snippets/dax/calculate-patterns.md +0 -276
  61. package/library/snippets/dax/calculation-groups.md +0 -489
  62. package/library/snippets/dax/error-handling.md +0 -495
  63. package/library/snippets/dax/iterators-and-aggregations.md +0 -474
  64. package/library/snippets/dax/kpis-and-metrics.md +0 -293
  65. package/library/snippets/dax/rankings-and-topn.md +0 -235
  66. package/library/snippets/dax/security-patterns.md +0 -413
  67. package/library/snippets/dax/text-and-formatting.md +0 -316
  68. package/library/snippets/dax/time-intelligence.md +0 -196
  69. package/library/snippets/dax/user-defined-functions.md +0 -477
  70. package/library/snippets/dax/virtual-tables.md +0 -546
  71. package/library/snippets/excel-formulas/README.md +0 -84
  72. package/library/snippets/excel-formulas/aggregations.md +0 -330
  73. package/library/snippets/excel-formulas/dates-and-times.md +0 -361
  74. package/library/snippets/excel-formulas/dynamic-arrays.md +0 -314
  75. package/library/snippets/excel-formulas/lookups.md +0 -169
  76. package/library/snippets/excel-formulas/text-functions.md +0 -363
  77. package/library/snippets/governance/naming-conventions.md +0 -97
  78. package/library/snippets/governance/review-checklists.md +0 -107
  79. package/library/snippets/power-query/README.md +0 -389
  80. package/library/snippets/power-query/api-integration.md +0 -707
  81. package/library/snippets/power-query/connections.md +0 -434
  82. package/library/snippets/power-query/data-cleaning.md +0 -298
  83. package/library/snippets/power-query/error-handling.md +0 -526
  84. package/library/snippets/power-query/parameters.md +0 -350
  85. package/library/snippets/power-query/performance.md +0 -506
  86. package/library/snippets/power-query/transformations.md +0 -330
  87. package/library/snippets/report-design/accessibility.md +0 -78
  88. package/library/snippets/report-design/chart-selection.md +0 -54
  89. package/library/snippets/report-design/layout-patterns.md +0 -87
  90. package/library/templates/data-models/README.md +0 -93
  91. package/library/templates/data-models/finance-model.md +0 -627
  92. package/library/templates/data-models/retail-star-schema.md +0 -473
  93. package/library/templates/excel/README.md +0 -83
  94. package/library/templates/excel/budget-tracker.md +0 -432
  95. package/library/templates/excel/data-entry-form.md +0 -533
  96. package/library/templates/power-bi/README.md +0 -72
  97. package/library/templates/power-bi/finance-report.md +0 -449
  98. package/library/templates/power-bi/kpi-scorecard.md +0 -461
  99. package/library/templates/power-bi/sales-dashboard.md +0 -281
  100. package/library/themes/excel/README.md +0 -436
  101. package/library/themes/power-bi/README.md +0 -271
  102. package/library/themes/power-bi/accessible.json +0 -307
  103. package/library/themes/power-bi/bi-superpowers-default.json +0 -858
  104. package/library/themes/power-bi/corporate-blue.json +0 -291
  105. package/library/themes/power-bi/dark-mode.json +0 -291
  106. package/library/themes/power-bi/minimal.json +0 -292
  107. package/library/themes/power-bi/print-friendly.json +0 -309
@@ -1,434 +0,0 @@
1
- # Data Source Connections
2
-
3
- ## Overview
4
- Power Query patterns for connecting to various data sources including databases, cloud services, APIs, and files.
5
-
6
- ---
7
-
8
- ## SQL Server
9
-
10
- ### Basic Connection
11
- ```m
12
- let
13
- Source = Sql.Database("server-name", "database-name")
14
- in
15
- Source
16
- ```
17
-
18
- ### With Native Query
19
- ```m
20
- let
21
- Source = Sql.Database("server-name", "database-name", [
22
- Query = "
23
- SELECT
24
- CustomerID,
25
- CustomerName,
26
- Email,
27
- CreatedDate
28
- FROM dbo.Customers
29
- WHERE IsActive = 1
30
- "
31
- ])
32
- in
33
- Source
34
- ```
35
-
36
- ### With Parameters
37
- ```m
38
- let
39
- Source = Sql.Database(param_ServerName, param_DatabaseName, [
40
- Query = "
41
- SELECT * FROM dbo.Sales
42
- WHERE OrderDate >= '" & Date.ToText(param_StartDate, "yyyy-MM-dd") & "'
43
- ",
44
- CommandTimeout = #duration(0, 0, 10, 0) // 10 minutes
45
- ])
46
- in
47
- Source
48
- ```
49
-
50
- ### Navigation to Table
51
- ```m
52
- let
53
- Source = Sql.Database("server-name", "database-name"),
54
- Sales = Source{[Schema="dbo", Item="Sales"]}[Data]
55
- in
56
- Sales
57
- ```
58
-
59
- ---
60
-
61
- ## SharePoint
62
-
63
- ### SharePoint List
64
- ```m
65
- let
66
- Source = SharePoint.Tables(
67
- "https://company.sharepoint.com/sites/SiteName",
68
- [Implementation = "2.0", ViewMode = "All"]
69
- ),
70
- ListData = Source{[Title="ListName"]}[Items],
71
- // Remove SharePoint metadata columns
72
- RemovedColumns = Table.SelectColumns(ListData, {"Column1", "Column2", "Column3"})
73
- in
74
- RemovedColumns
75
- ```
76
-
77
- ### SharePoint Folder (Files)
78
- ```m
79
- let
80
- Source = SharePoint.Files(
81
- "https://company.sharepoint.com/sites/SiteName",
82
- [ApiVersion = 15]
83
- ),
84
- // Filter to specific folder
85
- FilteredFolder = Table.SelectRows(Source, each
86
- Text.Contains([Folder Path], "/Documents/Reports/")
87
- ),
88
- // Filter to Excel files
89
- FilteredFiles = Table.SelectRows(FilteredFolder, each
90
- Text.EndsWith([Name], ".xlsx")
91
- )
92
- in
93
- FilteredFiles
94
- ```
95
-
96
- ### SharePoint Excel File
97
- ```m
98
- let
99
- Source = SharePoint.Files(
100
- "https://company.sharepoint.com/sites/SiteName",
101
- [ApiVersion = 15]
102
- ),
103
- // Find specific file
104
- FileRow = Table.SelectRows(Source, each [Name] = "SalesData.xlsx"){0},
105
- // Load Excel content
106
- ExcelContent = Excel.Workbook(FileRow[Content], true, true),
107
- // Get specific sheet
108
- Sheet1 = ExcelContent{[Item="Sheet1", Kind="Sheet"]}[Data]
109
- in
110
- Sheet1
111
- ```
112
-
113
- ---
114
-
115
- ## REST API / Web Services
116
-
117
- ### Basic JSON API
118
- ```m
119
- let
120
- Source = Json.Document(
121
- Web.Contents("https://api.example.com/data")
122
- ),
123
- ToTable = Table.FromRecords(Source)
124
- in
125
- ToTable
126
- ```
127
-
128
- ### With Authentication Header
129
- ```m
130
- let
131
- Source = Json.Document(
132
- Web.Contents(
133
- "https://api.example.com/data",
134
- [
135
- Headers = [
136
- #"Authorization" = "Bearer " & param_APIToken,
137
- #"Content-Type" = "application/json"
138
- ]
139
- ]
140
- )
141
- )
142
- in
143
- Source
144
- ```
145
-
146
- ### With Query Parameters
147
- ```m
148
- let
149
- Source = Json.Document(
150
- Web.Contents(
151
- "https://api.example.com/data",
152
- [
153
- Query = [
154
- startDate = Date.ToText(param_StartDate, "yyyy-MM-dd"),
155
- endDate = Date.ToText(param_EndDate, "yyyy-MM-dd"),
156
- limit = "1000",
157
- page = "1"
158
- ]
159
- ]
160
- )
161
- )
162
- in
163
- Source
164
- ```
165
-
166
- ### Pagination Pattern
167
- ```m
168
- let
169
- // Function to get one page
170
- GetPage = (pageNum as number) as table =>
171
- let
172
- Source = Json.Document(
173
- Web.Contents(
174
- "https://api.example.com/data",
175
- [Query = [page = Text.From(pageNum), limit = "100"]]
176
- )
177
- ),
178
- ToTable = Table.FromRecords(Source[data])
179
- in
180
- ToTable,
181
-
182
- // Get all pages
183
- PageCount = 10, // Or dynamically determine
184
- Pages = List.Generate(
185
- () => [Page = 1, Data = GetPage(1)],
186
- each [Page] <= PageCount,
187
- each [Page = [Page] + 1, Data = GetPage([Page] + 1)],
188
- each [Data]
189
- ),
190
- Combined = Table.Combine(Pages)
191
- in
192
- Combined
193
- ```
194
-
195
- ### POST Request
196
- ```m
197
- let
198
- Body = Json.FromValue([
199
- startDate = Date.ToText(param_StartDate, "yyyy-MM-dd"),
200
- filters = {[field = "status", value = "active"]}
201
- ]),
202
- Source = Json.Document(
203
- Web.Contents(
204
- "https://api.example.com/query",
205
- [
206
- Headers = [
207
- #"Authorization" = "Bearer " & param_APIToken,
208
- #"Content-Type" = "application/json"
209
- ],
210
- Content = Body
211
- ]
212
- )
213
- )
214
- in
215
- Source
216
- ```
217
-
218
- ---
219
-
220
- ## Excel Files
221
-
222
- ### Local Excel File
223
- ```m
224
- let
225
- Source = Excel.Workbook(
226
- File.Contents(param_FilePath),
227
- true, // Use first row as headers
228
- true // Infer types
229
- ),
230
- Sheet1 = Source{[Item="Sheet1", Kind="Sheet"]}[Data]
231
- in
232
- Sheet1
233
- ```
234
-
235
- ### Excel Table (Named Range)
236
- ```m
237
- let
238
- Source = Excel.Workbook(File.Contents(param_FilePath), true, true),
239
- SalesTable = Source{[Item="tbl_Sales", Kind="Table"]}[Data]
240
- in
241
- SalesTable
242
- ```
243
-
244
- ### Current Workbook (Excel Only)
245
- ```m
246
- let
247
- Source = Excel.CurrentWorkbook(){[Name="tbl_Data"]}[Content]
248
- in
249
- Source
250
- ```
251
-
252
- ---
253
-
254
- ## CSV Files
255
-
256
- ### Basic CSV
257
- ```m
258
- let
259
- Source = Csv.Document(
260
- File.Contents(param_FilePath),
261
- [
262
- Delimiter = ",",
263
- Encoding = 65001, // UTF-8
264
- QuoteStyle = QuoteStyle.Csv
265
- ]
266
- ),
267
- PromotedHeaders = Table.PromoteHeaders(Source, [PromoteAllScalars=true])
268
- in
269
- PromotedHeaders
270
- ```
271
-
272
- ### CSV with Specific Columns
273
- ```m
274
- let
275
- Source = Csv.Document(
276
- File.Contents(param_FilePath),
277
- [
278
- Delimiter = ",",
279
- Columns = 5,
280
- Encoding = 65001,
281
- QuoteStyle = QuoteStyle.Csv
282
- ]
283
- )
284
- in
285
- Source
286
- ```
287
-
288
- ---
289
-
290
- ## Folder of Files
291
-
292
- ### Combine All Files in Folder
293
- ```m
294
- let
295
- Source = Folder.Files(param_FolderPath),
296
- // Filter to specific file type
297
- FilteredFiles = Table.SelectRows(Source, each Text.EndsWith([Name], ".csv")),
298
- // Add file content
299
- AddContent = Table.AddColumn(FilteredFiles, "Data", each
300
- Csv.Document([Content], [Delimiter=",", Encoding=65001])
301
- ),
302
- // Expand all rows
303
- ExpandedData = Table.ExpandTableColumn(AddContent, "Data",
304
- Table.ColumnNames(AddContent{0}[Data])
305
- ),
306
- // Keep data columns plus filename for reference
307
- SelectColumns = Table.SelectColumns(ExpandedData,
308
- List.Combine({{"Name"}, Table.ColumnNames(FilteredFiles{0}[Data])})
309
- )
310
- in
311
- SelectColumns
312
- ```
313
-
314
- ### Binary Combine Pattern (Power Query UI)
315
- ```m
316
- // This is generated by "Combine Files" in Power Query
317
- let
318
- Source = Folder.Files(param_FolderPath),
319
- FilteredHiddenFiles = Table.SelectRows(Source, each [Attributes]?[Hidden]? <> true),
320
- InvokeCustomFunction = Table.AddColumn(FilteredHiddenFiles, "Transform File", each #"Transform File"([Content])),
321
- RemovedOtherColumns = Table.SelectColumns(InvokeCustomFunction, {"Name", "Transform File"}),
322
- ExpandedTable = Table.ExpandTableColumn(RemovedOtherColumns, "Transform File", Table.ColumnNames(#"Transform File"(#"Sample File")))
323
- in
324
- ExpandedTable
325
- ```
326
-
327
- ---
328
-
329
- ## OData
330
-
331
- ### Basic OData Connection
332
- ```m
333
- let
334
- Source = OData.Feed("https://services.odata.org/V4/Northwind/Northwind.svc/"),
335
- Products = Source{[Name="Products", Signature="table"]}[Data]
336
- in
337
- Products
338
- ```
339
-
340
- ### With Query Options
341
- ```m
342
- let
343
- Source = OData.Feed(
344
- "https://api.example.com/odata/Sales",
345
- null,
346
- [
347
- Query = [
348
- #"$filter" = "Year eq 2024",
349
- #"$select" = "ID,Date,Amount",
350
- #"$orderby" = "Date desc"
351
- ]
352
- ]
353
- )
354
- in
355
- Source
356
- ```
357
-
358
- ---
359
-
360
- ## Azure
361
-
362
- ### Azure SQL Database
363
- ```m
364
- let
365
- Source = Sql.Database(
366
- "your-server.database.windows.net",
367
- "your-database",
368
- [
369
- Query = "SELECT * FROM dbo.Sales WHERE Year = 2024"
370
- ]
371
- )
372
- in
373
- Source
374
- ```
375
-
376
- ### Azure Blob Storage
377
- ```m
378
- let
379
- Source = AzureStorage.Blobs("https://youraccount.blob.core.windows.net/container"),
380
- FilteredFiles = Table.SelectRows(Source, each Text.EndsWith([Name], ".csv")),
381
- FirstFile = FilteredFiles{0}[Content],
382
- Data = Csv.Document(FirstFile, [Delimiter=",", Encoding=65001])
383
- in
384
- Data
385
- ```
386
-
387
- ### Azure Data Lake Gen2
388
- ```m
389
- let
390
- Source = AzureStorage.DataLake("https://youraccount.dfs.core.windows.net/filesystem"),
391
- NavigateToFolder = Source{[Folder="path/to/folder"]}[Content],
392
- CsvFile = NavigateToFolder{[Name="data.csv"]}[Content],
393
- Data = Csv.Document(CsvFile, [Delimiter=","])
394
- in
395
- Data
396
- ```
397
-
398
- ---
399
-
400
- ## Error Handling for Connections
401
-
402
- ### Try-Otherwise Pattern
403
- ```m
404
- let
405
- Source = try Sql.Database(param_ServerName, param_DatabaseName) otherwise null,
406
- Result = if Source = null then
407
- #table({"Error"}, {{"Connection failed"}})
408
- else
409
- Source{[Schema="dbo", Item="Sales"]}[Data]
410
- in
411
- Result
412
- ```
413
-
414
- ### Timeout Handling
415
- ```m
416
- let
417
- Source = Sql.Database(
418
- param_ServerName,
419
- param_DatabaseName,
420
- [
421
- CommandTimeout = #duration(0, 0, 5, 0), // 5 minutes
422
- CreateNavigationProperties = false // Faster load
423
- ]
424
- )
425
- in
426
- Source
427
- ```
428
-
429
- ---
430
-
431
- ## Related Resources
432
-
433
- - [Parameters](./parameters.md)
434
- - [Power Query Skill](../../skills/power-query/SKILL.md)
@@ -1,298 +0,0 @@
1
- # Data Cleaning Power Query Patterns
2
-
3
- Reusable M code for cleaning and preparing data.
4
-
5
- ## Remove Empty and Null Values
6
-
7
- ### Remove Null Rows
8
- ```m
9
- let
10
- Source = YourSource,
11
- // Remove rows where specific column is null
12
- FilteredRows = Table.SelectRows(Source, each [ColumnName] <> null)
13
- in
14
- FilteredRows
15
- ```
16
-
17
- ### Remove Empty Strings
18
- ```m
19
- let
20
- Source = YourSource,
21
- FilteredRows = Table.SelectRows(
22
- Source,
23
- each [ColumnName] <> null and [ColumnName] <> ""
24
- )
25
- in
26
- FilteredRows
27
- ```
28
-
29
- ### Remove Rows with Any Null
30
- ```m
31
- let
32
- Source = YourSource,
33
- ColumnNames = Table.ColumnNames(Source),
34
- FilteredRows = Table.SelectRows(
35
- Source,
36
- each not List.Contains(
37
- List.Transform(ColumnNames, (col) => Record.Field(_, col)),
38
- null
39
- )
40
- )
41
- in
42
- FilteredRows
43
- ```
44
-
45
- ### Replace Null with Default
46
- ```m
47
- let
48
- Source = YourSource,
49
- ReplacedNull = Table.ReplaceValue(
50
- Source,
51
- null,
52
- 0,
53
- Replacer.ReplaceValue,
54
- {"NumericColumn"}
55
- )
56
- in
57
- ReplacedNull
58
- ```
59
-
60
- ## Text Cleaning
61
-
62
- ### Trim and Clean Text
63
- ```m
64
- let
65
- Source = YourSource,
66
- Cleaned = Table.TransformColumns(
67
- Source,
68
- {{"TextColumn", each Text.Trim(Text.Clean(_)), type text}}
69
- )
70
- in
71
- Cleaned
72
- ```
73
-
74
- ### Standardize Case
75
- ```m
76
- let
77
- Source = YourSource,
78
- // Proper Case
79
- ProperCase = Table.TransformColumns(
80
- Source,
81
- {{"Name", Text.Proper, type text}}
82
- ),
83
- // Upper Case
84
- UpperCase = Table.TransformColumns(
85
- Source,
86
- {{"Code", Text.Upper, type text}}
87
- ),
88
- // Lower Case
89
- LowerCase = Table.TransformColumns(
90
- Source,
91
- {{"Email", Text.Lower, type text}}
92
- )
93
- in
94
- ProperCase
95
- ```
96
-
97
- ### Remove Extra Spaces
98
- ```m
99
- let
100
- Source = YourSource,
101
- // Replace multiple spaces with single space
102
- CleanSpaces = Table.TransformColumns(
103
- Source,
104
- {{"TextColumn", each Text.Combine(
105
- List.Select(Text.Split(_, " "), each _ <> ""),
106
- " "
107
- ), type text}}
108
- )
109
- in
110
- CleanSpaces
111
- ```
112
-
113
- ### Remove Special Characters
114
- ```m
115
- let
116
- Source = YourSource,
117
- RemoveSpecial = Table.TransformColumns(
118
- Source,
119
- {{"TextColumn", each Text.Select(_, {"A".."Z", "a".."z", "0".."9", " "}), type text}}
120
- )
121
- in
122
- RemoveSpecial
123
- ```
124
-
125
- ## Duplicate Handling
126
-
127
- ### Remove Duplicate Rows
128
- ```m
129
- let
130
- Source = YourSource,
131
- RemovedDuplicates = Table.Distinct(Source)
132
- in
133
- RemovedDuplicates
134
- ```
135
-
136
- ### Remove Duplicates by Key Column
137
- ```m
138
- let
139
- Source = YourSource,
140
- RemovedDuplicates = Table.Distinct(Source, {"KeyColumn"})
141
- in
142
- RemovedDuplicates
143
- ```
144
-
145
- ### Keep First/Last Occurrence
146
- ```m
147
- let
148
- Source = YourSource,
149
- Sorted = Table.Sort(Source, {{"Date", Order.Descending}}),
150
- // Keeps first (most recent) per group
151
- KeepFirst = Table.Distinct(Sorted, {"CustomerID"})
152
- in
153
- KeepFirst
154
- ```
155
-
156
- ### Flag Duplicates
157
- ```m
158
- let
159
- Source = YourSource,
160
- Grouped = Table.Group(
161
- Source,
162
- {"KeyColumn"},
163
- {{"Count", each Table.RowCount(_), Int64.Type}}
164
- ),
165
- Merged = Table.NestedJoin(Source, {"KeyColumn"}, Grouped, {"KeyColumn"}, "Counts", JoinKind.LeftOuter),
166
- Expanded = Table.ExpandTableColumn(Merged, "Counts", {"Count"}),
167
- FlagDuplicates = Table.AddColumn(Expanded, "IsDuplicate", each [Count] > 1, type logical)
168
- in
169
- FlagDuplicates
170
- ```
171
-
172
- ## Data Type Fixes
173
-
174
- ### Safe Number Conversion
175
- ```m
176
- let
177
- Source = YourSource,
178
- SafeNumber = Table.TransformColumns(
179
- Source,
180
- {{"Amount", each try Number.From(_) otherwise null, type number}}
181
- )
182
- in
183
- SafeNumber
184
- ```
185
-
186
- ### Safe Date Conversion
187
- ```m
188
- let
189
- Source = YourSource,
190
- SafeDate = Table.TransformColumns(
191
- Source,
192
- {{"DateColumn", each try Date.From(_) otherwise null, type date}}
193
- )
194
- in
195
- SafeDate
196
- ```
197
-
198
- ### Parse Multiple Date Formats
199
- ```m
200
- let
201
- Source = YourSource,
202
- ParseDate = Table.TransformColumns(
203
- Source,
204
- {{"DateColumn", each
205
- let
206
- TryMDY = try Date.FromText(_, [Format="M/d/yyyy"]),
207
- TryDMY = try Date.FromText(_, [Format="d/M/yyyy"]),
208
- TryISO = try Date.FromText(_, [Format="yyyy-MM-dd"])
209
- in
210
- if TryMDY[HasError] = false then TryMDY[Value]
211
- else if TryDMY[HasError] = false then TryDMY[Value]
212
- else if TryISO[HasError] = false then TryISO[Value]
213
- else null
214
- , type date}}
215
- )
216
- in
217
- ParseDate
218
- ```
219
-
220
- ## Standardization
221
-
222
- ### Standardize Column Names
223
- ```m
224
- let
225
- Source = YourSource,
226
- // Replace spaces with underscores, remove special chars
227
- RenameColumns = Table.TransformColumnNames(
228
- Source,
229
- each Text.Replace(
230
- Text.Replace(
231
- Text.Replace(Text.Proper(_), " ", "_"),
232
- "#", ""
233
- ),
234
- "(", ""
235
- )
236
- )
237
- in
238
- RenameColumns
239
- ```
240
-
241
- ### Map Values to Standard Names
242
- ```m
243
- let
244
- Source = YourSource,
245
- MappingTable = #table(
246
- {"Original", "Standard"},
247
- {
248
- {"NY", "New York"},
249
- {"NYC", "New York"},
250
- {"CA", "California"},
251
- {"Cali", "California"}
252
- }
253
- ),
254
- Merged = Table.NestedJoin(Source, {"State"}, MappingTable, {"Original"}, "Mapping", JoinKind.LeftOuter),
255
- Expanded = Table.ExpandTableColumn(Merged, "Mapping", {"Standard"}),
256
- Final = Table.AddColumn(Expanded, "CleanState", each if [Standard] = null then [State] else [Standard])
257
- in
258
- Final
259
- ```
260
-
261
- ## Error Handling
262
-
263
- ### Replace Errors with Null
264
- ```m
265
- let
266
- Source = YourSource,
267
- ReplacedErrors = Table.ReplaceErrorValues(
268
- Source,
269
- {{"Column1", null}, {"Column2", null}}
270
- )
271
- in
272
- ReplacedErrors
273
- ```
274
-
275
- ### Log Errors to Separate Table
276
- ```m
277
- let
278
- Source = YourSource,
279
- AddErrorFlag = Table.AddColumn(
280
- Source,
281
- "HasError",
282
- each try [Amount] > 0 otherwise true,
283
- type logical
284
- ),
285
- ErrorRows = Table.SelectRows(AddErrorFlag, each [HasError] = true),
286
- CleanRows = Table.SelectRows(AddErrorFlag, each [HasError] = false)
287
- in
288
- CleanRows
289
- // ErrorRows can be returned separately for logging
290
- ```
291
-
292
- ## Usage Notes
293
-
294
- - Always preview results after each transformation
295
- - Use `try...otherwise` for safe operations
296
- - Consider query folding when working with databases
297
- - Document complex transformations with comments
298
- - Test with edge cases (empty strings, nulls, special characters)