├── .gitignore ├── .vscode ├── extensions.json └── launch.json ├── Collection Archived └── Macros │ ├── Backup_Restore │ ├── Backup database.csx │ ├── Backup database.json │ ├── Restore database.csx │ └── Restore database.json │ ├── Cancel processing.csx │ ├── Cancel processing.json │ ├── Format all DAX vTE2.csx │ ├── Format all DAX vTE2.json │ ├── Launch │ ├── Perspective Editor.csx │ └── Perspective Editor.json │ ├── Remove │ ├── Sort by column.csx │ └── Sort by column.json │ └── Toggle │ ├── Disallow applying default formatting annotation.csx │ └── Disallow applying default formatting annotation.json ├── Collection └── Macros │ ├── Apply │ ├── Best Practice Rules │ │ ├── Add VertiPaq annotations from VPAX file.csx │ │ ├── Add VertiPaq annotations from VPAX file.json │ │ ├── Add VertiPaq annotations.csx │ │ ├── Add VertiPaq annotations.json │ │ ├── Add long length column annotations.csx │ │ ├── Add long length column annotations.json │ │ ├── Add split datetime annotations.csx │ │ ├── Add split datetime annotations.json │ │ ├── Download best practice rules.csx │ │ └── Download best practice rules.json │ ├── Best practice rules.csx │ ├── Best practice rules.json │ ├── Blank lineage tags.csx │ ├── Blank lineage tags.json │ ├── DAX as description.csx │ ├── DAX as description.json │ ├── Default format strings.csx │ ├── Default format strings.json │ ├── Default sort by columns.csx │ ├── Default sort by columns.json │ ├── Discourage implicit measures.csx │ ├── Discourage implicit measures.json │ ├── Encoding hints.csx │ ├── Encoding hints.json │ ├── Hide from-side relationship columns.csx │ └── Hide from-side relationship columns.json │ ├── Create │ ├── 'Avg of' measure(s).csx │ ├── 'Avg of' measure(s).json │ ├── 'Max of' measure(s).csx │ ├── 'Max of' measure(s).json │ ├── 'Min of' measure(s).csx │ ├── 'Min of' measure(s).json │ ├── 'Sum of' measure(s).csx │ ├── 'Sum of' measure(s).json │ ├── 'dCnt of' measure(s).csx │ ├── 'dCnt of' measure(s).json │ ├── Calculation group measures.csx │ ├── Calculation group measures.json │ ├── Field parameter.csx │ ├── Field parameter.json │ ├── Parent-child hierarchy.csx │ └── Parent-child hierarchy.json │ ├── DataType │ ├── Decimal.csx │ ├── Decimal.json │ ├── Double.csx │ ├── Double.json │ ├── Int64.csx │ └── Int64.json │ ├── Format all DAX vTE3.csx │ ├── Format all DAX vTE3.json │ ├── Format │ ├── $ English (New Zealand).csx │ ├── $ English (New Zealand).json │ ├── $ English (United States).csx │ ├── $ English (United States).json │ ├── Custom display units.csx │ ├── Custom display units.json │ ├── Custom_ #,#;(#,#);0.csx │ ├── Custom_ #,#;(#,#);0.json │ ├── Custom_ #,0.00;(#,0.00);0.00.csx │ ├── Custom_ #,0.00;(#,0.00);0.00.json │ ├── Decimal number.csx │ ├── Decimal number.json │ ├── Default.csx │ ├── Default.json │ ├── Long Date.csx │ ├── Long Date.json │ ├── Long Time.csx │ ├── Long Time.json │ ├── Percentage.csx │ ├── Percentage.json │ ├── Short Date.csx │ ├── Short Date.json │ ├── Short Time.csx │ ├── Short Time.json │ ├── Whole number.csx │ ├── Whole number.json │ ├── d_m_yyyy.csx │ └── d_m_yyyy.json │ ├── Launch │ ├── ALM Toolkit.csx │ ├── ALM Toolkit.json │ ├── Analyze In Excel.csx │ ├── Analyze In Excel.json │ ├── DAX Studio.csx │ └── DAX Studio.json │ ├── Preview columns and measures.csx │ ├── Preview columns and measures.json │ ├── Remove │ ├── Annotations and ExtendedProperties.csx │ ├── Annotations and ExtendedProperties.json │ ├── Format strings.csx │ ├── Format strings.json │ ├── Name underscores.csx │ └── Name underscores.json │ ├── Scratchpad.csx │ ├── Scratchpad.json │ └── Toggle │ ├── ParameterMetadata property.csx │ └── ParameterMetadata property.json ├── LICENSE ├── Management ├── Assemblies │ ├── System.Drawing.Common.dll │ ├── System.Windows.Forms.Primitives.dll │ ├── System.Windows.Forms.dll │ ├── TOMWrapper14.dll │ ├── TabularEditor.exe │ └── newtonsoft.json.dll ├── Breaks EULA (Testing Only) │ ├── Macros Export from TE3 (TE3 Assemblies).csx │ └── Macros Import into TE3 (TE3 Assemblies).csx ├── Common Library.csx ├── Macros Export from TE.csx ├── Macros Import into TE.csx ├── Settings Backup.csx ├── Settings Restore.csx ├── TE2 Script Compiler Update.csx ├── TOM-Extract-CompatibilityRequirementsAttributes.csx ├── TOM-Extract-CompatibilityRequirementsAttributes.md └── Update Assemblies.csx ├── README.md ├── Resources ├── GetVersion.csx ├── InputBox.csx ├── SelectObject.csx └── SelectString.csx └── omnisharp.json /.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.toptal.com/developers/gitignore/api/windows,visualstudiocode 4 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows,visualstudiocode 5 | 6 | ### VisualStudioCode ### 7 | .vscode/* 8 | !.vscode/settings.json 9 | !.vscode/tasks.json 10 | !.vscode/launch.json 11 | !.vscode/extensions.json 12 | !.vscode/*.code-snippets 13 | 14 | # Local History for Visual Studio Code 15 | .history/ 16 | 17 | # Built Visual Studio Code Extensions 18 | *.vsix 19 | 20 | ### VisualStudioCode Patch ### 21 | # Ignore all local history of files 22 | .history 23 | .ionide 24 | 25 | # Support for Project snippet scope 26 | 27 | ### Windows ### 28 | # Windows thumbnail cache files 29 | Thumbs.db 30 | Thumbs.db:encryptable 31 | ehthumbs.db 32 | ehthumbs_vista.db 33 | 34 | # Dump file 35 | *.stackdump 36 | 37 | # Folder config file 38 | [Dd]esktop.ini 39 | 40 | # Recycle Bin used on file shares 41 | $RECYCLE.BIN/ 42 | 43 | # Windows Installer files 44 | *.cab 45 | *.msi 46 | *.msix 47 | *.msm 48 | *.msp 49 | 50 | # Windows shortcuts 51 | *.lnk 52 | 53 | # End of https://www.toptal.com/developers/gitignore/api/windows,visualstudiocode 54 | 55 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 56 | 57 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "ms-dotnettools.csharp", 8 | "formulahendry.code-runner" 9 | ], 10 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 11 | "unwantedRecommendations": [ 12 | 13 | ] 14 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": ".NET Script Debug", 6 | "type": "coreclr", 7 | "request": "launch", 8 | "program": "dotnet", 9 | "args": [ 10 | "exec", 11 | "C:/Users/maguires/.dotnet/tools/.store/dotnet-script/1.3.1/dotnet-script/1.3.1/tools/net6.0/any/dotnet-script.dll", 12 | "${file}" 13 | ], 14 | "cwd": "${workspaceRoot}", 15 | "stopAtEntry": false 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /Collection Archived/Macros/Backup_Restore/Backup database.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | #load "..\..\..\Management\Custom Classes.csx" 3 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 4 | #r "Microsoft.AnalysisServices.Core" 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Collections.Generic; 9 | using Newtonsoft.Json; 10 | using TabularEditor; 11 | using TabularEditor.TOMWrapper; 12 | using TabularEditor.TOMWrapper.Utils; 13 | using TabularEditor.UI; 14 | using TabularEditor.Scripting; 15 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 16 | using Microsoft.AnalysisServices.Core; 17 | 18 | Model.Database.TOMDatabase.Backup( 19 | file: Model.Database.Name + ".abf", 20 | allowOverwrite: true, 21 | backupRemotePartitions: default, 22 | locations: default, 23 | applyCompression: true, 24 | password: default 25 | ); -------------------------------------------------------------------------------- /Collection Archived/Macros/Backup_Restore/Backup database.json: -------------------------------------------------------------------------------- 1 | { 2 | "Id": 14, 3 | "Name": "Macros\\Backup/Restore\\Backup database", 4 | "Enabled": "true", 5 | "Tooltip": "", 6 | "ValidContexts": "Model" 7 | } -------------------------------------------------------------------------------- /Collection Archived/Macros/Backup_Restore/Restore database.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | #load "..\..\..\Management\Custom Classes.csx" 3 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 4 | #r "Microsoft.AnalysisServices.Core" 5 | #r "Microsoft.VisualBasic" 6 | 7 | using System; 8 | using System.Linq; 9 | using System.Collections.Generic; 10 | using Newtonsoft.Json; 11 | using TabularEditor; 12 | using TabularEditor.TOMWrapper; 13 | using TabularEditor.TOMWrapper.Utils; 14 | using TabularEditor.UI; 15 | using TabularEditor.Scripting; 16 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 17 | using Microsoft.AnalysisServices.Core; 18 | using Microsoft.VisualBasic; 19 | 20 | var dbName = Interaction.InputBox( 21 | Prompt: "Provide the name of the restored database. (This will overwrite the database if it already exists.)", 22 | Title: "Set database name:", 23 | DefaultResponse: Model.Database.Name 24 | ); 25 | 26 | if(dbName == String.Empty) { return; } 27 | 28 | Model.Database.TOMDatabase.Server.Restore( 29 | file: Model.Database.Name + ".abf", 30 | databaseName: dbName, 31 | allowOverwrite: true 32 | ); -------------------------------------------------------------------------------- /Collection Archived/Macros/Backup_Restore/Restore database.json: -------------------------------------------------------------------------------- 1 | { 2 | "Id": 15, 3 | "Name": "Macros\\Backup/Restore\\Restore database", 4 | "Enabled": "true", 5 | "Tooltip": "", 6 | "ValidContexts": "Model" 7 | } -------------------------------------------------------------------------------- /Collection Archived/Macros/Cancel processing.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\Management\Common Library.csx" 2 | #load "..\..\Management\Custom Classes.csx" 3 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 4 | #r "Microsoft.AnalysisServices.Core" 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Collections.Generic; 9 | using Newtonsoft.Json; 10 | using TabularEditor; 11 | using TabularEditor.TOMWrapper; 12 | using TabularEditor.TOMWrapper.Utils; 13 | using TabularEditor.UI; 14 | using TabularEditor.Scripting; 15 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 16 | using Microsoft.AnalysisServices.Core; 17 | using System.Data; 18 | 19 | string databaseId = Model.Database.ID; 20 | string databaseName = Model.Database.Name; 21 | DataTable dtDmvCmd = ScriptHelper.ExecuteDax("SELECT [SESSION_ID],[SESSION_LAST_COMMAND] FROM $SYSTEM.DISCOVER_SESSIONS").Tables[0]; 22 | string sId = null; 23 | 24 | foreach (DataRow drDmvCmd in dtDmvCmd.Rows) 25 | { 26 | var sessionId = drDmvCmd["SESSION_ID"].ToString(); 27 | var sessionCmd = drDmvCmd["SESSION_LAST_COMMAND"].ToString(); 28 | if (sessionCmd.StartsWith("" + databaseId + "")) { sId = sessionId; } 29 | } 30 | 31 | if (sId == null) 32 | { 33 | ScriptHelper.Error("No processing Session ID found for the '" + databaseName + "' Model."); 34 | } 35 | else 36 | { 37 | Model.Database.TOMDatabase.Server.CancelSession(sId); 38 | ScriptHelper.Info("Processing for the '" + databaseName + "' model has been cancelled (Session ID: " + sId + ")."); 39 | } -------------------------------------------------------------------------------- /Collection Archived/Macros/Cancel processing.json: -------------------------------------------------------------------------------- 1 | { 2 | "Id": 0, 3 | "Name": "Macros\\Cancel processing", 4 | "Enabled": "true", 5 | "Tooltip": "", 6 | "ValidContexts": "Model" 7 | } -------------------------------------------------------------------------------- /Collection Archived/Macros/Format all DAX vTE2.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\Management\Common Library.csx" 2 | #load "..\..\Management\Custom Classes.csx" 3 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using TabularEditor; 10 | using TabularEditor.TOMWrapper; 11 | using TabularEditor.TOMWrapper.Utils; 12 | using TabularEditor.UI; 13 | using TabularEditor.Scripting; 14 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 15 | 16 | var useShortFormat = true; 17 | var insertSpaceAfterFunctionName = false; 18 | var insertLineBreakOnFirstLine = true; 19 | 20 | foreach (var m in Model.AllMeasures) { ScriptHelper.FormatDax(m); } 21 | foreach (var i in Model.AllCalculationItems) { ScriptHelper.FormatDax(i); } 22 | foreach (var t in Model.Tables.OfType().Where(x => !x.Name.Contains("DateTableTemplate_") && !x.Name.Contains("LocalDateTable_"))) { ScriptHelper.FormatDax(t); } 23 | foreach (var c in Model.AllColumns.OfType().Where(x => !x.DaxTableName.Contains("DateTableTemplate_") && !x.DaxTableName.Contains("LocalDateTable_"))) { ScriptHelper.FormatDax(c); } 24 | 25 | ScriptHelper.CallDaxFormatter(shortFormat: useShortFormat, skipSpaceAfterFunctionName: !insertSpaceAfterFunctionName); 26 | 27 | if (insertLineBreakOnFirstLine) 28 | { 29 | foreach (var m in Model.AllMeasures) { m.Expression = "\r\n" + m.Expression; } 30 | foreach (var i in Model.AllCalculationItems) { i.Expression = "\r\n" + i.Expression; } 31 | foreach (var t in Model.Tables.OfType().Where(x => !x.Name.Contains("DateTableTemplate_") && !x.Name.Contains("LocalDateTable_"))) { t.Expression = "\r\n" + t.Expression; } 32 | foreach (var c in Model.AllColumns.OfType().Where(x => !x.DaxTableName.Contains("DateTableTemplate_") && !x.DaxTableName.Contains("LocalDateTable_"))) { c.Expression = "\r\n" + c.Expression; } 33 | } -------------------------------------------------------------------------------- /Collection Archived/Macros/Format all DAX vTE2.json: -------------------------------------------------------------------------------- 1 | { 2 | "Id": 1, 3 | "Name": "Macros\\Format all DAX vTE2", 4 | "Enabled": "true", 5 | "Tooltip": "", 6 | "ValidContexts": "Model" 7 | } -------------------------------------------------------------------------------- /Collection Archived/Macros/Launch/Perspective Editor.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | #load "..\..\..\Management\Custom Classes.csx" 3 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 4 | #r "System.Drawing" 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Collections.Generic; 9 | using Newtonsoft.Json; 10 | using TabularEditor; 11 | using TabularEditor.TOMWrapper; 12 | using TabularEditor.TOMWrapper.Utils; 13 | using TabularEditor.UI; 14 | using TabularEditor.Scripting; 15 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 16 | using System.Drawing; 17 | using System.IO; 18 | 19 | // https://www.elegantbi.com/post/perspectiveeditor 20 | 21 | // Create elements 22 | System.Windows.Forms.Form newForm = new System.Windows.Forms.Form(); 23 | System.Windows.Forms.Panel newPanel = new System.Windows.Forms.Panel(); 24 | System.Windows.Forms.Label toolLabel = new System.Windows.Forms.Label(); 25 | System.Windows.Forms.TreeView treeView = new System.Windows.Forms.TreeView(); 26 | System.Windows.Forms.Button createButton = new System.Windows.Forms.Button(); 27 | System.Windows.Forms.TextBox enterTextBox = new System.Windows.Forms.TextBox(); 28 | System.Windows.Forms.Label nameLabel = new System.Windows.Forms.Label(); 29 | System.Windows.Forms.ImageList imageList = new System.Windows.Forms.ImageList(); 30 | System.Windows.Forms.RadioButton newmodelButton = new System.Windows.Forms.RadioButton(); 31 | System.Windows.Forms.RadioButton existingmodelButton = new System.Windows.Forms.RadioButton(); 32 | System.Windows.Forms.Button goButton = new System.Windows.Forms.Button(); 33 | System.Windows.Forms.ComboBox enterComboBox = new System.Windows.Forms.ComboBox(); 34 | System.Net.Http.HttpClient w = new System.Net.Http.HttpClient(); 35 | System.Windows.Forms.LinkLabel ebiHome = new System.Windows.Forms.LinkLabel(); 36 | 37 | // Colors 38 | System.Drawing.Color visibleColor = Color.Black; 39 | System.Drawing.Color hiddenColor = Color.Gray; 40 | System.Drawing.Color bkgrdColor = ColorTranslator.FromHtml("#F2F2F2"); 41 | System.Drawing.Color darkblackColor = ColorTranslator.FromHtml("#0D1117"); 42 | System.Drawing.Color darkgrayColor = ColorTranslator.FromHtml("#21262D"); 43 | System.Drawing.Color lightgrayColor = ColorTranslator.FromHtml("#C9D1D9"); 44 | 45 | // Fonts 46 | string fontName = "Century Gothic"; 47 | System.Drawing.Font homeToolNameFont = new Font(fontName, 24); 48 | System.Drawing.Font stdFont = new Font(fontName, 10); 49 | System.Drawing.Font elegantFont = new Font(fontName, 10, FontStyle.Italic); 50 | 51 | // Add images from web to Image List 52 | string urlPrefix = "https://github.com/m-kovalsky/Tabular/raw/master/Icons/"; 53 | string urlSuffix = "Icon.png"; 54 | string toolName = "Perspective Editor"; 55 | string ebiURL = @"https://www.elegantbi.com"; 56 | 57 | string[] imageURLList = { "Table", "Column", "Measure", "Hierarchy" }; 58 | for (int b = 0; b < imageURLList.Count(); b++) 59 | { 60 | string url = urlPrefix + imageURLList[b] + urlSuffix; 61 | byte[] imageByte = w.GetByteArrayAsync(url).GetAwaiter().GetResult(); 62 | System.IO.MemoryStream ms = new System.IO.MemoryStream(imageByte); 63 | System.Drawing.Image im = System.Drawing.Image.FromStream(ms); 64 | imageList.Images.Add(im); 65 | } 66 | 67 | // Images 68 | treeView.ImageList = imageList; 69 | treeView.ImageIndex = 0; 70 | imageList.ImageSize = new Size(16, 16); 71 | 72 | // Form 73 | newForm.Text = toolName; 74 | int formWidth = 600; 75 | int formHeight = 600; 76 | newForm.TopLevel = true; 77 | newForm.Size = new Size(formWidth, formHeight); 78 | newForm.Controls.Add(newPanel); 79 | newForm.BackColor = bkgrdColor; 80 | newForm.MaximumSize = new Size(formWidth, formHeight); 81 | newForm.MinimumSize = new Size(formWidth, formHeight); 82 | 83 | // Panel 84 | newPanel.Size = new Size(formWidth, formHeight); 85 | newPanel.Location = new Point(0, 0); 86 | newPanel.BorderStyle = System.Windows.Forms.BorderStyle.None; 87 | newPanel.BackColor = bkgrdColor; 88 | newPanel.Controls.Add(treeView); 89 | newPanel.Controls.Add(createButton); 90 | newPanel.Controls.Add(enterTextBox); 91 | newPanel.Controls.Add(nameLabel); 92 | newPanel.Visible = false; 93 | 94 | // TreeView 95 | int treeViewWidth = formWidth * 2 / 3; 96 | int treeViewHeight = formHeight - 100; 97 | int treeViewX = 10; 98 | int treeViewY = 50; 99 | treeView.CheckBoxes = false; 100 | treeView.Size = new Size(treeViewWidth, treeViewHeight); 101 | treeView.Location = new Point(treeViewX, treeViewY); 102 | treeView.StateImageList = new System.Windows.Forms.ImageList(); 103 | treeView.Visible = false; 104 | bool IsExpOrCol = false; 105 | string perspName = string.Empty; 106 | 107 | // Add images for tri-state tree view 108 | string[] stateimageURLList = { "Unchecked", "Checked", "PartiallyChecked" }; 109 | for (int c = 0; c < stateimageURLList.Count(); c++) 110 | { 111 | var url = urlPrefix + stateimageURLList[c] + urlSuffix; 112 | byte[] imageByte = w.GetByteArrayAsync(url).GetAwaiter().GetResult(); 113 | // byte[] imageByte = w.DownloadData(url); 114 | System.IO.MemoryStream ms = new System.IO.MemoryStream(imageByte); 115 | System.Drawing.Image im = System.Drawing.Image.FromStream(ms); 116 | treeView.StateImageList.Images.Add(im); 117 | } 118 | 119 | // Create Button 120 | createButton.Size = new Size(130, 55); 121 | createButton.Location = new Point(treeViewWidth + 35, treeViewY); 122 | createButton.Text = "Create Perspective"; 123 | createButton.Visible = false; 124 | createButton.Font = stdFont; 125 | 126 | int startScreenX = 200; 127 | int startScreenY = 200; 128 | 129 | toolLabel.Size = new Size(300, 60); 130 | toolLabel.Text = toolName; 131 | toolLabel.Location = new Point(150, 100); 132 | toolLabel.Font = homeToolNameFont; 133 | toolLabel.ForeColor = visibleColor; 134 | 135 | // New Model Button 136 | newmodelButton.Size = new Size(250, 40); 137 | newmodelButton.Location = new Point(startScreenX, startScreenY); 138 | newmodelButton.Text = "Create New Perspective"; 139 | newmodelButton.Font = stdFont; 140 | 141 | // Existing Model Button 142 | existingmodelButton.Size = new Size(250, 40); 143 | existingmodelButton.Location = new Point(startScreenX, startScreenY + 30); 144 | existingmodelButton.Text = "Modify Existing Perspective"; 145 | existingmodelButton.Font = stdFont; 146 | 147 | // Enter Combo Box 148 | enterComboBox.Visible = false; 149 | enterComboBox.Size = new Size(215, 40); 150 | enterComboBox.Location = new Point(startScreenX - 10, startScreenY + 80); 151 | enterComboBox.Font = stdFont; 152 | 153 | // Add items to combo box 154 | foreach (var p in Model.Perspectives.ToList()) 155 | { 156 | string pName = p.Name; 157 | enterComboBox.Items.Add(pName); 158 | } 159 | 160 | // New Model Button 161 | goButton.Size = new Size(140, 30); 162 | goButton.Location = new Point(startScreenX + 80, startScreenY + 80); 163 | goButton.Text = "Go"; 164 | goButton.Font = stdFont; 165 | goButton.Visible = false; 166 | goButton.Enabled = false; 167 | 168 | // Add starting elements to form 169 | newForm.Controls.Add(newmodelButton); 170 | newForm.Controls.Add(existingmodelButton); 171 | newForm.Controls.Add(enterComboBox); 172 | newForm.Controls.Add(goButton); 173 | newForm.Controls.Add(toolLabel); 174 | newForm.Controls.Add(ebiHome); 175 | 176 | ebiHome.Text = "Designed by Elegant BI"; 177 | ebiHome.Size = new Size(200, 40); 178 | ebiHome.Location = new Point(220, 400); 179 | ebiHome.Font = elegantFont; 180 | 181 | ebiHome.LinkClicked += (System.Object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e) => 182 | { 183 | 184 | System.Diagnostics.Process.Start(ebiURL); 185 | }; 186 | 187 | // Label 188 | nameLabel.Size = new Size(60, 40); 189 | nameLabel.Location = new Point(treeViewX, 20); 190 | nameLabel.Text = "Name:"; 191 | nameLabel.Font = stdFont; 192 | nameLabel.Visible = false; 193 | 194 | // Text box 195 | enterTextBox.Size = new Size(348, 40); 196 | enterTextBox.Location = new Point(63, 18); 197 | enterTextBox.Visible = false; 198 | enterTextBox.Font = stdFont; 199 | 200 | // Add nodes to treeview 201 | foreach (var t in Model.Tables.OrderBy(a => a.Name).ToList()) 202 | { 203 | // Add table nodes 204 | string tableName = t.Name; 205 | var tn = treeView.Nodes.Add(tableName); 206 | tn.StateImageIndex = 0; 207 | tn.ImageIndex = 0; 208 | tn.SelectedImageIndex = 0; 209 | 210 | if (t.IsHidden) 211 | { 212 | tn.ForeColor = hiddenColor; 213 | } 214 | 215 | // Add column sub-nodes 216 | foreach (var c in t.Columns.OrderBy(a => a.Name).ToList()) 217 | { 218 | string columnName = c.Name; 219 | var x = tn.Nodes.Add(columnName); 220 | x.StateImageIndex = 0; 221 | x.ImageIndex = 1; 222 | x.SelectedImageIndex = 1; 223 | 224 | if (c.IsHidden) 225 | { 226 | x.ForeColor = hiddenColor; 227 | } 228 | } 229 | 230 | // Add measure sub-nodes 231 | foreach (var m in t.Measures.OrderBy(a => a.Name).ToList()) 232 | { 233 | string measureName = m.Name; 234 | var x = tn.Nodes.Add(measureName); 235 | x.StateImageIndex = 0; 236 | x.ImageIndex = 2; 237 | x.SelectedImageIndex = 2; 238 | 239 | if (m.IsHidden) 240 | { 241 | x.ForeColor = hiddenColor; 242 | } 243 | } 244 | 245 | // Add hierarchy sub-nodes 246 | foreach (var h in t.Hierarchies.OrderBy(a => a.Name).ToList()) 247 | { 248 | string hierarchyName = h.Name; 249 | var x = tn.Nodes.Add(hierarchyName); 250 | x.ImageIndex = 3; 251 | x.StateImageIndex = 0; 252 | x.SelectedImageIndex = 3; 253 | 254 | if (h.IsHidden) 255 | { 256 | x.ForeColor = hiddenColor; 257 | } 258 | } 259 | } 260 | 261 | newmodelButton.Click += (System.Object sender1, System.EventArgs e1) => 262 | { 263 | 264 | goButton.Visible = true; 265 | existingmodelButton.Checked = false; 266 | newmodelButton.Checked = true; 267 | goButton.Location = new Point(startScreenX + 25, startScreenY + 80); 268 | enterComboBox.Visible = false; 269 | goButton.Enabled = true; 270 | enterComboBox.Text = string.Empty; 271 | createButton.Text = "Create Perspective"; 272 | enterTextBox.Enabled = true; 273 | }; 274 | 275 | existingmodelButton.Click += (System.Object sender2, System.EventArgs e2) => 276 | { 277 | 278 | goButton.Location = new Point(startScreenX + 25, startScreenY + 120); 279 | enterComboBox.Visible = true; 280 | goButton.Visible = true; 281 | newmodelButton.Checked = false; 282 | existingmodelButton.Checked = true; 283 | createButton.Text = "Modify Perspective"; 284 | enterTextBox.Enabled = false; 285 | 286 | // Add items to combo box 287 | enterComboBox.Items.Clear(); 288 | foreach (var p in Model.Perspectives.ToList()) 289 | { 290 | string pName = p.Name; 291 | enterComboBox.Items.Add(pName); 292 | } 293 | 294 | if (enterComboBox.SelectedItem == null) 295 | { 296 | goButton.Enabled = false; 297 | } 298 | }; 299 | 300 | enterComboBox.SelectedValueChanged += (System.Object sender3, System.EventArgs e3) => 301 | { 302 | 303 | goButton.Enabled = true; 304 | }; 305 | 306 | goButton.Click += (System.Object sender4, System.EventArgs e4) => 307 | { 308 | 309 | // Hide initial buttons 310 | newmodelButton.Visible = false; 311 | existingmodelButton.Visible = false; 312 | enterComboBox.Visible = false; 313 | goButton.Visible = false; 314 | toolLabel.Visible = false; 315 | ebiHome.Visible = false; 316 | 317 | string p = enterComboBox.Text; 318 | 319 | // Make panel items visible 320 | newPanel.Visible = true; 321 | createButton.Visible = true; 322 | treeView.Visible = true; 323 | nameLabel.Visible = true; 324 | enterTextBox.Visible = true; 325 | 326 | // Populate tree from perspective if modifying existing mini model 327 | if (p != string.Empty) 328 | { 329 | enterTextBox.Text = p; 330 | 331 | foreach (System.Windows.Forms.TreeNode rootNode in treeView.Nodes) 332 | { 333 | string tableName = rootNode.Text; 334 | int childNodeCount = rootNode.Nodes.Count; 335 | int childNodeCheckedCount = 0; 336 | 337 | // Loop through checked child nodes (columns, measures, hierarchies) 338 | foreach (System.Windows.Forms.TreeNode childNode in rootNode.Nodes) 339 | { 340 | var objectName = childNode.Text; 341 | 342 | if (childNode.ImageIndex == 1) 343 | { 344 | if (Model.Tables[tableName].Columns[objectName].InPerspective[p] == true) 345 | { 346 | childNode.StateImageIndex = 1; 347 | } 348 | } 349 | else if (childNode.ImageIndex == 2) 350 | { 351 | if (Model.Tables[tableName].Measures[objectName].InPerspective[p] == true) 352 | { 353 | childNode.StateImageIndex = 1; 354 | } 355 | } 356 | else if (childNode.ImageIndex == 3) 357 | { 358 | if (Model.Tables[tableName].Hierarchies[objectName].InPerspective[p] == true) 359 | { 360 | childNode.StateImageIndex = 1; 361 | } 362 | } 363 | 364 | if (childNode.StateImageIndex == 1) 365 | { 366 | childNodeCheckedCount += 1; 367 | } 368 | } 369 | 370 | // Finish populating tree root nodes (tables) 371 | // If all child nodes are checked, set parent node to checked 372 | if (childNodeCheckedCount == childNodeCount) 373 | { 374 | rootNode.StateImageIndex = 1; 375 | } 376 | // If no child nodes are checked, set parent node to unchecked 377 | else if (childNodeCheckedCount == 0) 378 | { 379 | rootNode.StateImageIndex = 0; 380 | } 381 | // If not all children nodes are selected, set parent node to partially checked icon 382 | else if (childNodeCheckedCount < childNodeCount) 383 | { 384 | rootNode.StateImageIndex = 2; 385 | } 386 | } 387 | } 388 | }; 389 | 390 | treeView.NodeMouseClick += (System.Object sender, System.Windows.Forms.TreeNodeMouseClickEventArgs e) => 391 | { 392 | 393 | if (IsExpOrCol == false) 394 | { 395 | if (e.Node.StateImageIndex != 1) 396 | { 397 | e.Node.StateImageIndex = 1; 398 | } 399 | else if (e.Node.StateImageIndex == 1) 400 | { 401 | e.Node.StateImageIndex = 0; 402 | } 403 | 404 | // If parent node is checked, check all child nodes 405 | if (e.Node.Nodes.Count > 0 && e.Node.StateImageIndex == 1) 406 | { 407 | foreach (System.Windows.Forms.TreeNode childNode in e.Node.Nodes) 408 | { 409 | childNode.StateImageIndex = 1; 410 | } 411 | } 412 | 413 | // If parent node is unhecked, uncheck all child nodes 414 | else if (e.Node.Nodes.Count > 0 && e.Node.StateImageIndex == 0) 415 | { 416 | foreach (System.Windows.Forms.TreeNode childNode in e.Node.Nodes) 417 | { 418 | childNode.StateImageIndex = 0; 419 | } 420 | } 421 | 422 | if (e.Node.Parent != null) 423 | { 424 | int childNodeCount = e.Node.Parent.Nodes.Count; 425 | int childNodeCheckedCount = 0; 426 | 427 | foreach (System.Windows.Forms.TreeNode n in e.Node.Parent.Nodes) 428 | { 429 | if (n.StateImageIndex == 1) 430 | { 431 | childNodeCheckedCount += 1; 432 | } 433 | } 434 | 435 | // If all child nodes are checked, set parent node to checked 436 | if (childNodeCheckedCount == childNodeCount) 437 | { 438 | e.Node.Parent.StateImageIndex = 1; 439 | } 440 | // If no child nodes are checked, set parent node to unchecked 441 | else if (childNodeCheckedCount == 0) 442 | { 443 | e.Node.Parent.StateImageIndex = 0; 444 | } 445 | // If not all children nodes are selected, set parent node to partially checked icon 446 | else if (childNodeCheckedCount < childNodeCount) 447 | { 448 | e.Node.Parent.StateImageIndex = 2; 449 | } 450 | } 451 | } 452 | 453 | IsExpOrCol = false; 454 | }; 455 | 456 | treeView.AfterExpand += (System.Object sender9, System.Windows.Forms.TreeViewEventArgs e9) => 457 | { 458 | 459 | IsExpOrCol = true; 460 | }; 461 | 462 | treeView.AfterCollapse += (System.Object sender10, System.Windows.Forms.TreeViewEventArgs e10) => 463 | { 464 | 465 | IsExpOrCol = true; 466 | }; 467 | 468 | createButton.Click += (System.Object sender6, System.EventArgs e6) => 469 | { 470 | 471 | perspName = enterTextBox.Text; 472 | 473 | if (perspName == string.Empty) 474 | { 475 | // Invalid perspective name 476 | ScriptHelper.Error("Please enter a name for the new perspective."); 477 | } 478 | else 479 | { 480 | if (!Model.Perspectives.Any(a => a.Name == perspName)) 481 | { 482 | // Create new perspective 483 | Model.AddPerspective(perspName); 484 | } 485 | 486 | // Clear perspective 487 | foreach (var t in Model.Tables.ToList()) 488 | { 489 | string tableName = t.Name; 490 | Model.Tables[tableName].InPerspective[perspName] = false; 491 | } 492 | 493 | // Loop through root nodes (tables) 494 | foreach (System.Windows.Forms.TreeNode rootNode in treeView.Nodes) 495 | { 496 | string tableName = rootNode.Text; 497 | 498 | // Loop through checked child nodes (columns, measures, hierarchies) 499 | foreach (System.Windows.Forms.TreeNode childNode in rootNode.Nodes) 500 | { 501 | string objectName = childNode.Text; 502 | 503 | if (childNode.StateImageIndex == 1) 504 | { 505 | // Columns 506 | if (childNode.ImageIndex == 1) 507 | { 508 | Model.Tables[tableName].Columns[objectName].InPerspective[perspName] = true; 509 | } 510 | // Measures 511 | else if (childNode.ImageIndex == 2) 512 | { 513 | Model.Tables[tableName].Measures[objectName].InPerspective[perspName] = true; 514 | } 515 | // Hierarchies 516 | else if (childNode.ImageIndex == 3) 517 | { 518 | Model.Tables[tableName].Hierarchies[objectName].InPerspective[perspName] = true; 519 | } 520 | } 521 | } 522 | } 523 | } 524 | }; 525 | 526 | newForm.Show(); -------------------------------------------------------------------------------- /Collection Archived/Macros/Launch/Perspective Editor.json: -------------------------------------------------------------------------------- 1 | { 2 | "Id": 37, 3 | "Name": "Macros\\Launch\\Perspective Editor", 4 | "Enabled": "true", 5 | "Tooltip": "", 6 | "ValidContexts": "Model" 7 | } -------------------------------------------------------------------------------- /Collection Archived/Macros/Remove/Sort by column.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | #load "..\..\..\Management\Custom Classes.csx" 3 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using TabularEditor; 10 | using TabularEditor.TOMWrapper; 11 | using TabularEditor.TOMWrapper.Utils; 12 | using TabularEditor.UI; 13 | using TabularEditor.Scripting; 14 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 15 | using Newtonsoft.Json.Linq; 16 | 17 | foreach (var c in Selected.Columns) 18 | { 19 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 20 | c.SortByColumn = null; 21 | } -------------------------------------------------------------------------------- /Collection Archived/Macros/Remove/Sort by column.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Remove\\Sort by column", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Column" 6 | } -------------------------------------------------------------------------------- /Collection Archived/Macros/Toggle/Disallow applying default formatting annotation.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | #load "..\..\..\Management\Custom Classes.csx" 3 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using TabularEditor; 10 | using TabularEditor.TOMWrapper; 11 | using TabularEditor.TOMWrapper.Utils; 12 | using TabularEditor.UI; 13 | using TabularEditor.Scripting; 14 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 15 | 16 | foreach (var m in Selected.Measures) 17 | { 18 | if (String.IsNullOrEmpty(m.GetAnnotation("DisallowApplyingDefaultFormatting"))) 19 | { 20 | m.SetAnnotation("DisallowApplyingDefaultFormatting", "true"); 21 | } 22 | else 23 | { 24 | m.RemoveAnnotation("DisallowApplyingDefaultFormatting"); 25 | } 26 | } 27 | 28 | foreach (var c in Selected.Columns) 29 | { 30 | if (String.IsNullOrEmpty(c.GetAnnotation("DisallowApplyingDefaultFormatting"))) 31 | { 32 | c.SetAnnotation("DisallowApplyingDefaultFormatting", "true"); 33 | } 34 | else 35 | { 36 | c.RemoveAnnotation("DisallowApplyingDefaultFormatting"); 37 | } 38 | } -------------------------------------------------------------------------------- /Collection Archived/Macros/Toggle/Disallow applying default formatting annotation.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Toggle\\Disallow applying default formatting annotation", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best Practice Rules/Add VertiPaq annotations from VPAX file.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | #r "System.IO" 4 | #r "System.IO.Compression.FileSystem" 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Collections.Generic; 9 | using Newtonsoft.Json; 10 | using TabularEditor; 11 | using TabularEditor.TOMWrapper; 12 | using TabularEditor.TOMWrapper.Utils; 13 | using TabularEditor.UI; 14 | using TabularEditor.Scripting; 15 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 16 | using System.IO; 17 | using System.IO.Compression; 18 | 19 | // https://github.com/microsoft/Analysis-Services/tree/master/BestPracticeRules 20 | 21 | // Enter .vpax file path 22 | string vpaxFile = @"C:\Desktop\ModelVertipaq.vpax"; 23 | 24 | string fileExt = Path.GetExtension(vpaxFile); 25 | 26 | if (fileExt != ".vpax") 27 | { 28 | ScriptHelper.Error("Must use a valid .vpax file"); 29 | } 30 | 31 | string fileName = Path.GetFileNameWithoutExtension(vpaxFile); 32 | string folderName = Path.GetDirectoryName(vpaxFile) + @"\"; 33 | string zipPath = folderName + fileName + ".zip"; 34 | string unzipPath = folderName + fileName; 35 | 36 | try 37 | { 38 | // Make a copy of a vpax and turn it into a zip file 39 | File.Copy(vpaxFile, zipPath); 40 | // Unzip file 41 | System.IO.Compression.ZipFile.ExtractToDirectory(zipPath, unzipPath); 42 | // Delete zip file 43 | File.Delete(zipPath); 44 | } 45 | 46 | catch 47 | { 48 | ScriptHelper.Error("File does not exist. Must use a valid .vpax file"); 49 | } 50 | 51 | // Remove Existing Vertipaq Annotations 52 | Model.RemoveAnnotation("Vertipaq_ModelSize"); 53 | 54 | foreach (var o in Model.AllHierarchies) 55 | { 56 | o.RemoveAnnotation("Vertipaq_UserHierarchySize"); 57 | o.RemoveAnnotation("Vertipaq_TableSizePctOfModel"); 58 | } 59 | 60 | foreach (var o in Model.AllColumns) 61 | { 62 | o.RemoveAnnotation("Vertipaq_ColumnHierarchySize"); 63 | o.RemoveAnnotation("Vertipaq_DataSize"); 64 | o.RemoveAnnotation("Vertipaq_DictionarySize"); 65 | o.RemoveAnnotation("Vertipaq_Cardinality"); 66 | o.RemoveAnnotation("Vertipaq_ColumnSize"); 67 | o.RemoveAnnotation("Vertipaq_ColumnSizePctOfTable"); 68 | o.RemoveAnnotation("Vertipaq_ColumnSizePctOfModel"); 69 | } 70 | 71 | foreach (var o in Model.Relationships.ToList()) 72 | { 73 | o.RemoveAnnotation("Vertipaq_RelationshipSize"); 74 | o.RemoveAnnotation("Vertipaq_MaxFromCardinality"); 75 | o.RemoveAnnotation("Vertipaq_MaxToCardinality"); 76 | } 77 | 78 | foreach (var o in Model.Tables.ToList()) 79 | { 80 | o.RemoveAnnotation("Vertipaq_RowCount"); 81 | o.RemoveAnnotation("Vertipaq_TableSize"); 82 | } 83 | 84 | foreach (var o in Model.AllPartitions) 85 | { 86 | o.RemoveAnnotation("Vertipaq_RecordCount"); 87 | o.RemoveAnnotation("Vertipaq_RecordsPerSegment"); 88 | o.RemoveAnnotation("Vertipaq_SegmentCount"); 89 | } 90 | 91 | // Deseralize json file 92 | string jsonFilePath = folderName + fileName + @"\" + "DaxVpaView.json"; 93 | var unformattedJson = File.ReadAllText(jsonFilePath, System.Text.UnicodeEncoding.Unicode); 94 | var formattedJson = Newtonsoft.Json.Linq.JToken.Parse(unformattedJson).ToString(); 95 | 96 | dynamic json = Newtonsoft.Json.Linq.JObject.Parse(formattedJson); 97 | 98 | // Delete previously created folder 99 | try 100 | { 101 | Directory.Delete(folderName + fileName, true); 102 | } 103 | catch 104 | { 105 | } 106 | 107 | int tableCount = (int)json["Tables"].Count; 108 | int columnCount = (int)json["Columns"].Count; 109 | int relationshipCount = (int)json["Relationships"].Count; 110 | int hierarchiesCount = (int)json["UserHierarchies"].Count; 111 | int columnSegmentCount = (int)json["ColumnsSegments"].Count; 112 | 113 | // Add table annotations 114 | for (int i = 0; i < tableCount; i++) 115 | { 116 | string tableName = (string)json["Tables"][i]["TableName"]; 117 | string rowCount = (string)json["Tables"][i]["RowsCount"]; 118 | string tableSize = (string)json["Tables"][i]["TableSize"]; 119 | 120 | if (Model.Tables.Where(a => a.Name == tableName).Count() == 1) 121 | { 122 | var obj = Model.Tables[tableName]; 123 | 124 | obj.SetAnnotation("Vertipaq_RowCount", rowCount); 125 | obj.SetAnnotation("Vertipaq_TableSize", tableSize); 126 | } 127 | } 128 | 129 | // Add column annotations 130 | for (int i = 0; i < columnCount; i++) 131 | { 132 | string columnName = (string)json["Columns"][i]["ColumnName"]; 133 | string tableName = (string)json["Columns"][i]["TableName"]; 134 | string columnCardinality = (string)json["Columns"][i]["ColumnCardinality"]; 135 | string dictionarySize = (string)json["Columns"][i]["DictionarySize"]; 136 | string dataSize = (string)json["Columns"][i]["DataSize"]; 137 | string hierarchiesSize = (string)json["Columns"][i]["HierarchiesSize"]; 138 | string totalSize = (string)json["Columns"][i]["TotalSize"]; 139 | 140 | if (Model.Tables.Where(a => a.Name == tableName && a.Columns.Any(b => b.Name == columnName)).Count() == 1) 141 | 142 | { 143 | var obj = Model.Tables[tableName].Columns[columnName]; 144 | 145 | obj.SetAnnotation("Vertipaq_Cardinality", columnCardinality); 146 | obj.SetAnnotation("Vertipaq_ColumnHierarchySize", hierarchiesSize); 147 | obj.SetAnnotation("Vertipaq_ColumnSize", totalSize); 148 | obj.SetAnnotation("Vertipaq_DataSize", dataSize); 149 | obj.SetAnnotation("Vertipaq_DictionarySize", dictionarySize); 150 | } 151 | } 152 | 153 | // Add relationship annotations 154 | for (int i = 0; i < relationshipCount; i++) 155 | { 156 | string relationshipName = (string)json["Relationships"][i]["RelationshipName"]; 157 | string fromCardinality = (string)json["Relationships"][i]["FromCardinality"]; 158 | string toCardinality = (string)json["Relationships"][i]["ToCardinality"]; 159 | string usedSize = (string)json["Relationships"][i]["UsedSize"]; 160 | 161 | if (Model.Relationships.Where(a => a.ID == relationshipName).Count() == 1) 162 | { 163 | var obj = Model.Relationships[relationshipName]; 164 | 165 | obj.SetAnnotation("Vertipaq_MaxFromCardinality", fromCardinality); 166 | obj.SetAnnotation("Vertipaq_MaxToCardinality", toCardinality); 167 | obj.SetAnnotation("Vertipaq_RelationshipSize", usedSize); 168 | } 169 | } 170 | 171 | // Add hierarchies annotations 172 | for (int i = 0; i < hierarchiesCount; i++) 173 | { 174 | string hierarchyName = (string)json["UserHierarchies"][i]["UserHierarchyName"]; 175 | string tableName = (string)json["UserHierarchies"][i]["TableName"]; 176 | string usedSize = (string)json["UserHierarchies"][i]["UsedSize"]; 177 | 178 | if (Model.AllHierarchies.Where(a => a.Name == hierarchyName && a.Table.Name == tableName).Count() == 1) 179 | { 180 | var obj = Model.Tables[tableName].Hierarchies[hierarchyName]; 181 | 182 | obj.SetAnnotation("Vertipaq_UserHierarchySize", usedSize); 183 | } 184 | } 185 | 186 | // Add partition annotations 187 | for (int i = 0; i < columnSegmentCount; i++) 188 | { 189 | string tableName = (string)json["ColumnsSegments"][i]["TableName"]; 190 | string partitionName = (string)json["ColumnsSegments"][i]["PartitionName"]; 191 | string columnName = (string)json["ColumnsSegments"][i]["ColumnName"]; 192 | string segmentNumber = (string)json["ColumnsSegments"][i]["SegmentNumber"]; 193 | string tablePartitionNumber = (string)json["ColumnsSegments"][i]["TablePartitionNumber"]; 194 | string segmentRows = (string)json["ColumnsSegments"][i]["SegmentRows"]; 195 | int segmentNumberInt = Convert.ToInt32(segmentNumber); 196 | int tablePartitionNumberInt = Convert.ToInt32(tablePartitionNumber); 197 | long segmentRowsInt = Convert.ToInt64(segmentRows); 198 | 199 | var obj = Model.Tables[tableName].Partitions[partitionName]; 200 | 201 | int s = 0; 202 | foreach (var t in Model.Tables.Where(a => a.Name == tableName).ToList()) 203 | { 204 | foreach (var p in t.Partitions.Where(b => b.MetadataIndex < tablePartitionNumberInt)) 205 | { 206 | s = s + Convert.ToInt32(p.GetAnnotation("Vertipaq_SegmentCount")); 207 | } 208 | } 209 | 210 | obj.SetAnnotation("Vertipaq_SegmentCount", (segmentNumberInt - s + 1).ToString()); 211 | 212 | if (columnName.StartsWith("RowNumber-")) 213 | { 214 | long rc = Convert.ToInt64(obj.GetAnnotation("Vertipaq_RecordCount")); 215 | obj.SetAnnotation("Vertipaq_RecordCount", (segmentRowsInt + rc).ToString()); 216 | } 217 | } 218 | 219 | // Add Records per Segment 220 | long maxRPS = 8388608; 221 | foreach (var t in Model.Tables.ToList()) 222 | { 223 | foreach (var p in t.Partitions.ToList()) 224 | { 225 | long rc = Convert.ToInt64(p.GetAnnotation("Vertipaq_RecordCount")); 226 | long sc = Convert.ToInt64(p.GetAnnotation("Vertipaq_SegmentCount")); 227 | string rps = "Vertipaq_RecordsPerSegment"; 228 | 229 | if (sc > 1) 230 | { 231 | p.SetAnnotation(rps, maxRPS.ToString()); 232 | } 233 | else if (sc == 0) 234 | { 235 | p.SetAnnotation(rps, "0"); 236 | } 237 | else 238 | { 239 | p.SetAnnotation(rps, (rc / sc).ToString()); 240 | } 241 | } 242 | } 243 | 244 | // Add model size annotation 245 | string ms = Model.Tables.Sum(a => Convert.ToInt64(a.GetAnnotation("Vertipaq_TableSize"))).ToString(); 246 | Model.SetAnnotation("Vertipaq_ModelSize", ms); 247 | 248 | // Percent of Table and Model 249 | float modelSize = Convert.ToInt64(Model.GetAnnotation("Vertipaq_ModelSize")); 250 | 251 | foreach (var t in Model.Tables.ToList()) 252 | { 253 | string tableName = t.Name; 254 | var obj = Model.Tables[tableName]; 255 | 256 | float tableSize = Convert.ToInt64(obj.GetAnnotation("Vertipaq_TableSize")); 257 | double tblpct = Math.Round(tableSize / modelSize, 3); 258 | 259 | obj.SetAnnotation("Vertipaq_TableSizePctOfModel", tblpct.ToString()); 260 | 261 | foreach (var c in t.Columns.ToList()) 262 | { 263 | string colName = c.Name; 264 | var col = Model.Tables[tableName].Columns[colName]; 265 | 266 | float colSize = Convert.ToInt64(col.GetAnnotation("Vertipaq_ColumnSize")); 267 | double colpctTbl = Math.Round(colSize / tableSize, 3); 268 | double colpctModel = Math.Round(colSize / modelSize, 3); 269 | 270 | col.SetAnnotation("Vertipaq_ColumnSizePctOfTable", colpctTbl.ToString()); 271 | col.SetAnnotation("Vertipaq_ColumnSizePctOfModel", colpctModel.ToString()); 272 | } 273 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best Practice Rules/Add VertiPaq annotations from VPAX file.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Best Practice Rules\\Add VertiPaq annotations from VPAX file", 3 | "Enabled": "false", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best Practice Rules/Add VertiPaq annotations.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | using System.Xml; 15 | 16 | // https://github.com/microsoft/Analysis-Services/tree/master/BestPracticeRules 17 | 18 | // Store DMV Queries as Data Tables 19 | var DMV_Tables = ScriptHelper.ExecuteDax("SELECT [ID],[Name] FROM $SYSTEM.TMSCHEMA_TABLES").Tables[0]; 20 | var DMV_Dimensions = ScriptHelper.ExecuteDax("SELECT [DIMENSION_NAME], [DIMENSION_CARDINALITY] FROM $SYSTEM.MDSCHEMA_DIMENSIONS").Tables[0]; 21 | var DMV_Relationships = ScriptHelper.ExecuteDax("SELECT [ID],[Name] FROM $SYSTEM.TMSCHEMA_RELATIONSHIPS").Tables[0]; 22 | var DMV_Hierarchies = ScriptHelper.ExecuteDax("SELECT [ID], [TableID], [Name] FROM $SYSTEM.TMSCHEMA_HIERARCHIES").Tables[0]; 23 | var DMV_Columns = ScriptHelper.ExecuteDax("SELECT [ID],[TableID],[ExplicitName] FROM $SYSTEM.TMSCHEMA_COLUMNS").Tables[0]; 24 | var DMV_Partitions = ScriptHelper.ExecuteDax("SELECT [ID],[TableID],[Name] FROM $SYSTEM.TMSCHEMA_PARTITIONS").Tables[0]; 25 | var DMV_PartitionStorages = ScriptHelper.ExecuteDax("SELECT [ID],[PartitionID] FROM $SYSTEM.TMSCHEMA_PARTITION_STORAGES").Tables[0]; 26 | var DMV_SegmentMapStorages = ScriptHelper.ExecuteDax("SELECT [PartitionStorageID],[RecordCount],[SegmentCount],[RecordsPerSegment] FROM $SYSTEM.TMSCHEMA_SEGMENT_MAP_STORAGES").Tables[0]; 27 | var DMV_StorageTableColumns = ScriptHelper.ExecuteDax("SELECT [DIMENSION_NAME],[ATTRIBUTE_NAME],[COLUMN_TYPE],[DICTIONARY_SIZE] FROM $SYSTEM.DISCOVER_STORAGE_TABLE_COLUMNS").Tables[0]; 28 | var DMV_StorageTables = ScriptHelper.ExecuteDax("SELECT [DIMENSION_NAME],[TABLE_ID],[ROWS_COUNT] FROM $SYSTEM.DISCOVER_STORAGE_TABLES").Tables[0]; 29 | var DMV_ColumnSegments = ScriptHelper.ExecuteDax("SELECT [DIMENSION_NAME],[TABLE_ID],[COLUMN_ID],[USED_SIZE] FROM $SYSTEM.DISCOVER_STORAGE_TABLE_COLUMN_SEGMENTS").Tables[0]; 30 | 31 | // Remove Existing Vertipaq Annotations 32 | Model.RemoveAnnotation("Vertipaq_ModelSize"); 33 | 34 | foreach (var o in Model.AllHierarchies) 35 | { 36 | o.RemoveAnnotation("Vertipaq_HierarchyID"); 37 | o.RemoveAnnotation("Vertipaq_UserHierarchySize"); 38 | } 39 | 40 | foreach (var o in Model.AllColumns) 41 | { 42 | o.RemoveAnnotation("Vertipaq_ColumnID"); 43 | o.RemoveAnnotation("Vertipaq_ColumnHierarchySize"); 44 | o.RemoveAnnotation("Vertipaq_DataSize"); 45 | o.RemoveAnnotation("Vertipaq_DictionarySize"); 46 | o.RemoveAnnotation("Vertipaq_Cardinality"); 47 | o.RemoveAnnotation("Vertipaq_ColumnSize"); 48 | o.RemoveAnnotation("Vertipaq_ColumnSizePctOfTable"); 49 | o.RemoveAnnotation("Vertipaq_ColumnSizePctOfModel"); 50 | } 51 | 52 | foreach (var o in Model.Relationships.ToList()) 53 | { 54 | o.RemoveAnnotation("Vertipaq_RelationshipID"); 55 | o.RemoveAnnotation("Vertipaq_RelationshipSize"); 56 | o.RemoveAnnotation("Vertipaq_MaxFromCardinality"); 57 | o.RemoveAnnotation("Vertipaq_MaxToCardinality"); 58 | 59 | } 60 | 61 | foreach (var o in Model.Tables.ToList()) 62 | { 63 | o.RemoveAnnotation("Vertipaq_TableID"); 64 | o.RemoveAnnotation("Vertipaq_RowCount"); 65 | o.RemoveAnnotation("Vertipaq_TableSize"); 66 | o.RemoveAnnotation("Vertipaq_TableSizePctOfModel"); 67 | } 68 | 69 | foreach (var o in Model.AllPartitions) 70 | { 71 | o.RemoveAnnotation("Vertipaq_PartitionID"); 72 | o.RemoveAnnotation("Vertipaq_PartitionStorageID"); 73 | o.RemoveAnnotation("Vertipaq_RecordCount"); 74 | o.RemoveAnnotation("Vertipaq_RecordsPerSegment"); 75 | o.RemoveAnnotation("Vertipaq_SegmentCount"); 76 | } 77 | 78 | // Set Table IDs 79 | for (int r = 0; r < DMV_Tables.Rows.Count; r++) 80 | { 81 | string tblID = DMV_Tables.Rows[r][0].ToString(); 82 | string tblName = DMV_Tables.Rows[r][1].ToString(); 83 | 84 | Model.Tables[tblName].SetAnnotation("Vertipaq_TableID", tblID); 85 | } 86 | 87 | // Set Table Row Counts 88 | for (int r = 0; r < DMV_Dimensions.Rows.Count; r++) 89 | { 90 | string tblName = DMV_Dimensions.Rows[r][0].ToString(); 91 | string recordCount = DMV_Dimensions.Rows[r][1].ToString(); 92 | 93 | if (tblName != "Measures") 94 | { 95 | Model.Tables[tblName].SetAnnotation("Vertipaq_RowCount", recordCount); 96 | } 97 | } 98 | 99 | // Set Relationship IDs 100 | for (int r = 0; r < DMV_Relationships.Rows.Count; r++) 101 | { 102 | string ID = DMV_Relationships.Rows[r][0].ToString(); 103 | string relID = DMV_Relationships.Rows[r][1].ToString(); 104 | 105 | Model.Relationships[relID].SetAnnotation("Vertipaq_RelationshipID", ID); 106 | } 107 | 108 | // Set Hierarchy IDs 109 | for (int r = 0; r < DMV_Hierarchies.Rows.Count; r++) 110 | { 111 | string hID = DMV_Hierarchies.Rows[r][0].ToString(); 112 | string tableID = DMV_Hierarchies.Rows[r][1].ToString(); 113 | string hName = DMV_Hierarchies.Rows[r][2].ToString(); 114 | 115 | foreach (var t in Model.Tables.Where(a => a.GetAnnotation("Vertipaq_TableID") == tableID)) 116 | { 117 | string tableName = t.Name; 118 | Model.Tables[tableName].Hierarchies[hName].SetAnnotation("Vertipaq_HierarchyID", hID); 119 | } 120 | } 121 | 122 | // Set Column IDs 123 | for (int r = 0; r < DMV_Columns.Rows.Count; r++) 124 | { 125 | string colID = DMV_Columns.Rows[r][0].ToString(); 126 | string tableID = DMV_Columns.Rows[r][1].ToString(); 127 | string colName = DMV_Columns.Rows[r][2].ToString(); 128 | 129 | foreach (var t in Model.Tables.Where(a => a.GetAnnotation("Vertipaq_TableID") == tableID)) 130 | { 131 | string tableName = t.Name; 132 | 133 | if (colName.StartsWith("RowNumber-") == false && colName != String.Empty) 134 | { 135 | Model.Tables[tableName].Columns[colName].SetAnnotation("Vertipaq_ColumnID", colID); 136 | } 137 | } 138 | } 139 | 140 | // Set Partition IDs 141 | for (int r = 0; r < DMV_Partitions.Rows.Count; r++) 142 | { 143 | string pID = DMV_Partitions.Rows[r][0].ToString(); 144 | string tableID = DMV_Partitions.Rows[r][1].ToString(); 145 | string pName = DMV_Partitions.Rows[r][2].ToString(); 146 | 147 | foreach (var t in Model.Tables.Where(a => a.GetAnnotation("Vertipaq_TableID") == tableID)) 148 | { 149 | string tableName = t.Name; 150 | 151 | Model.Tables[tableName].Partitions[pName].SetAnnotation("Vertipaq_PartitionID", pID); 152 | } 153 | } 154 | 155 | // Set Partition Storage IDs 156 | for (int r = 0; r < DMV_PartitionStorages.Rows.Count; r++) 157 | { 158 | string psID = DMV_PartitionStorages.Rows[r][0].ToString(); 159 | string pID = DMV_PartitionStorages.Rows[r][1].ToString(); 160 | 161 | foreach (var p in Model.AllPartitions.Where(a => a.GetAnnotation("Vertipaq_PartitionID") == pID)) 162 | { 163 | string tableName = p.Table.Name; 164 | string pName = p.Name; 165 | 166 | Model.Tables[tableName].Partitions[pName].SetAnnotation("Vertipaq_PartitionStorageID", psID); 167 | } 168 | } 169 | 170 | // Set Partition Stats 171 | for (int r = 0; r < DMV_SegmentMapStorages.Rows.Count; r++) 172 | { 173 | string psID = DMV_SegmentMapStorages.Rows[r][0].ToString(); 174 | string recordCount = DMV_SegmentMapStorages.Rows[r][1].ToString(); 175 | string segmentCount = DMV_SegmentMapStorages.Rows[r][2].ToString(); 176 | string recordsPerSegment = DMV_SegmentMapStorages.Rows[r][3].ToString(); 177 | 178 | foreach (var p in Model.AllPartitions.Where(a => a.GetAnnotation("Vertipaq_PartitionStorageID") == psID)) 179 | { 180 | string tableName = p.Table.Name; 181 | string pName = p.Name; 182 | Model.Tables[tableName].Partitions[pName].SetAnnotation("Vertipaq_RecordCount", recordCount); 183 | Model.Tables[tableName].Partitions[pName].SetAnnotation("Vertipaq_SegmentCount", segmentCount); 184 | Model.Tables[tableName].Partitions[pName].SetAnnotation("Vertipaq_RecordsPerSegment", recordsPerSegment); 185 | } 186 | } 187 | 188 | // Set Dictionary Size 189 | for (int r = 0; r < DMV_StorageTableColumns.Rows.Count; r++) 190 | { 191 | string tableName = DMV_StorageTableColumns.Rows[r][0].ToString(); 192 | string colName = DMV_StorageTableColumns.Rows[r][1].ToString(); 193 | string colType = DMV_StorageTableColumns.Rows[r][2].ToString(); 194 | string dictSize = DMV_StorageTableColumns.Rows[r][3].ToString(); 195 | 196 | if (colType == "BASIC_DATA" && colName.StartsWith("RowNumber-") == false) 197 | { 198 | Model.Tables[tableName].Columns[colName].SetAnnotation("Vertipaq_DictionarySize", dictSize); 199 | } 200 | } 201 | 202 | // Set Column Row Counts 203 | for (int r = 0; r < DMV_StorageTables.Rows.Count; r++) 204 | { 205 | string tableName = DMV_StorageTables.Rows[r][0].ToString(); 206 | string usedColumn = DMV_StorageTables.Rows[r][1].ToString(); 207 | string rowCount = DMV_StorageTables.Rows[r][2].ToString(); 208 | int lastInd = usedColumn.LastIndexOf("("); 209 | string usedColumnID = usedColumn.Substring(lastInd + 1, usedColumn.Length - lastInd - 2); 210 | 211 | foreach (var c in Model.Tables[tableName].Columns.Where(a => a.GetAnnotation("Vertipaq_ColumnID") == usedColumnID)) 212 | { 213 | var colName = c.Name; 214 | Model.Tables[tableName].Columns[colName].SetAnnotation("Vertipaq_Cardinality", rowCount); 215 | } 216 | } 217 | 218 | // User Hierarchy Size 219 | for (int r = 0; r < DMV_ColumnSegments.Rows.Count; r++) 220 | { 221 | string tableName = DMV_ColumnSegments.Rows[r][0].ToString(); 222 | string usedObj = DMV_ColumnSegments.Rows[r][1].ToString(); 223 | string usedCol = DMV_ColumnSegments.Rows[r][2].ToString(); 224 | string usedSize = DMV_ColumnSegments.Rows[r][3].ToString(); 225 | 226 | int lastInd = usedObj.LastIndexOf("("); 227 | string usedObjID = usedObj.Substring(lastInd + 1, usedObj.Length - lastInd - 2); 228 | 229 | int lastInd2 = usedCol.LastIndexOf("("); 230 | string usedObjID2 = usedCol.Substring(lastInd2 + 1, usedCol.Length - lastInd2 - 2); 231 | 232 | // User Hierarchy Size 233 | foreach (var o in Model.Tables[tableName].Hierarchies.Where(a => a.GetAnnotation("Vertipaq_HierarchyID") == usedObjID)) 234 | { 235 | string hName = o.Name; 236 | int hSize = Convert.ToInt32(Model.Tables[tableName].Hierarchies[hName].GetAnnotation("Vertipaq_UserHierarchySize")); 237 | 238 | if (usedObj.StartsWith("U$")) 239 | { 240 | hSize = hSize + Convert.ToInt32(usedSize); 241 | Model.Tables[tableName].Hierarchies[hName].SetAnnotation("Vertipaq_UserHierarchySize", hSize.ToString()); 242 | } 243 | } 244 | 245 | // Relationship Size 246 | foreach (var o in Model.Relationships.Where(a => a.GetAnnotation("Vertipaq_RelationshipID") == usedObjID)) 247 | { 248 | string rName = o.ID; 249 | int rSize = Convert.ToInt32(Model.Relationships[rName].GetAnnotation("Vertipaq_RelationshipSize")); 250 | 251 | if (usedObj.StartsWith("R$")) 252 | { 253 | rSize = rSize + Convert.ToInt32(usedSize); 254 | Model.Relationships[rName].SetAnnotation("Vertipaq_RelationshipSize", rSize.ToString()); 255 | } 256 | } 257 | 258 | // Column Hierarchy Size 259 | foreach (var o in Model.Tables[tableName].Columns.Where(a => a.GetAnnotation("Vertipaq_ColumnID") == usedObjID)) 260 | { 261 | string colName = o.Name; 262 | long colSize = Convert.ToInt64(Model.Tables[tableName].Columns[colName].GetAnnotation("Vertipaq_ColumnHierarchySize")); 263 | 264 | if (usedObj.StartsWith("H$")) 265 | { 266 | colSize = colSize + Convert.ToInt32(usedSize); 267 | Model.Tables[tableName].Columns[colName].SetAnnotation("Vertipaq_ColumnHierarchySize", colSize.ToString()); 268 | } 269 | } 270 | 271 | // Column Data Size 272 | foreach (var o in Model.Tables[tableName].Columns.Where(a => a.GetAnnotation("Vertipaq_ColumnID") == usedObjID2)) 273 | { 274 | string colName = o.Name; 275 | long colSize = Convert.ToInt64(Model.Tables[tableName].Columns[colName].GetAnnotation("Vertipaq_DataSize")); 276 | 277 | if (usedObj.StartsWith("H$") == false && usedObj.StartsWith("R$") == false && usedObj.StartsWith("U$") == false) 278 | { 279 | colSize = colSize + Convert.ToInt64(usedSize); 280 | Model.Tables[tableName].Columns[colName].SetAnnotation("Vertipaq_DataSize", colSize.ToString()); 281 | } 282 | } 283 | } 284 | 285 | // Set Column & Table Size 286 | long tableSizeCumulative = 0; 287 | 288 | foreach (var t in Model.Tables.ToList()) 289 | { 290 | string tableName = t.Name; 291 | long colSizeCumulative = 0; 292 | long userHierSizeCumulative = 0; 293 | long relSizeCumulative = 0; 294 | 295 | foreach (var c in t.Columns.ToList()) 296 | { 297 | string colName = c.Name; 298 | var obj = Model.Tables[tableName].Columns[colName]; 299 | 300 | long colHierSize = Convert.ToInt64(obj.GetAnnotation("Vertipaq_ColumnHierarchySize")); 301 | long dataSize = Convert.ToInt64(obj.GetAnnotation("Vertipaq_DataSize")); 302 | long dictSize = Convert.ToInt64(obj.GetAnnotation("Vertipaq_DictionarySize")); 303 | 304 | long colSize = colHierSize + dataSize + dictSize; 305 | colSizeCumulative = colSizeCumulative + colSize; 306 | 307 | // Set Column Size 308 | obj.SetAnnotation("Vertipaq_ColumnSize", colSize.ToString()); 309 | } 310 | 311 | foreach (var h in t.Hierarchies.ToList()) 312 | { 313 | string hName = h.Name; 314 | var obj = Model.Tables[tableName].Hierarchies[hName]; 315 | 316 | long userHierSize = Convert.ToInt32(obj.GetAnnotation("Vertipaq_UserHierarchySize")); 317 | userHierSizeCumulative = userHierSizeCumulative + userHierSize; 318 | } 319 | 320 | foreach (var r in Model.Relationships.Where(a => a.FromTable.Name == tableName).ToList()) 321 | { 322 | string rName = r.ID; 323 | var obj = Model.Relationships[rName]; 324 | 325 | long relSize = Convert.ToInt32(obj.GetAnnotation("Vertipaq_RelationshipSize")); 326 | 327 | relSizeCumulative = relSizeCumulative + relSize; 328 | } 329 | 330 | long tableSize = colSizeCumulative + userHierSizeCumulative + relSizeCumulative; 331 | tableSizeCumulative = tableSizeCumulative + tableSize; 332 | 333 | // Set Table Size 334 | Model.Tables[tableName].SetAnnotation("Vertipaq_TableSize", tableSize.ToString()); 335 | } 336 | 337 | // Set Model Size 338 | Model.SetAnnotation("Vertipaq_ModelSize", tableSizeCumulative.ToString()); 339 | 340 | // Set Max From/To Cardinality 341 | foreach (var r in Model.Relationships.ToList()) 342 | { 343 | string rName = r.ID; 344 | string fromTbl = r.FromTable.Name; 345 | string fromCol = r.FromColumn.Name; 346 | string toTbl = r.ToTable.Name; 347 | string toCol = r.ToColumn.Name; 348 | var obj = Model.Relationships[rName]; 349 | 350 | string fromCard = Model.Tables[fromTbl].Columns[fromCol].GetAnnotation("Vertipaq_Cardinality"); 351 | string toCard = Model.Tables[toTbl].Columns[toCol].GetAnnotation("Vertipaq_Cardinality"); 352 | 353 | obj.SetAnnotation("Vertipaq_MaxFromCardinality", fromCard); 354 | obj.SetAnnotation("Vertipaq_MaxToCardinality", toCard); 355 | } 356 | 357 | // Percent of Table and Model 358 | float modelSize = Convert.ToInt64(Model.GetAnnotation("Vertipaq_ModelSize")); 359 | 360 | foreach (var t in Model.Tables.ToList()) 361 | { 362 | string tableName = t.Name; 363 | var obj = Model.Tables[tableName]; 364 | 365 | float tableSize = Convert.ToInt64(obj.GetAnnotation("Vertipaq_TableSize")); 366 | double tblpct = Math.Round(tableSize / modelSize, 3); 367 | 368 | obj.SetAnnotation("Vertipaq_TableSizePctOfModel", tblpct.ToString()); 369 | 370 | foreach (var c in t.Columns.ToList()) 371 | { 372 | string colName = c.Name; 373 | var col = Model.Tables[tableName].Columns[colName]; 374 | 375 | float colSize = Convert.ToInt64(col.GetAnnotation("Vertipaq_ColumnSize")); 376 | double colpctTbl = Math.Round(colSize / tableSize, 3); 377 | double colpctModel = Math.Round(colSize / modelSize, 3); 378 | 379 | col.SetAnnotation("Vertipaq_ColumnSizePctOfTable", colpctTbl.ToString()); 380 | col.SetAnnotation("Vertipaq_ColumnSizePctOfModel", colpctModel.ToString()); 381 | } 382 | } 383 | 384 | // Remove Vertipaq ID Annotations 385 | foreach (var o in Model.AllHierarchies) 386 | { 387 | o.RemoveAnnotation("Vertipaq_HierarchyID"); 388 | } 389 | 390 | foreach (var o in Model.AllColumns) 391 | { 392 | o.RemoveAnnotation("Vertipaq_ColumnID"); 393 | } 394 | 395 | foreach (var o in Model.Relationships.ToList()) 396 | { 397 | o.RemoveAnnotation("Vertipaq_RelationshipID"); 398 | } 399 | 400 | foreach (var o in Model.Tables.ToList()) 401 | { 402 | o.RemoveAnnotation("Vertipaq_TableID"); 403 | } 404 | 405 | foreach (var o in Model.AllPartitions) 406 | { 407 | o.RemoveAnnotation("Vertipaq_PartitionID"); 408 | o.RemoveAnnotation("Vertipaq_PartitionStorageID"); 409 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best Practice Rules/Add VertiPaq annotations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Best Practice Rules\\Add VertiPaq annotations", 3 | "Enabled": "false", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best Practice Rules/Add long length column annotations.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | // https://github.com/microsoft/Analysis-Services/tree/master/BestPracticeRules 16 | 17 | int maxLen = 100; 18 | string annName = "LongLengthRowCount"; 19 | 20 | foreach (var c in Model.AllColumns.Where(a => a.DataType == DataType.String)) 21 | { 22 | 23 | string tableName = c.Table.Name; 24 | string columnName = c.Name; 25 | 26 | var obj = Model.Tables[tableName].Columns[columnName]; 27 | var result = ScriptHelper.EvaluateDax("SUMMARIZECOLUMNS(\"test\",CALCULATE(COUNTROWS(DISTINCT('"+tableName+"'["+columnName+"])),LEN('"+tableName+"'["+columnName+"]) > "+maxLen+"))"); 28 | 29 | obj.SetAnnotation(annName,result.ToString()); 30 | 31 | if (obj.GetAnnotation(annName) == "Table") 32 | { 33 | obj.SetAnnotation(annName,"0"); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best Practice Rules/Add long length column annotations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Best Practice Rules\\Add long length column annotations", 3 | "Enabled": "false", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best Practice Rules/Add split datetime annotations.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | using System.Xml; 15 | 16 | // https://github.com/microsoft/Analysis-Services/tree/master/BestPracticeRules 17 | 18 | // Split Date/Time recommendation 19 | string annName = "DateTimeWithHourMinSec"; 20 | foreach (var c in Model.AllColumns.Where(a => a.DataType == DataType.DateTime)) 21 | { 22 | string columnName = c.Name; 23 | string tableName = c.Table.Name; 24 | var obj = Model.Tables[tableName].Columns[columnName]; 25 | 26 | var result = ScriptHelper.ExecuteDax("EVALUATE TOPN(5,SUMMARIZECOLUMNS('"+tableName+"'["+columnName+"]))").Tables[0]; 27 | 28 | for (int r = 0; r < result.Rows.Count; r++) 29 | { 30 | string resultValue = result.Rows[r][0].ToString(); 31 | if (!resultValue.EndsWith("12:00:00 AM")) 32 | { 33 | obj.SetAnnotation(annName,"1"); 34 | r=50; 35 | } 36 | else 37 | { 38 | obj.SetAnnotation(annName,"0"); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best Practice Rules/Add split datetime annotations.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Best Practice Rules\\Add split datetime annotations", 3 | "Enabled": "false", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best Practice Rules/Download best practice rules.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | #r "System.Net.Http" 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using TabularEditor; 10 | using TabularEditor.TOMWrapper; 11 | using TabularEditor.TOMWrapper.Utils; 12 | using TabularEditor.UI; 13 | using TabularEditor.Scripting; 14 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 15 | using System.IO; 16 | 17 | // https://github.com/microsoft/Analysis-Services/tree/master/BestPracticeRules 18 | 19 | var url = "https://raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json"; 20 | var path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData); 21 | var jsonPath = String.Empty; 22 | var version = typeof(Model).Assembly.GetName().Version.Major; 23 | 24 | switch (version) 25 | { 26 | 27 | case 3: 28 | jsonPath = path + @"\TabularEditor3\BPARules.json"; 29 | break; 30 | 31 | case 2: 32 | jsonPath = path + @"\TabularEditor\BPARules.json"; 33 | break; 34 | 35 | default: 36 | ScriptHelper.Error("Couldn't identify the version of Tabular Editor: " + version); 37 | return; 38 | 39 | } 40 | 41 | using (var client = new System.Net.Http.HttpClient()) 42 | { 43 | var responseBody = client.GetStringAsync(url).GetAwaiter().GetResult(); 44 | File.WriteAllText(jsonPath, responseBody, System.Text.Encoding.UTF8); 45 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best Practice Rules/Download best practice rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Best Practice Rules\\Download best practice rules", 3 | "Enabled": "false", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best practice rules.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | ScriptHelper.CustomAction("Macros\\Apply\\Best Practice Rules\\Download best practice rules"); 16 | ScriptHelper.CustomAction("Macros\\Apply\\Best Practice Rules\\Add long length column annotations"); 17 | ScriptHelper.CustomAction("Macros\\Apply\\Best Practice Rules\\Add split datetime annotations"); 18 | ScriptHelper.CustomAction("Macros\\Apply\\Best Practice Rules\\Add VertiPaq annotations"); -------------------------------------------------------------------------------- /Collection/Macros/Apply/Best practice rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Best practice rules", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Blank lineage tags.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | // not a complete list 16 | foreach (var m in Model.AllMeasures) { m.LineageTag = String.Empty; } 17 | foreach (var l in Model.AllLevels) { l.LineageTag = String.Empty; } 18 | foreach (var h in Model.AllHierarchies) { h.LineageTag = String.Empty; } 19 | foreach (var c in Model.AllColumns) { c.LineageTag = String.Empty; } 20 | foreach (var t in Model.Tables) { t.LineageTag = String.Empty; } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Blank lineage tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Blank lineage tags", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/DAX as description.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach (var m in Model.AllMeasures) 16 | { 17 | m.Description = m.Expression.TrimStart('\r', '\n'); 18 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/DAX as description.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\DAX as description", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Default format strings.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | string GetFormatString(DataType dataType) 16 | { 17 | switch (dataType) 18 | { 19 | case DataType.Boolean: 20 | return "\"TRUE\";\"TRUE\";\"FALSE\""; 21 | case DataType.DateTime: 22 | return "General Date"; 23 | case DataType.Decimal: 24 | return "\\$#,0.###############;-\\$#,0.###############;\\$#,0.###############"; 25 | // return "$ #,##0.00"; // TE3 version 26 | case DataType.Double: 27 | return string.Empty; 28 | case DataType.Int64: 29 | return "0"; 30 | case DataType.String: 31 | return string.Empty; 32 | case DataType.Variant: 33 | return string.Empty; 34 | default: 35 | return string.Empty; 36 | } 37 | } 38 | 39 | foreach (var m in Model.AllMeasures) 40 | { 41 | m.FormatString = GetFormatString(m.DataType); 42 | m.FormatStringExpression = string.Empty; 43 | } 44 | 45 | foreach (var c in Model.AllColumns) 46 | { 47 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 48 | c.FormatString = GetFormatString(c.DataType); 49 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Default format strings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Default format strings", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Default sort by columns.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach (var c in Model.AllColumns) 16 | { 17 | 18 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 19 | 20 | var tableColumns = c.Table.Columns.Where(tc => 21 | tc.Name.ToLower() == (c.Name + " Number").ToLower() || 22 | tc.Name.ToLower() == (c.Name + " Sort").ToLower() || 23 | tc.Name.ToLower() == (c.Name + " SortBy").ToLower() || 24 | tc.Name.ToLower() == (c.Name + " Order").ToLower() || 25 | tc.Name.ToLower() == (c.Name + " OrderBy").ToLower() || 26 | tc.Name.ToLower() == (c.Name + " Ordinal").ToLower() 27 | ).OrderBy(tc => tc); 28 | 29 | if (tableColumns.Any()) 30 | { 31 | c.SortByColumn = tableColumns.First(); 32 | tableColumns.First().IsHidden = true; 33 | } 34 | else 35 | { 36 | c.SortByColumn = null; 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Default sort by columns.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Default sort by columns", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Discourage implicit measures.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach (var c in Model.AllColumns.Where(x => x.SummarizeBy != AggregateFunction.None)) 16 | { 17 | c.SummarizeBy = AggregateFunction.None; 18 | } 19 | 20 | Model.DiscourageImplicitMeasures = true; -------------------------------------------------------------------------------- /Collection/Macros/Apply/Discourage implicit measures.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Discourage implicit measures", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Encoding hints.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | // https://community.powerbi.com/t5/Desktop/Vertipaq-Engine-VALUE-vs-HASH/m-p/690874#M333145 16 | 17 | foreach (var c in Model.AllColumns) 18 | { 19 | if (c.DataType == DataType.DateTime || c.DataType == DataType.Decimal || c.DataType == DataType.Double || c.DataType == DataType.Int64) 20 | { 21 | c.EncodingHint = EncodingHintType.Value; 22 | } 23 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Encoding hints.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Encoding hints", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Hide from-side relationship columns.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach (var r in Model.Relationships) 16 | { 17 | var c = r.FromColumn.Name; 18 | var t = r.FromTable.Name; 19 | Model.Tables[t].Columns[c].IsHidden = true; 20 | } -------------------------------------------------------------------------------- /Collection/Macros/Apply/Hide from-side relationship columns.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Apply\\Hide from-side relationship columns", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/'Avg of' measure(s).csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach(var c in Selected.Columns) 16 | { 17 | c.Table.AddMeasure( 18 | name: $"Avg of {c.Name}", 19 | expression: $"AVERAGE( {c.DaxObjectFullName} )", 20 | displayFolder: string.IsNullOrEmpty(c.DisplayFolder) ? $"Avg of Measures" : $"{c.DisplayFolder}\\Avg of Measures" 21 | ); 22 | c.IsHidden = true; 23 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/'Avg of' measure(s).json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Create\\'Avg of' measure(s)", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/'Max of' measure(s).csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach(var c in Selected.Columns) 16 | { 17 | c.Table.AddMeasure( 18 | name: $"Max of {c.Name}", 19 | expression: $"MAX( {c.DaxObjectFullName} )", 20 | displayFolder: string.IsNullOrEmpty(c.DisplayFolder) ? $"Max of Measures" : $"{c.DisplayFolder}\\Max of Measures" 21 | ); 22 | c.IsHidden = true; 23 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/'Max of' measure(s).json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Create\\'Max of' measure(s)", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/'Min of' measure(s).csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach(var c in Selected.Columns) 16 | { 17 | c.Table.AddMeasure( 18 | name: $"Min of {c.Name}", 19 | expression: $"MIN( {c.DaxObjectFullName} )", 20 | displayFolder: string.IsNullOrEmpty(c.DisplayFolder) ? $"Min of Measures" : $"{c.DisplayFolder}\\Min of Measures" 21 | ); 22 | c.IsHidden = true; 23 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/'Min of' measure(s).json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Create\\'Min of' measure(s)", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/'Sum of' measure(s).csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach(var c in Selected.Columns) 16 | { 17 | c.Table.AddMeasure( 18 | name: $"Sum of {c.Name}", 19 | expression: $"SUM( {c.DaxObjectFullName} )", 20 | displayFolder: string.IsNullOrEmpty(c.DisplayFolder) ? $"Sum of Measures" : $"{c.DisplayFolder}\\Sum of Measures" 21 | ); 22 | c.IsHidden = true; 23 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/'Sum of' measure(s).json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Create\\'Sum of' measure(s)", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/'dCnt of' measure(s).csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach(var c in Selected.Columns) 16 | { 17 | c.Table.AddMeasure( 18 | name: $"dCnt of {c.Name}", 19 | expression: $"DISTINCTCOUNT( {c.DaxObjectFullName} )", 20 | displayFolder: string.IsNullOrEmpty(c.DisplayFolder) ? $"dCnt of Measures" : $"{c.DisplayFolder}\\dCnt of Measures" 21 | ); 22 | c.IsHidden = true; 23 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/'dCnt of' measure(s).json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Create\\'dCnt of' measure(s)", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/Calculation group measures.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | // Check measure(s) are selected 16 | if (!Selected.Measures.Any()) 17 | { 18 | ScriptHelper.Error("No measure(s) Selected."); 19 | return; 20 | } 21 | 22 | // Get calculation group table 23 | var ts = Model.Tables.Where(x => x.ObjectType == (ObjectType.CalculationGroupTable)).Where(x => x.Name != "getFormatString"); 24 | var t = null as CalculationGroupTable; 25 | if (ts.Any()) 26 | { 27 | t = ScriptHelper.SelectTable(ts, label: "Select calculation group table:") as CalculationGroupTable; 28 | if (t == null) { return; } 29 | } 30 | else 31 | { 32 | ScriptHelper.Error("No calculation group tables in the Model."); 33 | } 34 | 35 | // Get calculation group's calculation items data column 36 | var cs = t.DataColumns.Where(x => x.SourceColumn == "Name"); 37 | var c = null as DataColumn; 38 | if (cs.Count() != 1) 39 | { 40 | ScriptHelper.Warning("Cannot identify calculation items column."); 41 | c = ScriptHelper.SelectColumn(t, label: "Select calculation items column:") as DataColumn; 42 | if (c == null) { return; } 43 | } 44 | else 45 | { 46 | c = cs.First(); 47 | } 48 | 49 | // Update model's compatibility level if required 50 | if (Model.Database.CompatibilityLevel < 1601) 51 | { 52 | Model.Database.CompatibilityLevel = 1601; 53 | } 54 | 55 | // Create helper calculation group table, deleting existing table if it exists 56 | if(Model.Tables.Where(x => x.Name == "getFormatString").Any()) { Model.Tables["getFormatString"].Delete(); } 57 | var cg = Model.AddCalculationGroup( 58 | name: "getFormatString" 59 | ); 60 | cg.AddCalculationItem( 61 | name: "getFormatString", 62 | expression: "SELECTEDMEASUREFORMATSTRING()" 63 | ); 64 | cg.IsHidden = true; 65 | 66 | // Set template measure expression 67 | var templateMeasureExpression = @" 68 | CALCULATE( 69 | , 70 | = """" 71 | )"; 72 | 73 | // Set template measure format string expression 74 | var templateMeasureFormatStringExpression = @" 75 | VAR CalculationItemFormat = 76 | CALCULATE( 77 | CALCULATE( 78 | , 79 | = """" 80 | ), 81 | 'getFormatString'[Name] = ""getFormatString"" 82 | ) 83 | VAR MeasureFormat = 84 | CALCULATE( 85 | , 86 | 'getFormatString'[Name] = ""getFormatString"" 87 | ) 88 | RETURN 89 | CONVERT( 90 | COALESCE(CalculationItemFormat, MeasureFormat), 91 | STRING 92 | )"; 93 | 94 | // Cycle through selected measures 95 | foreach (var m in Selected.Measures) 96 | { 97 | 98 | // If current measure was derived from a calculation group then continue to the next measure 99 | bool isCalculationGroupMeasure = Convert.ToBoolean(m.GetAnnotation("isCalculationGroupMeasure")); 100 | if (isCalculationGroupMeasure) { continue; } 101 | 102 | // Cycle through calculation group items 103 | foreach (var i in t.CalculationItems) 104 | { 105 | 106 | // Define core measure properties 107 | var measureName = m.Name + " " + i.Name; 108 | var measureExpression = templateMeasureExpression 109 | .Replace("", m.DaxObjectName) 110 | .Replace("", c.DaxObjectFullName) 111 | .Replace("", i.Name); 112 | var measureDisplayFolder = m.DisplayFolder + "\\› " + t.Name + "\\› " + m.Name; 113 | 114 | // Add measure to the model, deleting existing measure if it exists 115 | foreach (var mm in Model.AllMeasures.Where(x => x.Name == measureName).ToList()) { mm.Delete(); } 116 | var nm = m.Table.AddMeasure( 117 | name: measureName, 118 | expression: measureExpression, 119 | displayFolder: measureDisplayFolder 120 | ); 121 | 122 | // Flag the new measure as derived from a calculation group 123 | nm.SetAnnotation("isCalculationGroupMeasure", "true"); 124 | 125 | // Set the new measure's format string expression to derive from either the calculation group item or the source measure 126 | nm.FormatStringExpression = templateMeasureFormatStringExpression 127 | .Replace("", m.DaxObjectName) 128 | .Replace("", c.DaxObjectFullName) 129 | .Replace("", i.Name); 130 | 131 | // Use the descriptions from both the source measure and calculation group item 132 | if (!string.IsNullOrEmpty(m.Description) && !string.IsNullOrEmpty(i.Description)) 133 | { 134 | nm.Description = m.Description + "\n\r--\n\r" + i.Description; 135 | } 136 | else 137 | { 138 | nm.Description = m.Description + i.Description; 139 | } 140 | 141 | } 142 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/Calculation group measures.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Create\\Calculation group measures", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/Field parameter.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | // Before running the script, select the measures or columns that you 16 | // would like to use as field parameters (hold down CTRL to select multiple 17 | // objects). Also, you may change the name of the field parameter table 18 | // below. NOTE: If used against Power BI Desktop, you must enable unsupported 19 | // features under File > Preferences (TE2) or Tools > Preferences (TE3). 20 | var name = "Parameter"; 21 | 22 | if(Selected.Columns.Count == 0 && Selected.Measures.Count == 0) throw new Exception("No columns or measures selected!"); 23 | 24 | // Construct the DAX for the calculated table based on the current selection: 25 | var objects = Selected.Columns.Any() ? Selected.Columns.Cast() : Selected.Measures; 26 | var dax = "{\n " + string.Join(",\n ", objects.Select((c,i) => string.Format("(\"{0}\", NAMEOF('{1}'[{0}]), {2})", c.Name, c.Table.Name, i))) + "\n}"; 27 | 28 | // Add the calculated table to the model: 29 | var table = Model.AddCalculatedTable(name, dax); 30 | 31 | // In TE2 columns are not created automatically from a DAX expression, so 32 | // we will have to add them manually: 33 | var te2 = table.Columns.Count == 0; 34 | var nameColumn = te2 ? table.AddCalculatedTableColumn(name, "[Value1]") : table.Columns["Value1"] as CalculatedTableColumn; 35 | var fieldColumn = te2 ? table.AddCalculatedTableColumn(name + " Fields", "[Value2]") : table.Columns["Value2"] as CalculatedTableColumn; 36 | var orderColumn = te2 ? table.AddCalculatedTableColumn(name + " Order", "[Value3]") : table.Columns["Value3"] as CalculatedTableColumn; 37 | 38 | if(!te2) { 39 | // Rename the columns that were added automatically in TE3: 40 | nameColumn.IsNameInferred = false; 41 | nameColumn.Name = name; 42 | fieldColumn.IsNameInferred = false; 43 | fieldColumn.Name = name + " Fields"; 44 | orderColumn.IsNameInferred = false; 45 | orderColumn.Name = name + " Order"; 46 | } 47 | // Set remaining properties for field parameters to work 48 | // See: https://twitter.com/markbdi/status/1526558841172893696 49 | nameColumn.SortByColumn = orderColumn; 50 | nameColumn.GroupByColumns.Add(fieldColumn); 51 | fieldColumn.SortByColumn = orderColumn; 52 | fieldColumn.SetExtendedProperty("ParameterMetadata", "{\"version\":3,\"kind\":2}", ExtendedPropertyType.Json); 53 | fieldColumn.IsHidden = true; 54 | orderColumn.IsHidden = true; -------------------------------------------------------------------------------- /Collection/Macros/Create/Field parameter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Create\\Field parameter", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/Parent-child hierarchy.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var t = Selected.Table; 16 | 17 | // check if annotation already exists because first phase has already been completed 18 | if (t.GetAnnotation("Parent-child hierarchy") == null) 19 | { 20 | 21 | // get EntityKey from user 22 | var cEntityKey = ScriptHelper.SelectColumn( 23 | label: "Select 'EntityKey' column:", 24 | table: t 25 | ); 26 | if (cEntityKey == null) { return; } 27 | 28 | // get EntityParentKey from user 29 | var cEntityParentKey = ScriptHelper.SelectColumn( 30 | label: "Select 'EntityParentKey' column:", 31 | table: t 32 | ); 33 | if (cEntityParentKey == null) { return; } 34 | 35 | // get EntityName from user 36 | var cEntityName = ScriptHelper.SelectColumn( 37 | label: "Select 'EntityName' column:", 38 | table: t 39 | ); 40 | if (cEntityName == null) { return; } 41 | 42 | // set datatype for use in PATHITEM() 43 | var dataTypeEntityKey = String.Empty; 44 | if (cEntityKey.DataType == DataType.Int64) { dataTypeEntityKey = "INTEGER"; } else { dataTypeEntityKey = "TEXT"; } 45 | 46 | // create ParentSafe column 47 | var nameParentSafe = cEntityKey.Name + "ParentSafe"; 48 | var daxParentSafe = @" 49 | VAR ParentId = 50 | VAR Result = 51 | IF( 52 | NOT ISEMPTY( FILTER( VALUES( ), = ParentId ) ), 53 | ParentId 54 | ) 55 | RETURN 56 | Result" 57 | .Replace("", cEntityKey.DaxObjectFullName) 58 | .Replace("", cEntityParentKey.DaxObjectFullName); 59 | foreach (var c in t.Columns.Where(x => x.Name == nameParentSafe).ToList()) { c.Delete(); } 60 | var cParentSafe = t.AddCalculatedColumn( 61 | name: nameParentSafe, 62 | expression: daxParentSafe 63 | ); 64 | cParentSafe.IsHidden = true; 65 | 66 | // create ParentMissing column 67 | var nameParentMissing = cEntityKey.Name + "ParentMissing"; 68 | var daxParentMissing = @" 69 | NOT == " 70 | .Replace("", cEntityParentKey.DaxObjectFullName) 71 | .Replace("", cParentSafe.DaxObjectFullName); 72 | 73 | foreach (var c in t.Columns.Where(x => x.Name == nameParentMissing).ToList()) { c.Delete(); } 74 | var cParentMissing = t.AddCalculatedColumn( 75 | name: nameParentMissing, 76 | expression: daxParentMissing 77 | ); 78 | cParentMissing.IsHidden = true; 79 | 80 | // create Path column 81 | var namePath = cEntityKey.Name + "Path"; 82 | var daxPath = @" 83 | PATH( , )" 84 | .Replace("", cEntityKey.DaxObjectFullName) 85 | .Replace("", cParentSafe.DaxObjectFullName); 86 | foreach (var c in t.Columns.Where(x => x.Name == namePath).ToList()) { c.Delete(); } 87 | var cPath = t.AddCalculatedColumn( 88 | name: namePath, 89 | expression: daxPath 90 | ); 91 | cPath.IsHidden = true; 92 | 93 | // create Detached column 94 | var nameDetached = cEntityKey.Name + "Detached"; 95 | var daxDetached = @" 96 | VAR LevelKey = PATHITEM( , 1, ) 97 | VAR LevelParent = 98 | LOOKUPVALUE( 99 | , 100 | , 101 | LevelKey 102 | ) 103 | VAR Result = NOT LevelParent = BLANK( ) 104 | RETURN 105 | Result" 106 | .Replace("", cPath.DaxObjectFullName) 107 | .Replace("", cEntityParentKey.DaxObjectFullName) 108 | .Replace("", cEntityKey.DaxObjectFullName) 109 | .Replace("", dataTypeEntityKey); 110 | foreach (var c in t.Columns.Where(x => x.Name == nameDetached).ToList()) { c.Delete(); } 111 | var cDetached = t.AddCalculatedColumn( 112 | name: nameDetached, 113 | expression: daxDetached 114 | ); 115 | cDetached.IsHidden = true; 116 | 117 | // create Depth column 118 | var nameDepth = cEntityKey.Name + "Depth"; 119 | var daxDepth = @" 120 | IF( 121 | NOT , 122 | PATHLENGTH( ) 123 | )" 124 | .Replace("", cDetached.DaxObjectFullName) 125 | .Replace("", cPath.DaxObjectFullName); 126 | foreach (var c in t.Columns.Where(x => x.Name == nameDepth).ToList()) { c.Delete(); } 127 | var cDepth = t.AddCalculatedColumn( 128 | name: nameDepth, 129 | expression: daxDepth 130 | ); 131 | cDepth.IsHidden = true; 132 | 133 | // save details to annotation 134 | t.SetAnnotation("Parent-child hierarchy", $"{cEntityKey.Name}|{cEntityParentKey.Name}|{cEntityName.Name}"); 135 | 136 | // warn to save the model and re-run 137 | ScriptHelper.Warning("Please save the model to the server and re-run the script to continue."); 138 | 139 | // if a power bi desktop model, warn to refresh 140 | if (Model.Database.ServerVersion.Contains("Power BI Desktop")) 141 | { 142 | ScriptHelper.Warning("Power BI Desktop detected. Please also manually refresh when prompted."); 143 | } 144 | 145 | // exit script 146 | return; 147 | 148 | } 149 | else 150 | { 151 | 152 | // trigger calculation refresh of the table if NOT a power bi desktop model 153 | if (!Model.Database.ServerVersion.Contains("Power BI Desktop")) 154 | { 155 | ScriptHelper.ExecuteCommand( 156 | tmslOrXmla: $"{{ \"refresh\": {{ \"type\": \"calculate\", \"objects\": [ {{ \"database\": \"{Model.Database.Name}\", \"table\": \"{t.Name}\" }} ] }} }}", 157 | isXmla: false 158 | ); 159 | } 160 | 161 | // get details from annotation 162 | var annotatedColumnNames = t.GetAnnotation("Parent-child hierarchy").Split('|'); 163 | var cEntityKey = t.Columns[annotatedColumnNames[0]]; 164 | var cEntityParentKey = t.Columns[annotatedColumnNames[1]]; 165 | var cEntityName = t.Columns[annotatedColumnNames[2]]; 166 | 167 | // set datatype for use in PATHITEM() 168 | var dataTypeEntityKey = String.Empty; 169 | if (cEntityKey.DataType == DataType.Int64) { dataTypeEntityKey = "INTEGER"; } else { dataTypeEntityKey = "TEXT"; } 170 | 171 | // set previously created objects 172 | var cParentSafe = t.Columns[cEntityKey.Name + "ParentSafe"]; 173 | var cParentMissing = t.Columns[cEntityKey.Name + "ParentMissing"]; 174 | var cPath = t.Columns[cEntityKey.Name + "Path"]; 175 | var cDetached = t.Columns[cEntityKey.Name + "Detached"]; 176 | var cDepth = t.Columns[cEntityKey.Name + "Depth"]; 177 | 178 | // create hierarchy 179 | var nameLevels = cEntityName.Name + " Hierarchy"; 180 | foreach (var h in t.Hierarchies.Where(x => x.Name == nameLevels).ToList()) { h.Delete(); } 181 | var hLevels = t.AddHierarchy( 182 | name: nameLevels 183 | ); 184 | 185 | // setup hashset for generating daxBrowseDepth measure expression 186 | var daxBrowseDepthHashSet = new HashSet(); 187 | 188 | // get max hierarchy depth 189 | var maxDepth = Convert.ToInt64(ScriptHelper.EvaluateDax($"VAR maxValue = MAX( {cDepth.DaxObjectFullName} ) RETURN IF( maxValue = BLANK( ), 0, maxValue )")); 190 | 191 | // loop for each level and create columns 192 | for (int i = 1; i <= maxDepth; i++) 193 | { 194 | 195 | // set column details 196 | var nameLevel = cEntityName.Name + $" Level {i}"; 197 | var daxLevel = @" 198 | IF( 199 | NOT , 200 | VAR LevelNumber = 201 | VAR LevelKey = 202 | PATHITEM( , LevelNumber, ) 203 | VAR Result = 204 | LOOKUPVALUE( , , LevelKey ) 205 | RETURN 206 | Result 207 | )" 208 | .Replace("", cDetached.DaxObjectFullName) 209 | .Replace("", i.ToString()) 210 | .Replace("", cPath.DaxObjectFullName) 211 | .Replace("", dataTypeEntityKey) 212 | .Replace("", cEntityName.DaxObjectFullName) 213 | .Replace("", cEntityKey.DaxObjectFullName); 214 | 215 | // remove column if it exists 216 | foreach (var c in t.Columns.Where(x => x.Name == nameLevel).ToList()) { c.Delete(); } 217 | 218 | // add hierarchy column 219 | var cLevel = t.AddCalculatedColumn( 220 | name: nameLevel, 221 | expression: daxLevel 222 | ); 223 | cLevel.IsHidden = true; 224 | 225 | // add column to hierarchy 226 | hLevels.AddLevel( 227 | column: cLevel, 228 | levelName: nameLevel 229 | ); 230 | 231 | // add column to hashset 232 | daxBrowseDepthHashSet.Add( 233 | $"ISINSCOPE( {cLevel.DaxObjectFullName} )" 234 | ); 235 | 236 | } 237 | 238 | // create BrowseDepth measure 239 | var nameBrowseDepth = cEntityName.Name + " Browse Depth"; 240 | var daxBrowseDepth = string.Join(Environment.NewLine + "+ ", daxBrowseDepthHashSet); 241 | foreach (var m in t.Measures.Where(x => x.Name == nameBrowseDepth).ToList()) { m.Delete(); } 242 | var mBrowseDepth = t.AddMeasure( 243 | name: nameBrowseDepth, 244 | expression: daxBrowseDepth 245 | ); 246 | mBrowseDepth.IsHidden = true; 247 | 248 | // remove annotation 249 | t.RemoveAnnotation("Parent-child hierarchy"); 250 | 251 | } -------------------------------------------------------------------------------- /Collection/Macros/Create/Parent-child hierarchy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Create\\Parent-child hierarchy", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Table" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/DataType/Decimal.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach (var c in Selected.Columns) 16 | { 17 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 18 | if (c.DataType != DataType.Decimal && c.DataType != DataType.Double && c.DataType != DataType.Int64) { continue; } 19 | c.ChangedProperties = "DataType"; 20 | c.DataType = DataType.Decimal; 21 | } -------------------------------------------------------------------------------- /Collection/Macros/DataType/Decimal.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\DataType\\Decimal", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/DataType/Double.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach (var c in Selected.Columns) 16 | { 17 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 18 | if (c.DataType != DataType.Decimal && c.DataType != DataType.Double && c.DataType != DataType.Int64) { continue; } 19 | c.ChangedProperties = "DataType"; 20 | c.DataType = DataType.Double; 21 | } -------------------------------------------------------------------------------- /Collection/Macros/DataType/Double.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\DataType\\Double", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/DataType/Int64.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach (var c in Selected.Columns) 16 | { 17 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 18 | if (c.DataType != DataType.Decimal && c.DataType != DataType.Double && c.DataType != DataType.Int64) { continue; } 19 | c.ChangedProperties = "DataType"; 20 | c.DataType = DataType.Int64; 21 | } -------------------------------------------------------------------------------- /Collection/Macros/DataType/Int64.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\DataType\\Int64", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format all DAX vTE3.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var useShortFormat = false; 16 | var insertSpaceAfterFunctionName = false; 17 | var insertLineBreakOnFirstLine = true; 18 | 19 | Func GetFormattedDax = (string daxInput) => 20 | { 21 | var formattedDax = ScriptHelper.FormatDax(daxInput, shortFormat: useShortFormat, skipSpaceAfterFunctionName: !insertSpaceAfterFunctionName).Replace("( )", "()"); 22 | return insertLineBreakOnFirstLine ? "\r\n" + formattedDax : formattedDax; 23 | }; 24 | 25 | foreach (var m in Model.AllMeasures) 26 | { 27 | if (!String.IsNullOrEmpty(m.Expression)) { m.Expression = GetFormattedDax(m.Expression); } else { m.Delete(); } 28 | } 29 | 30 | foreach (var i in Model.AllCalculationItems) 31 | { 32 | if (!String.IsNullOrEmpty(i.Expression)) { i.Expression = GetFormattedDax(i.Expression); } else { i.Delete(); } 33 | if (!String.IsNullOrEmpty(i.FormatStringExpression)) { i.FormatStringExpression = GetFormattedDax(i.FormatStringExpression); } 34 | } 35 | 36 | foreach (var t in Model.Tables.OfType().Where(x => !x.Name.StartsWith("DateTableTemplate_") && !x.Name.StartsWith("LocalDateTable_"))) 37 | { 38 | if (!String.IsNullOrEmpty(t.Expression)) { t.Expression = GetFormattedDax(t.Expression); } else { t.Delete(); } 39 | } 40 | 41 | foreach (var c in Model.AllColumns.OfType().Where(x => !x.DaxTableName.StartsWith("DateTableTemplate_") && !x.DaxTableName.StartsWith("LocalDateTable_"))) 42 | { 43 | if (!String.IsNullOrEmpty(c.Expression)) { c.Expression = GetFormattedDax(c.Expression); } else { c.Delete(); } 44 | } -------------------------------------------------------------------------------- /Collection/Macros/Format all DAX vTE3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format all DAX vTE3", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/$ English (New Zealand).csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var formatString = "\\$#,0.00;-\\$#,0.00;\\$#,0.00"; 16 | 17 | foreach (var m in Selected.Measures) 18 | { 19 | if (m.DataType != DataType.Decimal && m.DataType != DataType.Double && m.DataType != DataType.Int64 && m.DataType != DataType.Variant) { continue; } 20 | m.FormatString = formatString; 21 | } 22 | 23 | foreach (var c in Selected.Columns) 24 | { 25 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 26 | if (c.DataType != DataType.Decimal && c.DataType != DataType.Double && c.DataType != DataType.Int64) { continue; } 27 | c.FormatString = formatString; 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/$ English (New Zealand).json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\$ English (New Zealand)", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/$ English (United States).csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var formatString = "\\$#,0.00;(\\$#,0.00);\\$#,0.00"; 16 | 17 | foreach (var m in Selected.Measures) 18 | { 19 | if (m.DataType != DataType.Decimal && m.DataType != DataType.Double && m.DataType != DataType.Int64 && m.DataType != DataType.Variant) { continue; } 20 | m.FormatString = formatString; 21 | } 22 | 23 | foreach (var c in Selected.Columns) 24 | { 25 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 26 | if (c.DataType != DataType.Decimal && c.DataType != DataType.Double && c.DataType != DataType.Int64) { continue; } 27 | c.FormatString = formatString; 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/$ English (United States).json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\$ English (United States)", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Custom display units.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | #r "Microsoft.VisualBasic" 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using TabularEditor; 10 | using TabularEditor.TOMWrapper; 11 | using TabularEditor.TOMWrapper.Utils; 12 | using TabularEditor.UI; 13 | using TabularEditor.Scripting; 14 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 15 | using Microsoft.VisualBasic; 16 | 17 | // Get the number of decimal places from the user 18 | var decimalPlaces = Interaction.InputBox( 19 | Prompt: "Provide the number of decimal places to be displayed.", 20 | Title: "Set decimal places:", 21 | DefaultResponse: "1" 22 | ); 23 | 24 | if(decimalPlaces == String.Empty) { return; } 25 | 26 | // Update model's compatibility level if required 27 | if (Model.Database.CompatibilityLevel < 1601) 28 | { 29 | Model.Database.CompatibilityLevel = 1601; 30 | } 31 | 32 | // Set template measure format string expression 33 | var templateMeasureFormatStringExpression = @" 34 | VAR DecimalPlaces = 35 | VAR CurrentValue = SELECTEDMEASURE() 36 | VAR ValueLog = 37 | IF( 38 | CurrentValue <> 0, 39 | INT( LOG( ABS( CurrentValue ), 1000 ) ), 40 | 0 41 | ) 42 | VAR Commas = REPT( "","", MIN( 4, ValueLog ) ) 43 | VAR Suffix = 44 | SWITCH( 45 | ValueLog, 46 | 0, """", 47 | 1, ""K"", 48 | 2, ""M"", 49 | 3, ""bn"", 50 | ""T"" 51 | ) 52 | VAR Decimals = ""."" & REPT( 0, DecimalPlaces ) 53 | RETURN 54 | IF( 55 | DecimalPlaces > 0, 56 | ""#,##0"" & Commas & Decimals & Suffix 57 | & "";-#,##0"" & Commas & Decimals & Suffix 58 | & "";-"", 59 | ""#,##0"" & Commas & Suffix 60 | & "";-#,##0"" & Commas & Suffix 61 | & "";-"" 62 | )"; 63 | 64 | // Apply format string expression 65 | foreach (var m in Selected.Measures) 66 | { 67 | m.FormatString = string.Empty; 68 | m.FormatStringExpression = templateMeasureFormatStringExpression 69 | .Replace("", decimalPlaces); 70 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Custom display units.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\Custom display units", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Custom_ #,#;(#,#);0.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var formatString = "#,#;(#,#);0"; 16 | 17 | foreach (var m in Selected.Measures) 18 | { 19 | if (m.DataType != DataType.Decimal && m.DataType != DataType.Double && m.DataType != DataType.Int64 && m.DataType != DataType.Variant) { continue; } 20 | m.FormatString = formatString; 21 | } 22 | 23 | foreach (var c in Selected.Columns) 24 | { 25 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 26 | if (c.DataType != DataType.Decimal && c.DataType != DataType.Double && c.DataType != DataType.Int64) { continue; } 27 | c.FormatString = formatString; 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Custom_ #,#;(#,#);0.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\Custom: #,#;(#,#);0", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Custom_ #,0.00;(#,0.00);0.00.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var formatString = "#,0.00;(#,0.00);0.00"; 16 | 17 | foreach (var m in Selected.Measures) 18 | { 19 | if (m.DataType != DataType.Decimal && m.DataType != DataType.Double && m.DataType != DataType.Int64 && m.DataType != DataType.Variant) { continue; } 20 | m.FormatString = formatString; 21 | } 22 | 23 | foreach (var c in Selected.Columns) 24 | { 25 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 26 | if (c.DataType != DataType.Decimal && c.DataType != DataType.Double && c.DataType != DataType.Int64) { continue; } 27 | c.FormatString = formatString; 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Custom_ #,0.00;(#,0.00);0.00.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\Custom: #,0.00;(#,0.00);0.00", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Decimal number.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var formatString = "#,0.00"; 16 | 17 | foreach (var m in Selected.Measures) 18 | { 19 | if (m.DataType != DataType.Decimal && m.DataType != DataType.Double && m.DataType != DataType.Int64 && m.DataType != DataType.Variant) { continue; } 20 | m.FormatString = formatString; 21 | } 22 | 23 | foreach (var c in Selected.Columns) 24 | { 25 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 26 | if (c.DataType != DataType.Decimal && c.DataType != DataType.Double && c.DataType != DataType.Int64) { continue; } 27 | c.FormatString = formatString; 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Decimal number.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\Decimal number", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Default.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | string GetFormatString(DataType dataType) 16 | { 17 | switch (dataType) 18 | { 19 | case DataType.Boolean: 20 | return "\"TRUE\";\"TRUE\";\"FALSE\""; 21 | case DataType.DateTime: 22 | return "General Date"; 23 | case DataType.Decimal: 24 | return string.Empty; 25 | // return "\\$#,0.###############;-\\$#,0.###############;\\$#,0.###############"; 26 | // return "$ #,##0.00"; // TE3 version 27 | case DataType.Double: 28 | return string.Empty; 29 | case DataType.Int64: 30 | return "0"; 31 | case DataType.String: 32 | return string.Empty; 33 | case DataType.Variant: 34 | return string.Empty; 35 | default: 36 | return string.Empty; 37 | } 38 | } 39 | 40 | foreach (var m in Selected.Measures) 41 | { 42 | m.FormatString = GetFormatString(m.DataType); 43 | m.FormatStringExpression = string.Empty; 44 | } 45 | 46 | foreach (var c in Selected.Columns) 47 | { 48 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 49 | c.FormatString = GetFormatString(c.DataType); 50 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Default.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\Default", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Long Date.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var formatString = "Long Date"; 16 | 17 | foreach (var m in Selected.Measures) 18 | { 19 | if (m.DataType != DataType.DateTime && m.DataType != DataType.Variant) { continue; } 20 | m.FormatString = formatString; 21 | } 22 | 23 | foreach (var c in Selected.Columns) 24 | { 25 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 26 | if (c.DataType != DataType.DateTime) { continue; } 27 | c.FormatString = formatString; 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Long Date.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\Long Date", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Long Time.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var formatString = "Long Time"; 16 | 17 | foreach (var m in Selected.Measures) 18 | { 19 | if (m.DataType != DataType.DateTime && m.DataType != DataType.Variant) { continue; } 20 | m.FormatString = formatString; 21 | } 22 | 23 | foreach (var c in Selected.Columns) 24 | { 25 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 26 | if (c.DataType != DataType.DateTime) { continue; } 27 | c.FormatString = formatString; 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Long Time.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\Long Time", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Percentage.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | // var formatString = "0.00%;-0.00%;0.00%"; 16 | var formatString = "#,0.00%;-#,0.00%;#,0.00%"; 17 | 18 | foreach (var m in Selected.Measures) 19 | { 20 | if (m.DataType != DataType.Decimal && m.DataType != DataType.Double && m.DataType != DataType.Int64 && m.DataType != DataType.Variant) { continue; } 21 | m.FormatString = formatString; 22 | } 23 | 24 | foreach (var c in Selected.Columns) 25 | { 26 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 27 | if (c.DataType != DataType.Decimal && c.DataType != DataType.Double && c.DataType != DataType.Int64) { continue; } 28 | c.FormatString = formatString; 29 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Percentage.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\Percentage", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Short Date.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var formatString = "Short Date"; 16 | 17 | foreach (var m in Selected.Measures) 18 | { 19 | if (m.DataType != DataType.DateTime && m.DataType != DataType.Variant) { continue; } 20 | m.FormatString = formatString; 21 | } 22 | 23 | foreach (var c in Selected.Columns) 24 | { 25 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 26 | if (c.DataType != DataType.DateTime) { continue; } 27 | c.FormatString = formatString; 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Short Date.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\Short Date", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Short Time.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var formatString = "Short Time"; 16 | 17 | foreach (var m in Selected.Measures) 18 | { 19 | if (m.DataType != DataType.DateTime && m.DataType != DataType.Variant) { continue; } 20 | m.FormatString = formatString; 21 | } 22 | 23 | foreach (var c in Selected.Columns) 24 | { 25 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 26 | if (c.DataType != DataType.DateTime) { continue; } 27 | c.FormatString = formatString; 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Short Time.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\Short Time", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Whole number.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var formatString = "#,0"; 16 | 17 | foreach (var m in Selected.Measures) 18 | { 19 | if (m.DataType != DataType.Decimal && m.DataType != DataType.Double && m.DataType != DataType.Int64 && m.DataType != DataType.Variant) { continue; } 20 | m.FormatString = formatString; 21 | } 22 | 23 | foreach (var c in Selected.Columns) 24 | { 25 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 26 | if (c.DataType != DataType.Decimal && c.DataType != DataType.Double && c.DataType != DataType.Int64) { continue; } 27 | c.FormatString = formatString; 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/Whole number.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\Whole number", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/d_m_yyyy.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | var formatString = "d/mm/yyyy"; 16 | 17 | foreach (var m in Selected.Measures) 18 | { 19 | if (m.DataType != DataType.DateTime && m.DataType != DataType.Variant) { continue; } 20 | m.FormatString = formatString; 21 | } 22 | 23 | foreach (var c in Selected.Columns) 24 | { 25 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 26 | if (c.DataType != DataType.DateTime) { continue; } 27 | c.FormatString = formatString; 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Format/d_m_yyyy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Format\\d/m/yyyy", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Launch/ALM Toolkit.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | using System.Diagnostics; 15 | 16 | // https://github.com/TabularEditor/TabularEditor3/issues/249#issuecomment-939848828 17 | 18 | var connectionInfo = Model.Database.TOMDatabase.Server.ConnectionInfo; 19 | string server = null; 20 | string database = null; 21 | 22 | if (connectionInfo.Port == null) 23 | { 24 | server = connectionInfo.Server; 25 | database = Model.Database.Name; 26 | } 27 | else 28 | { 29 | server = connectionInfo.Server + ":" + connectionInfo.Port; 30 | database = Model.Database.ID; 31 | } 32 | 33 | Process.Start("C:\\Program Files\\Power BI ALM Toolkit\\Power BI ALM Toolkit\\AlmToolkit.exe", "\"" + server + "\" \"" + database + "\""); -------------------------------------------------------------------------------- /Collection/Macros/Launch/ALM Toolkit.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Launch\\ALM Toolkit", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Launch/Analyze In Excel.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | using System.Diagnostics; 15 | 16 | // https://github.com/TabularEditor/TabularEditor3/issues/249#issuecomment-939848828 17 | // Works with version 1.1.3 when MSOLAP isn't separately installed as per Analyze In Excel warning. 18 | 19 | var connectionInfo = Model.Database.TOMDatabase.Server.ConnectionInfo; 20 | string server = null; 21 | string database = null; 22 | 23 | if (connectionInfo.Port == null) 24 | { 25 | server = connectionInfo.Server; 26 | database = Model.Database.Name; 27 | } 28 | else 29 | { 30 | server = connectionInfo.Server + ":" + connectionInfo.Port; 31 | database = Model.Database.ID; 32 | } 33 | 34 | Process.Start("C:\\Program Files (x86)\\Sqlbi\\Analyze in Excel for Power BI Desktop\\AnalyzeInExcel.exe", "--server=\"" + server + "\" --database=\"" + database + "\" --telemetry"); -------------------------------------------------------------------------------- /Collection/Macros/Launch/Analyze In Excel.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Launch\\Analyze In Excel", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Launch/DAX Studio.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | using System.Diagnostics; 15 | 16 | // https://github.com/TabularEditor/TabularEditor3/issues/249#issuecomment-939848828 17 | 18 | var connectionInfo = Model.Database.TOMDatabase.Server.ConnectionInfo; 19 | string server = null; 20 | string database = null; 21 | 22 | if (connectionInfo.Port == null) 23 | { 24 | server = connectionInfo.Server; 25 | database = Model.Database.Name; 26 | } 27 | else 28 | { 29 | server = connectionInfo.Server + ":" + connectionInfo.Port; 30 | database = Model.Database.ID; 31 | } 32 | 33 | Process.Start("C:\\Program Files\\DAX Studio\\DaxStudio.exe", "-s \"" + server + "\" -d \"" + database + "\""); -------------------------------------------------------------------------------- /Collection/Macros/Launch/DAX Studio.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Launch\\DAX Studio", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Preview columns and measures.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | //https://data-goblins.com/power-bi/tabular-editor-query-data 16 | 17 | // Instructions 18 | // ------------ 19 | // 1. Save this script as a macro with a context of 'Column' and 'Measure' 20 | // 2. Configure a keyboard shortcut for the macro (i.e. ALT + C) if using Tabular Editor 3 21 | // 3. Select any combination of columns & measures related in the model & run the script 22 | // 4. The output will show you the evaluation result for all selected objects, presuming evaluation is valid 23 | 24 | // Get column names 25 | var _ColumnsList = new List(); 26 | foreach ( var _SelectedColumn in Selected.Columns ) 27 | { 28 | _ColumnsList.Add(_SelectedColumn.DaxObjectFullName); 29 | } 30 | string _Columns = String.Join(",", _ColumnsList ); 31 | 32 | // Get measure names 33 | var _MeasuresList = new List(); 34 | var _MeasuresOnlyList = new List(); 35 | foreach ( var _SelectedMeasure in Selected.Measures ) 36 | { 37 | // Create a syntax for evaluating objects when measures + columns are selected 38 | _MeasuresList.Add( @"""@" + _SelectedMeasure.Name + @"""" ); 39 | _MeasuresList.Add(_SelectedMeasure.DaxObjectFullName); 40 | 41 | // Create a syntax for evaluating objects when only measures are selected 42 | _MeasuresOnlyList.Add( 43 | "\nADDCOLUMNS (\n{" + 44 | @"""" + _SelectedMeasure.Name + @"""" + 45 | "},\n" + 46 | @"""" + "Result" + @"""" + 47 | ",\n" + 48 | _SelectedMeasure.DaxObjectFullName + ")"); 49 | } 50 | string _Measures = String.Join(",", _MeasuresList ); 51 | 52 | // Results differ depending on how many columns, measures are selected 53 | int _NrMeasures = Selected.Measures.Count(); 54 | int _NrColumns = Selected.Columns.Count(); 55 | 56 | // ----------------------------------------------------------------------------------------------------------// 57 | // Result if a combination of measures and columns are selected 58 | if ( _NrMeasures > 0 && _NrColumns > 0 ) 59 | { 60 | // Summarize selected columns + measures with DAX 61 | string _dax = 62 | "SUMMARIZECOLUMNS ( " + _Columns + ", " + _Measures + ")"; 63 | 64 | // Return output in pop-up 65 | ScriptHelper.EvaluateDax(_dax).Output(); 66 | } 67 | 68 | // ----------------------------------------------------------------------------------------------------------// 69 | // Result if no columns selected and more than one measure selected 70 | else if ( _NrColumns == 0 && _NrMeasures > 1 ) 71 | { 72 | // Evaluate each measure as a separate row 73 | string _dax = 74 | "SELECTCOLUMNS( UNION ( " + // SELECTCOLUMNS to re-name cols, UNION to combine rows 75 | String.Join(",", _MeasuresOnlyList ) + ")," + // Concatenate list of measure evaluations 76 | @"""" + "Measure Name" + @"""" + // Re-name first column as "Measure Name" 77 | ", [Value]," + // 78 | @"""" + "Measure Result" + @"""" + // Re-name second column as "Measure Result" 79 | ", [Result])" ; // 80 | 81 | // Return output in pop-up 82 | ScriptHelper.EvaluateDax(_dax).Output(); 83 | } 84 | 85 | // ----------------------------------------------------------------------------------------------------------// 86 | // Result if no columns selected and exactly one measure selected 87 | else if ( _NrColumns == 0 && _NrMeasures == 1 ) 88 | { 89 | // Evaluate each measure as a separate row 90 | string _dax = 91 | "SELECTCOLUMNS( " + // SELECTCOLUMNS to re-name cols 92 | String.Join(",", _MeasuresOnlyList ) + "," + // Concatenate list of measure evaluations 93 | @"""" + "Measure Name" + @"""" + // Re-name first column as "Measure Name" 94 | ", [Value]," + // 95 | @"""" + "Measure Result" + @"""" + // Re-name second column as "Measure Result" 96 | ", [Result])" ; // 97 | 98 | // Return output in pop-up 99 | ScriptHelper.EvaluateDax(_dax).Output(); 100 | } 101 | 102 | // ----------------------------------------------------------------------------------------------------------// 103 | // Result if no measures and only columns are selected 104 | else 105 | { 106 | // Summarize selected columns with DAX 107 | string _dax = 108 | "SUMMARIZECOLUMNS ( " + _Columns + ")"; 109 | 110 | // Return output in pop-up 111 | ScriptHelper.EvaluateDax(_dax).Output(); 112 | } -------------------------------------------------------------------------------- /Collection/Macros/Preview columns and measures.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Preview columns and measures", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Measure, Column" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Remove/Annotations and ExtendedProperties.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | Model.ClearAnnotations(); 16 | Model.ClearExtendedProperties(); -------------------------------------------------------------------------------- /Collection/Macros/Remove/Annotations and ExtendedProperties.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Remove\\Annotations and ExtendedProperties", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Remove/Format strings.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach (var m in Model.AllMeasures) 16 | { 17 | m.FormatString = string.Empty; 18 | m.FormatStringExpression = string.Empty; 19 | } 20 | 21 | foreach (var c in Model.AllColumns) 22 | { 23 | if (c.Table.ObjectType == ObjectType.CalculationGroupTable) { continue; } 24 | c.FormatString = string.Empty; 25 | } -------------------------------------------------------------------------------- /Collection/Macros/Remove/Format strings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Remove\\Format strings", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Remove/Name underscores.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | #r "System.Globalization" 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using TabularEditor; 10 | using TabularEditor.TOMWrapper; 11 | using TabularEditor.TOMWrapper.Utils; 12 | using TabularEditor.UI; 13 | using TabularEditor.Scripting; 14 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 15 | using System.Globalization; 16 | 17 | CultureInfo cultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture; 18 | TextInfo textInfo = cultureInfo.TextInfo; 19 | 20 | string ReplaceUnderscoreAndCapitalize(string input) => textInfo.ToTitleCase(input.Replace("_", " ")); 21 | 22 | foreach (var c in Model.AllColumns) { c.Name = ReplaceUnderscoreAndCapitalize(c.Name); } 23 | foreach (var t in Model.Tables) { t.Name = ReplaceUnderscoreAndCapitalize(t.Name); } -------------------------------------------------------------------------------- /Collection/Macros/Remove/Name underscores.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Remove\\Name underscores", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Scratchpad.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | // CustomClass.CreateCT("tst", "{0}"); -------------------------------------------------------------------------------- /Collection/Macros/Scratchpad.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Scratchpad", 3 | "Enabled": "false", 4 | "Tooltip": "", 5 | "ValidContexts": "Model" 6 | } -------------------------------------------------------------------------------- /Collection/Macros/Toggle/ParameterMetadata property.csx: -------------------------------------------------------------------------------- 1 | #load "..\..\..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | using TabularEditor; 9 | using TabularEditor.TOMWrapper; 10 | using TabularEditor.TOMWrapper.Utils; 11 | using TabularEditor.UI; 12 | using TabularEditor.Scripting; 13 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 14 | 15 | foreach (var c in Selected.Columns) 16 | { 17 | if (c.DataType == DataType.Decimal || c.DataType == DataType.Double || c.DataType == DataType.Int64) 18 | { 19 | if (String.IsNullOrEmpty(c.GetExtendedProperty("ParameterMetadata"))) 20 | { 21 | c.SetExtendedProperty("ParameterMetadata", "{\"version\":0}", ExtendedPropertyType.Json); 22 | } 23 | else 24 | { 25 | c.RemoveExtendedProperty("ParameterMetadata"); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Collection/Macros/Toggle/ParameterMetadata property.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Macros\\Toggle\\ParameterMetadata property", 3 | "Enabled": "true", 4 | "Tooltip": "", 5 | "ValidContexts": "Column" 6 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Stephen Maguire 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Management/Assemblies/System.Drawing.Common.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samaguire/TabularEditorScripts/fea2d9cb0d5ccd6da5166c8e76afadf496d2d958/Management/Assemblies/System.Drawing.Common.dll -------------------------------------------------------------------------------- /Management/Assemblies/System.Windows.Forms.Primitives.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samaguire/TabularEditorScripts/fea2d9cb0d5ccd6da5166c8e76afadf496d2d958/Management/Assemblies/System.Windows.Forms.Primitives.dll -------------------------------------------------------------------------------- /Management/Assemblies/System.Windows.Forms.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samaguire/TabularEditorScripts/fea2d9cb0d5ccd6da5166c8e76afadf496d2d958/Management/Assemblies/System.Windows.Forms.dll -------------------------------------------------------------------------------- /Management/Assemblies/TOMWrapper14.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samaguire/TabularEditorScripts/fea2d9cb0d5ccd6da5166c8e76afadf496d2d958/Management/Assemblies/TOMWrapper14.dll -------------------------------------------------------------------------------- /Management/Assemblies/TabularEditor.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samaguire/TabularEditorScripts/fea2d9cb0d5ccd6da5166c8e76afadf496d2d958/Management/Assemblies/TabularEditor.exe -------------------------------------------------------------------------------- /Management/Assemblies/newtonsoft.json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samaguire/TabularEditorScripts/fea2d9cb0d5ccd6da5166c8e76afadf496d2d958/Management/Assemblies/newtonsoft.json.dll -------------------------------------------------------------------------------- /Management/Breaks EULA (Testing Only)/Macros Export from TE3 (TE3 Assemblies).csx: -------------------------------------------------------------------------------- 1 | #r "C:\Program Files\Tabular Editor 3\TabularEditor3.Shared.dll" 2 | 3 | using TabularEditor.Shared.Scripting; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Linq; 6 | 7 | /* TODO 8 | 9 | - Update to the new logic used in the standard export/import 10 | 11 | */ 12 | 13 | // *** Warning! *** this will clear all existing script content in the 'outFolder' folder 14 | // *** Warning! *** this will clear all existing script content in the 'outFolder' folder 15 | // *** Warning! *** this will clear all existing script content in the 'outFolder' folder 16 | 17 | var outFolder = @".\CollectionTE3"; 18 | 19 | // Check folder path and clear existing files and empty directories 20 | if (!Directory.Exists(outFolder)) { Directory.CreateDirectory(outFolder); } 21 | foreach (var f in Directory.EnumerateFiles(outFolder, "*.csx", SearchOption.AllDirectories)) { File.Delete(f); } 22 | foreach (var f in Directory.EnumerateFiles(outFolder, "*.json", SearchOption.AllDirectories)) { File.Delete(f); } 23 | foreach (var d in Directory.EnumerateDirectories(outFolder, "*", SearchOption.AllDirectories)) 24 | { 25 | if (!Directory.EnumerateFiles(d).Any() && !Directory.EnumerateDirectories(d).Any()) { Directory.Delete(d); } 26 | } 27 | 28 | // Define C# scripting environment 29 | var winFormsPath = Directory.GetFiles(@"C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref", "System.Windows.Forms.dll", SearchOption.AllDirectories)[0]; 30 | var scriptingEnvironment = string.Format(@"#r ""{0}"" 31 | #r ""C:\Program Files\Tabular Editor 3\TabularEditor3.Shared.dll"" 32 | using System; 33 | using System.Linq; 34 | using System.Collections.Generic; 35 | using Newtonsoft.Json; 36 | using TabularEditor; 37 | using TabularEditor.TOMWrapper; 38 | using TabularEditor.TOMWrapper.Utils; 39 | using TabularEditor.Shared; 40 | using TabularEditor.Shared.Scripting; 41 | using TabularEditor.Shared.Interaction; 42 | using TabularEditor.Shared.Services; 43 | 44 | /*** Everything ABOVE this point is required for the C# scripting environment, remove in TE3 ***/ 45 | 46 | ", winFormsPath); 47 | 48 | // Load MacroActions 49 | var jsonFile = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\TabularEditor3\MacroActions.json"; 50 | var json = JObject.Parse(File.ReadAllText(jsonFile)); 51 | 52 | // Export each MacroAction 53 | foreach (var jtokenItem in json["Actions"]) 54 | { 55 | 56 | // Generate filename without extension and relataive path 57 | var fileName = string.Join("_", jtokenItem["Name"].Value().Replace('\\', '~').Split(Path.GetInvalidFileNameChars())).Replace('~', '\\') + " [" + jtokenItem["Id"].Value() + "]"; 58 | 59 | // Get csxContent and adapt to C# scripting environment 60 | var csxContent = "\n" + jtokenItem["Execute"].Value(); 61 | var scripthostMethods = typeof(ScriptHost).GetMethods().Select(x => x.Name.Replace("get_", "").Replace("set_", "")).Distinct().ToList(); 62 | foreach (var item in scripthostMethods) 63 | { 64 | csxContent = csxContent 65 | .Replace(" " + item, " ScriptHost." + item) 66 | .Replace("(" + item, "(ScriptHost." + item) 67 | .Replace("{" + item, "{ScriptHost." + item) 68 | .Replace("!" + item, "!ScriptHost." + item) 69 | .Replace("\n" + item, "\nScriptHost." + item); 70 | } 71 | csxContent = csxContent 72 | .Replace("ScriptHelper", "ScriptHost") 73 | .Replace("typeof(ScriptHost.", "typeof(") 74 | .Replace("#r", "// #r") 75 | .Trim('\n'); 76 | 77 | // Save csxContent (and create directory) 78 | var csxFilePath = outFolder + @"\" + fileName + ".csx"; 79 | if (!Directory.Exists(Path.GetDirectoryName(csxFilePath))) { Directory.CreateDirectory(Path.GetDirectoryName(csxFilePath)); } 80 | File.WriteAllText(csxFilePath, scriptingEnvironment + csxContent, System.Text.Encoding.UTF8); 81 | 82 | // Save jsonContent 83 | jtokenItem["Execute"].Parent.Remove(); 84 | var jsonContent = JsonConvert.SerializeObject(jtokenItem, Newtonsoft.Json.Formatting.Indented); 85 | var jsonFilePath = outFolder + @"\" + fileName + ".json"; 86 | File.WriteAllText(jsonFilePath, jsonContent, System.Text.Encoding.UTF8); 87 | 88 | } -------------------------------------------------------------------------------- /Management/Breaks EULA (Testing Only)/Macros Import into TE3 (TE3 Assemblies).csx: -------------------------------------------------------------------------------- 1 | #r "C:\Program Files\Tabular Editor 3\TabularEditor3.Shared.dll" 2 | 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | 6 | /* TODO 7 | 8 | - Update to the new logic used in the standard export/import 9 | 10 | */ 11 | 12 | // *** Warning! *** this will clear all existing macros in Tabular Editor 3 13 | // *** Warning! *** this will clear all existing macros in Tabular Editor 3 14 | // *** Warning! *** this will clear all existing macros in Tabular Editor 3 15 | 16 | var inFolder = @".\CollectionTE3"; 17 | 18 | // Check folder path exists 19 | if (!Directory.Exists(inFolder)) { return; } 20 | 21 | // Pull details from csx and json files 22 | var jsonArray = new JArray(); 23 | foreach (var filePath in Directory.EnumerateFiles(inFolder, "*.csx", SearchOption.AllDirectories)) 24 | { 25 | 26 | // Extract macros, removing C# scripting environment modifications 27 | var csxContent = String.Join("\n", File.ReadAllLines(filePath).Skip(15)) 28 | .Replace("ScriptHost.", string.Empty) 29 | .Replace("// #r", "#r") 30 | .Trim('\n'); 31 | var jsonContent = (JObject.Parse(File.ReadAllText(filePath.Replace(".csx", ".json")))); 32 | 33 | // Recreate json objects of macros and build array 34 | jsonContent.Add("Execute", csxContent); 35 | jsonArray.Add(jsonContent); 36 | 37 | } 38 | 39 | // Recreate 'Actions' object 40 | var jsonObject = new JObject(); 41 | jsonObject.Add("Actions", jsonArray); 42 | 43 | // Write json to Tabular Editor 3 settings file 44 | var jsonFilePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\TabularEditor3\MacroActions.json"; 45 | var jsonContent = JsonConvert.SerializeObject(jsonObject, Newtonsoft.Json.Formatting.Indented); 46 | File.WriteAllText(jsonFilePath, jsonContent, System.Text.Encoding.UTF8); -------------------------------------------------------------------------------- /Management/Common Library.csx: -------------------------------------------------------------------------------- 1 | #r ".\Assemblies\TabularEditor.exe" 2 | #r ".\Assemblies\TOMWrapper14.dll" 3 | #r ".\Assemblies\newtonsoft.json.dll" 4 | #r ".\Assemblies\System.Drawing.Common.dll" 5 | #r ".\Assemblies\System.Windows.Forms.dll" 6 | #r ".\Assemblies\System.Windows.Forms.Primitives.dll" 7 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 8 | 9 | using System; 10 | using System.Linq; 11 | using System.Collections.Generic; 12 | using Newtonsoft.Json; 13 | using TabularEditor; 14 | using TabularEditor.TOMWrapper; 15 | using TabularEditor.TOMWrapper.Utils; 16 | using TabularEditor.UI; 17 | using TabularEditor.Scripting; 18 | // using TOM = Microsoft.AnalysisServices.Tabular; 19 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 20 | 21 | static readonly Model Model; 22 | static readonly UITreeSelection Selected; 23 | // *** The above class variables are required for the C# scripting environment, remove in Tabular Editor *** 24 | 25 | // https://docs.tabulareditor.com/te3/features/csharp-scripts.html -------------------------------------------------------------------------------- /Management/Macros Export from TE.csx: -------------------------------------------------------------------------------- 1 | #load "..\Management\Common Library.csx" 2 | 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | using TabularEditor.Scripting; 6 | 7 | // *** Warning! *** this will clear all existing script content in the 'collectionFolder' folder 8 | 9 | var collectionFolder = @".\Collection"; 10 | var TE3overTE2 = true; 11 | 12 | // Load MacroActions 13 | var jsonFile = string.Empty; 14 | var jsonFileV2 = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\TabularEditor\MacroActions.json"; 15 | var jsonFileV3 = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\TabularEditor3\MacroActions.json"; 16 | 17 | if (File.Exists(jsonFileV2)) { jsonFile = jsonFileV2; } 18 | if (File.Exists(jsonFileV3) && TE3overTE2) { jsonFile = jsonFileV3; } 19 | if (string.IsNullOrEmpty(jsonFile)) 20 | { 21 | Console.WriteLine("\"MacroActions.json\" location not found!"); 22 | return; 23 | } 24 | else 25 | { 26 | Console.WriteLine("Using \"" + jsonFile + "\""); 27 | } 28 | 29 | var json = JObject.Parse(File.ReadAllText(jsonFile)); 30 | 31 | // Check folder path and clear existing files and empty directories 32 | if (!Directory.Exists(collectionFolder)) 33 | { 34 | Directory.CreateDirectory(collectionFolder); 35 | } 36 | 37 | // Delete files with .csx and .json extensions and remove empty directories 38 | foreach (var entry in Directory.EnumerateFileSystemEntries(collectionFolder, "*", SearchOption.AllDirectories)) 39 | { 40 | if (File.Exists(entry) && (entry.EndsWith(".csx") || entry.EndsWith(".json"))) 41 | { 42 | File.Delete(entry); 43 | } 44 | else if (Directory.Exists(entry) && !Directory.EnumerateFileSystemEntries(entry).Any()) 45 | { 46 | Directory.Delete(entry); 47 | } 48 | } 49 | 50 | // Export each MacroAction 51 | foreach (var jtokenItem in json["Actions"]) 52 | { 53 | 54 | // Generate filename without extension and relataive path 55 | var fileName = string.Join("_", jtokenItem["Name"].Value().Replace('\\', '~').Split(Path.GetInvalidFileNameChars())).Replace('~', '\\'); 56 | // if (jsonFile == jsonFileV3) { fileName = fileName + " [" + jtokenItem["Id"].Value() + "]"; } // ignored as causes git version control changes when macro ids are reset, however, the watchout is the duplicate names 57 | 58 | // Generate "Common Library.csx" load directive 59 | var relativeBasePath = string.Concat (Enumerable.Repeat (@"..\", collectionFolder.Count(x => x == '\\'))); 60 | var relativeMacroPath = string.Concat (Enumerable.Repeat (@"..\", fileName.Count(x => x == '\\'))); 61 | var loadCommonLibrary = @"#load """ + relativeBasePath + relativeMacroPath + @"Management\Common Library.csx"""; 62 | 63 | // Get csxContent and adapt to C# scripting environment 64 | var csxContent = "\n" + jtokenItem["Execute"].Value(); 65 | 66 | // Prefix ScriptHelper to ScriptHelper methods 67 | // Run in TE2 to get the list of ScriptHelper methods: 68 | // var scripthelperMethods = typeof(ScriptHelper).GetMethods() 69 | // .Select(x => x.Name.Replace("get_", "").Replace("set_", "")) 70 | // .Distinct().OrderBy(name => name) 71 | // .ToList(); 72 | // Output(scripthelperMethods); 73 | var scripthelperMethods = new List() 74 | { 75 | @"AfterScriptExecution", 76 | @"BeforeScriptExecution", 77 | @"CallDaxFormatter", 78 | @"ConvertDax", 79 | @"CustomAction", 80 | @"Equals", 81 | @"Error", 82 | @"EvaluateDax", 83 | @"ExecuteCommand", 84 | @"ExecuteDax", 85 | @"ExecuteReader", 86 | @"FormatDax", 87 | @"GetHashCode", 88 | @"GetType", 89 | @"Info", 90 | @"Output", 91 | @"OutputErrors", 92 | @"ReadFile", 93 | @"ReferenceEquals", 94 | @"SaveFile", 95 | @"SchemaCheck", 96 | @"SelectColumn", 97 | @"SelectMeasure", 98 | @"SelectObject", 99 | @"SelectTable", 100 | @"SuspendWaitForm", 101 | @"ToString", 102 | @"WaitFormVisible", 103 | @"Warning" 104 | }; 105 | foreach (var item in scripthelperMethods) 106 | { 107 | csxContent = csxContent 108 | .Replace(" " + item, " ScriptHelper." + item) 109 | .Replace("(" + item, "(ScriptHelper." + item) 110 | .Replace("{" + item, "{ScriptHelper." + item) 111 | .Replace("!" + item, "!ScriptHelper." + item) 112 | .Replace("\n" + item, "\nScriptHelper." + item); 113 | } 114 | 115 | // General cleanup 116 | csxContent = csxContent 117 | .Replace("ScriptHost", "ScriptHelper") 118 | .Replace("typeof(ScriptHelper.", "typeof("); 119 | 120 | // Define C# scripting environment 121 | var assemblyList = new List() 122 | { 123 | loadCommonLibrary, 124 | @"// *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor ***" 125 | }; 126 | var assemblyHashset = new HashSet(assemblyList); 127 | var namespaceList = new List() 128 | { 129 | @"using System;", 130 | @"using System.Linq;", 131 | @"using System.Collections.Generic;", 132 | @"using Newtonsoft.Json;", 133 | @"using TabularEditor;", 134 | @"using TabularEditor.TOMWrapper;", 135 | @"using TabularEditor.TOMWrapper.Utils;", 136 | @"using TabularEditor.UI;", 137 | @"using TabularEditor.Scripting;", 138 | @"// *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor ***" 139 | }; 140 | var namespaceHashset = new HashSet(namespaceList); 141 | var scriptBodyList = new List(); 142 | 143 | // Deconstruct csxContent to C# scripting environment lists 144 | using (var reader = new StringReader(csxContent)) 145 | { 146 | var line = String.Empty; 147 | while ((line = reader.ReadLine()) != null) 148 | { 149 | if (line.StartsWith("#r ")) 150 | { 151 | if (assemblyHashset.Add(line)) { assemblyList.Add(line); } 152 | } 153 | else if (line.StartsWith("using ") && !line.StartsWith("using (")) 154 | { 155 | if (namespaceHashset.Add(line)) { namespaceList.Add(line); } 156 | } 157 | else { scriptBodyList.Add(line); } 158 | } 159 | } 160 | 161 | // Reconstruct csxContent from lists 162 | csxContent = string.Join(Environment.NewLine, new List() 163 | { 164 | string.Join(Environment.NewLine, assemblyList) + Environment.NewLine, 165 | string.Join(Environment.NewLine, namespaceList) + Environment.NewLine, 166 | string.Join(Environment.NewLine, scriptBodyList).Trim('\r', '\n') 167 | .Replace(Environment.NewLine + Environment.NewLine + Environment.NewLine, Environment.NewLine + Environment.NewLine) 168 | }); 169 | 170 | // Save csxContent (and create directory) 171 | var csxFilePath = collectionFolder + @"\" + fileName + ".csx"; 172 | if (!Directory.Exists(Path.GetDirectoryName(csxFilePath))) { Directory.CreateDirectory(Path.GetDirectoryName(csxFilePath)); } 173 | File.WriteAllText(csxFilePath, csxContent, System.Text.Encoding.UTF8); 174 | 175 | // Save jsonContent 176 | jtokenItem["Id"].Parent.Remove(); 177 | jtokenItem["Execute"].Parent.Remove(); 178 | var jsonContent = JsonConvert.SerializeObject(jtokenItem, Newtonsoft.Json.Formatting.Indented); 179 | var jsonFilePath = collectionFolder + @"\" + fileName + ".json"; 180 | File.WriteAllText(jsonFilePath, jsonContent, System.Text.Encoding.UTF8); 181 | 182 | } -------------------------------------------------------------------------------- /Management/Macros Import into TE.csx: -------------------------------------------------------------------------------- 1 | #load "..\Management\Common Library.csx" 2 | 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | 6 | // *** Warning! *** this will clear all existing macros in Tabular Editor / Tabular Editor 3 7 | // *** Warning! *** this will clear all existing macros in Tabular Editor / Tabular Editor 3 8 | // *** Warning! *** this will clear all existing macros in Tabular Editor / Tabular Editor 3 9 | 10 | var collectionFolder = @".\Collection"; 11 | var TE3overTE2 = true; 12 | 13 | // Check paths 14 | if (!Directory.Exists(collectionFolder)) { return; } 15 | 16 | var jsonFile = string.Empty; 17 | var jsonFileV2 = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\TabularEditor\MacroActions.json"; 18 | var jsonFileV3 = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\TabularEditor3\MacroActions.json"; 19 | if (Directory.Exists(Path.GetDirectoryName(jsonFileV2))) { jsonFile = jsonFileV2; } 20 | if (Directory.Exists(Path.GetDirectoryName(jsonFileV3)) && TE3overTE2) { jsonFile = jsonFileV3; } 21 | if (string.IsNullOrEmpty(jsonFile)) 22 | { 23 | Console.WriteLine("\"MacroActions.json\" location not found!"); 24 | return; 25 | } 26 | else 27 | { 28 | Console.WriteLine("Using \"" + jsonFile + "\""); 29 | } 30 | 31 | // Define C# scripting environment 32 | var assemblyList = new List() 33 | { 34 | @"Common Library.csx""", 35 | "// *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor ***" 36 | }; 37 | var namespaceList = new List() 38 | { 39 | @"using System;", 40 | @"using System.Linq;", 41 | @"using System.Collections.Generic;", 42 | @"using Newtonsoft.Json;", 43 | @"using TabularEditor;", 44 | @"using TabularEditor.TOMWrapper;", 45 | @"using TabularEditor.TOMWrapper.Utils;", 46 | @"using TabularEditor.UI;", 47 | @"using TabularEditor.Scripting;", 48 | @"// using TOM = Microsoft.AnalysisServices.Tabular;", 49 | @"// *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor ***" 50 | }; 51 | 52 | // Pull details from csx and json files 53 | var jsonArray = new JArray(); 54 | int loopCounter = 0; 55 | foreach (var filePath in Directory.EnumerateFiles(collectionFolder, "*.csx", SearchOption.AllDirectories)) 56 | { 57 | 58 | // Extract macro, removing C# scripting environment modifications 59 | var scriptBodyList = new List(){}; 60 | foreach (var line in File.ReadLines(filePath)) 61 | { 62 | if (!((line.StartsWith("#") && assemblyList.Contains(line.Split('\\').Last())) || assemblyList.Contains(line)) && !namespaceList.Contains(line)) { scriptBodyList.Add(line); } 63 | } 64 | var csxContent = String.Join(Environment.NewLine, scriptBodyList) 65 | .Replace("ScriptHelper.", string.Empty) 66 | .Trim('\r', '\n'); 67 | 68 | // Build MacroAction and add to 'Actions' json array 69 | var jsonContent = (JObject.Parse(File.ReadAllText(filePath.Replace(".csx", ".json")))); 70 | jsonContent["Id"] = loopCounter; 71 | jsonContent.Add("Execute", csxContent); 72 | jsonArray.Add(jsonContent); 73 | 74 | loopCounter++; 75 | 76 | } 77 | 78 | // Write MacroActions 79 | var jsonObject = new JObject(); 80 | jsonObject.Add("Actions", jsonArray); 81 | var jsonContent = JsonConvert.SerializeObject(jsonObject, Newtonsoft.Json.Formatting.Indented); 82 | File.WriteAllText(jsonFile, jsonContent, System.Text.Encoding.UTF8); -------------------------------------------------------------------------------- /Management/Settings Backup.csx: -------------------------------------------------------------------------------- 1 | using System.IO.Compression; 2 | 3 | string localFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 4 | string[] appSubfolders = { "TabularEditor", "TabularEditor3" }; 5 | 6 | foreach (var item in appSubfolders) 7 | { 8 | var folderPath = $"{localFolder}\\{item}"; 9 | var zipPath = $"{localFolder}\\{item}.zip"; 10 | if (Directory.Exists(folderPath)) 11 | { 12 | File.Delete(zipPath); 13 | ZipFile.CreateFromDirectory(folderPath, zipPath); 14 | } 15 | } -------------------------------------------------------------------------------- /Management/Settings Restore.csx: -------------------------------------------------------------------------------- 1 | using System.IO.Compression; 2 | 3 | string localFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 4 | string[] appSubfolders = { "TabularEditor", "TabularEditor3" }; 5 | 6 | foreach (var item in appSubfolders) 7 | { 8 | var folderPath = $"{localFolder}\\{item}"; 9 | var zipPath = $"{localFolder}\\{item}.zip"; 10 | if (File.Exists(zipPath)) 11 | { 12 | ZipFile.ExtractToDirectory(zipPath, folderPath, true); 13 | } 14 | } -------------------------------------------------------------------------------- /Management/TE2 Script Compiler Update.csx: -------------------------------------------------------------------------------- 1 | #r "nuget: Newtonsoft.Json, 13.0.1" 2 | #r "nuget: Microsoft.Net.Compilers.Toolset, 4.12.0" 3 | 4 | // https://www.nuget.org/packages/Microsoft.Net.Compilers.Toolset/4.12.0 5 | 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Linq; 8 | 9 | var jsonFile = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\TabularEditor\Preferences.json"; 10 | var json = JObject.Parse(File.ReadAllText(jsonFile)); 11 | 12 | json["ScriptCompilerDirectoryPath"] = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\.nuget\packages\microsoft.net.compilers.toolset\4.12.0\tasks\net472"; 13 | json["ScriptCompilerOptions"] = "-langversion:default"; 14 | 15 | File.WriteAllText(jsonFile, JsonConvert.SerializeObject(json, Newtonsoft.Json.Formatting.Indented), System.Text.Encoding.UTF8); -------------------------------------------------------------------------------- /Management/TOM-Extract-CompatibilityRequirementsAttributes.csx: -------------------------------------------------------------------------------- 1 | // https://notes.mthierba.net/power-bi/analysis-services/list-new-features-in-tom-library#linqpad-script 2 | // https://www.nuget.org/packages/Microsoft.AnalysisServices.retail.amd64#versions-body-tab 3 | 4 | #r "nuget: Microsoft.AnalysisServices.retail.amd64, 19.69.6.2" 5 | 6 | using System.Reflection; 7 | using TOM = Microsoft.AnalysisServices.Tabular; 8 | 9 | var asm = typeof(TOM.Server).Assembly; 10 | var compatAttr = asm.GetType("Microsoft.AnalysisServices.Tabular.CompatibilityRequirementAttribute"); 11 | 12 | string ReadProperty(string name, object attr) => compatAttr.GetProperty(name).GetValue(attr).ToString(); 13 | string GetMemberType(Type t) => t.IsEnum ? "Enum" : t.IsInterface ? "Interface" : "Class"; 14 | Attribute GetCustomAttributeSafe(MemberInfo member, Type t) 15 | { // This is needed to avoid errors on a few specific attributes containing unsupported expressions - we're simply ignoring those 16 | try 17 | { 18 | return member.GetCustomAttribute(t); 19 | } 20 | catch (TOM.TomException) 21 | { 22 | return null; 23 | } 24 | } 25 | 26 | var members = asm.GetTypes() 27 | .Select(t => new 28 | { 29 | Type = t, 30 | CompatAttribute = t.GetCustomAttribute(compatAttr) 31 | }) 32 | .Where(x => x.CompatAttribute != null) 33 | .Select(x => new 34 | { 35 | Name = x.Type.FullName, 36 | MemberType = GetMemberType(x.Type), 37 | Box = ReadProperty("Box", x.CompatAttribute), 38 | Excel = ReadProperty("Excel", x.CompatAttribute), 39 | PBI = ReadProperty("Pbi", x.CompatAttribute) 40 | }) 41 | .Union( 42 | asm.GetTypes() 43 | .SelectMany(t => t.GetMembers()) 44 | .Select(m => new 45 | { 46 | Member = m, 47 | Name = $"{m.DeclaringType.FullName}.{m.Name}", 48 | CompatAttribute = GetCustomAttributeSafe(m, compatAttr), 49 | MemberType = m.MemberType.ToString() 50 | }) 51 | .Where(x => x.CompatAttribute != null) 52 | .Select(x => new 53 | { 54 | x.Name, 55 | x.MemberType, 56 | Box = ReadProperty("Box", x.CompatAttribute), 57 | Excel = ReadProperty("Excel", x.CompatAttribute), 58 | PBI = ReadProperty("Pbi", x.CompatAttribute) 59 | }) 60 | ) 61 | .Where(x => /* toggle this for 1200/1400: */ !(((int.TryParse(x.Box, out var box) && box <= 1400) || x.Box == "Unsupported") 62 | && ((int.TryParse(x.Excel, out var excel) && excel <= 1400) || x.Excel == "Unsupported") 63 | && ((int.TryParse(x.PBI, out var pbi) && pbi <= 1400) || x.PBI == "Unsupported"))) 64 | .OrderBy(x => x.Name) 65 | .ToArray(); 66 | 67 | // Convert to markdown table for blog post: 68 | using (StreamWriter outputFile = new StreamWriter(@".\Management\TOM-Extract-CompatibilityRequirementsAttributes.md")) 69 | { 70 | outputFile.WriteLine($"|Name|MemberType|Box|Excel|PBI|"); 71 | outputFile.WriteLine($"|-|-|-|-|-|"); 72 | Array.ForEach(members, x => outputFile.WriteLine($"| {x.Name} | {x.MemberType} | {x.Box} | {x.Excel} | {x.PBI} |")); 73 | } -------------------------------------------------------------------------------- /Management/TOM-Extract-CompatibilityRequirementsAttributes.md: -------------------------------------------------------------------------------- 1 | |Name|MemberType|Box|Excel|PBI| 2 | |-|-|-|-|-| 3 | | Microsoft.AnalysisServices.Tabular.AlternateOf | Class | 1460 | 1460 | 1460 | 4 | | Microsoft.AnalysisServices.Tabular.AlternateOfAnnotationCollection | Class | 1460 | 1460 | 1460 | 5 | | Microsoft.AnalysisServices.Tabular.AnalyticsAIMetadata | Class | Preview | Preview | Preview | 6 | | Microsoft.AnalysisServices.Tabular.AnalyticsAIMetadataCollection | Class | Preview | Preview | Preview | 7 | | Microsoft.AnalysisServices.Tabular.AutomaticAggregationOptions | Class | 1564 | 1564 | 1564 | 8 | | Microsoft.AnalysisServices.Tabular.BasicRefreshPolicy | Class | 1450 | 1450 | 1450 | 9 | | Microsoft.AnalysisServices.Tabular.CalculatedColumn.EvaluationBehavior | Property | Preview | Preview | Preview | 10 | | Microsoft.AnalysisServices.Tabular.CalculationGroup | Class | 1470 | 1470 | 1470 | 11 | | Microsoft.AnalysisServices.Tabular.CalculationGroup.MultipleOrEmptySelectionExpression | Property | 1605 | 1605 | 1605 | 12 | | Microsoft.AnalysisServices.Tabular.CalculationGroup.NoSelectionExpression | Property | 1605 | 1605 | 1605 | 13 | | Microsoft.AnalysisServices.Tabular.CalculationGroupAnnotationCollection | Class | 1470 | 1470 | 1470 | 14 | | Microsoft.AnalysisServices.Tabular.CalculationGroupExpression | Class | 1605 | 1605 | 1605 | 15 | | Microsoft.AnalysisServices.Tabular.CalculationGroupExpressionCollection | Class | 1605 | 1605 | 1605 | 16 | | Microsoft.AnalysisServices.Tabular.CalculationGroupSelectionMode | Enum | 1605 | 1605 | 1605 | 17 | | Microsoft.AnalysisServices.Tabular.CalculationGroupSource | Class | 1470 | 1470 | 1470 | 18 | | Microsoft.AnalysisServices.Tabular.CalculationItem | Class | 1470 | 1470 | 1470 | 19 | | Microsoft.AnalysisServices.Tabular.CalculationItem.Ordinal | Property | 1500 | 1500 | 1500 | 20 | | Microsoft.AnalysisServices.Tabular.CalculationItemCollection | Class | 1470 | 1470 | 1470 | 21 | | Microsoft.AnalysisServices.Tabular.Calendar | Class | Preview | Preview | Preview | 22 | | Microsoft.AnalysisServices.Tabular.CalendarCollection | Class | Preview | Preview | Preview | 23 | | Microsoft.AnalysisServices.Tabular.CalendarColumnReference | Class | Preview | Preview | Preview | 24 | | Microsoft.AnalysisServices.Tabular.CalendarColumnReferenceCollection | Class | Preview | Preview | Preview | 25 | | Microsoft.AnalysisServices.Tabular.ChangedProperty | Class | 1567 | 1567 | 1567 | 26 | | Microsoft.AnalysisServices.Tabular.Column.AlternateOf | Property | 1460 | 1460 | 1460 | 27 | | Microsoft.AnalysisServices.Tabular.Column.ChangedProperties | Property | 1567 | 1567 | 1567 | 28 | | Microsoft.AnalysisServices.Tabular.Column.LineageTag | Property | 1540 | 1540 | 1540 | 29 | | Microsoft.AnalysisServices.Tabular.Column.SourceLineageTag | Property | 1550 | 1550 | 1550 | 30 | | Microsoft.AnalysisServices.Tabular.ColumnChangedPropertyCollection | Class | 1567 | 1567 | 1567 | 31 | | Microsoft.AnalysisServices.Tabular.ContentType | Enum | 1465 | 1465 | 1465 | 32 | | Microsoft.AnalysisServices.Tabular.DataCoverageDefinition | Class | 1603 | 1603 | 1603 | 33 | | Microsoft.AnalysisServices.Tabular.DataCoverageDefinitionAnnotationCollection | Class | 1603 | 1603 | 1603 | 34 | | Microsoft.AnalysisServices.Tabular.DataSourceVariablesOverrideBehaviorType | Enum | 1475 | 1475 | 1475 | 35 | | Microsoft.AnalysisServices.Tabular.DirectLakeBehavior | Enum | 1604 | 1604 | 1604 | 36 | | Microsoft.AnalysisServices.Tabular.EntityPartitionSource.SchemaName | Property | 1604 | 1604 | 1604 | 37 | | Microsoft.AnalysisServices.Tabular.EvaluationBehavior | Enum | Preview | Preview | Preview | 38 | | Microsoft.AnalysisServices.Tabular.ExcludedArtifact | Class | Preview | Preview | Preview | 39 | | Microsoft.AnalysisServices.Tabular.FormatStringDefinition | Class | 1470 | 1470 | 1470 | 40 | | Microsoft.AnalysisServices.Tabular.Hierarchy.ChangedProperties | Property | 1567 | 1567 | 1567 | 41 | | Microsoft.AnalysisServices.Tabular.Hierarchy.ExcludedArtifacts | Property | Preview | Preview | Preview | 42 | | Microsoft.AnalysisServices.Tabular.Hierarchy.LineageTag | Property | 1540 | 1540 | 1540 | 43 | | Microsoft.AnalysisServices.Tabular.Hierarchy.SourceLineageTag | Property | 1550 | 1550 | 1550 | 44 | | Microsoft.AnalysisServices.Tabular.HierarchyChangedPropertyCollection | Class | 1567 | 1567 | 1567 | 45 | | Microsoft.AnalysisServices.Tabular.HierarchyExcludedArtifactCollection | Class | Preview | Preview | Preview | 46 | | Microsoft.AnalysisServices.Tabular.InferredPartitionSource | Class | 1563 | 1563 | 1563 | 47 | | Microsoft.AnalysisServices.Tabular.Level.ChangedProperties | Property | 1567 | 1567 | 1567 | 48 | | Microsoft.AnalysisServices.Tabular.Level.LineageTag | Property | 1540 | 1540 | 1540 | 49 | | Microsoft.AnalysisServices.Tabular.Level.SourceLineageTag | Property | 1550 | 1550 | 1550 | 50 | | Microsoft.AnalysisServices.Tabular.LevelChangedPropertyCollection | Class | 1567 | 1567 | 1567 | 51 | | Microsoft.AnalysisServices.Tabular.LinguisticMetadata.ContentType | Property | 1465 | 1465 | 1465 | 52 | | Microsoft.AnalysisServices.Tabular.Measure.ChangedProperties | Property | 1567 | 1567 | 1567 | 53 | | Microsoft.AnalysisServices.Tabular.Measure.DataCategory | Property | 1455 | 1455 | 1455 | 54 | | Microsoft.AnalysisServices.Tabular.Measure.FormatStringDefinition | Property | 1601 | 1601 | 1601 | 55 | | Microsoft.AnalysisServices.Tabular.Measure.LineageTag | Property | 1540 | 1540 | 1540 | 56 | | Microsoft.AnalysisServices.Tabular.Measure.SourceLineageTag | Property | 1550 | 1550 | 1550 | 57 | | Microsoft.AnalysisServices.Tabular.MeasureChangedPropertyCollection | Class | 1567 | 1567 | 1567 | 58 | | Microsoft.AnalysisServices.Tabular.Model.AnalyticsAIMetadata | Property | Preview | Preview | Preview | 59 | | Microsoft.AnalysisServices.Tabular.Model.ApplyAutomaticAggregations | Method | 1564 | 1564 | 1564 | 60 | | Microsoft.AnalysisServices.Tabular.Model.ApplyPerformanceRecommendations | Method | 1564 | 1564 | 1564 | 61 | | Microsoft.AnalysisServices.Tabular.Model.AutomaticAggregationOptions | Property | 1564 | 1564 | 1564 | 62 | | Microsoft.AnalysisServices.Tabular.Model.DataSourceDefaultMaxConnections | Property | 1510 | 1510 | 1510 | 63 | | Microsoft.AnalysisServices.Tabular.Model.DataSourceVariablesOverrideBehavior | Property | 1475 | 1475 | 1475 | 64 | | Microsoft.AnalysisServices.Tabular.Model.DefaultPowerBIDataSourceVersion | Property | 1450 | 1450 | 1450 | 65 | | Microsoft.AnalysisServices.Tabular.Model.DirectLakeBehavior | Property | 1604 | 1604 | 1604 | 66 | | Microsoft.AnalysisServices.Tabular.Model.DisableAutoExists | Property | 1566 | 1566 | 1566 | 67 | | Microsoft.AnalysisServices.Tabular.Model.DisableSystemDefaultExpression | Property | Preview | Preview | Preview | 68 | | Microsoft.AnalysisServices.Tabular.Model.DiscourageCompositeModels | Property | 1560 | 1560 | 1560 | 69 | | Microsoft.AnalysisServices.Tabular.Model.DiscourageImplicitMeasures | Property | 1470 | 1470 | 1470 | 70 | | Microsoft.AnalysisServices.Tabular.Model.DiscourageReportMeasures | Property | Internal | Internal | Internal | 71 | | Microsoft.AnalysisServices.Tabular.Model.ExcludedArtifacts | Property | Preview | Preview | Preview | 72 | | Microsoft.AnalysisServices.Tabular.Model.ForceUniqueNames | Property | 1465 | 1465 | 1465 | 73 | | Microsoft.AnalysisServices.Tabular.Model.MAttributes | Property | 1535 | 1535 | 1535 | 74 | | Microsoft.AnalysisServices.Tabular.Model.MaxParallelismPerQuery | Property | 1569 | 1569 | 1569 | 75 | | Microsoft.AnalysisServices.Tabular.Model.MaxParallelismPerRefresh | Property | 1568 | 1568 | 1568 | 76 | | Microsoft.AnalysisServices.Tabular.Model.QueryGroups | Property | 1480 | 1480 | 1480 | 77 | | Microsoft.AnalysisServices.Tabular.Model.SourceQueryCulture | Property | 1520 | 1520 | 1520 | 78 | | Microsoft.AnalysisServices.Tabular.ModelExcludedArtifactCollection | Class | Preview | Preview | Preview | 79 | | Microsoft.AnalysisServices.Tabular.ModeType.DirectLake | Field | 1604 | 1604 | 1604 | 80 | | Microsoft.AnalysisServices.Tabular.ModeType.Dual | Field | 1455 | 1455 | 1455 | 81 | | Microsoft.AnalysisServices.Tabular.NamedExpression.ExcludedArtifacts | Property | Preview | Preview | Preview | 82 | | Microsoft.AnalysisServices.Tabular.NamedExpression.ExpressionSource | Property | 1570 | 1570 | 1570 | 83 | | Microsoft.AnalysisServices.Tabular.NamedExpression.LineageTag | Property | 1540 | 1540 | 1540 | 84 | | Microsoft.AnalysisServices.Tabular.NamedExpression.MAttributes | Property | 1535 | 1535 | 1535 | 85 | | Microsoft.AnalysisServices.Tabular.NamedExpression.ParameterValuesColumn | Property | 1545 | 1545 | 1545 | 86 | | Microsoft.AnalysisServices.Tabular.NamedExpression.QueryGroup | Property | 1480 | 1480 | 1480 | 87 | | Microsoft.AnalysisServices.Tabular.NamedExpression.RemoteParameterName | Property | 1570 | 1570 | 1570 | 88 | | Microsoft.AnalysisServices.Tabular.NamedExpression.SourceLineageTag | Property | 1550 | 1550 | 1550 | 89 | | Microsoft.AnalysisServices.Tabular.NamedExpressionExcludedArtifactCollection | Class | Preview | Preview | Preview | 90 | | Microsoft.AnalysisServices.Tabular.ObjectTranslation.Altered | Property | 1571 | 1571 | 1571 | 91 | | Microsoft.AnalysisServices.Tabular.ObjectType.AlternateOf | Field | 1460 | 1460 | 1460 | 92 | | Microsoft.AnalysisServices.Tabular.ObjectType.AnalyticsAIMetadata | Field | Preview | Preview | Preview | 93 | | Microsoft.AnalysisServices.Tabular.ObjectType.CalculationExpression | Field | 1605 | 1605 | 1605 | 94 | | Microsoft.AnalysisServices.Tabular.ObjectType.CalculationGroup | Field | 1470 | 1470 | 1470 | 95 | | Microsoft.AnalysisServices.Tabular.ObjectType.CalculationItem | Field | 1470 | 1470 | 1470 | 96 | | Microsoft.AnalysisServices.Tabular.ObjectType.Calendar | Field | Preview | Preview | Preview | 97 | | Microsoft.AnalysisServices.Tabular.ObjectType.CalendarColumnReference | Field | Preview | Preview | Preview | 98 | | Microsoft.AnalysisServices.Tabular.ObjectType.ChangedProperty | Field | 1567 | 1567 | 1567 | 99 | | Microsoft.AnalysisServices.Tabular.ObjectType.DataCoverageDefinition | Field | 1603 | 1603 | 1603 | 100 | | Microsoft.AnalysisServices.Tabular.ObjectType.ExcludedArtifact | Field | Preview | Preview | Preview | 101 | | Microsoft.AnalysisServices.Tabular.ObjectType.FormatStringDefinition | Field | 1470 | 1470 | 1470 | 102 | | Microsoft.AnalysisServices.Tabular.ObjectType.QueryGroup | Field | 1480 | 1480 | 1480 | 103 | | Microsoft.AnalysisServices.Tabular.ObjectType.RefreshPolicy | Field | 1450 | 1450 | 1450 | 104 | | Microsoft.AnalysisServices.Tabular.ObjectType.TimeUnitColumnAssociation | Field | Preview | Preview | Preview | 105 | | Microsoft.AnalysisServices.Tabular.ParquetPartitionSource | Class | 1566 | 1566 | 1566 | 106 | | Microsoft.AnalysisServices.Tabular.Partition.DataCoverageDefinition | Property | 1603 | 1603 | 1603 | 107 | | Microsoft.AnalysisServices.Tabular.Partition.QueryGroup | Property | 1480 | 1480 | 1480 | 108 | | Microsoft.AnalysisServices.Tabular.PartitionSourceType.CalculationGroup | Field | 1470 | 1470 | 1470 | 109 | | Microsoft.AnalysisServices.Tabular.PartitionSourceType.Inferred | Field | 1563 | 1563 | 1563 | 110 | | Microsoft.AnalysisServices.Tabular.PartitionSourceType.Parquet | Field | Internal | Internal | Internal | 111 | | Microsoft.AnalysisServices.Tabular.PartitionSourceType.PolicyRange | Field | 1450 | 1450 | 1450 | 112 | | Microsoft.AnalysisServices.Tabular.PolicyRangePartitionSource | Class | 1450 | 1450 | 1450 | 113 | | Microsoft.AnalysisServices.Tabular.PowerBIDataSourceVersion | Enum | 1450 | 1450 | 1450 | 114 | | Microsoft.AnalysisServices.Tabular.PowerBIDataSourceVersion.PowerBI_V3 | Field | 1465 | 1465 | 1465 | 115 | | Microsoft.AnalysisServices.Tabular.QueryGroup | Class | 1480 | 1480 | 1480 | 116 | | Microsoft.AnalysisServices.Tabular.QueryGroupAnnotationCollection | Class | 1480 | 1480 | 1480 | 117 | | Microsoft.AnalysisServices.Tabular.QueryGroupCollection | Class | 1480 | 1480 | 1480 | 118 | | Microsoft.AnalysisServices.Tabular.RefreshGranularityType | Enum | 1450 | 1450 | 1450 | 119 | | Microsoft.AnalysisServices.Tabular.RefreshPolicy | Class | 1450 | 1450 | 1450 | 120 | | Microsoft.AnalysisServices.Tabular.RefreshPolicy.Mode | Property | 1565 | 1565 | 1565 | 121 | | Microsoft.AnalysisServices.Tabular.RefreshPolicyAnnotationCollection | Class | 1450 | 1450 | 1450 | 122 | | Microsoft.AnalysisServices.Tabular.RefreshPolicyExtendedPropertyCollection | Class | 1450 | 1450 | 1450 | 123 | | Microsoft.AnalysisServices.Tabular.RefreshPolicyMode | Enum | 1565 | 1565 | 1565 | 124 | | Microsoft.AnalysisServices.Tabular.RefreshPolicyType | Enum | 1450 | 1450 | 1450 | 125 | | Microsoft.AnalysisServices.Tabular.Relationship.ChangedProperties | Property | 1567 | 1567 | 1567 | 126 | | Microsoft.AnalysisServices.Tabular.RelationshipChangedPropertyCollection | Class | 1567 | 1567 | 1567 | 127 | | Microsoft.AnalysisServices.Tabular.SecurityFilteringBehavior.None | Field | 1561 | 1561 | 1561 | 128 | | Microsoft.AnalysisServices.Tabular.SummarizationType | Enum | 1460 | 1460 | 1460 | 129 | | Microsoft.AnalysisServices.Tabular.Table.AlternateSourcePrecedence | Property | 1460 | 1460 | 1460 | 130 | | Microsoft.AnalysisServices.Tabular.Table.CalculationGroup | Property | 1470 | 1470 | 1470 | 131 | | Microsoft.AnalysisServices.Tabular.Table.Calendars | Property | Preview | Preview | Preview | 132 | | Microsoft.AnalysisServices.Tabular.Table.ChangedProperties | Property | 1567 | 1567 | 1567 | 133 | | Microsoft.AnalysisServices.Tabular.Table.ExcludedArtifacts | Property | Preview | Preview | Preview | 134 | | Microsoft.AnalysisServices.Tabular.Table.ExcludeFromAutomaticAggregations | Property | 1572 | 1572 | 1572 | 135 | | Microsoft.AnalysisServices.Tabular.Table.ExcludeFromModelRefresh | Property | 1480 | 1480 | 1480 | 136 | | Microsoft.AnalysisServices.Tabular.Table.LineageTag | Property | 1540 | 1540 | 1540 | 137 | | Microsoft.AnalysisServices.Tabular.Table.RefreshPolicy | Property | 1450 | 1450 | 1450 | 138 | | Microsoft.AnalysisServices.Tabular.Table.SourceLineageTag | Property | 1550 | 1550 | 1550 | 139 | | Microsoft.AnalysisServices.Tabular.Table.SystemManaged | Property | 1562 | 1562 | 1562 | 140 | | Microsoft.AnalysisServices.Tabular.TableChangedPropertyCollection | Class | 1567 | 1567 | 1567 | 141 | | Microsoft.AnalysisServices.Tabular.TableExcludedArtifactCollection | Class | Preview | Preview | Preview | 142 | | Microsoft.AnalysisServices.Tabular.TimeUnit | Enum | Preview | Preview | Preview | 143 | | Microsoft.AnalysisServices.Tabular.TimeUnitColumnAssociation | Class | Preview | Preview | Preview | 144 | | Microsoft.AnalysisServices.Tabular.TimeUnitColumnAssociationCollection | Class | Preview | Preview | Preview | 145 | -------------------------------------------------------------------------------- /Management/Update Assemblies.csx: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | var rootFolder = @"C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref"; 7 | // Get all subfolders and order them by version number in descending order 8 | var subFolders = Directory.GetDirectories(rootFolder).OrderByDescending(folder => new Version(Path.GetFileName(folder))).ToList(); 9 | if (subFolders.Count == 0) 10 | { 11 | throw new DirectoryNotFoundException("No versioned subfolders found."); 12 | } 13 | // Use the newest version subfolder 14 | var dotNetPath = Path.Combine(rootFolder, subFolders[0]); 15 | 16 | var assemblyFiles = new List() 17 | { 18 | Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Tabular Editor", "TabularEditor.exe"), 19 | Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TabularEditor", "TOMWrapper14.dll"), 20 | Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TabularEditor", "newtonsoft.json.dll"), 21 | Directory.GetFiles(dotNetPath, "System.Drawing.Common.dll", SearchOption.AllDirectories).FirstOrDefault(), 22 | Directory.GetFiles(dotNetPath, "System.Windows.Forms.dll", SearchOption.AllDirectories).FirstOrDefault(), 23 | Directory.GetFiles(dotNetPath, "System.Windows.Forms.Primitives.dll", SearchOption.AllDirectories).FirstOrDefault() 24 | }; 25 | 26 | var destFolder = @".\Management\Assemblies"; 27 | 28 | foreach (var sourceFile in assemblyFiles.Where(file => !string.IsNullOrEmpty(file))) 29 | { 30 | try 31 | { 32 | var destFile = Path.Combine(destFolder, Path.GetFileName(sourceFile)); 33 | Directory.CreateDirectory(destFolder); 34 | File.Copy(sourceFile, destFile, true); 35 | // Console.WriteLine($"Successfully copied {sourceFile} to {destFile}"); 36 | } 37 | catch (Exception ex) 38 | { 39 | Console.WriteLine($"*** Warning! *** Failed to copy {sourceFile}. Error: {ex.Message}"); 40 | } 41 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tabular Editor Scripting in Visual Studio Code 2 | 3 | For those interested in writing scripts for Tabular Editor 2/3 from within Visual Studio Code; giving you the added benefit of source control for your Tabular Editor scripts as well as IntelliSense if you don't use Tabular Editor 3 👏. 4 | 5 | ![image](https://user-images.githubusercontent.com/62320770/210715420-487a4a8a-6b2f-47d1-84b3-d511b2778060.png) 6 | 7 | ## Requirements 8 | 9 | The below software needs to be installed. (Portable versions of Tabular Editor are not compatible.) 10 | 11 | ### Required 12 | 13 | - [Visual Studio Code](https://code.visualstudio.com/) 14 | - [.NET SDK 6.0 or 7.0](https://dotnet.microsoft.com/en-us/download) 15 | - [Tabular Editor 2](https://github.com/TabularEditor/TabularEditor) (free, open-source version) 16 | 17 | ### Optional 18 | 19 | - [Tabular Editor 3](https://tabulareditor.com/) 20 | 21 | ## Installation 22 | 23 | ### Quick Install 24 | 25 | 1. Opening this repo in Visual Studio Code will prompt to install the C# and Code Runner extensions if not already installed. 26 | 2. The dotnet script tool can be installed from the terminal using the command `dotnet tool install -g dotnet-script --version 1.3.1` 27 | 3. Update the Code Runner Executor Map setting for C# to `"csharp": "dotnet script --isolated-load-context"` from `"csharp": "cscript"` 28 | 29 | ### Visual Studio Extensions 30 | 31 | To author C# scripts (and code) from Visual Studio Code, you need to install: 32 | 33 | - The [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) extension. 34 | 35 | To run C# scripts from Visual Studio Code (**the Management scripts in this case**) you need to install: 36 | 37 | - The [dotnet script](https://github.com/filipw/dotnet-script) extension, and 38 | - optionally, the [Code Runner](https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner) extension (but highly recommended). 39 | 40 | ### dotnet script 41 | 42 | If you get the "`Tool 'dotnet-script' failed to install`" error when executing the install command from terminal, try updating the package source in your NuGet config file as per [this](https://stackoverflow.com/a/68140757) post on Stack Overflow. 43 | 44 | ## Macro Management Scripts 45 | 46 | The scripts in the "Management" folder of this repo are designed to be run from within Visual Studio Code without Tabular Editor running. 47 | 48 | - Macros Export from TE.csx 49 | - Macros Import into TE.csx 50 | - Settings Backup.csx 51 | - Settings Restore.csx 52 | - TE2 Script Compiler Update.csx 53 | 54 | These scripts are compatible with either Tabular Editor 3 or Tabular Editor 2. (With the exception of the 'TE2 Script Compiler Update.csx' script which applies to Tabular Editor 2 only.) 55 | 56 | The default behaviour is to export/import into/from Tabular Editor 3 if it is detected, otherwise, Tabular Editor 2 will be used. This behaviour can be controlled by setting the variable 'TE3overTE2' to 'true' or 'false' in the export/import scripts. If the variable 'TE3overTE2' is set to 'false', then Tabular Editor 2 will be exported/imported into/from even if Tabular Editor 3 is installed. 57 | 58 | ### Run & Debug 59 | 60 | If you intend to to use the native debugger to run the Macro Management Scripts then you will also need to execute the command `dotnet script init` to reset the environemnt before running the debugger. 61 | 62 | ## Warning 63 | 64 | Use at your own risk! Although I put much care into ensuring these scripts work, I offer no support if the use of these scripts breaks either Tabular Editor or a model you are working on. 65 | -------------------------------------------------------------------------------- /Resources/GetVersion.csx: -------------------------------------------------------------------------------- 1 | #load "..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | #r "Microsoft.VisualBasic" 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using TabularEditor; 10 | using TabularEditor.TOMWrapper; 11 | using TabularEditor.TOMWrapper.Utils; 12 | using TabularEditor.UI; 13 | using TabularEditor.Scripting; 14 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 15 | using Microsoft.VisualBasic; 16 | 17 | int version = typeof(TabularEditor.TOMWrapper.Model).Assembly.GetName().Version.Major; 18 | if (version == 2) 19 | { 20 | // Tabular Editor 2.x specific code 21 | } 22 | if (version == 3) 23 | { 24 | // Tabular Editor 3.x specific code 25 | } -------------------------------------------------------------------------------- /Resources/InputBox.csx: -------------------------------------------------------------------------------- 1 | #load "..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | #r "Microsoft.VisualBasic" 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using TabularEditor; 10 | using TabularEditor.TOMWrapper; 11 | using TabularEditor.TOMWrapper.Utils; 12 | using TabularEditor.UI; 13 | using TabularEditor.Scripting; 14 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 15 | using Microsoft.VisualBasic; 16 | using System.Windows.Forms; 17 | 18 | Func InputBox = (string promptText, string titleText, string defaultText) => 19 | { 20 | 21 | var labelText = new Label() 22 | { 23 | Text = promptText, 24 | Dock = DockStyle.Fill, 25 | }; 26 | 27 | var textboxText = new TextBox() 28 | { 29 | Text = defaultText, 30 | Dock = DockStyle.Bottom 31 | }; 32 | 33 | var panelButtons = new Panel() 34 | { 35 | Height = 30, 36 | Dock = DockStyle.Bottom 37 | }; 38 | 39 | var buttonOK = new Button() 40 | { 41 | Text = "OK", 42 | DialogResult = DialogResult.OK, 43 | Top = 8, 44 | Left = 120 45 | }; 46 | 47 | var buttonCancel = new Button() 48 | { 49 | Text = "Cancel", 50 | DialogResult = DialogResult.Cancel, 51 | Top = 8, 52 | Left = 204 53 | }; 54 | 55 | var formInputBox = new Form() 56 | { 57 | Text = titleText, 58 | Height = 143, 59 | Padding = new System.Windows.Forms.Padding(8), 60 | FormBorderStyle = FormBorderStyle.FixedDialog, 61 | MinimizeBox = false, 62 | MaximizeBox = false, 63 | StartPosition = FormStartPosition.CenterScreen, 64 | AcceptButton = buttonOK, 65 | CancelButton = buttonCancel 66 | }; 67 | 68 | formInputBox.Controls.AddRange(new Control[] { labelText, textboxText, panelButtons }); 69 | panelButtons.Controls.AddRange(new Control[] { buttonOK, buttonCancel }); 70 | 71 | return formInputBox.ShowDialog() == DialogResult.OK ? textboxText.Text : null; 72 | 73 | }; 74 | 75 | var stringDefault = "Time Intelligence"; 76 | 77 | stringDefault = InputBox( 78 | "Provide the common name for time/period intelligence tables. This is used to determine the calculation group's suffix, e.g. '(ISO)' in 'Time Intelligence (ISO)'.", 79 | "Default Time Intelligence Name", 80 | stringDefault 81 | ); 82 | 83 | stringDefault.Output(); -------------------------------------------------------------------------------- /Resources/SelectObject.csx: -------------------------------------------------------------------------------- 1 | #load "..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | #r "Microsoft.VisualBasic" 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using TabularEditor; 10 | using TabularEditor.TOMWrapper; 11 | using TabularEditor.TOMWrapper.Utils; 12 | using TabularEditor.UI; 13 | using TabularEditor.Scripting; 14 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 15 | using Microsoft.VisualBasic; 16 | 17 | var selectedObject = ScriptHelper.SelectObject( 18 | new TabularNamedObject[] 19 | { 20 | Model.Tables["Account"], 21 | (Model.Tables["Dates"].Columns["Date"] as DataColumn), 22 | Model.Tables["Transactional"].Measures["Total Actual COGS"] 23 | } 24 | ); 25 | 26 | selectedObject.Output(); -------------------------------------------------------------------------------- /Resources/SelectString.csx: -------------------------------------------------------------------------------- 1 | #load "..\Management\Common Library.csx" 2 | // *** The above assemblies are required for the C# scripting environment, remove in Tabular Editor *** 3 | #r "Microsoft.VisualBasic" 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using TabularEditor; 10 | using TabularEditor.TOMWrapper; 11 | using TabularEditor.TOMWrapper.Utils; 12 | using TabularEditor.UI; 13 | using TabularEditor.Scripting; 14 | // *** The above namespaces are required for the C# scripting environment, remove in Tabular Editor *** 15 | using Microsoft.VisualBasic; 16 | using System.Windows.Forms; 17 | 18 | Func, string, string> SelectString = (IList listText, string titleText) => 19 | { 20 | 21 | var listboxText = new ListBox() 22 | { 23 | Dock = DockStyle.Fill 24 | }; 25 | 26 | var panelButtons = new Panel() 27 | { 28 | Height = 22, 29 | Dock = DockStyle.Bottom 30 | }; 31 | 32 | var buttonOK = new Button() 33 | { 34 | Text = "OK", 35 | DialogResult = DialogResult.OK, 36 | Left = 120 37 | }; 38 | 39 | var buttonCancel = new Button() 40 | { 41 | Text = "Cancel", 42 | DialogResult = DialogResult.Cancel, 43 | Left = 204 44 | }; 45 | 46 | var formInputBox = new Form() 47 | { 48 | Text = titleText, 49 | Padding = new System.Windows.Forms.Padding(8), 50 | FormBorderStyle = FormBorderStyle.FixedDialog, 51 | MinimizeBox = false, 52 | MaximizeBox = false, 53 | StartPosition = FormStartPosition.CenterScreen, 54 | AcceptButton = buttonOK, 55 | CancelButton = buttonCancel 56 | }; 57 | 58 | listboxText.Items.AddRange(listText.ToArray()); 59 | listboxText.SelectedItem = listText[0]; 60 | formInputBox.Controls.AddRange(new Control[] { listboxText, panelButtons }); 61 | panelButtons.Controls.AddRange(new Control[] { buttonOK, buttonCancel }); 62 | 63 | return formInputBox.ShowDialog() == DialogResult.OK ? listboxText.SelectedItem.ToString() : null; 64 | 65 | }; 66 | 67 | var stringSelected = SelectString( 68 | new string[] { "123", "abc", "456", "def", "789", "ghi" }, 69 | "Select list item:" 70 | ); 71 | 72 | stringSelected.Output(); 73 | 74 | var tmp = Model.AllColumns; 75 | -------------------------------------------------------------------------------- /omnisharp.json: -------------------------------------------------------------------------------- 1 | { 2 | "script": { 3 | "enableScriptNuGetReferences": true, 4 | "defaultTargetFramework": "net6.0" 5 | } 6 | } --------------------------------------------------------------------------------