{ "Actions": [ { "Id": 1, "Name": "Formatting\\Beginner\\Format Numeric Measures", "Enabled": "true", "Execute": "// This script is meant to format all measures with a default formatstring\nforeach (var ms in Selected.Measures) {\n//Don't set format string on hidden measures\n\tif (ms.IsHidden) continue;\n// If the format string is empty continue. \n\tif (!string.IsNullOrWhiteSpace(ms.FormatString)) continue;\n//If the data type is int set a whole number format string\n\tif (ms.DataType == DataType.Int64) ms.FormatString = \"#,##0\";\n//If the datatype is double or decimal \n\tif (ms.DataType == DataType.Double || ms.DataType == DataType.Decimal) {\n //and the name contains # or QTY then set the format string to a whole number\n\t\tif (ms.Name.Contains(\"#\")\n\t\t\t|| ms.Name.IndexOf(\"QTY\", StringComparison.OrdinalIgnoreCase) >= 0) ms.FormatString = \"#,##0\";\n\t\t//otherwise set it a decimal format string. \n else ms.FormatString = \"#,##0.00\";\n\t}\n}", "Tooltip": "", "ValidContexts": "Measure" }, { "Id": 2, "Name": "Modelling\\Advanced\\Power BI\\Configure Incremental refresh policy", "Enabled": "true", "Execute": "// This script will automatically generate an Incremental Refresh policy for a selected table\n// It is generated based on the selected column\n// It requires input from the user with a dialogue pop-up box.\nusing System.Drawing;\nusing System.Windows.Forms;\n\n// Hide the 'Running Macro' spinbox\nScriptHelper.WaitFormVisible = false;\n\n// Initialize Variables\nTable _Table = Model.Tables[0];\nstring _MExpression = \"\";\nColumn _Column = Model.AllColumns.ToList()[0];\nstring _ColumnName = \"\";\nDataType _ColumnDataType = DataType.DateTime;\n\ntry\n { \n // Select a Table for which you will configure Incremental Refresh.\n // The Refresh Policy will be enabled and configured for this table.\n _Table = \n Model.Tables.Where(\n\n // Exclude tables that already have a refresh policy\n t => \n t.EnableRefreshPolicy != true && \n\n // Include only 'Table' objects\n t.ObjectType == ObjectType.Table && \n\n // Exclude Calculated Tables\n t.Columns[0].Type != ColumnType.CalculatedTableColumn && \n\n // Exclude tables that have columns on the 'From' end of a Relationship\n t.Columns.Any(c => Model.Relationships.Any(r => r.FromColumn == c) ) && \n\n // Exclude tables that don't have a DateTime or Integer column\n (\n t.Columns.Any(c => c.DataType == DataType.DateTime) || \n t.Columns.Any(c => c.DataType == DataType.Int64)\n )\n ).SelectTable(null,\"Select a Table for which you will configure Incremental Refresh:\");\n \n _MExpression = _Table.Partitions[0].Expression;\n \n try\n {\n // Select the column to apply the Refresh Policy. \n // The M Expression will be modified using the name of this column.\n _Column = \n _Table.Columns.Where(\n\n // Include only DateTime or Int columns\n c => \n c.DataType == DataType.DateTime || \n c.DataType == DataType.Int64\n\n ).SelectColumn(null, \"Select a DateTime or DateKey (Int) Column to apply the Refresh Policy.\");\n \n _ColumnName = _Column.DaxObjectName;\n _ColumnDataType = _Column.DataType;\n \n try \n { // Test if 'RangeStart' exists\n Model.Expressions.Contains(Model.Expressions[\"RangeStart\"]);\n Info (\"RangeStart already exists!\");\n }\n catch\n {\n // Add RangeStart parameter\n Model.AddExpression( \n \"RangeStart\", \n @\"\n #datetime(2023, 01, 01, 0, 0, 0) meta\n [\n IsParameterQuery = true,\n IsParameterQueryRequired = true,\n Type = type datetime\n ]\"\n );\n \n // Success message for adding 'RangeStart'\n Info ( \"Created 'RangeStart' M Parameter!\" );\n }\n \n // Test if the RangeEnd parameter exists\n try \n { // Test if 'RangeEnd' exists\n Model.Expressions.Contains(Model.Expressions[\"RangeEnd\"]);\n Info (\"RangeEnd already exists!\");\n }\n catch\n {\n // Add RangeEnd parameter\n Model.AddExpression( \n \"RangeEnd\", \n @\"\n #datetime(2023, 31, 01, 0, 0, 0) meta\n [\n IsParameterQuery = true,\n IsParameterQueryRequired = true,\n Type = type datetime\n ]\"\n );\n \n // Success message for adding 'RangeEnd'\n Info ( \"Created 'RangeEnd' M Parameter!\" );\n \n }\n \n // Incremental Refresh Configuration\n // Input box config\n Font _fontConfig = new Font(\"Segoe UI\", 11);\n \n // Label for how long data should be stored\n var storeDataLabel = new Label();\n storeDataLabel.Text = \"Store data in the last:\";\n storeDataLabel.Location = new Point(20, 20);\n storeDataLabel.AutoSize = true;\n storeDataLabel.Font = _fontConfig;\n \n // User input for how long data should be stored\n var storeDataTextBox = new TextBox();\n storeDataTextBox.Location = new Point(storeDataLabel.Location.X + TextRenderer.MeasureText(storeDataLabel.Text, storeDataLabel.Font).Width + 20, storeDataLabel.Location.Y);\n storeDataTextBox.Size = new Size(100, 20);\n storeDataTextBox.Text = \"3\";\n storeDataTextBox.Font = _fontConfig;\n \n // User selection for how long data should be stored (granularity)\n var storeDataComboBox = new ComboBox();\n storeDataComboBox.Location = new Point(storeDataTextBox.Location.X + storeDataTextBox.Width + 20, storeDataLabel.Location.Y);\n storeDataComboBox.Size = new Size(100, 20);\n storeDataComboBox.DropDownStyle = ComboBoxStyle.DropDownList;\n storeDataComboBox.Items.AddRange(new object[] { \"days\", \"months\", \"quarters\", \"years\" });\n storeDataComboBox.SelectedIndex = 3;\n storeDataComboBox.Font = _fontConfig;\n \n // Label for how much data should be refreshed\n var refreshDataLabel = new Label();\n refreshDataLabel.Text = \"Refresh data in the last:\";\n refreshDataLabel.Location = new Point(20, storeDataLabel.Location.Y + storeDataLabel.Height + 15);\n refreshDataLabel.AutoSize = true;\n refreshDataLabel.Font = _fontConfig;\n \n // User input for how much data should be refreshed\n var refreshDataTextBox = new TextBox();\n refreshDataTextBox.Location = new Point(storeDataTextBox.Location.X, refreshDataLabel.Location.Y);\n refreshDataTextBox.Size = new Size(100, 20);\n refreshDataTextBox.Text = \"30\";\n refreshDataTextBox.Font = _fontConfig;\n \n // User selection for how much data should be refreshed (Period)\n var refreshDataComboBox = new ComboBox();\n refreshDataComboBox.Location = new Point(storeDataComboBox.Location.X, refreshDataLabel.Location.Y);\n refreshDataComboBox.Size = new Size(100, 20);\n refreshDataComboBox.DropDownStyle = ComboBoxStyle.DropDownList;\n refreshDataComboBox.Items.AddRange(new object[] { \"days\", \"months\", \"quarters\", \"years\" });\n refreshDataComboBox.SelectedIndex = 0;\n refreshDataComboBox.Font = _fontConfig;\n \n // User input to refresh full periods or not\n var fullPeriodsCheckBox = new CheckBox();\n fullPeriodsCheckBox.Text = \"Refresh only full periods\";\n fullPeriodsCheckBox.Location = new Point(storeDataLabel.Location.X + 3, refreshDataLabel.Location.Y + refreshDataLabel.Height + 15);\n fullPeriodsCheckBox.AutoSize = true;\n fullPeriodsCheckBox.Font = _fontConfig;\n \n // Form OK button\n var okButton = new Button();\n okButton.Text = \"OK\";\n okButton.Location = new Point(storeDataLabel.Location.X, fullPeriodsCheckBox.Location.Y + fullPeriodsCheckBox.Height + 15);\n okButton.MinimumSize = new Size(80, 25);\n okButton.AutoSize = true;\n okButton.DialogResult = DialogResult.OK;\n okButton.Font = _fontConfig;\n \n // Form cancel button\n var cancelButton = new Button();\n cancelButton.Text = \"Cancel\";\n cancelButton.Location = new Point(okButton.Location.X + okButton.Width + 10, okButton.Location.Y);\n cancelButton.MinimumSize = new Size(80, 25);\n cancelButton.AutoSize = true;\n cancelButton.DialogResult = DialogResult.Cancel;\n cancelButton.Font = _fontConfig;\n \n // Adjust the Location of the storeDataLabel to align with the storeDataTextBox\n storeDataLabel.Location = new Point(storeDataLabel.Location.X, storeDataLabel.Location.Y + 4);\n refreshDataLabel.Location = new Point(refreshDataLabel.Location.X, refreshDataLabel.Location.Y + 4);\n \n // Form config\n var form = new Form();\n form.Text = \"Incremental Refresh configuration:\";\n form.AutoSize = true;\n form.MinimumSize = new Size(450, 0);\n form.FormBorderStyle = FormBorderStyle.FixedDialog;\n form.MaximizeBox = false;\n form.MinimizeBox = false;\n \n // Open the dialogue in the center of the screen\n form.StartPosition = FormStartPosition.CenterScreen;\n \n // Set the AutoScaleMode property to Dpi\n form.AutoScaleMode = AutoScaleMode.Dpi;\n \n // Add controls to form specified above\n form.Controls.Add(storeDataLabel);\n form.Controls.Add(storeDataTextBox);\n form.Controls.Add(storeDataComboBox);\n form.Controls.Add(refreshDataLabel);\n form.Controls.Add(refreshDataTextBox);\n form.Controls.Add(refreshDataComboBox);\n form.Controls.Add(fullPeriodsCheckBox);\n form.Controls.Add(okButton);\n form.Controls.Add(cancelButton);\n \n // Draw the form\n var result = form.ShowDialog();\n \n // Get the values of the user input if entered\n if (result == DialogResult.OK)\n {\n // Enables the refresh policy\n _Table.EnableRefreshPolicy = true;\n \n var storeDataValue = storeDataTextBox.Text;\n var storeDataComboBoxValue = storeDataComboBox.SelectedItem.ToString();\n var refreshDataValue = refreshDataTextBox.Text;\n var refreshDataComboBoxValue = refreshDataComboBox.SelectedItem.ToString();\n var fullPeriodsChecked = fullPeriodsCheckBox.Checked;\n \n // Display the input values in a message box\n var message = string.Format(\n \"Store data in the last: {0} {1}\" + \n \"\\nRefresh data in the last: {2} {3}\" + \n \"\\nRefresh only full periods: {4}\",\n storeDataTextBox.Text,\n storeDataComboBox.SelectedItem.ToString(),\n refreshDataTextBox.Text,\n refreshDataComboBox.SelectedItem.ToString(),\n fullPeriodsCheckBox.Checked);\n \n Info(message);\n \n // Convert StoreDataGranularity to correct TOM Property\n RefreshGranularityType StoreDataGranularity = RefreshGranularityType.Day;\n switch (storeDataComboBox.SelectedItem.ToString())\n {\n case \"years\":\n StoreDataGranularity = RefreshGranularityType.Year;\n break;\n \n case \"quarters\":\n StoreDataGranularity = RefreshGranularityType.Quarter;\n break;\n \n case \"months\":\n StoreDataGranularity = RefreshGranularityType.Month;\n break;\n \n case \"days\":\n StoreDataGranularity = RefreshGranularityType.Day;\n break; \n \n default:\n Error(\"Bad selection for Incremental Granularity.\");\n break;\n }\n\n \n // Convert IncrementalGranularity to correct TOM Property\n RefreshGranularityType IncrementalPeriodGranularity = RefreshGranularityType.Year;\n switch (refreshDataComboBox.SelectedItem.ToString())\n {\n case \"years\":\n IncrementalPeriodGranularity = RefreshGranularityType.Year;\n break;\n \n case \"quarters\":\n IncrementalPeriodGranularity = RefreshGranularityType.Quarter;\n break;\n \n case \"months\":\n IncrementalPeriodGranularity = RefreshGranularityType.Month;\n break;\n \n case \"days\":\n IncrementalPeriodGranularity = RefreshGranularityType.Day;\n break; \n \n default:\n Error ( \"Bad selection for Incremental Granularity.\" );\n break;\n }\n \n // Convert RefreshCompletePeriods checkbox to correct TOM property\n int RefreshCompletePeriods;\n if ( fullPeriodsCheckBox.Checked == true )\n { \n RefreshCompletePeriods = -1;\n }\n else\n {\n RefreshCompletePeriods = 0;\n }\n \n // Set incremental window: period to be refreshed\n _Table.IncrementalGranularity = IncrementalPeriodGranularity;\n \n // Default: 30 days - change # if you want\n _Table.IncrementalPeriods = Convert.ToInt16(refreshDataTextBox.Text);\n \n // Only refresh complete days. Change to 0 if you don't want.\n _Table.IncrementalPeriodsOffset = RefreshCompletePeriods;\n \n // Set rolling window: period to be archived\n // Granularity = day, can change to month, quarter, year...\n _Table.RollingWindowGranularity = StoreDataGranularity;\n \n // Keep data for 1 year. Includes 1 full year and current partial year \n // i.e. if it is Nov 2023, keeps data from Jan 1, 2022. \n // On Jan 1, 2024, it will drop 2022 automatically.\n _Table.RollingWindowPeriods = Convert.ToInt16(storeDataTextBox.Text);\n \n // If the selected date column is an integer of type YYYYMMDD...\n if ( _ColumnDataType == DataType.Int64 )\n {\n // Add DateTimeToInt Function\n var _DateTimeToInt = \n Model.AddExpression( \n \"fxDateTimeToInt\", \n @\"(x as datetime) => Date.Year(x) * 10000 + Date.Month(x) * 100 + Date.Day(x)\"\n );\n \n _DateTimeToInt.SetAnnotation(\"PBI_ResultType\", \"Function\");\n _DateTimeToInt.Kind = ExpressionKind.M;\n \n // Source expression obtained from the original M partition\n _Table.SourceExpression = \n \n // Gets expression before final \"in\" keyword\n _MExpression.Split(\"\\nin\")[0].TrimEnd() +\n \n // Adds comma and newline\n \",\\n\" +\n \n // Adds step called \"Incremental Refresh\" for filtering\n @\" #\"\"Incremental Refresh\"\" = Table.SelectRows( \" +\n \n // Gets name of last step (after \"in\" keyword)\n _MExpression.Split(\"\\nin\")[1].TrimStart() +\n \n // Adds 'each' keyword\n @\", each \" +\n \n // Bases incremental refresh on current column name\n _ColumnName +\n \n // Greater than or equal to RangeStart\n @\" >= fxDateTimeToInt ( #\"\"RangeStart\"\" ) and \" +\n \n // and\n _ColumnName +\n \n // Less than RangeEnd\n @\" < fxDateTimeToInt ( #\"\"RangeEnd\"\" ) )\" +\n \n // re-add 'in' keyword\n \"\\nin\\n\" +\n \n // Reference final step just added\n @\" #\"\"Incremental Refresh\"\"\";\n }\n \n \n // Otherwise treat it like a normal date/datetime column\n else\n {\n // Source expression obtained from the original M partition\n _Table.SourceExpression = \n // Gets expression before final \"in\" keyword\n _MExpression.Split(\"\\nin\")[0].TrimEnd() +\n \n // Adds comma and newline\n \",\\n\" +\n \n // Adds step called \"Incremental Refresh\" for filtering\n @\" #\"\"Incremental Refresh\"\" = Table.SelectRows( \" +\n \n // Gets name of last step (after \"in\" keyword)\n _MExpression.Split(\"\\nin\")[1].TrimStart() +\n \n // Adds 'each' keyword\n @\", each \" +\n \n // Bases incremental refresh on current column name\n _ColumnName +\n \n // Greater than or equal to RangeStart\n @\" >= Date.From ( #\"\"RangeStart\"\" ) and \" +\n \n // and\n _ColumnName +\n \n // Less than RangeEnd\n @\" < Date.From ( #\"\"RangeEnd\"\" ) )\" +\n \n // re-add 'in' keyword\n \"\\nin\\n\" +\n \n // Reference final step just added\n @\" #\"\"Incremental Refresh\"\"\";\n }\n \n // Success message for Refresh Policy configuration\n Info ( \n \"Successfully configured the Incremental Refresh policy.\\n\" + \n \"\\nSelect the table and right-click on 'Apply Refresh Policy...'\" + \n \"\\nSelect & peform a 'Full Refresh' of all new policy partitons that are created.\" \n );\n }\n else if (result == DialogResult.Cancel)\n {\n // if the user clicks the Cancel button, close the form and exit the script\n form.Close();\n Error ( \"Cancelled configuration! Ending script without changes.\" );\n return;\n }\n }\n catch\n {\n Error( \"No valid column selected! Ending script without changes.\" );\n }\n \n}\ncatch\n{\n Error( \"No valid table selected! Ending script without changes.\" );\n}", "Tooltip": "", "ValidContexts": "Model" }, { "Id": 3, "Name": "Modelling\\Advanced\\Create a Date Table", "Enabled": "true", "Execute": "// To use this C# Script:\n//\n// 1. Run the script\n// 2. Select the column that has the earliest date\n// 3. Select the column that has the latest date\n\n// List of all DateTime columns in the model\nvar _dateColumns = Model.AllColumns.Where(c => c.DataType == DataType.DateTime ).ToList();\n\n// Select the column with the earliest date in the model\ntry\n{\n string _EarliestDate = \n SelectColumn(\n _dateColumns, \n null, \n \"Select the Column with the Earliest Date:\"\n ).DaxObjectFullName;\n \n try\n {\n // Select the column with the latest date in the model\n string _LatestDate = \n SelectColumn(\n _dateColumns, \n null, \n \"Select the Column with the Latest Date:\"\n ).DaxObjectFullName;\n \n \n // Create measure for reference date\n var _RefDateMeasure = _dateColumns[0].Table.AddMeasure(\n \"RefDate\",\n \"CALCULATE ( MAX ( \" + _LatestDate + \" ), REMOVEFILTERS ( ) )\"\n );\n \n \n // Formatted date table DAX\n // Based on date table from https://www.sqlbi.com/topics/date-table/\n // To adjust, copy everything after the @\" into a DAX query window & replace\n \n var _DateDaxExpression = @\"-- Reference date for the latest date in the report\n -- Until when the business wants to see data in reports\n VAR _Refdate_Measure = [RefDate]\n VAR _Today = TODAY ( )\n \n -- Replace with \"\"Today\"\" if [RefDate] evaluates blank\n VAR _Refdate = IF ( ISBLANK ( _Refdate_Measure ), _Today, _Refdate_Measure )\n VAR _RefYear = YEAR ( _Refdate )\n VAR _RefQuarter = _RefYear * 100 + QUARTER(_Refdate)\n VAR _RefMonth = _RefYear * 100 + MONTH(_Refdate)\n VAR _RefWeek_EU = _RefYear * 100 + WEEKNUM(_Refdate, 2)\n \n -- Earliest date in the model scope\n VAR _EarliestDate = DATE ( YEAR ( MIN ( \" + _EarliestDate + @\" ) ) - 2, 1, 1 )\n VAR _EarliestDate_Safe = MIN ( _EarliestDate, DATE ( YEAR ( _Today ) + 1, 1, 1 ) )\n \n -- Latest date in the model scope\n VAR _LatestDate_Safe = DATE ( YEAR ( _Refdate ) + 2, 12, 1 )\n \n ------------------------------------------\n -- Base calendar table\n VAR _Base_Calendar = CALENDAR ( _EarliestDate_Safe, _LatestDate_Safe )\n ------------------------------------------\n \n \n \n ------------------------------------------\n VAR _IntermediateResult = \n ADDCOLUMNS ( _Base_Calendar,\n \n ------------------------------------------\n \"\"Calendar Year Number (ie 2021)\"\", --|\n YEAR ([Date]), --|-- Year\n --|\n \"\"Calendar Year (ie 2021)\"\", --|\n FORMAT ([Date], \"\"YYYY\"\"), --|\n ------------------------------------------\n \n ------------------------------------------\n \"\"Calendar Quarter Year (ie Q1 2021)\"\", --|\n \"\"Q\"\" & --|-- Quarter\n CONVERT(QUARTER([Date]), STRING) & --|\n \"\" \"\" & --|\n CONVERT(YEAR([Date]), STRING), --|\n --|\n \"\"Calendar Year Quarter (ie 202101)\"\", --|\n YEAR([Date]) * 100 + QUARTER([Date]), --|\n ------------------------------------------\n \n ------------------------------------------\n \"\"Calendar Month Year (ie Jan 21)\"\", --|\n FORMAT ( [Date], \"\"MMM YY\"\" ), --|-- Month\n --|\n \"\"Calendar Year Month (ie 202101)\"\", --|\n YEAR([Date]) * 100 + MONTH([Date]), --|\n --|\n \"\"Calendar Month (ie Jan)\"\", --|\n FORMAT ( [Date], \"\"MMM\"\" ), --|\n --|\n \"\"Calendar Month # (ie 1)\"\", --|\n MONTH ( [Date] ), --|\n ------------------------------------------\n \n ------------------------------------------\n \"\"Calendar Week EU (ie WK25)\"\", --|\n \"\"WK\"\" & WEEKNUM( [Date], 2 ), --|-- Week\n --|\n \"\"Calendar Week Number EU (ie 25)\"\", --|\n WEEKNUM( [Date], 2 ), --|\n --|\n \"\"Calendar Year Week Number EU (ie 202125)\"\", --|\n YEAR ( [Date] ) * 100 --|\n + --|\n WEEKNUM( [Date], 2 ), --|\n --|\n \"\"Calendar Week US (ie WK25)\"\", --|\n \"\"WK\"\" & WEEKNUM( [Date], 1 ), --|\n --|\n \"\"Calendar Week Number US (ie 25)\"\", --|\n WEEKNUM( [Date], 1 ), --|\n --|\n \"\"Calendar Year Week Number US (ie 202125)\"\", --|\n YEAR ( [Date] ) * 100 --|\n + --|\n WEEKNUM( [Date], 1 ), --|\n --|\n \"\"Calendar Week ISO (ie WK25)\"\", --|\n \"\"WK\"\" & WEEKNUM( [Date], 21 ), --|\n --|\n \"\"Calendar Week Number ISO (ie 25)\"\", --|\n WEEKNUM( [Date], 21 ), --|\n --|\n \"\"Calendar Year Week Number ISO (ie 202125)\"\",--|\n YEAR ( [Date] ) * 100 --|\n + --|\n WEEKNUM( [Date], 21 ), --|\n ------------------------------------------\n \n ------------------------------------------\n \"\"Weekday Short (i.e. Mon)\"\", --|\n FORMAT ( [Date], \"\"DDD\"\" ), --|-- Weekday\n --|\n \"\"Weekday Name (i.e. Monday)\"\", --|\n FORMAT ( [Date], \"\"DDDD\"\" ), --|\n --|\n \"\"Weekday Number EU (i.e. 1)\"\", --|\n WEEKDAY ( [Date], 2 ), --|\n ------------------------------------------\n \n ------------------------------------------\n \"\"Calendar Month Day (i.e. Jan 05)\"\", --|\n FORMAT ( [Date], \"\"MMM DD\"\" ), --|-- Day\n --|\n \"\"Calendar Month Day (i.e. 0105)\"\", --|\n MONTH([Date]) * 100 --|\n + --|\n DAY([Date]), --|\n --|\n \"\"YYYYMMDD\"\", --|\n YEAR ( [Date] ) * 10000 --|\n + --|\n MONTH ( [Date] ) * 100 --|\n + --|\n DAY ( [Date] ), --|\n ------------------------------------------\n \n \n ------------------------------------------\n \"\"IsDateInScope\"\", --|\n [Date] <= _Refdate --|-- Boolean\n && --|\n YEAR([Date]) > YEAR(_EarliestDate), --|\n --|\n \"\"IsBeforeThisMonth\"\", --|\n [Date] <= EOMONTH ( _Refdate, -1 ), --|\n --|\n \"\"IsLastMonth\"\", --|\n [Date] <= EOMONTH ( _Refdate, 0 ) --|\n && --|\n [Date] > EOMONTH ( _Refdate, -1 ), --|\n --|\n \"\"IsYTD\"\", --|\n MONTH([Date]) --|\n <= --|\n MONTH(EOMONTH ( _Refdate, 0 )), --|\n --|\n \"\"IsActualToday\"\", --|\n [Date] = _Today, --|\n --|\n \"\"IsRefDate\"\", --|\n [Date] = _Refdate, --|\n --|\n \"\"IsHoliday\"\", --|\n MONTH([Date]) * 100 --|\n + --|\n DAY([Date]) --|\n IN {0101, 0501, 1111, 1225}, --|\n --|\n \"\"IsWeekday\"\", --|\n WEEKDAY([Date], 2) --|\n IN {1, 2, 3, 4, 5}) --|\n ------------------------------------------\n \n VAR _Result = \n \n --------------------------------------------\n ADDCOLUMNS ( --|\n _IntermediateResult, --|-- Boolean #2\n \"\"IsThisYear\"\", --|\n [Calendar Year Number (ie 2021)] --|\n = _RefYear, --|\n --|\n \"\"IsThisMonth\"\", --|\n [Calendar Year Month (ie 202101)] --|\n = _RefMonth, --|\n --|\n \"\"IsThisQuarter\"\", --|\n [Calendar Year Quarter (ie 202101)] --|\n = _RefQuarter, --|\n --|\n \"\"IsThisWeek\"\", --|\n [Calendar Year Week Number EU (ie 202125)]--|\n = _RefWeek_EU --|\n ) --|\n --------------------------------------------\n \n RETURN \n _Result\";\n \n // Create date table\n var _date = Model.AddCalculatedTable(\n \"Date\",\n _DateDaxExpression\n );\n \n //-------------------------------------------------------------------------------------------//\n \n // Sort by...\n \n // Sort Weekdays\n (_date.Columns[\"Weekday Name (i.e. Monday)\"] as CalculatedTableColumn).SortByColumn = (_date.Columns[\"Weekday Number EU (i.e. 1)\"] as CalculatedTableColumn);\n (_date.Columns[\"Weekday Short (i.e. Mon)\"] as CalculatedTableColumn).SortByColumn = (_date.Columns[\"Weekday Number EU (i.e. 1)\"] as CalculatedTableColumn);\n \n // Sort Weeks\n (_date.Columns[\"Calendar Week EU (ie WK25)\"] as CalculatedTableColumn).SortByColumn = (_date.Columns[\"Calendar Week Number EU (ie 25)\"] as CalculatedTableColumn);\n (_date.Columns[\"Calendar Week ISO (ie WK25)\"] as CalculatedTableColumn).SortByColumn = (_date.Columns[\"Calendar Week Number ISO (ie 25)\"] as CalculatedTableColumn);\n (_date.Columns[\"Calendar Week US (ie WK25)\"] as CalculatedTableColumn).SortByColumn = (_date.Columns[\"Calendar Week Number US (ie 25)\"] as CalculatedTableColumn);\n \n // Sort Months\n (_date.Columns[\"Calendar Month (ie Jan)\"] as CalculatedTableColumn).SortByColumn = (_date.Columns[\"Calendar Month # (ie 1)\"] as CalculatedTableColumn);\n (_date.Columns[\"Calendar Month Day (i.e. Jan 05)\"] as CalculatedTableColumn).SortByColumn = (_date.Columns[\"Calendar Month Day (i.e. 0105)\"] as CalculatedTableColumn);\n (_date.Columns[\"Calendar Month Year (ie Jan 21)\"] as CalculatedTableColumn).SortByColumn = (_date.Columns[\"Calendar Year Month (ie 202101)\"] as CalculatedTableColumn);\n \n // Sort Quarters\n (_date.Columns[\"Calendar Quarter Year (ie Q1 2021)\"] as CalculatedTableColumn).SortByColumn = (_date.Columns[\"Calendar Year Quarter (ie 202101)\"] as CalculatedTableColumn);\n \n // Sort Years\n (_date.Columns[\"Calendar Year (ie 2021)\"] as CalculatedTableColumn).SortByColumn = (_date.Columns[\"Calendar Year Number (ie 2021)\"] as CalculatedTableColumn);\n \n \n //-------------------------------------------------------------------------------------------//\n \n \n // For all the columns in the date table:\n foreach (var c in _date.Columns )\n {\n c.DisplayFolder = \"7. Boolean Fields\";\n c.IsHidden = true;\n \n // Organize the date table into folders\n if ( ( c.DataType == DataType.DateTime & c.Name.Contains(\"Date\") ) )\n {\n c.DisplayFolder = \"6. Calendar Date\";\n c.IsHidden = false;\n c.IsKey = true;\n }\n \n if ( c.Name == \"YYMMDDDD\" )\n {\n c.DisplayFolder = \"6. Calendar Date\";\n c.IsHidden = true;\n }\n \n if ( c.Name.Contains(\"Year\") & c.DataType != DataType.Boolean )\n {\n c.DisplayFolder = \"1. Year\";\n c.IsHidden = false;\n }\n \n if ( c.Name.Contains(\"Week\") & c.DataType != DataType.Boolean )\n {\n c.DisplayFolder = \"4. Week\";\n c.IsHidden = true;\n }\n \n if ( c.Name.Contains(\"day\") & c.DataType != DataType.Boolean )\n {\n c.DisplayFolder = \"5. Weekday / Workday\\\\Weekday\";\n c.IsHidden = false;\n }\n \n if ( c.Name.Contains(\"Month\") & c.DataType != DataType.Boolean )\n {\n c.DisplayFolder = \"3. Month\";\n c.IsHidden = false;\n }\n \n if ( c.Name.Contains(\"Quarter\") & c.DataType != DataType.Boolean )\n {\n c.DisplayFolder = \"2. Quarter\";\n c.IsHidden = false;\n }\n \n }\n \n // Mark as date table\n _date.DataCategory = \"Time\";\n \n \n //-------------------------------------------------------------------------------------------//\n \n \n // Create Workdays MTD, QTD, YTD logic \n // (separate into measures & calc. column to be easier to maintain)\n //\n // Add calculated columns for Workdays MTD, QTD, YTD\n \n string _WorkdaysDax = @\"VAR _Holidays =\n CALCULATETABLE (\n DISTINCT ('Date'[Date]),\n 'Date'[IsHoliday] <> TRUE\n )\n VAR _WeekdayName = CALCULATE ( SELECTEDVALUE ( 'Date'[Weekday Short (i.e. Mon)] ) )\n VAR _WeekendDays = SWITCH (\n _WeekdayName,\n \"\"Sat\"\", 2,\n \"\"Sun\"\", 3,\n 0\n )\n VAR _WorkdaysMTD =\n CALCULATE (\n NETWORKDAYS (\n CALCULATE (\n MIN ('Date'[Date]),\n ALLEXCEPT ('Date', 'Date'[Calendar Month Year (ie Jan 21)])\n ),\n CALCULATE (MAX ('Date'[Date]) - _WeekendDays),\n 1,\n _Holidays\n )\n )\n + 1\n RETURN\n IF (_WorkdaysMTD < 1, 1, _WorkdaysMTD)\";\n \n _date.AddCalculatedColumn(\n \"Workdays MTD\",\n _WorkdaysDax,\n \"5. Weekday / Workday\\\\Workdays\"\n );\n \n _date.AddCalculatedColumn(\n \"Workdays QTD\",\n _WorkdaysDax.Replace(\"'Date'[Calendar Month Year (ie Jan 21)]\", \"'Date'[Calendar Quarter Year (ie Q1 2021)]\"),\n \"5. Weekday / Workday\\\\Workdays\"\n );\n \n _date.AddCalculatedColumn(\n \"Workdays YTD\",\n _WorkdaysDax.Replace(\"'Date'[Calendar Month Year (ie Jan 21)]\", \"'Date'[Calendar Year (ie 2021)]\"),\n \"5. Weekday / Workday\\\\Workdays\"\n );\n \n \n //-------------------------------------------------------------------------------------------//\n \n \n // Create measures for showing how many workdays passed\n _WorkdaysDax = @\"CALCULATE(\n MAX( 'Date'[Workdays MTD] ),\n 'Date'[IsDateInScope] = TRUE\n )\";\n \n _date.AddMeasure(\n \"# Workdays MTD\",\n _WorkdaysDax,\n \"5. Weekday / Workday\\\\Measures\\\\# Workdays\"\n );\n \n _date.AddMeasure(\n \"# Workdays QTD\",\n _WorkdaysDax.Replace(\"MTD\", \"QTD\"),\n \"5. Weekday / Workday\\\\Measures\\\\# Workdays\"\n );\n \n _date.AddMeasure(\n \"# Workdays YTD\",\n _WorkdaysDax.Replace(\"MTD\", \"YTD\"),\n \"5. Weekday / Workday\\\\Measures\\\\# Workdays\"\n );\n \n // Create measures showing how many workdays are in the selected period\n \n _WorkdaysDax = @\"IF (\n HASONEVALUE ('Date'[Calendar Month Year (ie Jan 21)]),\n CALCULATE (\n MAX ('Date'[Workdays MTD]),\n VALUES ('Date'[Calendar Month Year (ie Jan 21)])\n )\n )\";\n \n _date.AddMeasure(\n \"# Workdays in Selected Month\",\n _WorkdaysDax,\n \"5. Weekday / Workday\\\\Measures\\\\# Workdays\"\n );\n \n _date.AddMeasure(\n \"# Workdays in Selected Quarter\",\n _WorkdaysDax.Replace(\"MTD\", \"QTD\").Replace(\"'Date'[Calendar Month Year (ie Jan 21)]\", \"'Date'[Calendar Quarter Year (ie Q1 2021)]\"),\n \"5. Weekday / Workday\\\\Measures\\\\# Workdays\"\n );\n \n _date.AddMeasure(\n \"# Workdays in Selected Year\",\n _WorkdaysDax.Replace(\"MTD\", \"YTD\").Replace(\"'Date'[Calendar Month Year (ie Jan 21)]\", \"'Date'[Calendar Year (ie 2021)]\"),\n \"5. Weekday / Workday\\\\Measures\\\\# Workdays\"\n );\n \n \n // Create measures showing how many workdays passed as a %\n \n _WorkdaysDax = @\"IF (\n HASONEVALUE ('Date'[Calendar Month Year (ie Jan 21)]),\n MROUND (\n DIVIDE ([# Workdays MTD], [# Workdays in Selected Month]),\n 0.01\n )\n )\";\n \n _date.AddMeasure(\n \"% Workdays MTD\",\n _WorkdaysDax,\n \"5. Weekday / Workday\\\\Measures\\\\# Workdays\"\n );\n \n _date.AddMeasure(\n \"% Workdays QTD\",\n _WorkdaysDax.Replace(\"MTD\", \"QTD\").Replace(\"'Date'[Calendar Month Year (ie Jan 21)]\", \"'Date'[Calendar Quarter Year (ie Q1 2021)]\").Replace(\"Month\", \"Quarter\"),\n \"5. Weekday / Workday\\\\Measures\\\\# Workdays\"\n );\n \n _date.AddMeasure(\n \"% Workdays YTD\",\n _WorkdaysDax.Replace(\"MTD\", \"YTD\").Replace(\"'Date'[Calendar Month Year (ie Jan 21)]\", \"'Date'[Calendar Year (ie 2021)]\").Replace(\"Month\", \"Year\"),\n \"5. Weekday / Workday\\\\Measures\\\\# Workdays\"\n );\n \n \n //-------------------------------------------------------------------------------------------//\n \n \n // Move the reference measure to the newly created 'Date' table.\n _RefDateMeasure.Delete();\n _RefDateMeasure = Model.Tables[\"Date\"].AddMeasure(\n \"RefDate\",\n \"CALCULATE ( MAX ( \" + _LatestDate + \" ), REMOVEFILTERS ( ) )\",\n \"0. Measures\"\n );\n \n _RefDateMeasure.IsHidden = true;\n \n Info ( \"Created a new, organized 'Date' table based on the template in the C# Script.\\nThe Earliest Date is taken from \" + _EarliestDate + \"\\nThe Latest Date is taken from \" + _LatestDate );\n \n }\n catch\n {\n Error( \"Latest column not selected! Ending script without making changes.\" );\n }\n}\ncatch\n{\n Error( \"Earliest column not selected! Ending script without making changes.\" );\n}\n\n", "Tooltip": "", "ValidContexts": "Model" }, { "Id": 4, "Name": "Modelling\\Advanced\\Create New M Parameter and Add it to Existing M Partitions", "Enabled": "true", "Execute": "// This script creates a new M Parameter as a 'Shared Expression'.\n// It will also find the default value in all M partitions and replace them with the parameter object name.\n//#r \"System.Drawing\"\n\nusing System.Drawing;\nusing System.Text.RegularExpressions;\nusing System.Windows.Forms;\n\n// Hide the 'Running Macro' spinbox\nScriptHelper.WaitFormVisible = false;\n\n// Initialize variables\nstring _ParameterName = \"New Parameter\";\nstring _ParameterValue = \"ParameterValue\";\n\n// WinForms prompt to get Parameter Name / Value input\nusing (Form prompt = new Form())\n{\n Font formFont = new Font(\"Segoe UI\", 11); \n\n // Prompt config\n prompt.AutoSize = true;\n prompt.MinimumSize = new Size(380, 120);\n prompt.Text = \"Create New M Parameter\";\n prompt.StartPosition = FormStartPosition.CenterScreen;\n\n // Find: label\n Label parameterNameLabel = new Label() { Text = \"Enter Name:\" };\n parameterNameLabel.Location = new Point(20, 20);\n parameterNameLabel.AutoSize = true;\n parameterNameLabel.Font = formFont;\n\n // Textbox for inputing the substring text\n TextBox parameterNameBox = new TextBox();\n parameterNameBox.Width = 200;\n parameterNameBox.Location = new Point(parameterNameLabel.Location.X + parameterNameLabel.Width + 20, parameterNameLabel.Location.Y - 4);\n parameterNameBox.SelectedText = \"New Parameter\";\n parameterNameBox.Font = formFont;\n\n // Replace: label\n Label parameterValueLabel = new Label() { Text = \"Enter Value:\" };\n parameterValueLabel.Location = new Point(parameterNameLabel.Location.X, parameterNameLabel.Location.Y + parameterNameLabel.Height + 20);\n parameterValueLabel.AutoSize = true;\n parameterValueLabel.Font = formFont;\n\n // Textbox for inputting the substring text\n TextBox parameterValueBox = new TextBox() { Left = parameterValueLabel.Right + 20, Top = parameterValueLabel.Location.Y - 4, Width = parameterNameBox.Width };\n parameterValueBox.SelectedText = \"Parameter Value\";\n parameterValueBox.Font = formFont;\n\n // OK Button\n Button okButton = new Button() { Text = \"Create\", Left = 20, Width = 75, Top = parameterValueBox.Location.Y + parameterValueBox.Height + 20 };\n okButton.MinimumSize = new Size(75, 25);\n okButton.AutoSize = true;\n okButton.Font = formFont;\n\n // Cancel Button\n Button cancelButton = new Button() { Text = \"Cancel\", Left = okButton.Location.X + okButton.Width + 10, Top = okButton.Location.Y };\n cancelButton.MinimumSize = new Size(75, 25);\n cancelButton.AutoSize = true;\n cancelButton.Font = formFont;\n\n // Button actions\n okButton.Click += (sender, e) => { _ParameterName = parameterNameBox.Text; _ParameterValue = parameterValueBox.Text; prompt.DialogResult = DialogResult.OK; };\n cancelButton.Click += (sender, e) => { prompt.DialogResult = DialogResult.Cancel; };\n\n prompt.AcceptButton = okButton;\n prompt.CancelButton = cancelButton;\n\n prompt.Controls.Add(parameterNameLabel);\n prompt.Controls.Add(parameterNameBox);\n prompt.Controls.Add(parameterValueLabel);\n prompt.Controls.Add(parameterValueBox);\n prompt.Controls.Add(okButton);\n prompt.Controls.Add(cancelButton);\n\n // The user clicked OK, so perform the find-and-replace logic\n if (prompt.ShowDialog() == DialogResult.OK)\n {\n\n // Creates the parameter\n Model.AddExpression( \n _ParameterName, \n @\"\n \"\"\" + _ParameterValue +\n @\"\"\" meta\n [\n IsParameterQuery = true,\n IsParameterQueryRequired = true,\n Type = type text\n ]\"\n );\n \n \n // Informs the user that the parameter was successfully created\n Info ( \n \"Successfully created a new parameter: \" + @\"\"\"\" +\n _ParameterName + @\"\"\"\" +\n \"\\nDefault value: \" + @\"\"\"\" +\n _ParameterValue + @\"\"\"\");\n \n \n // Finds the parameter default value in M Partitions & replaces with the parameter name\n string _Find = @\"\"\"\" + _ParameterValue + @\"\"\"\";\n string _Replace = @\"#\"\"\" + _ParameterName + @\"\"\"\";\n \n int _NrMPartitions = 0;\n int _NrReplacements = 0;\n var _ReplacementsList = new List();\n \n foreach ( var _Tables in Model.Tables )\n {\n foreach ( var _p in _Tables.Partitions )\n {\n if ( _p.SourceType == PartitionSourceType.M )\n {\n if ( _p.Expression != _p.Expression.Replace( _Find, _Replace ) )\n {\n _p.Expression = _p.Expression.Replace( _Find, _Replace );\n \n // Tracks which M partitions were replaced (and how many)\n _NrReplacements = _NrReplacements + 1;\n _ReplacementsList.Add( _p.Name );\n }\n \n // Counts the total # M Partitions\n _NrMPartitions = _NrMPartitions + 1;\n }\n }\n }\n \n \n // Makes a bulleted list of all the M partitions that were replaced\n string _ReplacedPartitions = \" • \" + String.Join(\"\\n • \", _ReplacementsList );\n \n \n // Informs \n // - Whether the Find & Replace was successful\n // - How many M partitions were replaced\n // - Which M partitions had the Find & Replace done\n Info (\n \"Successfully replaced\\n\\n \" +\n _Find + \n \"\\n\\n with: \\n\\n\" + \n _Replace + \n \"\\n\\n in \" + \n Convert.ToString(_NrReplacements) +\n \" of \" +\n Convert.ToString(_NrMPartitions) + \n \" M Partitions:\\n\" +\n _ReplacedPartitions\n );\n\n }\n else\n {\n Error ( \"Cancelled input! Ended script without changes.\");\n }\n}\n", "Tooltip": "", "ValidContexts": "Model" }, { "Id": 5, "Name": "Modelling\\Beginner\\Create Table Groups", "Enabled": "true", "Execute": "// Loop through all tables:\nforeach(var table in Model.Tables)\n{\n if (table is CalculationGroupTable)\n {\n table.TableGroup = \"Calculation Groups\";\n }\n else if (!table.UsedInRelationships.Any() && table.Measures.Any(m => m.IsVisible))\n {\n // Tables containing visible measures, but no relationships to other tables\n table.TableGroup = \"Measure Groups\";\n }\n else if (table.UsedInRelationships.All(r => r.FromTable == table) && table.UsedInRelationships.Any())\n {\n // Tables exclusively on the \"many\" side of relationships:\n table.TableGroup = \"Facts\";\n }\n else if (!table.UsedInRelationships.Any() && table is CalculatedTable && !table.Measures.Any())\n {\n // Tables without any relationships, that are Calculated Tables and do not have measures:\n table.TableGroup = \"Parameter Tables\";\n }\n else if (table.UsedInRelationships.Any(r => r.ToTable == table))\n {\n // Tables on the \"one\" side of relationships:\n table.TableGroup = \"Dimensions\";\n }\n else\n {\n // All other tables:\n table.TableGroup = \"Misc\";\n }\n}\n", "Tooltip": "", "ValidContexts": "Model" }, { "Id": 6, "Name": "Modelling\\Beginner\\Create Measure Table", "Enabled": "true", "Execute": "// Create a calculated table with a single column which is hidden:\nvar table = Model.AddCalculatedTable(\"Model Measures\", \"{0}\");\ntable.Columns[0].IsHidden = true;\n\n", "Tooltip": "", "ValidContexts": "Model" }, { "Id": 7, "Name": "Modelling\\Power BI\\Create Field Parameter table", "Enabled": "true", "Execute": "// Before running the script, select the measures or columns that you\n// would like to use as field parameters (hold down CTRL to select multiple\n// objects). Also, you may change the name of the field parameter table\n// below. NOTE: If used against Power BI Desktop, you must enable unsupported\n// features under File > Preferences (TE2) or Tools > Preferences (TE3).\nvar name = \"Parameter\";\n\nif(Selected.Columns.Count == 0 && Selected.Measures.Count == 0) throw new Exception(\"No columns or measures selected!\");\n\n// Construct the DAX for the calculated table based on the current selection:\nvar objects = Selected.Columns.Any() ? Selected.Columns.Cast() : Selected.Measures;\nvar dax = \"{\\n \" + string.Join(\",\\n \", objects.Select((c,i) => string.Format(\"(\\\"{0}\\\", NAMEOF('{1}'[{0}]), {2})\", c.Name, c.Table.Name, i))) + \"\\n}\";\n\n// Add the calculated table to the model:\nvar table = Model.AddCalculatedTable(name, dax);\n\n// In TE2 columns are not created automatically from a DAX expression, so \n// we will have to add them manually:\nvar te2 = table.Columns.Count == 0;\nvar nameColumn = te2 ? table.AddCalculatedTableColumn(name, \"[Value1]\") : table.Columns[\"Value1\"] as CalculatedTableColumn;\nvar fieldColumn = te2 ? table.AddCalculatedTableColumn(name + \" Fields\", \"[Value2]\") : table.Columns[\"Value2\"] as CalculatedTableColumn;\nvar orderColumn = te2 ? table.AddCalculatedTableColumn(name + \" Order\", \"[Value3]\") : table.Columns[\"Value3\"] as CalculatedTableColumn;\n\nif(!te2) {\n // Rename the columns that were added automatically in TE3:\n nameColumn.IsNameInferred = false;\n nameColumn.Name = name;\n fieldColumn.IsNameInferred = false;\n fieldColumn.Name = name + \" Fields\";\n orderColumn.IsNameInferred = false;\n orderColumn.Name = name + \" Order\";\n}\n// Set remaining properties for field parameters to work\n// See: https://twitter.com/markbdi/status/1526558841172893696\nnameColumn.SortByColumn = orderColumn;\nnameColumn.GroupByColumns.Add(fieldColumn);\nfieldColumn.SortByColumn = orderColumn;\nfieldColumn.SetExtendedProperty(\"ParameterMetadata\", \"{\\\"version\\\":3,\\\"kind\\\":2}\", ExtendedPropertyType.Json);\nfieldColumn.IsHidden = true;\norderColumn.IsHidden = true;", "Tooltip": "", "ValidContexts": "Measure, Column" }, { "Id": 8, "Name": "Analysis\\Beginner\\Count Rows in the Selected Table", "Enabled": "true", "Execute": "// Get table name\nstring _TableName = \n Selected.Table.DaxObjectFullName;\n\n// Count table rows\nstring _dax = \n \"{ FORMAT( COUNTROWS (\" + _TableName + \"), \\\"#,##0\\\" ) }\";\n\n// Evaluate DAX\nstring _TableRows = \n Convert.ToString(EvaluateDax( _dax ));\n\n// Return output in pop-up\nInfo ( \"Number of rows in \" + _TableName + \": \" + _TableRows);", "Tooltip": "", "ValidContexts": "Table" }, { "Id": 9, "Name": "Analysis\\Beginner\\Count the number of model objects by Type", "Enabled": "true", "Execute": "// This script counts objects in your model and displays them in a pop-up info box.\n// It does not write any changes to this model.\n//\n// Use this script when you open a new model and need a 'helicopter view' on the contents.\n//\n// Count calculation groups & calculation items\nint _calcgroups = 0;\nint _calcitems = 0;\nforeach ( var _calcgroup in Model.CalculationGroups )\n{\n _calcgroups = _calcgroups + 1;\n  foreach ( var _item in _calcgroup.CalculationItems )\n  {\n  _calcitems = _calcitems + 1;\n   }\n}\n\n// Count partitions and DAX parameters\nint _partitions = 0;\nint _whatifparameters = 0;\nint _fieldparameters = 0;\nforeach ( var _table in Model.Tables )\n{\n  foreach ( var _partition in _table.Partitions )\n  {\n string _type = Convert.ToString(_partition.SourceType);\n string _exp = Convert.ToString(_partition.Expression);\n if ( _type == \"M\" )\n  {\n _partitions = _partitions + 1;\n }\n else if ( _type == \"Calculated\" && _exp.Contains(\"NAMEOF\") )\n {\n _fieldparameters = _fieldparameters + 1;\n }\n else if ( _type == \"Calculated\" && _exp.Contains(\"GENERATESERIES\") )\n {\n _whatifparameters = _whatifparameters + 1;\n }\n \n   }\n}\n\n// Average measure length\ndecimal _numLines = 0;\ndecimal _numChars = 0;\nint _measures = Model.AllMeasures.Count();\nforeach ( var _measure in Model.AllMeasures )\n{\n _numLines = _numLines + _measure.Expression.Split(\"\\n\").Length;\n _numChars = _numChars + _measure.Expression.Length;\n}\n_numLines = Math.Round(_numLines / _measures, 1);\n_numChars = Math.Round(_numChars / _measures, 1);\n\n\n// Return the pop-up\nInfo ( \"In the model, we see the below objects:\\n\\n\"\n\n + \"-----------------------------------------\\n\"\n + \"Data Objects\\n\"\n + \"-----------------------------------------\\n\"\n + \" ├─ PQ Expressions: \" + Convert.ToString(Model.Expressions.Count()) + \"\\n\"\n + \" │\\n\"\n + \" └─ Tables: \" + Convert.ToString(Model.Tables.Count()) + \"\\n\"\n + \" ├─ Incremental Refresh Tables: \" + \n Convert.ToString(Model.Tables.Where(\n _ir => \n Convert.ToString(_ir.EnableRefreshPolicy) \n == \n \"True\").Count()) + \"\\n\"\n \n + \" │\\n\"\n + \" ├─ Calculated Tables: \" + \n Convert.ToString(\n Model.Tables.Where(\n _tables => \n Convert.ToString(_tables.Columns[0].Type) \n == \n \"CalculatedTableColumn\").Count()) + \"\\n\"\n\n + \" │ ├─ What if parameters: \" + \n Convert.ToString(_whatifparameters) + \"\\n\"\n + \" │ └─ Field parameters: \" + \n Convert.ToString(_fieldparameters) + \"\\n\"\n + \" │\\n\"\n + \" ├─ M Partitions: \" + \n Convert.ToString(_partitions) + \"\\n\"\n + \" │\\n\"\n + \" └─ Total Table Columns: \" + \n Convert.ToString(Model.AllColumns.Count()) + \"\\n\\n\"\n\n + \"-----------------------------------------\\n\"\n + \"DAX Objects\\n\"\n + \"-----------------------------------------\\n\"\n + \" ├─ Relationships: \" + \n Convert.ToString(Model.Relationships.Count()) + \"\\n\"\n + \" │ ├─ Bi-directional: \" + \n Convert.ToString(Model.Relationships.Where(\n _relationships => \n Convert.ToString(_relationships.CrossFilteringBehavior) \n == \n \"BothDirections\").Count()) + \"\\n\"\n\n + \" │ ├─ Many-to-Many: \" + \n Convert.ToString(Model.Relationships.Where(\n _relationships => \n Convert.ToString(_relationships.FromCardinality) \n == \n \"Many\" \n && \n Convert.ToString(_relationships.ToCardinality) \n == \n \"Many\").Count()) + \"\\n\"\n\n + \" │ ├─ One-to-One: \" + \n Convert.ToString(Model.Relationships.Where(\n _relationships => \n Convert.ToString(_relationships.FromCardinality) \n == \n \"One\" \n && \n Convert.ToString(_relationships.ToCardinality) \n == \n \"One\").Count()) + \"\\n\"\n\n + \" │ └─ Inactive: \" + \n Convert.ToString(Model.Relationships.Where(\n _relationships => \n Convert.ToString(_relationships.IsActive) \n == \n \"False\").Count()) + \"\\n\"\n\n + \" │\\n\"\n + \" ├─ Calculation Groups: \" + \n Convert.ToString(_calcgroups) + \"\\n\"\n + \" │ └─ Calculation Items: \" + \n Convert.ToString(_calcitems) + \"\\n\" \n + \" │\\n\"\n + \" ├─ Calculated Columns: \" + \n Convert.ToString(Model.AllColumns.Where(\n _columns => \n Convert.ToString(_columns.Type) \n == \n \"Calculated\").Count()) + \"\\n\"\n\n + \" │\\n\"\n + \" └─ Measures: \" + \n Convert.ToString(_measures) + \"\\n\" \n + \" └─ Avg. Lines of DAX: \" + \n Convert.ToString(_numLines) + \" Lines \\n\" \n + \" └─ Avg. Chars of DAX: \" + \n Convert.ToString(_numChars) + \" Characters \\n\\n\" \n \n + \"-----------------------------------------\\n\"\n + \"Other Objects\\n\"\n + \"-----------------------------------------\\n\"\n + \" ├─ Data Security Roles: \" + \n Convert.ToString(Model.Roles.Count()) + \"\\n\"\n + \" ├─ Explicit Data Sources: \" + \n Convert.ToString(Model.DataSources.Count()) + \"\\n\"\n + \" ├─ Perspectives: \" + \n Convert.ToString(Model.Perspectives.Count()) + \"\\n\"\n + \" └─ Translations: \" + \n Convert.ToString(Model.Cultures.Count()));", "Tooltip": "", "ValidContexts": "Model" }, { "Id": 10, "Name": "Formating\\Advanced\\Format Power Query", "Enabled": "true", "Execute": "// This script formats the Power Query (M Code) of any selected M Partition (not Shared Expression or Source Expression).\n// It will send an HTTPS POST request of the expression to the Power Query Formatter API and replace the code with the result.\n//\nusing System.Net.Http;\nusing System.Net.Http.Headers;\nusing System.Text;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\n\n// URL of the powerqueryformatter.com API\nstring powerqueryformatterAPI = \"https://m-formatter.azurewebsites.net/api/v2\";\n\n// HttpClient method to initiate the API call POST method for the URL\nHttpClient client = new HttpClient();\nHttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, powerqueryformatterAPI);\n\n// Get the M Expression of the selected partition\nstring partitionExpression = Selected.Partition.Expression;\n\n// Serialize the request body as a JSON object\nvar requestBody = JsonConvert.SerializeObject(\n new { \n code = partitionExpression, \n resultType = \"text\", \n lineWidth = 40, \n alignLineCommentsToPosition = true, \n includeComments = true\n });\n\n// Set the \"Content-Type\" header of the request to \"application/json\" and the encoding to UTF-8\nvar content = new StringContent(requestBody, Encoding.UTF8, \"application/json\");\ncontent.Headers.ContentType = new MediaTypeHeaderValue(\"application/json\");\n\n// Retrieve the response\nvar response = client.PostAsync(powerqueryformatterAPI, content).Result;\n\n// If the response is successful\nif (response.IsSuccessStatusCode)\n{\n // Get the result of the response\n var result = response.Content.ReadAsStringAsync().Result;\n\n // Parse the response JSON object from the string\n JObject data = JObject.Parse(result.ToString());\n\n // Get the formatted Power Query response\n string formattedPowerQuery = (string)data[\"result\"];\n\n ///////////////////////////////////////////////////////////////////////\n // OPTIONAL MANUAL FORMATTING\n // Manually add a new line and comment to each step\n var replace = new Dictionary \n { \n { \" //\", \"\\n\\n//\" }, \n { \"\\n #\", \"\\n\\n // Step\\n #\" }, \n { \"\\n Source\", \"\\n\\n // Data Source\\n Source\" }, \n { \"\\n Dataflow\", \"\\n\\n // Dataflow Connection Info\\n Dataflow\" }, \n {\"\\n Data =\", \"\\n\\n // Step\\n Data =\"}, \n {\"\\n Navigation =\", \"\\n\\n // Step\\n Navigation =\"}, \n {\"in\\n\\n // Step\\n #\", \"in\\n #\"}, \n {\"\\nin\", \"\\n\\n// Result\\nin\"} \n };\n\n // Replace the first string in the dictionary with the second\n var manuallyformattedPowerQuery = replace.Aggregate(\n formattedPowerQuery, \n (before, after) => before.Replace(after.Key, after.Value));\n\n // Replace the auto-formatted code with the manually formatted version\n formattedPowerQuery = manuallyformattedPowerQuery;\n ////////////////////////////////////////////////////////////////////////\n\n // Replace the unformatted M expression with the formatted expression\n Selected.Partition.Expression = formattedPowerQuery;\n\n // Pop-up to inform of completion\n Info(\"Formatted \" + Selected.Partition.Name);\n}\n\n// Otherwise return an error message\nelse\n{\nInfo(\n \"API call unsuccessful.\" +\n \"\\nCheck that you are selecting a partition with a valid M Expression.\"\n );\n}", "Tooltip": "", "ValidContexts": "Partition" }, { "Id": 11, "Name": "Modelling\\Beginner\\Create a new M Partition", "Enabled": "true", "Execute": "// This script creates a new M parameter in the 'Shared Expressions' of a model.\n//\n// Create a new shared expression called \"New Parameter\"\nModel.AddExpression( \n \"New Parameter\", \n @\"\n\"\"Parameter Text\"\" meta\n[\n\tIsParameterQuery = true,\n\tIsParameterQueryRequired = true,\n\tType = type text\n]\"\n);\n\n// Provides an output informing how to configure and use the parameter\nInfo ( \n \"Created a new Shared Expression called 'New Parameter', which is an M Parameter template.\" + \n \"\\n------------------------------------------------------\\n\" + \n \"To configure:\" +\n \"\\n------------------------------------------------------\\n \" + \n \"1. Replace the text 'New Parameter' with the desired parameter value\\n \" +\n \"2. Set the data type appropriately\\n \" +\n \"3. Replace any values found in the M partitions with the parameter reference.\" );", "Tooltip": "", "ValidContexts": "Model" }, { "Id": 12, "Name": "Modelling\\Beginner\\Edit Hidden Partitions", "Enabled": "true", "Execute": "Selected.Table.Partitions[0].Output();", "Tooltip": "", "ValidContexts": "Table" }, { "Id": 13, "Name": "Measures\\Beginner\\Find & Replace Substring in Measures", "Enabled": "true", "Execute": "#r \"System.Drawing\"\n\nusing System.Drawing;\nusing System.Text.RegularExpressions;\nusing System.Windows.Forms;\n\n// Hide the 'Running Macro' spinbox\nScriptHelper.WaitFormVisible = false;\n\n// Replace Selected.Measures with Model.AllMeasures to scan all measures\nvar _measures = Model.AllMeasures;\n // Optional: Replace _m.Expression with _m.Name to find & replace in names.\n\n// Initialize _find and _replace string variables\nstring _find = \"Find\";\nstring _replace = \"Replace\";\n\n// WinForms prompt to get Find & Replace input\nusing (Form prompt = new Form())\n{\n Font formFont = new Font(\"Segoe UI\", 11); \n\n // Prompt config\n prompt.AutoSize = true;\n prompt.MinimumSize = new Size(350, 120);\n prompt.Text = \"Find and Replace Dialog\";\n prompt.StartPosition = FormStartPosition.CenterScreen;\n\n // Set the AutoScaleMode property to Dpi\n prompt.AutoScaleMode = AutoScaleMode.Dpi;\n\n // Find: label\n Label findLabel = new Label() { Text = \"Find:\" };\n findLabel.Location = new Point(20, 20);\n findLabel.Width = 80;\n findLabel.Font = formFont;\n\n // Textbox for inputing the substring text\n TextBox findBox = new TextBox();\n findBox.Width = 200;\n findBox.Location = new Point(findLabel.Location.X + findLabel.Width + 20, findLabel.Location.Y - 4);\n findBox.SelectedText = \"Find this Text\";\n findBox.Font = formFont;\n\n // Replace: label\n Label replaceLabel = new Label() { Left = 20, Top = 60, Text = \"Replace:\" };\n replaceLabel.Width = 80;\n replaceLabel.Font = formFont;\n\n // Textbox for inputting the substring text\n TextBox replaceBox = new TextBox() { Left = replaceLabel.Right + 20, Top = replaceLabel.Location.Y - 4, Width = findBox.Width };\n replaceBox.SelectedText = \"Replace with this Text\";\n replaceBox.Font = formFont;\n\n // OK Button\n Button okButton = new Button() { Text = \"OK\", Left = 20, Width = 75, Top = replaceBox.Location.Y + replaceBox.Height + 20 };\n okButton.MinimumSize = new Size(75, 25);\n okButton.AutoSize = true;\n okButton.Font = formFont;\n\n // Cancel Button\n Button cancelButton = new Button() { Text = \"Cancel\", Left = okButton.Location.X + okButton.Width + 10, Top = okButton.Location.Y };\n cancelButton.MinimumSize = new Size(75, 25);\n cancelButton.AutoSize = true;\n cancelButton.Font = formFont;\n\n // Button actions\n okButton.Click += (sender, e) => { _find = findBox.Text; _replace = replaceBox.Text; prompt.DialogResult = DialogResult.OK; };\n cancelButton.Click += (sender, e) => { prompt.DialogResult = DialogResult.Cancel; };\n\n prompt.AcceptButton = okButton;\n prompt.CancelButton = cancelButton;\n\n prompt.Controls.Add(findLabel);\n prompt.Controls.Add(findBox);\n prompt.Controls.Add(replaceLabel);\n prompt.Controls.Add(replaceBox);\n prompt.Controls.Add(okButton);\n prompt.Controls.Add(cancelButton);\n\n // The user clicked OK, so perform the find-and-replace logic\n if (prompt.ShowDialog() == DialogResult.OK)\n {\n \n int _occurrences = 0;\n var _ReplacedList = new List();\n \n foreach (var _m in _measures)\n {\n if (_m.Expression != _m.Expression.Replace(_find, _replace))\n {\n try\n {\n // Count number of occurrences of _find substring in the string\n string _pattern = Regex.Escape(_find);\n _occurrences = Regex.Matches(_m.Expression, _pattern).Count;\n }\n catch\n {\n // If it's not found there are 0 occurrences\n _occurrences = 0;\n }\n \n // Perform the Find/Replace\n _m.Expression = _m.Expression.Replace(_find, _replace);\n _ReplacedList.Add(_m.DaxObjectName);\n }\n }\n \n // Create a list of all the measures replaced\n string _Replaced = _ReplacedList.Count > 0\n ? \"\\n\\nMeasures with Replacements:\\n • \" + string.Join(\"\\n • \", _ReplacedList)\n : \"\";\n \n // Return a success Info box pop-up\n Info(\n \"Replaced \" + \n _occurrences + \n \" occurrences of '\" + \n _find + \n \"' with '\" + \n _replace + \n \"'\" + \n _Replaced);\n }\n else\n {\n Error(\"Find/Replace cancelled!\");\n }\n}", "Tooltip": "", "ValidContexts": "Model" } ] }